package web import ( "io" "log" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/config" "gitea.dooplex.hu/admin/felhom-controller/internal/monitor" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) // R-516 item 8 — a disconnected drive showed TWO banners on every page: its own // (alert.storage.disconnected, in the household's language) and the health check's Hungarian // „Meghajtó leválasztva" warning for the same drive. The dedicated banner is the one kept. // // COMPANION RED-PROOF: drop the WarnKindStorageDisconnected `continue` in AlertManager.Refresh — the // first half fails with two banners. func TestR516_DisconnectedDriveHasOneBanner(t *testing.T) { const warn = "Meghajtó leválasztva: Külső (/mnt/usb)" hr := &monitor.HealthReport{Status: "warn", Warnings: []string{warn, "CPU usage high: 91%"}, WarningKinds: []string{monitor.WarnKindStorageDisconnected, ""}} cfg := &config.Config{} cfg.Hub.Enabled = false sp := []settings.StoragePath{{Path: "/mnt/usb", Label: "Külső", Disconnected: true}} count := func(alerts []Alert) (dedicated, health, cpu int) { for _, a := range alerts { switch { case strings.HasPrefix(a.ID, "storage-disconnected-"): dedicated++ case a.Message == warn: health++ case a.Message == "CPU usage high: 91%": cpu++ } } return } am := NewAlertManager(log.New(io.Discard, "", 0)) am.Refresh(hr, cfg, nil, false, "", sp) d, h, c := count(am.GetAlerts("hu")) if d != 1 || h != 0 { t.Errorf("a disconnected drive must have exactly one banner: dedicated=%d health-warning=%d", d, h) } if c != 1 { t.Errorf("positive control: another health warning must still reach the banners (got %d)", c) } // Without the storage list there is no dedicated banner, so the health warning is the only one // that can say it — it must stay. am2 := NewAlertManager(log.New(io.Discard, "", 0)) am2.Refresh(hr, cfg, nil, false, "") if d, h, _ := count(am2.GetAlerts("hu")); d != 0 || h != 1 { t.Errorf("without the storage list the health warning is the only banner: dedicated=%d health-warning=%d", d, h) } } // R-516 item 7 — the storage page printed the disconnect time as the raw stored RFC3339 UTC string // („Leválasztva: 2026-09-14T19:58:02Z"). It now goes through fmtTimeStr, the page's local-time format, // in both languages. // // COMPANION RED-PROOF: put `{{.DisconnectedAt}}` back in storage.levalasztva_2 (hu.json) — the hu half // fails with the raw string. func TestR516_DisconnectTimeIsLocalNotRawUTC(t *testing.T) { const raw = "2026-09-14T19:58:02Z" for _, tc := range []struct{ lang, prefix string }{{"hu", "Leválasztva: "}, {"en", "Disconnected: "}} { d := i18nLayoutData("storage", "Tárhely") paths := i18nStoragePaths() paths[0]["DisconnectedAt"] = raw d["StoragePaths"] = paths h := renderBackupPageLang(t, tc.lang, "storage", d) if strings.Contains(h, raw) { t.Errorf("%s: the raw UTC timestamp reached the page", tc.lang) } if want := tc.prefix + fmtRFC3339Local(raw); !strings.Contains(h, want) { t.Errorf("%s: the page must carry %q", tc.lang, want) } } } // R-516 item 10 — a health warning with a dashboard sentence shows it in the household's language; // a warning without one shows its wire text verbatim (the fail-open fallback). func TestR516_HealthBannersFollowTheHousehold(t *testing.T) { hr := &monitor.HealthReport{Status: "fail", Issues: []string{"SSD disk usage critical: 95%"}, IssueMsgs: []monitor.MsgRef{{Key: "health.system_disk_critical", Args: []interface{}{95.0}}}, Warnings: []string{"SSD disk usage high: 90%", "Docker: something"}, WarningKinds: []string{"", ""}, WarningMsgs: []monitor.MsgRef{{Key: "health.system_disk_high", Args: []interface{}{90.0}}, {}}} cfg := &config.Config{} cfg.Hub.Enabled = false am := NewAlertManager(log.New(io.Discard, "", 0)) am.Refresh(hr, cfg, nil, false, "") got := map[string][]string{} for _, lang := range []string{"hu", "en"} { for _, a := range am.GetAlerts(lang) { if strings.HasPrefix(a.ID, "health-") { got[lang] = append(got[lang], a.Message) } } } want := map[string][]string{ "hu": {"A rendszer tárhelye majdnem megtelt: 95% foglalt", "A rendszer tárhelye fogy: 90% foglalt", "Docker: something"}, "en": {"System storage is almost full: 95% used", "System storage is filling up: 90% used", "Docker: something"}, } for lang, w := range want { if strings.Join(got[lang], "|") != strings.Join(w, "|") { t.Errorf("%s banners:\n got %q\n want %q", lang, got[lang], w) } } }