package api import ( "io" "log" "net/http/httptest" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/i18n" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) // Localisation slice 2 (R-557), scenario S2 — the JSON the page's own script reads follows the // household's language, and the Hungarian answer is byte-identical to what it was. // // This is the half of the dashboard a template test cannot see. Since slice 1 the PAGE has been // English, but every sentence that arrives by fetch — a refused deploy, a start blocked for memory, // "the backup has started" — was a Hungarian literal in internal/api. An English household clicked an // English button and was answered in Hungarian. // // The Hungarian side is a parity measurement, not a translation check: the `want` strings below are // the literals that stood at the call sites at 736f54b49610. func langRouter(t *testing.T, lang string) *Router { t.Helper() lg := log.New(io.Discard, "", 0) sett, err := settings.Load(filepath.Join(t.TempDir(), "settings.json"), lg) if err != nil { t.Fatal(err) } if lang != "" { if err := sett.SetLanguage(lang); err != nil { t.Fatal(err) } } return &Router{sett: sett, logger: lg} } func TestAPIMessagesFollowTheHouseholdLanguage(t *testing.T) { cases := []struct { key, hu, en string args []interface{} }{ {key: "api.deploy.started", hu: "Telepítés elindítva – az állapot a kártyán követhető", en: "The install has started – the card shows how it is going"}, {key: "api.backup.already_running", hu: "Mentés már folyamatban", en: "A backup is already running"}, {key: "api.start.drive_missing", args: []interface{}{"Kulso HDD"}, hu: "A(z) Kulso HDD tárhely jelenleg nem elérhető — az alkalmazás nem indítható, amíg a meghajtó vissza nem csatlakozik.", en: "Storage Kulso HDD cannot be reached right now — the app cannot start until the drive is back."}, {key: "api.start.not_enough_memory", args: []interface{}{512, 128, 1400, 1528}, hu: "Nincs elég memória az indításhoz. Szükséges: 512 MB, elérhető: 128 MB (használt: 1400 MB / használható: 1528 MB)", en: "There is not enough memory to start. Needs 512 MB, 128 MB is free (1400 MB used of 1528 MB usable)"}, } for _, c := range cases { t.Run(c.key, func(t *testing.T) { // hu: the saved setting, no query override — the ordinary household. r := langRouter(t, "hu") req := httptest.NewRequest("POST", "/api/stacks/x/start", nil) if got := r.msg(req, c.key, c.args...); got != c.hu { t.Errorf("Hungarian answer moved (parity)\n got %q\n want %q", got, c.hu) } // en: the same request shape on a box switched to English. r = langRouter(t, "en") if got := r.msg(req, c.key, c.args...); got != c.en { t.Errorf("English answer\n got %q\n want %q", got, c.en) } }) } } // TestAPILangForPrecedence — `?lang=` overrides the saved setting (the testing door slice 0 built), // an unsupported value is ignored rather than honoured, and a box with no setting answers Hungarian. func TestAPILangForPrecedence(t *testing.T) { cases := []struct{ saved, query, want string }{ {"hu", "", "hu"}, {"en", "", "en"}, {"hu", "en", "en"}, {"en", "hu", "hu"}, {"en", "de", "en"}, // unsupported: ignored, the saved setting stands {"", "", i18n.Default}, } for _, c := range cases { r := langRouter(t, c.saved) url := "/api/stacks" if c.query != "" { url += "?lang=" + c.query } if got := r.langFor(httptest.NewRequest("GET", url, nil)); got != c.want { t.Errorf("saved=%q query=%q: got %q, want %q", c.saved, c.query, got, c.want) } } } // TestGeoCountryNamesFollowTheLanguage — the picker's 237 country names. The cloudflare TABLE is not // translated (it validates codes, and internal/report puts codes on the wire); only the displayed // name is, and the list is re-sorted afterwards because alphabetical order is per language. func TestGeoCountryNamesFollowTheLanguage(t *testing.T) { r := langRouter(t, "hu") for _, c := range []struct{ lang, code, want string }{ {"hu", "DE", "Németország"}, {"en", "DE", "Germany"}, {"hu", "US", "Egyesült Államok"}, {"en", "US", "United States"}, {"hu", "AO", "Angola"}, // identical in both languages — must still resolve, not fall through {"en", "AO", "Angola"}, } { if got := r.countryName(c.lang, c.code, "FALLBACK"); got != c.want { t.Errorf("%s/%s: got %q, want %q", c.lang, c.code, got, c.want) } } // A code the bundle does not carry keeps the table's own name rather than showing a key. if got := r.countryName("en", "ZZ", "Ismeretlen"); got != "Ismeretlen" { t.Errorf("an unknown code must keep the table name, got %q", got) } // And the English list really is sorted by the English names: „Németország" sorts under N in // Hungarian and Germany under G, so the two orders must differ. if strings.EqualFold(r.countryName("hu", "DE", ""), r.countryName("en", "DE", "")) { t.Error("the country names did not change with the language at all") } }