test(platform-agent): regression for core#2496 ON CONFLICT updates runtime #2629

Merged
devops-engineer merged 3 commits from fix/core-2615-2496-platform-agent-on-conflict-runtime-test into main 2026-06-12 07:27:43 +00:00
@@ -290,3 +290,152 @@ func TestIntegration_PlatformAgentInstall_StatusNotOnline(t *testing.T) {
t.Logf("fresh platform-agent status=%q (expected 'offline', but accepting non-online)", status)
}
}
// TestIntegration_PlatformAgentInstall_OnConflictUpdatesRuntime locks in
// the core#2496 root-fix. Pre-#2496, the ON CONFLICT clause on the
// platform-agent upsert only set kind and parent_id; the runtime column
// was left at its INSERT-time value (the schema default). So if a
// platform-agent row had been self-registered first (creating a row with
// the schema default runtime) and the install endpoint was then called,
// the runtime would be left STALE — not updated to 'claude-code'.
//
// #2496 added `runtime = 'claude-code'` to the ON CONFLICT DO UPDATE
// SET clause, so the runtime is now corrected on every install, not just
// on the initial insert.
//
// This test seeds a platform-agent row with a NON-canonical runtime
// ('codex' — anything other than 'claude-code'), then calls
// installPlatformAgent. The post-install assertion is: the runtime
// MUST be 'claude-code'. Pre-#2496, this assertion would fail; post-#2496,
// the ON CONFLICT clause resets it.
//
// We also test the "fresh insert" path (no prior row) to confirm the
// regression is specifically about the conflict-path; if a future refactor
// accidentally drops the runtime from the INSERT VALUES too, that case
// catches it.
func TestIntegration_PlatformAgentInstall_OnConflictUpdatesRuntime(t *testing.T) {
conn := integrationDB_PlatformAgentInstall(t)
ctx := context.Background()
tag := uuid.New().String()[:8]
platformID := uuid.New().String()
paName := "Org Concierge " + tag
staleRuntime := "codex" // deliberately non-canonical
// The schema has `uniq_workspaces_one_platform_root` (UNIQUE
// partial index allowing at most one row with kind='platform' AND
// parent_id IS NULL per database). A prior sibling test in this
// package (e.g. TestIntegration_PlatformAgentInstall_ReparentsRoot
// AndMovesAnchors) leaves a platform-agent row behind; its cleanup
// deletes by its OWN uuid+name, so by the time this test runs,
// the slot may be occupied by a prior test's row (depending on
// test scheduling).
//
// To make this test robust to test ordering, the pre-seed
// cleanup DOWNGRADES any existing platform root to 'workspace'
// (matching what installPlatformAgent itself does for prior
// roots — see its step 0). We use UPDATE not DELETE because
// child workspaces may have `parent_id` pointing at the platform
// root, and a DELETE would trip `workspaces_parent_id_fkey`.
// Downgrading (UPDATE kind='workspace') is FK-safe and clears
// the uniq-platform-root slot.
cleanup := func() {
_, _ = conn.ExecContext(ctx, `DELETE FROM workspaces WHERE name = $1`, paName)
_, _ = conn.ExecContext(ctx, `DELETE FROM workspaces WHERE id = $1`, platformID)
// Belt-and-suspenders: also downgrade any other platform-agent
// row left by a sibling test, so the next test's seed slot
// is free.
_, _ = conn.ExecContext(ctx, `UPDATE workspaces SET kind = 'workspace' WHERE kind = 'platform' AND parent_id IS NULL AND id <> $1`, platformID)
}
t.Cleanup(cleanup)
// Pre-seed: clear the uniq-platform-root slot by downgrading
// any existing platform root to 'workspace'. This is FK-safe
// and matches installPlatformAgent's own step 0 (so we're
// exercising the same shape the production code uses).
if _, err := conn.ExecContext(ctx,
`UPDATE workspaces SET kind = 'workspace' WHERE kind = 'platform' AND parent_id IS NULL`); err != nil {
t.Fatalf("pre-seed: downgrade existing platform-agent rows: %v", err)
}
cleanup()
// Case 1: ON CONFLICT path. Seed the platform-agent row with a STALE
// runtime (simulates a self-registered row with the schema default).
// The seed is INSERT (not UPSERT) to deliberately NOT use the install
// endpoint's path — we're pre-arranging the conflict scenario.
if _, err := conn.ExecContext(ctx, `
INSERT INTO workspaces (id, name, kind, tier, status, runtime, parent_id)
VALUES ($1, $2, 'platform', 0, 'offline', $3, NULL)`,
platformID, paName, staleRuntime); err != nil {
t.Fatalf("seed stale platform agent: %v", err)
}
// Sanity: the seeded row has the stale runtime (not the canonical
// 'claude-code'). If this assertion fails, the seed is wrong; fix
// the seed, not the test assertion.
var seededRuntime string
if err := conn.QueryRowContext(ctx,
`SELECT runtime FROM workspaces WHERE id = $1`, platformID).Scan(&seededRuntime); err != nil {
t.Fatalf("read seeded runtime: %v", err)
}
if seededRuntime != staleRuntime {
t.Fatalf("seed precondition: runtime=%q, want %q (the test would be meaningless if the seed already had 'claude-code')",
seededRuntime, staleRuntime)
}
// Now call installPlatformAgent. Per the pre-#2496 bug, the
// ON CONFLICT DO UPDATE SET clause would have left runtime stuck
// at 'codex'. Per the #2496 fix, it must now be 'claude-code'.
if err := installPlatformAgent(ctx, conn, platformID, paName); err != nil {
t.Fatalf("install: %v", err)
}
// PRIMARY ASSERTION: the runtime is 'claude-code' after install.
// Pre-#2496, the runtime would be 'codex' (stale). Post-#2496, the
// ON CONFLICT clause sets it to 'claude-code'.
var postInstallRuntime string
if err := conn.QueryRowContext(ctx,
`SELECT runtime FROM workspaces WHERE id = $1`, platformID).Scan(&postInstallRuntime); err != nil {
t.Fatalf("read post-install runtime: %v", err)
}
if postInstallRuntime != "claude-code" {
t.Fatalf("core#2496 regression: post-install runtime=%q, want 'claude-code'. "+
"This means the ON CONFLICT DO UPDATE SET clause is NOT setting runtime on the conflict path. "+
"Pre-#2496, the clause was: `ON CONFLICT (id) DO UPDATE SET kind = 'platform', parent_id = NULL` "+
"(no runtime update). Post-#2496, it MUST be: `... SET kind = 'platform', runtime = 'claude-code', parent_id = NULL`.",
postInstallRuntime)
}
// Sanity: a second install (still the conflict path) keeps runtime
// at 'claude-code' (idempotency assertion).
if err := installPlatformAgent(ctx, conn, platformID, paName); err != nil {
t.Fatalf("second install: %v", err)
}
var post2Runtime string
if err := conn.QueryRowContext(ctx,
`SELECT runtime FROM workspaces WHERE id = $1`, platformID).Scan(&post2Runtime); err != nil {
t.Fatalf("read post-second-install runtime: %v", err)
}
if post2Runtime != "claude-code" {
t.Fatalf("idempotent: post-second-install runtime=%q, want 'claude-code'", post2Runtime)
}
// Case 2: Fresh insert path. Verify the runtime='claude-code' is
// also set on the INSERT VALUES, not just on conflict. This guards
// against a future refactor that moves the runtime to the conflict
// clause only and removes it from the INSERT — a regression that
// would let a self-registering row skip the runtime entirely.
freshID := uuid.New().String()
freshName := "Org Concierge fresh " + tag
if err := installPlatformAgent(ctx, conn, freshID, freshName); err != nil {
t.Fatalf("fresh install: %v", err)
}
var freshRuntime string
if err := conn.QueryRowContext(ctx,
`SELECT runtime FROM workspaces WHERE id = $1`, freshID).Scan(&freshRuntime); err != nil {
t.Fatalf("read fresh runtime: %v", err)
}
if freshRuntime != "claude-code" {
t.Fatalf("fresh insert path: runtime=%q, want 'claude-code'", freshRuntime)
}
_, _ = conn.ExecContext(ctx, `DELETE FROM workspaces WHERE id = $1`, freshID)
}