package web import ( "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/agentapi" "gitea.dooplex.hu/admin/felhom-controller/internal/i18n" ) // msgIn returns the real message bundle's text for lang — the same lookup the handler's msgLang does. func msgIn(t *testing.T, lang string) func(string) string { t.Helper() b, err := i18n.Shared() if err != nil { t.Fatalf("bundle: %v", err) } return func(key string) string { return b.Msg(lang, key) } } // enrichHostStorageTargets must produce a STABLE order regardless of how the agent enumerated the // storages (the #host-storage-bars reorder bug, item 2), and attach a friendly label + purpose per // entry without ever mutating the raw PVE id (Name). func TestEnrichHostStorageTargets_OrderAndLabels(t *testing.T) { // Deliberately shuffled relative to the desired display order. targets := []agentapi.StorageTarget{ {Name: "felhom-pbs", Type: "pbs"}, {Name: "local", Type: "local"}, {Name: "felhom-usb", Type: "usb"}, {Name: "local-lvm", Type: "lvmthin"}, } enrichHostStorageTargets(targets, msgIn(t, "hu")) wantOrder := []string{"felhom-usb", "local-lvm", "local", "felhom-pbs"} for i, want := range wantOrder { if targets[i].Name != want { t.Fatalf("position %d = %q, want %q (full order: %v)", i, targets[i].Name, want, names(targets)) } } // Friendly labels attached; raw ids untouched. for _, tgt := range targets { if tgt.Label == "" || tgt.Purpose == "" { t.Errorf("%s (%s): missing label/purpose (label=%q purpose=%q)", tgt.Name, tgt.Type, tgt.Label, tgt.Purpose) } } if targets[0].Label != "Külső adattároló (USB)" { t.Errorf("usb label = %q", targets[0].Label) } if targets[1].Label != "Belső SSD – rendszer és alkalmazások" { t.Errorf("lvmthin label = %q", targets[1].Label) } } // A second run with the same input must yield the same order (determinism / idempotence). func TestEnrichHostStorageTargets_Stable(t *testing.T) { mk := func() []agentapi.StorageTarget { return []agentapi.StorageTarget{ {Name: "b-usb", Type: "usb"}, {Name: "a-usb", Type: "usb"}, {Name: "local-lvm", Type: "lvmthin"}, } } a, b := mk(), mk() enrichHostStorageTargets(a, msgIn(t, "hu")) enrichHostStorageTargets(b, msgIn(t, "hu")) for i := range a { if a[i].Name != b[i].Name { t.Fatalf("non-deterministic at %d: %q vs %q", i, a[i].Name, b[i].Name) } } // Within the same tier, alphabetical by id. if a[0].Name != "a-usb" || a[1].Name != "b-usb" { t.Errorf("within-tier order = %v, want a-usb,b-usb first", names(a)) } } // An unrecognised type falls back to the raw id and an empty purpose. func TestEnrichHostStorageTargets_UnknownType(t *testing.T) { targets := []agentapi.StorageTarget{{Name: "weird-store", Type: "zfspool"}} enrichHostStorageTargets(targets, msgIn(t, "hu")) if targets[0].Label != "weird-store" || targets[0].Purpose != "" { t.Errorf("unknown type: label=%q purpose=%q, want raw id + empty", targets[0].Label, targets[0].Purpose) } } func names(ts []agentapi.StorageTarget) []string { out := make([]string, len(ts)) for i, t := range ts { out[i] = t.Name } return out } // W26 (rehearsal 2026-10-20 B6 #16): on the English dashboard the monitoring page showed the storage // labels and purposes in Hungarian. Every known storage type must come out in the reader's language: // English text for "en", and never a key name (a missing key) in either language. func TestEnrichHostStorageTargets_FollowsTheLanguage(t *testing.T) { mk := func() []agentapi.StorageTarget { return []agentapi.StorageTarget{ {Name: "u", Type: "usb"}, {Name: "d", Type: "local-dir"}, {Name: "local-lvm", Type: "lvmthin"}, {Name: "local", Type: "local"}, {Name: "felhom-pbs", Type: "pbs"}, {Name: "n", Type: "nfs"}, {Name: "c", Type: "cifs"}, } } en, hu := mk(), mk() enrichHostStorageTargets(en, msgIn(t, "en")) enrichHostStorageTargets(hu, msgIn(t, "hu")) for i := range en { for _, s := range []string{en[i].Label, en[i].Purpose, hu[i].Label, hu[i].Purpose} { if s == "" || strings.HasPrefix(s, "host_storage.") { t.Errorf("%s (%s): missing text %q", en[i].Name, en[i].Type, s) } } if en[i].Label == hu[i].Label || en[i].Purpose == hu[i].Purpose { t.Errorf("%s (%s): the English text is the Hungarian one: %q / %q", en[i].Name, en[i].Type, en[i].Label, en[i].Purpose) } } for _, tg := range en { if tg.Type == "lvmthin" && tg.Label != "Internal SSD – system and apps" { t.Errorf("lvmthin English label = %q", tg.Label) } } }