test: fix flaky unit tests on main - #2995
Conversation
Two distinct failures: - team-invites.spec.ts failed in CI with 'deadlock detected' (40P01): deleteAll() ran db.delete(user) — which cascades into account and session — in the same Promise.all as direct deletes of those tables, on separate pool connections. Delete sequentially, children before parents, and retry on deadlock since spec files share one database. Apply the same sequencing to fallback.spec.ts's cleanup. - api.spec.ts 'hybrid escapes to credits provider' fails locally when the repo .env has a real LLM_GOOGLE_AI_STUDIO_API_KEY (loaded by 'import dotenv/config' in app.ts): the retry loop picks up the real env key and calls the real Google API instead of escaping to vertex. Clear the studio env credentials for the duration of the test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WalkthroughTest database cleanup now runs sequentially in dependency-safe order with deadlock retries, while a hybrid routing test temporarily removes and restores Google AI Studio credentials to isolate fallback behavior. ChangesTest reliability
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/gateway/src/fallback.spec.ts`:
- Around line 102-114: Apply the same three-attempt retry policy for PostgreSQL
deadlock error code 40P01 to the sequential cleanup in resetTestState(),
preferably by extracting and reusing a shared helper also used by deleteAll();
retry the complete deletion sequence with a 40P01 check and rethrow any other
error or exhausted final attempt.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b558baf3-504b-4d3f-99b5-c5cfb57d0242
📒 Files selected for processing (3)
apps/api/src/testing.tsapps/gateway/src/api.spec.tsapps/gateway/src/fallback.spec.ts
| // Sequential, children before parents: concurrent deletes on | ||
| // cascade-linked tables (e.g. user -> account) deadlock in postgres. | ||
| await db.delete(tables.log); | ||
| await db.delete(tables.apiKeyIamRule); | ||
| await db.delete(tables.apiKey); | ||
| await db.delete(tables.providerKey); | ||
| await db.delete(tables.userOrganization); | ||
| await db.delete(tables.project); | ||
| await db.delete(tables.session); | ||
| await db.delete(tables.account); | ||
| await db.delete(tables.verification); | ||
| await db.delete(tables.organization); | ||
| await db.delete(tables.user); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Retry deadlocks in this cleanup path too.
Shared-database collisions can select resetTestState() as the deadlock victim; retries inside deleteAll() will not prevent this function from rejecting. Apply the same three-attempt 40P01 retry policy, preferably through a shared helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/gateway/src/fallback.spec.ts` around lines 102 - 114, Apply the same
three-attempt retry policy for PostgreSQL deadlock error code 40P01 to the
sequential cleanup in resetTestState(), preferably by extracting and reusing a
shared helper also used by deleteAll(); retry the complete deletion sequence
with a 40P01 check and rethrow any other error or exhausted final attempt.
Summary
Fixes two unit-test failures on
main— the CI flake that broke the last two main builds, plus a local-only failure uncovered while reproducing it.1. CI flake:
team-invites.spec.tsdeadlock (the failing main builds)The last two
ciruns on main failed inteam invites > expired invite is not accepted on sign-inwith postgresdeadlock detected(40P01), alwayswhile deleting tuple in relation "account", raised fromdeleteAll()inapps/api/src/testing.ts.Root cause:
deleteAll()randb.delete(user)— whichON DELETE CASCADEs intoaccountandsession— in the samePromise.allas direct deletes ofaccountandsession. Each statement runs on its own pool connection, so the cascade and the direct delete lock the same rows in different orders and postgres kills one transaction. Timing-dependent, hence flaky (it passed on the PR, then failed on main).Fix:
testdatabase and vitest runs files in parallel, so two files' cleanups can still collide cross-process.apps/gateway/src/fallback.spec.ts.2. Local-only failure: hybrid escape test calls the real Google API
api.spec.ts > hybrid escapes to credits provider when keyed provider failsfails deterministically on any machine whose repo.envcontains a realLLM_GOOGLE_AI_STUDIO_API_KEY(loaded into unit tests byimport "dotenv/config"inapps/gateway/src/app.ts). After the keyed google-ai-studio provider fails, the retry loop finds the real env key and gets a genuine (billed!) Gemini response withused_provider: google-ai-studioinstead of escaping togoogle-vertex. CI has no.env, which is why this only failed locally.Fix: the test now saves + deletes
LLM_GOOGLE_AI_STUDIO_API_KEY/_BASE_URLfor its duration (same pattern it already uses for the vertex vars), restoring them infinally.Testing
pnpm test:unit: 157 files / 2561 tests passed (previously 1 failure per run)..envpresent (previously failed 4/4 attempts).pnpm buildandpnpm formatpass.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests