80e6ad8c47
gates / gates (push) Successful in 26s
R-650: internal/dockerexec — every docker exec routed through it; under go test a real docker is refused (opt-in FELHOM_TEST_REAL_DOCKER=1; a stub under the temp dir is allowed). api/stacks/web tests run under a silent stub (TestMain). TestR650_NoBareDockerExec pins it repo-wide. R-640: a dump without its engine's completion marker is refused before the first mutation (unit + off-site restore) and again before any load. R-499: the Tier-2 page's system-disk sentence has four true branches. R-518: the backup button states the measured ~8 min stop. R-626: measured on 9202, not reproduced. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
106 lines
4.4 KiB
Go
106 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|