package web
import (
"html"
"io"
"log"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
"gitea.dooplex.hu/admin/felhom-controller/internal/util"
)
// R-606 (v0.264.0) — every sentence of the update path reaches the page in the READER's language, on
// BOTH pages that carry it (the app page and the apps list): the phase label, the update error and the
// hold sentence. Asserted both ways — the English present AND the Hungarian gone — because a page
// carrying both would pass a presence check (the composed-sentence class, R-573/R-590/R-596/R-598).
//
// COMPANION RED-PROOF (REPORT.md): make localeFuncs' updateErrorText return .UpdateError — the English
// page carries the Hungarian sentence and this test fails.
func TestR606_UpdateSentencesFollowTheReader(t *testing.T) {
s := i18nTestServer(t)
b := backup.NewManager(s.cfg, s.settings, log.New(io.Discard, "", 0))
s.backupMgr = b
at := time.Date(2026, 9, 23, 8, 0, 0, 0, time.UTC)
if err := b.HoldAfterFailedUpdateHolding("kimai", at, at.Add(-6*time.Hour), backup.UpdateTierLocal, b.UpdateCopyHolds("kimai", backup.UpdateTierLocal), ""); err != nil {
t.Fatal(err)
}
_, holdHU := b.RestoreHoldForLang("kimai", "hu")
errStack := i18nStack("gokapi", "Go Kapi", stacks.StateRunning, true)
errStack.UpdateErrorKey, errStack.UpdateErrorArgs = "update.error.pull_failed", nil
errStack.UpdateError = stacks.MsgUpdatePullFailed
upd := i18nStack("romm", "Rom Manager", stacks.StateRunning, true)
upd.Updating, upd.UpdatePhase, upd.UpdatePhaseLabel = true, stacks.UpdatePhaseCopying, stacks.UpdatePhaseLabel(stacks.UpdatePhaseCopying)
held := i18nStack("kimai", "Kimai Time", stacks.StateStopped, true)
held.HoldReason = holdHU
pages := []i18nCase{
{"r606_list", "stacks", func() map[string]interface{} {
d := i18nLayoutData("stacks", "Alkalmazások")
d["Stacks"] = []stacks.Stack{errStack, upd, held}
for _, k := range []string{"Subdomains", "MissingStorage", "NetworkStubs", "NetworkWarnings", "StorageLabels"} {
d[k] = map[string]string{}
}
return d
}},
{"r606_app_error", "app_info", func() map[string]interface{} {
d := i18nLayoutData("stacks", "Go Kapi")
st := errStack
d["Stack"], d["Meta"], d["AppInfo"] = &st, st.Meta, st.Meta.AppInfo
return d
}},
{"r606_app_held", "app_info", func() map[string]interface{} {
d := i18nLayoutData("stacks", "Kimai")
st := held
d["Stack"], d["Meta"], d["AppInfo"] = &st, st.Meta, st.Meta.AppInfo
return d
}},
{"r606_app_updating", "app_info", func() map[string]interface{} {
d := i18nLayoutData("stacks", "RomM")
st := upd
d["Stack"], d["Meta"], d["AppInfo"] = &st, st.Meta, st.Meta.AppInfo
return d
}},
}
type want struct{ en, hu string }
sentences := map[string][]want{
"r606_list": {
{util.Text("en", "update.error.pull_failed"), stacks.MsgUpdatePullFailed},
{"Copying the data before the update…", "Az adatok másolása a frissítés előtt…"},
{"own drive", "saját meghajtó"},
{"holds the settings, the database and the data volumes", "a beállításokat, az adatbázist és az adatköteteket tartalmazza"},
},
"r606_app_error": {{util.Text("en", "update.error.pull_failed"), stacks.MsgUpdatePullFailed}},
"r606_app_held": {{"The update of kimai at 2026-09-23 10:00 did not succeed", "A(z) kimai frissítése 2026-09-23 10:00-kor nem sikerült"}},
"r606_app_updating": {{"Copying the data before the update…", "Az adatok másolása a frissítés előtt…"}},
}
for _, c := range pages {
en := html.UnescapeString(renderI18nCase(t, s, "en", c))
hu := html.UnescapeString(renderI18nCase(t, s, "hu", c))
for _, w := range sentences[c.name] {
if !strings.Contains(en, w.en) {
t.Errorf("%s: the English page lacks %q", c.name, w.en)
}
if strings.Contains(en, w.hu) {
t.Errorf("%s: the Hungarian %q must be GONE from the English page", c.name, w.hu)
}
if !strings.Contains(hu, w.hu) {
t.Errorf("%s: the Hungarian page lacks %q", c.name, w.hu)
}
}
}
}