Skip to content

test: fix flaky unit tests on main - #2995

Merged
steebchen merged 1 commit into
mainfrom
fix-flaky-unit-tests-main
Jul 10, 2026
Merged

steebchen merged 1 commit into
mainfrom
fix-flaky-unit-tests-main

Conversation

@steebchen

@steebchen steebchen commented Jul 10, 2026 •

Copy link
Copy Markdown
Member

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.ts deadlock (the failing main builds)

The last two ci runs on main failed in team invites > expired invite is not accepted on sign-in with postgres deadlock detected (40P01), always while deleting tuple in relation "account", raised from deleteAll() in apps/api/src/testing.ts.

Root cause: deleteAll() ran db.delete(user) — which ON DELETE CASCADEs into account and session — in the same Promise.all as direct deletes of account and session. 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:

  • Run the deletes sequentially, children before parents, so cascades always land on already-empty tables.
  • Retry up to 3× on 40P01, since all spec files share one test database and vitest runs files in parallel, so two files' cleanups can still collide cross-process.
  • Apply the same sequencing to the identical pattern in 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 fails fails deterministically on any machine whose repo .env contains a real LLM_GOOGLE_AI_STUDIO_API_KEY (loaded into unit tests by import "dotenv/config" in apps/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 with used_provider: google-ai-studio instead of escaping to google-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_URL for its duration (same pattern it already uses for the vertex vars), restoring them in finally.

Testing

  • Full pnpm test:unit: 157 files / 2561 tests passed (previously 1 failure per run).
  • The hybrid escape test now passes in isolation with the real .env present (previously failed 4/4 attempts).
  • pnpm build and pnpm format pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved test database cleanup to avoid deadlocks by deleting related records in a safe order and retrying when needed.
    • Prevented local Google AI Studio credentials from affecting provider fallback tests.
  • Tests

    • Made test-state resets more deterministic by clearing related data sequentially.

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>
Copilot AI review requested due to automatic review settings July 10, 2026 19:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Test 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.

Changes

Test reliability

Layer / File(s) Summary
Deadlock-safe database cleanup
apps/api/src/testing.ts, apps/gateway/src/fallback.spec.ts
Cleanup deletes related tables sequentially from children to parents, and the shared helper retries PostgreSQL deadlock errors up to three attempts.
Hybrid provider credential isolation
apps/gateway/src/api.spec.ts
The hybrid escape test removes Google AI Studio environment credentials before the request and restores their previous values afterward.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR’s main goal: fixing flaky tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-flaky-unit-tests-main

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@steebchen
steebchen enabled auto-merge July 10, 2026 19:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 40bf982 and b2b8235.

📒 Files selected for processing (3)
  • apps/api/src/testing.ts
  • apps/gateway/src/api.spec.ts
  • apps/gateway/src/fallback.spec.ts

Comment on lines +102 to +114
// 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

@steebchen
steebchen added this pull request to the merge queue Jul 10, 2026
Merged via the queue into main with commit 1edecde Jul 10, 2026
17 checks passed
@steebchen
steebchen deleted the fix-flaky-unit-tests-main branch July 10, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants