package localapi import ( "errors" "strings" "testing" "gitea.dooplex.hu/admin/felhom-agent/internal/storage" ) // Test for [AGENT-001]: the inline customer-confirmed wipe must re-resolve the // confirmed durable id to the current device and refuse if anything changed in // the window, rather than formatting a mutable /dev path that may now point at a // different physical disk. func TestAntiRetargetResolve(t *testing.T) { const durable = "byid:wwn-0xCONFIRMED" const dev = "/dev/sdb1" // probe helpers dataBearing := storage.DeviceProbe{Probed: true, HasFilesystem: true, FSType: "ext4"} // DataBearing() true blank := storage.DeviceProbe{Probed: true} // Probed, no signatures → not data-bearing okInspect := func(string) (storage.DeviceProbe, error) { return dataBearing, nil } cases := []struct { name string durable string resolve func(string) (string, error) derive func(string) (string, error) inspect func(string) (storage.DeviceProbe, error) wantDev string wantErr string // substring; "" = expect success }{ { name: "happy-path-matches", durable: durable, resolve: func(string) (string, error) { return dev, nil }, derive: func(string) (string, error) { return durable, nil }, inspect: okInspect, wantDev: dev, }, { name: "empty-durable-refused", durable: "", resolve: func(string) (string, error) { return dev, nil }, derive: func(string) (string, error) { return durable, nil }, inspect: okInspect, wantErr: "path-only", }, { name: "durable-no-longer-resolves", durable: durable, resolve: func(string) (string, error) { return "", errors.New("gone") }, derive: func(string) (string, error) { return durable, nil }, inspect: okInspect, wantErr: "no longer resolves", }, { // THE core AGENT-001 case: the /dev node now points at a DIFFERENT disk, // so re-deriving its durable id yields a different value → refuse. name: "retarget-mismatch-refused", durable: durable, resolve: func(string) (string, error) { return dev, nil }, derive: func(string) (string, error) { return "byid:wwn-0xDIFFERENT", nil }, inspect: okInspect, wantErr: "durable-id mismatch", }, { name: "no-longer-data-bearing-refused", durable: durable, resolve: func(string) (string, error) { return dev, nil }, derive: func(string) (string, error) { return durable, nil }, inspect: func(string) (storage.DeviceProbe, error) { return blank, nil }, wantErr: "no longer data-bearing", }, { name: "reinspect-error-refused", durable: durable, resolve: func(string) (string, error) { return dev, nil }, derive: func(string) (string, error) { return durable, nil }, inspect: func(string) (storage.DeviceProbe, error) { return storage.DeviceProbe{}, errors.New("io") }, wantErr: "re-inspect", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, err := antiRetargetResolve(tc.durable, tc.resolve, tc.derive, tc.inspect) if tc.wantErr == "" { if err != nil { t.Fatalf("unexpected error: %v", err) } if got != tc.wantDev { t.Fatalf("device = %q, want %q", got, tc.wantDev) } return } if err == nil { t.Fatalf("expected refusal containing %q, got device %q and nil error", tc.wantErr, got) } if !strings.Contains(err.Error(), tc.wantErr) { t.Fatalf("error %q does not contain %q", err.Error(), tc.wantErr) } if got != "" { t.Fatalf("on refusal the device must be empty, got %q", got) } }) } }