package web import ( "context" "errors" "io" "log" "net/http/httptest" "os" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/agentapi" "gitea.dooplex.hu/admin/felhom-controller/internal/backup" "gitea.dooplex.hu/admin/felhom-controller/internal/config" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" "gitea.dooplex.hu/admin/felhom-controller/internal/stacks" ) // R-499 — an app on the system disk was told „már szerepelnek a teljes rendszermentésben (PBS)" and // „nincs külön teendő" on every box, including one whose only whole-system copy sat on the SAME disk // (measured 2026-09-14). The page must now say what is true for THIS box. // tier2PageServer builds a real Server with one deployed, driveless app ("privatebin") and the // agent's tier/disk answers injected through the production seams, then renders the real handler. func tier2PageServer(t *testing.T, tiers func(context.Context) (agentapi.TiersResponse, error), disks []agentapi.DiskInfo) string { t.Helper() lg := log.New(io.Discard, "", 0) dir := t.TempDir() cfg := &config.Config{} cfg.Paths.StacksDir = filepath.Join(dir, "stacks") cfg.Paths.DataDir = filepath.Join(dir, "data") cfg.Paths.SystemDataPath = filepath.Join(dir, "system") cfg.Stacks.ComposeCommand = "docker compose" cfg.Backup.Enabled = true app := filepath.Join(cfg.Paths.StacksDir, "privatebin") if err := os.MkdirAll(app, 0o755); err != nil { t.Fatal(err) } os.WriteFile(filepath.Join(app, "docker-compose.yml"), []byte("services:\n privatebin:\n image: privatebin/pdo:2.0.6\n"), 0o644) os.WriteFile(filepath.Join(app, "app.yaml"), []byte("deployed: true\n"), 0o600) sett, err := settings.Load(filepath.Join(dir, "settings.json"), lg) if err != nil { t.Fatal(err) } sm, err := stacks.NewManager(cfg, lg) if err != nil { t.Fatal(err) } if err := sm.ScanStacks(); err != nil { t.Fatal(err) } bm := backup.NewManager(cfg, sett, lg) bm.SetStackProvider(&blockProvider{hdd: ""}) // driveless: IsHDDApp false s := &Server{cfg: cfg, settings: sett, stackMgr: sm, backupMgr: bm, logger: lg, version: "test"} s.tiersFn = tiers s.disksFn = func(context.Context) (agentapi.DisksResponse, error) { return agentapi.DisksResponse{Disks: disks}, nil } s.loadTemplates() w := httptest.NewRecorder() s.tier2ConfigPageHandler(w, httptest.NewRequest("GET", "/stacks/privatebin/backup", nil), "privatebin") if w.Code != 200 { t.Fatalf("page answered %d", w.Code) } return w.Body.String() } func primaryTier(target string) func(context.Context) (agentapi.TiersResponse, error) { return func(context.Context) (agentapi.TiersResponse, error) { return agentapi.TiersResponse{Tiers: []agentapi.BackupTierInfo{{Target: target, Primary: true}}}, nil } } // COMPANION RED-PROOF: rendering the old sentence for every branch (the pre-fix template) makes the // same_disk case fail on `promises the backup protects this app`. func TestR499_Tier2PageSaysWhatIsTrueForThisBox(t *testing.T) { ownDrive := agentapi.DiskInfo{Name: "mentes", MountPath: "/mnt/mentes", GuestPath: "/mnt/felhom-drives/mentes", Role: "user-data", BackupTarget: true} cases := []struct { name, want string tiers func(context.Context) (agentapi.TiersResponse, error) disks []agentapi.DiskInfo }{ {"same disk (the measured box)", "same_disk", primaryTier("local"), nil}, {"own drive", "protected", primaryTier("felhom-backup"), []agentapi.DiskInfo{ownDrive}}, {"configured drive gone", "absent", primaryTier("felhom-backup"), nil}, {"agent not askable", "unknown", func(context.Context) (agentapi.TiersResponse, error) { return agentapi.TiersResponse{}, errors.New("down") }, nil}, } for _, c := range cases { body := tier2PageServer(t, c.tiers, c.disks) if !strings.Contains(body, `data-system-backup="`+c.want+`"`) { t.Errorf("%s: want branch %q on the page", c.name, c.want) } // ASCII fragments (ui-hungarian rule): "(PBS)" and "nincs k" of „nincs külön teendő". if strings.Contains(body, "(PBS)") { t.Errorf("%s: the page still names PBS, which this box may not have", c.name) } promises := strings.Contains(body, "nincs k") if c.want == "protected" && !promises { t.Errorf("%s: control — the protected branch must still say there is nothing to do", c.name) } if c.want != "protected" && promises { t.Errorf("%s: promises the backup protects this app (\"nothing to do\") on a box where it may not", c.name) } } }