package hostquarantine import ( "context" "errors" "os" "testing" "time" ) type fakeDirectory struct { actor Actor err error calls int } func (f *fakeDirectory) Lookup(_ context.Context, _ string) (Actor, error) { f.calls++ return f.actor, f.err } func TestCheckRequiresFreshExactActiveEnrollment(t *testing.T) { d := &fakeDirectory{actor: Actor{Enrollment: "enroll-1", State: StateActive, QuarantineRevision: 3}} if err := Check(t.Context(), d, "box-1", "enroll-1"); err != nil { t.Fatal(err) } d.actor.State = StateQuarantined if err := Check(t.Context(), d, "box-1", "enroll-1"); !errors.Is(err, ErrAdmission) { t.Fatalf("quarantine error = %v", err) } if d.calls != 2 { t.Fatalf("directory calls = %d, want a fresh lookup per check", d.calls) } } func TestCheckFailsClosed(t *testing.T) { cases := map[string]*fakeDirectory{ "missing": {err: os.ErrNotExist}, "unreadable": {err: errors.New("signature refused")}, "mismatch": {actor: Actor{Enrollment: "other", State: StateActive}}, "retired": {actor: Actor{Enrollment: "enroll-1", State: StateRetired}}, "unknown": {actor: Actor{Enrollment: "enroll-1", State: "paused"}}, } for name, d := range cases { t.Run(name, func(t *testing.T) { if err := Check(t.Context(), d, "box-1", "enroll-1"); !errors.Is(err, ErrAdmission) { t.Fatalf("error = %v", err) } }) } } func TestRetirementCompletionKeyAndValidation(t *testing.T) { key, err := RetirementKey("box-1", "enroll-1", 7) if err != nil || key != "control/host-retirement/box-1/enroll-1/7.json" { t.Fatalf("key = %q, %v", key, err) } hash := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" r := RetirementCompletion{ V: 1, Box: "box-1", Enrollment: "enroll-1", InstanceID: "i-123", QuarantineRevision: 7, BootID: "boot-1", CompletedAt: time.Date(2026, 10, 5, 12, 0, 0, 0, time.UTC), Complete: true, InventorySHA256: hash, Entries: []RetirementEntry{{Host: "app.example.com", Incarnation: "inc-1", Stamp: 8, RetirementID: "retire-1", JournalSHA256: hash}}, } if err := r.Validate(); err != nil { t.Fatal(err) } r.Entries = nil if err := r.Validate(); !errors.Is(err, ErrRecord) { t.Fatalf("nil entries error = %v", err) } r.Entries = []RetirementEntry{{Host: "app.example.com", Incarnation: "inc-1", Stamp: 8, RetirementID: "retire-1", JournalSHA256: hash}} r.Entries[0].JournalSHA256 = "" if err := r.Validate(); !errors.Is(err, ErrRecord) { t.Fatalf("error = %v", err) } }