Files
felhom-controller/controller/internal/web/intermediary_test.go
T
admin 3adfa41a09 controller v0.67.2: gate keys present on BoundUnderParent (reboot convergence)
The drive-absent gate treats a stable path usable only when bound under the parent
(BoundUnderParent), not merely host-mounted. Makes a host reboot converge: apps
stay gated until the agent binds the drive under the parent, then are restarted
(recreated) on the populated path. Test updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:10:02 +02:00

78 lines
3.5 KiB
Go

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)")
}
}