fix(seed): address seeded rows by their generated uuid, not by title (#32) #33
No reviewers
Labels
No labels
area/auth
area/backend
area/ci
area/deploy
area/e2e
area/frontend
area/shared
blocked
good-first-issue
needs-info
priority/high
priority/low
priority/medium
type/bug
type/chore
type/docs
type/feature
type/tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
celilo/lunacycle!33
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix-e2e-seed-unique"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes the
UNIQUE constraint failed: template_task_user.*that 500-ed every/api/test-reseedcall in run 65 — #32.e2e-nightly run 71, dispatched against this branch: 29 passed, 2 skipped, 2.2m — green.
Run 65 was 11 failed / 12 did not run / 6 passed in 20.4m. First green nightly there has ever been.
Root cause
createCategoryTasksAndAssignmentsminted a uuid for the template task, insertedit, threw the uuid away, and re-read the row back with
findFirst({ where: eq(templateTask.title, task.title) }).titleis not unique— and neither are the other three columns the seed read itself back by:
category.name,user.email,template.isActive.With a same-titled row already in the table the lookup resolves to the older
row, and assigning it to the same users again violates the
template_task_userprimary key./api/test-reseedclears the tables before itseeds, so the residue comes from a reseed the test runner had already abandoned
landing after the next one began — and each 500 makes Playwright retry, which
issues another reseed, which is how one collision became a run-long storm. The
20.4m → 2.2m drop is that storm going away.
Fix
Mint each id up front and use it. All four self-read-backs are gone — the seed
never asks the database for something it already knows. Dropped along the way:
the
templateIdprop no caller of the helper ever read, and afetchRandomAvatar()result that was computed and discarded.Evidence (watched to fail)
New
tests/seed/reseed.test.tsseeds twice without clearing — exactly theresidue condition — and reproduces CI's error byte for byte on the pre-fix code:
With the fix:
1 pass / 0 fail. Reverting it goes red again with the same trace.Also run end to end in the CI image (
mcr.microsoft.com/playwright:v1.60.0-noble,same install steps as
e2e-nightly.yml): 29 passed / 2 skipped.The test runs in
pr-validate.ymlasbun run test:seed— separate fromtest:unitbecause the seed path needsbun:sqlite, which vitest (node) can'tload.
Left for the operator
#32's last acceptance item — "should a red nightly notify anyone" — is a policy
call, not made here. It still fails silently.
Does not touch #30 or #31 (rebased onto #31's merge; the two
pr-validate.ymland
package.jsonoverlaps were both pure additions, kept side by side).createCategoryTasksAndAssignments inserted a template task with a fresh uuid, discarded it, then re-read the row by `title` — a non-unique column. With a same-titled row already present (a reseed the runner abandoned landing after the next one began) the lookup returned the OLDER row and re-inserted its existing (templateTaskId, userId) pairs: UNIQUE constraint failed: template_task_user.template_task_id, template_task_user.user_id which 500-ed /api/test-reseed and took every e2e beforeAll with it (#32). All four self-read-backs are gone (category.name, templateTask.title, user.email, template.isActive — none unique); the seed mints each id up front and uses it, which also drops the unused `templateId` prop and a discarded fetchRandomAvatar() call. `bun run test:seed` covers the invariant and runs in pr-validate. It lives outside test:unit because the seed path needs bun:sqlite, which vitest can't load.82112f9c4639529b311339529b3113beab6c0d59