package host import ( "context" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/registry" ) func TestRootRetirementJournalsBeforeAcknowledgementAndPreservesSuccessor(t *testing.T) { coordinator, req, _ := claimStopFixture(t) result, err := coordinator.FenceAndStop(t.Context(), req) if err != nil { t.Fatal(err) } base := t.TempDir() inventory, err := openLegacyInventory(t, filepath.Join(base, "inventory.db")) if err != nil { t.Fatal(err) } defer inventory.Close() reg, err := registry.Open(filepath.Join(base, "registry.db")) if err != nil { t.Fatal(err) } defer reg.Close() owner := registry.ClaimOwnership{ClaimIncarnation: req.Incarnation, ClaimStamp: req.InstallationStamp, ClaimOwner: "installation", ClaimRowID: "route-1"} row := registry.Route{Host: req.Host, ClaimOwnership: owner} if err = reg.Upsert(row); err != nil { t.Fatal(err) } in := hostbox.StopInventory{Complete: true, Host: req.Host, Incarnation: req.Incarnation, InstallationStamp: req.InstallationStamp, Box: req.Box, Enrollment: req.Enrollment, DirectoryRevision: req.ExpectedDirectoryRevision, Entries: []hostbox.StopInventoryEntry{{Kind: "route", ArtifactID: owner.ClaimRowID, ManagedRoot: "registry", RelativePath: req.Host, RowIdentity: owner.ClaimRowID, Ownership: owner.ClaimOwner, Materialization: "materialized"}}} if err = inventory.WriteStopInventory(t.Context(), in); err != nil { t.Fatal(err) } journal := filepath.Join(base, "retire") root := ClaimRootHTTP{Stop: ClaimStopHTTP{Coordinator: coordinator}, Roots: map[string]string{"data": base}, Reconcile: hostbox.ReconcileAdapter{Inventory: inventory, Registry: reg, Retirement: hostbox.TypedRetirement{JournalDir: journal}}} // The fixture deliberately has no claim in the evidence store. Root must // retain completed local evidence and retry acknowledgement separately. if err = root.retire(t.Context(), result.Target); err == nil { t.Fatal("missing acknowledgement claim accepted") } if got, e := reg.Get(req.Host); e != nil || got != nil { t.Fatalf("root did not retire owned row: %v", e) } if _, err = os.Stat(filepath.Join(journal, result.Target.FenceID+".json")); err != nil { t.Fatal(err) } row.ClaimStamp++ row.ClaimOwner = "successor" row.ClaimRowID = "route-2" if err = reg.Upsert(row); err != nil { t.Fatal(err) } if err = root.retire(context.Background(), result.Target); err == nil { t.Fatal("missing acknowledgement claim accepted on retry") } got, err := reg.Get(req.Host) if err != nil || got == nil || got.ClaimRowID != "route-2" { t.Fatalf("root replay removed successor: %v", err) } }