Files
felhom-controller/controller/internal/web/r542_registered_drive_test.go
T
admin f65ace0ca6
gates / gates (push) Successful in 1m2s
R-542: the disk candidates no longer offer a registered, in-use drive
The format wizard rendered the agent's initialize list as-is, and the
agent deliberately allows re-initialising Felhom's own drives, so a
registered data drive was offered for formatting. The controller proxy
now drops every candidate that backs a registered storage path (joined
through the guest mount table) from both lists; an unreadable mount
table empties initialize.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-06 20:52:15 +02:00

78 lines
3.9 KiB
Go

package web
import (
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
)
// R-542 — a drive that backs a REGISTERED storage path must not be offered for formatting (nor again
// for attaching). The mount table is the shape measured on demo-hp 2026-10-06 (guest /proc/mounts).
const r542Mounts = `/dev/mapper/pve-root /mnt/felhom-drives ext4 rw,relatime 0 0
/dev/nvme0n1 /mnt/felhom-drives/hdd_1 ext4 rw,relatime 0 0
/dev/sdb1 /mnt/felhom-drives/adatlemez ext4 rw,relatime 0 0
/dev/mapper/pve-vm--9201--disk--1 /mnt/sys_drive ext4 rw,relatime 0 0
`
// COMPANION RED-PROOF (observed): make dropRegisteredDrives `return resp` at its top (the pre-R-542
// pass-through) → this fails with "a registered, in-use drive is offered for FORMATTING". Restored.
func TestR542_RegisteredDriveNotOfferedForFormat(t *testing.T) {
agentSaid := agentapi.CandidatesResult{
Initialize: []agentapi.DiskCandidate{
{Device: "/dev/sdb", MountSource: "/dev/sdb1", FSType: "ext4", DataBearing: true}, // registered (partition source)
{Device: "/dev/nvme0n1", FSType: "ext4", DataBearing: true}, // registered (whole-disk source)
{Device: "/dev/sdc"}, // a fresh, blank drive
},
Attach: []agentapi.DiskCandidate{
{Device: "/dev/sdb", MountSource: "/dev/sdb1", FSType: "ext4"},
{Device: "/dev/sdd", MountSource: "/dev/sdd1", FSType: "ext4"}, // a fresh USB with a filesystem
},
}
registered := map[string]bool{"/mnt/felhom-drives/hdd_1": true, "/mnt/felhom-drives/adatlemez": true, "/mnt/sys_drive": true}
got := dropRegisteredDrives(agentSaid, r542Mounts, registered)
if len(got.Initialize) != 1 || got.Initialize[0].Device != "/dev/sdc" {
t.Fatalf("a registered, in-use drive is offered for FORMATTING: initialize=%+v (want only /dev/sdc)", got.Initialize)
}
if len(got.Attach) != 1 || got.Attach[0].Device != "/dev/sdd" {
t.Fatalf("a registered drive is offered again under attach: %+v (want only /dev/sdd)", got.Attach)
}
}
// An UNregistered drive mounted under the managed path keeps today's behaviour (the agent's rule:
// re-initialising Felhom's own, unregistered drive stays allowed — e.g. after a rebuild).
func TestR542_UnregisteredDriveStillOffered(t *testing.T) {
agentSaid := agentapi.CandidatesResult{Initialize: []agentapi.DiskCandidate{{Device: "/dev/sdb", MountSource: "/dev/sdb1"}}}
got := dropRegisteredDrives(agentSaid, r542Mounts, map[string]bool{"/mnt/sys_drive": true})
if len(got.Initialize) != 1 {
t.Fatalf("an unregistered drive must stay offered; got %+v", got.Initialize)
}
}
// FAIL-SAFE: an unreadable mount table cannot prove a drive is free → initialize is EMPTY, attach unchanged.
//
// COMPANION RED-PROOF (observed): drop the empty-mounts branch → this fails with "unreadable mount
// table must empty initialize". Restored.
func TestR542_UnreadableMountTableEmptiesInitialize(t *testing.T) {
agentSaid := agentapi.CandidatesResult{
Initialize: []agentapi.DiskCandidate{{Device: "/dev/sdb"}},
Attach: []agentapi.DiskCandidate{{Device: "/dev/sdd", MountSource: "/dev/sdd1"}},
}
got := dropRegisteredDrives(agentSaid, "", map[string]bool{"/mnt/felhom-drives/adatlemez": true})
if len(got.Initialize) != 0 || got.Initialize == nil {
t.Fatalf("unreadable mount table must empty initialize (non-nil, so JSON is []); got %+v", got.Initialize)
}
if len(got.Attach) != 1 {
t.Fatalf("attach is non-destructive and must pass through; got %+v", got.Attach)
}
}
// Partition matching must not over-match: /dev/sdb1 in use does not hide /dev/sdba or /dev/sdc.
func TestR542_PartitionMatchIsExact(t *testing.T) {
inUse := map[string]bool{"/dev/sdb1": true, "/dev/nvme0n1p2": true}
for dev, want := range map[string]bool{"/dev/sdb": true, "/dev/sdba": false, "/dev/sdc": false, "/dev/nvme0n1": true, "/dev/nvme0n12": false} {
if got := candidateBacksInUse(agentapi.DiskCandidate{Device: dev}, inUse); got != want {
t.Errorf("candidateBacksInUse(%s) = %v, want %v", dev, got, want)
}
}
}