//go:build unix package host import ( "errors" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" ) // Astra round 2 finding 8, ruling 61: an empty installation initialises with // a previous relocation's journals present under relocate/ (N9 classifies // the directory as the claim classifies its own journals); without the // classification the same tree refuses, which is the defect. func TestCensusInitializesWithOldRelocationJournals(t *testing.T) { t.Parallel() censusWithSharedFiles(t, "relocate", map[string]string{ "relocate/ops/op-old/install.json": `{"v":1,"host":"old.example","op":"op-old"}`, "relocate/ops/op-old/attempts.json": `{"v":1,"host":"old.example","op":"op-old"}`, "relocate/release/op-old.json": `{"v":1,"request":{"Host":"old.example"}}`, "relocate/build/op-old/old.example.db": "scratch", }) } // censusWithSharedFiles plants files under a production box's data root // and checks an empty installation of another host initialises with them, // and refuses once the shared entry named classified is taken out of // claimGlobalSharedPaths. func censusWithSharedFiles(t *testing.T, classified string, files map[string]string) { t.Helper() ctx := t.Context() r := claimCensusBox(t) reg, err := registry.Open(filepath.Join(r.DataDir, "registry.db")) if err != nil { t.Fatal(err) } defer reg.Close() inventory, err := openLegacyInventory(t, filepath.Join(r.DataDir, ".hostclaims", "inventory.db")) if err != nil { t.Fatal(err) } defer inventory.Close() launches, err := hostbox.OpenLaunchJournal(filepath.Join(r.DataDir, ".hostclaims", "launches.db")) if err != nil { t.Fatal(err) } defer launches.Close() for path, body := range files { full := filepath.Join(r.DataDir, filepath.FromSlash(path)) if err := os.MkdirAll(filepath.Dir(full), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(full, []byte(body), 0o600); err != nil { t.Fatal(err) } } store := release.NewDirStore(t.TempDir()) created := time.Date(2026, 10, 7, 0, 0, 0, 0, time.UTC) record := provision.Record{Schema: 1, Host: "shop.example", Slug: "shop", App: "app", Account: "acct", Region: "ie", Channel: "stable", CreatedAt: created} if err := provision.CreateRecord(ctx, store, record); err != nil { t.Fatal(err) } key := provision.RecordKey(record.Account, record.App, record.Host) obs, err := hostclaim.Read(ctx, store, record.Host) if err != nil { t.Fatal(err) } obs, err = hostclaim.Update(ctx, store, obs, hostclaim.Adopt(hostclaim.NewHolder{Family: "instance", Key: key, Account: record.Account, App: record.App, Host: record.Host, Region: record.Region, CreatedAt: created}, "box", nil, nil, "adopt", func() string { return "id" })) if err != nil { t.Fatal(err) } selected := hostbox.RecordSelection{Host: record.Host, Incarnation: obs.Claim.Holder.Incarnation} census := hostbox.EmptyInstallationCensus{Inventory: inventory, Registry: reg, Launches: launches, Box: "box", Enrollment: "enrollment", DirectoryRevision: "1", Roots: map[string]string{"data": r.DataDir, "opt": r.OptDir, "units": r.UnitsDir}, SharedPaths: claimGlobalSharedPaths(r)} unclassified := census unclassified.SharedPaths = map[string][]string{} for root, entries := range claimGlobalSharedPaths(r) { for _, e := range entries { if e != classified { unclassified.SharedPaths[root] = append(unclassified.SharedPaths[root], e) } } } if err := unclassified.Initialize(ctx, obs, selected); !errors.Is(err, hostbox.ErrTypedInventory) { t.Fatalf("without %s classified the files were accepted: %v", classified, err) } if err := census.Initialize(ctx, obs, selected); err != nil { t.Fatalf("initialisation with %s present: %v", classified, err) } }