fix(sandbox): delete api_keys and retag log in teardown (final backlog blockers) (#1454)

The last 9 backlog users decompose into exactly two classes: api_keys.sod_acknowledged_by (NO ACTION to auth.users) blocks every keyed sandbox, and the WORM dimension_retag_log blocks any sandbox where a voucher line was retagged. cleanup_sandbox_user now deletes the user's api_keys explicitly (they must die with the sandbox anyway) and the company's retag log under the standard bypass; dimension_retag_log_immutable's DELETE branch honors gnubok.sandbox_cleanup with the same per-row sandbox re-verification as every other guard, UPDATE stays blocked. Fixture seeds both row types. Validated end-to-end on staging.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-07 16:05:46 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 526f0315d0
commit c912fc5633
2 changed files with 164 additions and 0 deletions
+21
View File
@@ -75,6 +75,27 @@ async function seedSandboxUser(settingsCreatedAt?: string): Promise<{
VALUES ($1, $2, $3, 'manual', 'marked_sent', now(), '2033-12-31')`,
[companyId, userId, invoiceId],
)
// An API key with the SoD acknowledgement set: sod_acknowledged_by is a
// plain NO ACTION FK to auth.users that blocked teardown for every keyed
// sandbox until 20260807170000 deletes the keys explicitly.
await getPool().query(
`INSERT INTO public.api_keys
(user_id, company_id, key_hash, key_prefix, sod_acknowledged_by, sod_acknowledged_at)
VALUES ($1, $2, $3, 'gnubok_sk_pgtest', $1, now())`,
[userId, companyId, randomUUID()],
)
// A WORM retag-log row (dimension_retag_log_immutable raises on DELETE
// outside the teardown bypass).
const { rows: lineRows } = await getPool().query<{ id: string }>(
`SELECT id FROM public.journal_entry_lines WHERE journal_entry_id = $1 LIMIT 1`,
[entryId],
)
await getPool().query(
`INSERT INTO public.dimension_retag_log
(company_id, journal_entry_id, line_id, old_dimensions, new_dimensions, reason)
VALUES ($1, $2, $3, '{}', '{"1":"BUTIK"}', 'Sandbox cleanup test retag')`,
[companyId, entryId, lineRows[0]!.id],
)
return { userId, companyId, entryId }
}