| 1 | //go:build unix |
| 2 | |
| 3 | package hostguard |
| 4 | |
| 5 | import ( |
| 6 | "context" |
| 7 | "errors" |
| 8 | "testing" |
| 9 | "time" |
| 10 | |
| 11 | "github.com/carlosframework/platform/internal/hostclaim" |
| 12 | ) |
| 13 | |
| 14 | func TestCompletedServingStops(t *testing.T) { |
| 15 | for _, tc := range []string{"exact", "pending", "wrong-session", "operation", "phase", "obligation", "wrong-box", "mixed-sessions", "view"} { |
| 16 | t.Run(tc, func(t *testing.T) { |
| 17 | f, target, captured := fenceFixture(t) |
| 18 | f.obs.Claim.Op = hostclaim.Op{Kind: hostclaim.KindServing, Exec: target.Box} |
| 19 | target.Op, target.Phase = "", "" |
| 20 | target.Region = "uk" |
| 21 | f.obs.Claim.Holder.WriterBox = target.Box |
| 22 | f.obs.Claim.Holder.Generation = "g7" |
| 23 | f.obs.Claim.Holder.Admissions = []hostclaim.AdmissionRecord{{Session: target.Session, Box: target.Box, Region: target.Region, Generation: "g7"}} |
| 24 | if _, err := f.g.BeginFence(t.Context(), target); err != nil { |
| 25 | t.Fatal(err) |
| 26 | } |
| 27 | if tc != "pending" { |
| 28 | if err := f.g.CompleteFence(t.Context(), target, []byte("receipt"), time.Now(), func(context.Context, FenceTarget, []byte, time.Time) error { return nil }); err != nil { |
| 29 | t.Fatal(err) |
| 30 | } |
| 31 | } |
| 32 | if tc == "operation" || tc == "phase" || tc == "obligation" || tc == "mixed-sessions" { |
| 33 | // Negative fixtures deliberately put unusable evidence in the journal. |
| 34 | err := f.g.locked(t.Context(), target.Host, func(d *recordDir) error { |
| 35 | r, err := d.read(target.Host) |
| 36 | if err != nil { |
| 37 | return err |
| 38 | } |
| 39 | switch tc { |
| 40 | case "operation": |
| 41 | r.FenceJournal[0].Target.Op = "op" |
| 42 | case "phase": |
| 43 | r.FenceJournal[0].Target.Phase = "phase" |
| 44 | case "obligation": |
| 45 | r.FenceJournal[0].Target.ObligationID = WriterStopObligation(target.Session) |
| 46 | case "mixed-sessions": |
| 47 | other := r.FenceJournal[0] |
| 48 | other.Target.Session++ |
| 49 | other.Target.FenceID = "second" |
| 50 | r.FenceJournal = append(r.FenceJournal, other) |
| 51 | } |
| 52 | return d.write(r) |
| 53 | }) |
| 54 | if err != nil { |
| 55 | t.Fatal(err) |
| 56 | } |
| 57 | } |
| 58 | if tc == "view" { |
| 59 | r, err := readRecord(f.g.DataDir, target.Host) |
| 60 | if err != nil { |
| 61 | t.Fatal(err) |
| 62 | } |
| 63 | f.g.FenceView = func(context.Context, string, Captured) (FenceView, error) { |
| 64 | return FenceView{Schema: 1, Host: target.Host, Captured: captured, Installation: RootInstallation{Host: target.Host, Migration: 1}, Receipts: r.FenceJournal}, nil |
| 65 | } |
| 66 | } |
| 67 | session, box := target.Session, target.Box |
| 68 | if tc == "wrong-session" { |
| 69 | session++ |
| 70 | } |
| 71 | if tc == "wrong-box" { |
| 72 | box = "other" |
| 73 | } |
| 74 | got, err := f.g.CompletedServingStops(t.Context(), target.Host, captured, session, box) |
| 75 | if tc == "exact" || tc == "view" { |
| 76 | if err != nil || len(got) != 1 || got[0].Target != target || string(got[0].Bytes) != "receipt" { |
| 77 | t.Fatalf("receipts %+v: %v", got, err) |
| 78 | } |
| 79 | if _, err := f.g.CompletedWriterStops(t.Context(), target.Host, captured, "", box); !errors.Is(err, ErrInvalidInput) { |
| 80 | t.Fatalf("operation reader accepted serving: %v", err) |
| 81 | } |
| 82 | } else if !errors.Is(err, ErrNoFenceReceipt) || len(got) != 0 { |
| 83 | t.Fatalf("invalid evidence accepted: %+v %v", got, err) |
| 84 | } |
| 85 | }) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | func TestCompletedServingStopsRejectsZeroIdentity(t *testing.T) { |
| 90 | f, target, captured := fenceFixture(t) |
| 91 | for _, tc := range []struct { |
| 92 | host string |
| 93 | c Captured |
| 94 | session uint64 |
| 95 | box string |
| 96 | }{ |
| 97 | {"", captured, 7, "uk"}, {target.Host, Captured{}, 7, "uk"}, {target.Host, captured, 0, "uk"}, {target.Host, captured, 7, ""}, |
| 98 | } { |
| 99 | if _, err := f.g.CompletedServingStops(t.Context(), tc.host, tc.c, tc.session, tc.box); !errors.Is(err, ErrInvalidInput) { |
| 100 | t.Fatalf("%+v: %v", tc, err) |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |