Files
felhom-controller/controller/internal/agentapi/disks_test.go
T

129 lines
4.3 KiB
Go

package agentapi
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func diskStub(t *testing.T) (*httptest.Server, string) {
mux := http.NewServeMux()
mux.HandleFunc("GET /disks", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"ok":true,"data":{"vmid":8200,"disks":[{"name":"bulk","data_bearing":true,"data_reason":"has ext4"}]}}`))
})
mux.HandleFunc("POST /disks/assign", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"ok":true,"data":{"assigned":"/mnt/data"}}`))
})
mux.HandleFunc("POST /disks/eject", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"ok":true,"data":{"ejected":"/mnt/bulk","dependent_guests":[8200]}}`))
})
mux.HandleFunc("POST /disks/format", func(w http.ResponseWriter, r *http.Request) {
var body struct {
Device, FSType, DurableID string
Confirmed bool
}
_ = decodeJSON(r, &body)
switch {
case strings.Contains(body.Device, "protected"): // system/backup → operator signature
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte(`{"ok":false,"data":{"device":"` + body.Device + `","data_bearing":true,"role":"system","pending_op":{"op":"storage_wipe","host_scope":"h","durable_id":"byid:x","fstype":"ext4"}}}`))
case strings.Contains(body.Device, "data") && !body.Confirmed: // user-data, not yet confirmed
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte(`{"ok":false,"data":{"device":"` + body.Device + `","data_bearing":true,"role":"user-data","needs_confirmation":true,"durable_id":"byid:wwn-1"}}`))
default: // blank, or user-data confirmed
_, _ = w.Write([]byte(`{"ok":true,"data":{"device":"` + body.Device + `","formatted":true,"role":"user-data"}}`))
}
})
s := httptest.NewTLSServer(mux)
return s, strings.TrimPrefix(s.URL, "https://")
}
func decodeJSON(r *http.Request, v any) error {
return json.NewDecoder(r.Body).Decode(v)
}
func clientFor(t *testing.T, s *httptest.Server, endpoint string) *Client {
pin := leafPin(t, s)
c, err := New(endpoint, "TOK", pin)
if err != nil {
t.Fatal(err)
}
return c
}
func TestDisks_List(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
resp, err := c.Disks(context.Background())
if err != nil {
t.Fatal(err)
}
if len(resp.Disks) != 1 || !resp.Disks[0].DataBearing {
t.Fatalf("unexpected: %+v", resp)
}
}
func TestFormat_BlankOK(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
res, err := c.FormatDisk(context.Background(), "/dev/sdb", "ext4", false, "")
if err != nil || !res.Formatted {
t.Fatalf("blank format: %v %+v", err, res)
}
}
// SYSTEM/BACKUP data-bearing → ErrFormatRefused with the operator pending op.
func TestFormat_ProtectedRefused(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
res, err := c.FormatDisk(context.Background(), "/dev/protected-disk", "ext4", false, "")
if !errors.Is(err, ErrFormatRefused) {
t.Fatalf("expected ErrFormatRefused, got %v", err)
}
if res.PendingOp == nil {
t.Fatal("protected refusal should carry the operator pending op")
}
}
// USER-DATA data-bearing, not confirmed → ErrNeedsConfirmation with the durable id to confirm against.
func TestFormat_UserDataNeedsConfirmation(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
res, err := c.FormatDisk(context.Background(), "/dev/data-disk", "ext4", false, "")
if !errors.Is(err, ErrNeedsConfirmation) {
t.Fatalf("expected ErrNeedsConfirmation, got %v", err)
}
if !res.NeedsConfirmation || res.DurableID != "byid:wwn-1" || res.Role != "user-data" {
t.Fatalf("needs-confirmation payload not surfaced: %+v", res)
}
}
// USER-DATA data-bearing, confirmed + durable id → formatted.
func TestFormat_UserDataConfirmed(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
res, err := c.FormatDisk(context.Background(), "/dev/data-disk", "ext4", true, "byid:wwn-1")
if err != nil || !res.Formatted {
t.Fatalf("confirmed user-data format: %v %+v", err, res)
}
}
func TestEject_Dependents(t *testing.T) {
s, ep := diskStub(t)
defer s.Close()
c := clientFor(t, s, ep)
res, err := c.EjectDisk(context.Background(), "/mnt/bulk")
if err != nil || len(res.DependentGuests) != 1 || res.DependentGuests[0] != 8200 {
t.Fatalf("eject: %v %+v", err, res)
}
}