mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-17 15:19:00 +02:00
* 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>
136 lines
3.7 KiB
Go
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)
|
|
}
|
|
}
|