provision: golden — declared static record adopts the story-shaped route
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+108
−0
internal/provision/reconcile_test.go+108 −0
diff --git a/internal/provision/reconcile_test.go b/internal/provision/reconcile_test.go| index 4153d849d..609f977ae 100644 |
| --- a/internal/provision/reconcile_test.go |
| +++ b/internal/provision/reconcile_test.go |
| @@ -1496,3 +1496,111 @@ func TestReconcileNeverRemovesUnadoptedRoute(t *testing.T) { |
| t.Errorf("a hand-added route with no record was modified: %+v", got) |
| } |
| } |
| + |
| +// TestReconcileAdoptsStaticStoryRoute tests the backfill golden path: |
| +// a declared static record adopts the story-shaped route that was |
| +// hand-added before records existed. This is issue #82 backfill path. |
| +func TestReconcileAdoptsStaticStoryRoute(t *testing.T) { |
| + s := dirStore(t) |
| + // Create a different account for story (id=3, to avoid collision with the tito account) |
| + storyAcctSqid := writeAccountManifest(t, s, 3, "Story") |
| + // Create an app manifest for story |
| + if err := PutManifest(ctx, s, storyAcctSqid, "story", Manifest{ |
| + Schema: 1, App: "story", Domain: "carlosframework.com", Seed: []string{}, |
| + }); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Pre-Upsert the operator-added shape: a static route as it existed |
| + // before records were written to the bucket |
| + handAdded := registry.Route{ |
| + Host: "story.carlosframework.com", Kind: "static", |
| + App: "story", Account: storyAcctSqid, Channel: "canary/live", |
| + Status: "ready", |
| + } |
| + reg := regWith(t, handAdded) |
| + |
| + // Write the matching declared record |
| + rec := Record{ |
| + Schema: 1, Host: "story.carlosframework.com", App: "story", |
| + Account: storyAcctSqid, Channel: "canary/live", Kind: "static", |
| + CreatedAt: time.Now(), |
| + } |
| + if err := CreateRecord(ctx, s, rec); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Run Reconcile with an unconstrained region/pool (matches the static route) |
| + cfg := ReconcileConfig{DataDir: "/data"} |
| + if err := Reconcile(ctx, s, reg, cfg); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Assert the route is now Provisioned |
| + got := *mustGet(t, reg, "story.carlosframework.com") |
| + if !got.Provisioned { |
| + t.Fatal("the matching static route was not adopted") |
| + } |
| + |
| + // Static routes should have no Socket or DBPath |
| + if got.Socket != "" || got.DBPath != "" { |
| + t.Errorf("a static route derived filesystem paths: socket %q db %q", got.Socket, got.DBPath) |
| + } |
| + |
| + // Static routes should not be hibernating |
| + if got.Hibernate { |
| + t.Error("a static route was armed for hibernation — it has no process to wake") |
| + } |
| +} |
| + |
| +// Companion negative: a static record does NOT adopt an instance route. |
| +// The kind must match for adoption to happen. |
| +func TestReconcileRefusesAdoptInstanceRouteForStaticRecord(t *testing.T) { |
| + s := dirStore(t) |
| + // Create a different account for story (id=3) |
| + storyAcctSqid := writeAccountManifest(t, s, 3, "Story") |
| + // Create an app manifest for story |
| + if err := PutManifest(ctx, s, storyAcctSqid, "story", Manifest{ |
| + Schema: 1, App: "story", Domain: "carlosframework.com", Seed: []string{}, |
| + }); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Pre-Upsert a route with Kind: "instance" (not "static") |
| + handAdded := registry.Route{ |
| + Host: "story.carlosframework.com", Kind: "instance", |
| + Socket: "/run/carlos/story.carlosframework.com.sock", |
| + DBPath: "/data/story.carlosframework.com.db", |
| + App: "story", Account: storyAcctSqid, Channel: "canary/live", |
| + Status: "ready", |
| + } |
| + reg := regWith(t, handAdded) |
| + before := *mustGet(t, reg, "story.carlosframework.com") |
| + |
| + // Write the matching declared record WITH Kind: "static" |
| + rec := Record{ |
| + Schema: 1, Host: "story.carlosframework.com", App: "story", |
| + Account: storyAcctSqid, Channel: "canary/live", Kind: "static", |
| + CreatedAt: time.Now(), |
| + } |
| + if err := CreateRecord(ctx, s, rec); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Run Reconcile |
| + cfg := ReconcileConfig{DataDir: "/data"} |
| + if err := Reconcile(ctx, s, reg, cfg); err != nil { |
| + t.Fatal(err) |
| + } |
| + |
| + // Assert the route was NOT adopted (Provisioned still false) |
| + after := *mustGet(t, reg, "story.carlosframework.com") |
| + if after.Provisioned { |
| + t.Error("a kind-mismatched route was adopted — removal authority granted over a route the record does not describe") |
| + } |
| + |
| + // Verify the route was not modified at all |
| + if after != before { |
| + t.Errorf("a mismatched route was modified: before %+v after %+v", before, after) |
| + } |
| +} |