package web import ( "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/agentapi" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) func TestAgentWhere(t *testing.T) { cases := map[string]string{ "/mnt/felhom-drives/felhom-usb": "/mnt/felhom-usb", // stable → raw "/mnt/felhom-usb": "/mnt/felhom-usb", // legacy raw → raw (idempotent) "/mnt/felhom-drives/x": "/mnt/x", } for in, want := range cases { if got := agentWhere(in); got != want { t.Errorf("agentWhere(%q) = %q, want %q", in, got, want) } } } func TestStablePathForName(t *testing.T) { if got := stablePathForName("felhom-usb"); got != "/mnt/felhom-drives/felhom-usb" { t.Errorf("stablePathForName = %q", got) } } // TestPlanDriveGates pins the gate's pure decision across the four meaningful states. // // COMPANION GUARD: a trivial impl that gates every absent path regardless of the disconnected flag would // re-stop an already-disconnected drive (and never return it); one that ignores presence would never // gate. Both fail here. func TestPlanDriveGates(t *testing.T) { paths := []settings.StoragePath{ {Path: "/mnt/felhom-drives/usb"}, // present + connected → no action {Path: "/mnt/felhom-drives/flash"}, // ABSENT + connected → STOP {Path: "/mnt/felhom-drives/back", Disconnected: true}, // present + disconnected → RETURN {Path: "/mnt/felhom-drives/gone", Disconnected: true}, // ABSENT + disconnected → no action (steady) {Path: "/mnt/felhom-drives/dead", Decommissioned: true}, // decommissioned → never touched {Path: "/mnt/sys_drive/felhom-data"}, // INTERNAL SSD (absent from agent) → never gated } disks := []agentapi.DiskInfo{ // present = BoundUnderParent (the usable-in-guest signal), not merely State==attached. {MountPath: "/mnt/usb", GuestPath: "/mnt/felhom-drives/usb", State: "attached", BoundUnderParent: true}, {MountPath: "/mnt/back", GuestPath: "/mnt/felhom-drives/back", State: "attached", BoundUnderParent: true}, // flash reports State=attached but NOT bound under parent yet → still treated ABSENT (the // reboot-ordering case: raw drive mounted, agent hasn't bound it under the parent yet). {MountPath: "/mnt/flash", GuestPath: "/mnt/felhom-drives/flash", State: "attached", BoundUnderParent: false}, // gone + dead report NO present disk } actions := map[string]gateAction{} for _, a := range planDriveGates(paths, disks) { actions[a.Path] = a } if len(actions) != 2 { t.Fatalf("expected exactly 2 actions (stop flash, return back), got %d: %+v", len(actions), actions) } if a, ok := actions["/mnt/felhom-drives/flash"]; !ok || !a.Stop || a.Return { t.Errorf("flash should STOP (absent+connected): %+v", a) } if a, ok := actions["/mnt/felhom-drives/back"]; !ok || !a.Return || a.Stop || a.Raw != "/mnt/back" { t.Errorf("back should RETURN with raw /mnt/back (present+disconnected): %+v", a) } if _, gated := actions["/mnt/felhom-drives/usb"]; gated { t.Errorf("usb (present+connected) must not be gated") } if _, acted := actions["/mnt/felhom-drives/gone"]; acted { t.Errorf("gone (absent+already-disconnected) is steady — no action") } if _, acted := actions["/mnt/felhom-drives/dead"]; acted { t.Errorf("decommissioned drive must never be gated") } if _, acted := actions["/mnt/sys_drive/felhom-data"]; acted { t.Errorf("internal SSD/system path must NEVER be gated (only /mnt/felhom-drives/ externals)") } }