//go:build unix package hostguard import ( "context" "errors" "testing" "time" "github.com/carlosframework/platform/internal/hostclaim" ) func TestCompletedServingStops(t *testing.T) { for _, tc := range []string{"exact", "pending", "wrong-session", "operation", "phase", "obligation", "wrong-box", "mixed-sessions", "view"} { t.Run(tc, func(t *testing.T) { f, target, captured := fenceFixture(t) f.obs.Claim.Op = hostclaim.Op{Kind: hostclaim.KindServing, Exec: target.Box} target.Op, target.Phase = "", "" target.Region = "uk" f.obs.Claim.Holder.WriterBox = target.Box f.obs.Claim.Holder.Generation = "g7" f.obs.Claim.Holder.Admissions = []hostclaim.AdmissionRecord{{Session: target.Session, Box: target.Box, Region: target.Region, Generation: "g7"}} if _, err := f.g.BeginFence(t.Context(), target); err != nil { t.Fatal(err) } if tc != "pending" { if err := f.g.CompleteFence(t.Context(), target, []byte("receipt"), time.Now(), func(context.Context, FenceTarget, []byte, time.Time) error { return nil }); err != nil { t.Fatal(err) } } if tc == "operation" || tc == "phase" || tc == "obligation" || tc == "mixed-sessions" { // Negative fixtures deliberately put unusable evidence in the journal. err := f.g.locked(t.Context(), target.Host, func(d *recordDir) error { r, err := d.read(target.Host) if err != nil { return err } switch tc { case "operation": r.FenceJournal[0].Target.Op = "op" case "phase": r.FenceJournal[0].Target.Phase = "phase" case "obligation": r.FenceJournal[0].Target.ObligationID = WriterStopObligation(target.Session) case "mixed-sessions": other := r.FenceJournal[0] other.Target.Session++ other.Target.FenceID = "second" r.FenceJournal = append(r.FenceJournal, other) } return d.write(r) }) if err != nil { t.Fatal(err) } } if tc == "view" { r, err := readRecord(f.g.DataDir, target.Host) if err != nil { t.Fatal(err) } f.g.FenceView = func(context.Context, string, Captured) (FenceView, error) { return FenceView{Schema: 1, Host: target.Host, Captured: captured, Installation: RootInstallation{Host: target.Host, Migration: 1}, Receipts: r.FenceJournal}, nil } } session, box := target.Session, target.Box if tc == "wrong-session" { session++ } if tc == "wrong-box" { box = "other" } got, err := f.g.CompletedServingStops(t.Context(), target.Host, captured, session, box) if tc == "exact" || tc == "view" { if err != nil || len(got) != 1 || got[0].Target != target || string(got[0].Bytes) != "receipt" { t.Fatalf("receipts %+v: %v", got, err) } if _, err := f.g.CompletedWriterStops(t.Context(), target.Host, captured, "", box); !errors.Is(err, ErrInvalidInput) { t.Fatalf("operation reader accepted serving: %v", err) } } else if !errors.Is(err, ErrNoFenceReceipt) || len(got) != 0 { t.Fatalf("invalid evidence accepted: %+v %v", got, err) } }) } } func TestCompletedServingStopsRejectsZeroIdentity(t *testing.T) { f, target, captured := fenceFixture(t) for _, tc := range []struct { host string c Captured session uint64 box string }{ {"", captured, 7, "uk"}, {target.Host, Captured{}, 7, "uk"}, {target.Host, captured, 0, "uk"}, {target.Host, captured, 7, ""}, } { if _, err := f.g.CompletedServingStops(t.Context(), tc.host, tc.c, tc.session, tc.box); !errors.Is(err, ErrInvalidInput) { t.Fatalf("%+v: %v", tc, err) } } }