Files
multica/server/internal/handler/user_language_test.go
김보경/DAXTF 5aa4fb7487 MUL-2760: feat(i18n): add Korean locale support (#3369)
* feat: add korean locale support

* feat(i18n): localize Korean landing page

* fix(i18n): refine Korean landing copy

* fix(i18n): refine Korean translations

* fix(i18n): translate Korean landing subpages

* fix(i18n): route Korean landing docs links

* fix(i18n): add Korean use case content

* fix(i18n): polish Korean locale copy

* fix(i18n): improve Korean landing copy

* fix(onboarding): persist Korean helper artifacts

Co-authored-by: multica-agent <github@multica.ai>

* fix(web): add use case locale fallback

Co-authored-by: multica-agent <github@multica.ai>

* Align Korean pull requests wording

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): dedupe docs href helper

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): localize changelog dates

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): prerender Korean fallback pages

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): align fallback hreflang metadata

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): preserve Chinese CJK font fallback order

Co-authored-by: multica-agent <github@multica.ai>

* chore(onboarding): update localized comment wording

Co-authored-by: multica-agent <github@multica.ai>

* test(i18n): harden CJK font fallback assertions

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): keep Chinese font fallbacks first

Co-authored-by: multica-agent <github@multica.ai>

* test(i18n): harden locale fallback coverage

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-29 15:16:22 +08:00

136 lines
3.7 KiB
Go

package handler
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func newLanguageTestUser(t *testing.T, email string) string {
t.Helper()
ctx := context.Background()
var userID string
if err := testPool.QueryRow(ctx,
`INSERT INTO "user" (name, email) VALUES ($1, $2) RETURNING id`,
"Language Test", email,
).Scan(&userID); err != nil {
t.Fatalf("insert test user: %v", err)
}
t.Cleanup(func() {
testPool.Exec(ctx, `DELETE FROM "user" WHERE id = $1`, userID)
})
return userID
}
func newPatchMeRequest(userID, body string) *http.Request {
req := httptest.NewRequest("PATCH", "/api/me", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID)
return req
}
func TestUpdateMeAcceptsLanguage(t *testing.T) {
userID := newLanguageTestUser(t, "lang-set@multica.ai")
w := httptest.NewRecorder()
req := newPatchMeRequest(userID, `{"language":"zh-Hans"}`)
testHandler.UpdateMe(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
var lang *string
if err := testPool.QueryRow(context.Background(),
`SELECT language FROM "user" WHERE id = $1`, userID,
).Scan(&lang); err != nil {
t.Fatalf("lookup user: %v", err)
}
if lang == nil || *lang != "zh-Hans" {
t.Fatalf("expected language=zh-Hans, got %v", lang)
}
var resp map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode response: %v", err)
}
if got, _ := resp["language"].(string); got != "zh-Hans" {
t.Fatalf("expected response language=zh-Hans, got %v", resp["language"])
}
}
func TestUpdateMeAcceptsKoreanLanguage(t *testing.T) {
userID := newLanguageTestUser(t, "lang-ko@multica.ai")
w := httptest.NewRecorder()
req := newPatchMeRequest(userID, `{"language":"ko"}`)
testHandler.UpdateMe(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
var resp map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode response: %v", err)
}
if got, _ := resp["language"].(string); got != "ko" {
t.Fatalf("expected response language=ko, got %v", resp["language"])
}
}
func TestUpdateMeRejectsUnsupportedLanguage(t *testing.T) {
userID := newLanguageTestUser(t, "lang-reject@multica.ai")
w := httptest.NewRecorder()
req := newPatchMeRequest(userID, `{"language":"<script>"}`)
testHandler.UpdateMe(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d: %s", w.Code, w.Body.String())
}
var lang *string
if err := testPool.QueryRow(context.Background(),
`SELECT language FROM "user" WHERE id = $1`, userID,
).Scan(&lang); err != nil {
t.Fatalf("lookup user: %v", err)
}
if lang != nil {
t.Fatalf("expected language unchanged (NULL), got %v", *lang)
}
}
// COALESCE semantics: omitting language must NOT clear an existing value.
func TestUpdateMePreservesLanguageWhenNotProvided(t *testing.T) {
userID := newLanguageTestUser(t, "lang-preserve@multica.ai")
if _, err := testPool.Exec(context.Background(),
`UPDATE "user" SET language = 'en' WHERE id = $1`, userID,
); err != nil {
t.Fatalf("preset language: %v", err)
}
w := httptest.NewRecorder()
req := newPatchMeRequest(userID, `{"name":"Updated Name"}`)
testHandler.UpdateMe(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
var lang *string
if err := testPool.QueryRow(context.Background(),
`SELECT language FROM "user" WHERE id = $1`, userID,
).Scan(&lang); err != nil {
t.Fatalf("lookup user: %v", err)
}
if lang == nil || *lang != "en" {
t.Fatalf("expected language=en preserved, got %v", lang)
}
}