package hostfence import ( "context" "encoding/json" "testing" "time" "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/carlosframework/platform/internal/awsinv" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/release" ) // M1-5: journal evidence requires current writer authority. func TestPlan9ClaimFenceRequiresWriterUnfencedNow(t *testing.T) { for _, tc := range []struct { name string offset time.Duration valid bool }{ {"writer-unfenced", 0, true}, {"writer-fenced-before", -time.Minute, false}, {"writer-fenced-same-instant", 0, false}, {"writer-fenced-after", time.Minute, false}, {"writer-fence-malformed", 0, false}, } { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() w, s := terminationFixture(t) w.Reader = func(context.Context, string) (awsinv.ExactEC2API, error) { return terminatedEC2{state: types.InstanceStateNameTerminated, owner: "123"}, nil } got, err := w.Describe(ctx, "dead") if err != nil { t.Fatal(err) } raw, _, err := s.Get(ctx, got.Key) if err != nil { t.Fatal(err) } var seen Termination if err := json.Unmarshal(raw, &seen); err != nil { t.Fatal(err) } observed, err := time.Parse(time.RFC3339Nano, seen.ObservedAt) if err != nil { t.Fatal(err) } o, err := w.Directory.Read(ctx) if err != nil { t.Fatal(err) } self, dead := o.Roster.Boxes["worker"], o.Roster.Boxes["dead"] if tc.name != "writer-unfenced" { body := []byte("{") if tc.name != "writer-fence-malformed" { f := Termination{V: 1, Box: "worker", Enrollment: self.Enrollment, Cloud: self.Cloud, State: "terminated", ObservedAt: observed.Add(tc.offset).Format(time.RFC3339Nano), Via: "describe", RequestID: "request", WrittenBy: Worker{Box: "dead", Enrollment: dead.Enrollment}} if body, err = json.Marshal(f); err != nil { t.Fatal(err) } } if err := release.CreateExact(ctx, s, FenceKey(self.Enrollment), body); err != nil { t.Fatal(err) } } _, err = hostclaim.VerifyFence(ctx, s, ClaimFenceAuthority{Directory: w.Directory, Now: w.Now}, ClaimFenceRef(got)) if (err == nil) != tc.valid { t.Fatalf("%s: verify = %v; want valid = %v", tc.name, err, tc.valid) } }) } }