Skip to content

Fix flaky CI: stop the gateway restore tests from poisoning the shared test database - #2844

Merged
Pierre-Gilles merged 1 commit into
masterfrom
claude/slow-unit-tests-ci-4j4nks
Aug 11, 2026
Merged

Fix flaky CI: stop the gateway restore tests from poisoning the shared test database#2844
Pierre-Gilles merged 1 commit into
masterfrom
claude/slow-unit-tests-ci-4j4nks

Conversation

@Pierre-Gilles

@Pierre-Gilles Pierre-Gilles commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the intermittent CI failures seen since the test-speedup series (e.g. this run), where hundreds of tests failed in beforeEach with:

Error: SQLITE_ERROR: table t_session has 15 columns but 14 values were supplied

Root cause. gateway.restoreBackupEvent.test.js restores real backup fixtures over the worker's live SQLite database. The old fixture carries schema drift: a t_session.client_id column created by OAuth migrations that were later removed from the repo, so no migration drops it and the db.umzug.up() in the test's afterEach cannot repair it. After that test, the live t_session has 15 columns while a freshly migrated schema has 14. The fast snapshot reset introduced in #2818 rebuilds tables with a positional INSERT INTO ... SELECT *, which then fails for every test running after this file on the same worker. Whether any tests run after it depends on how mocha --parallel distributes files across workers — hence the flakiness, and why re-running the job usually goes green.

Fix.

  • gateway.restoreBackupEvent.test.js now restores into a throwaway /tmp file instead of the worker's live database — exactly what gateway.restoreBackup.test.js was already doing. Both files now copy the object returned by getConfig() instead of mutating it in place (it is a shared object, so the previous gateway.config.storage = ... override leaked to every other consumer of the config). The db.umzug.up() + cleanDb() repair in afterEach becomes unnecessary and is removed.
  • Defense in depth: resetDb is now self-healing. If the snapshot copy fails because the live schema no longer matches the snapshot's, it logs a warning, rebuilds the seeded state with the real seeders and takes a fresh snapshot, instead of failing the remainder of the worker. A regression test covers both drift directions (column added, column dropped).

Reproduced deterministically before the fix (5/5 locally by running any test file after the gateway restore file in the same mocha process); after the fix the same scenario passes and the full suite shows zero t_session errors.

Checklist

  • Tests pass: cd server && npm run coverage (Codecov requires 100% coverage on changed lines) and Cypress (npm run cypress:run) if the UI changed
  • Linter and prettier pass on both front and server (npm run eslint, npm run prettier)
  • No undocumented breaking change

🤖 Generated with Claude Code

https://claude.ai/code/session_01Xj8nWW9CWrNUvw5C6wd5yp


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved test database resets to recover from snapshot-copy failures without interrupting test execution.
    • Database snapshots now rebuild correctly after schema changes and restore original data and structure.
    • Gateway backup restoration tests now use isolated temporary storage, preventing changes to shared test data.
  • Tests

    • Added regression coverage for database schema drift and snapshot rebuilding.
    • Improved reset statistics and freshness tracking for more reliable test validation.

The restoreBackupEvent tests restored real backup fixtures over the
worker's live SQLite file. Old fixtures carry schema drift (a t_session
client_id column created by OAuth migrations later removed from the
repo), and since no migration drops it, db.umzug.up() could not repair
the schema: after the restore, the positional INSERT ... SELECT * of the
fast snapshot reset failed with "table t_session has 15 columns but 14
values were supplied" for every test running after this file on the same
worker. Whether tests run after it depends on how mocha --parallel
distributes files across workers, hence the intermittent CI failures.

Restore into a throwaway /tmp file instead, exactly like
gateway.restoreBackup.test.js already does, and copy the shared config
object returned by getConfig() instead of mutating it (the previous
in-place storage override leaked to every other consumer of the config).

Also make resetDb self-healing: when the snapshot copy fails because the
live schema no longer matches the snapshot's, rebuild the seeded state
and a fresh snapshot instead of failing the remainder of the worker. A
regression test covers both drift directions (column added, column
dropped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xj8nWW9CWrNUvw5C6wd5yp
@github-actions github-actions Bot added area:server Node.js server code type:chore Deps, CI, refactoring, docs. Hidden from user changelog labels Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The test database reset helper now rebuilds seeded snapshots after schema drift or snapshot-copy failures. Reset statistics and regression coverage track rebuilds. Gateway restore tests now use process-specific temporary SQLite files instead of shared test storage.

Changes

Database reset recovery

Layer / File(s) Summary
Snapshot recovery and regression coverage
server/test/helpers/db.test.js, server/test/helpers/dbReset.test.js
The reset helper detaches and rebuilds seeded snapshots, refreshes freshness markers, restores SQLite state after copy failures, logs recovery, and counts rebuilds. Tests cover schema drift and data restoration.

Gateway restore test isolation

Layer / File(s) Summary
Isolated gateway restore storage
server/test/lib/gateway/gateway.restoreBackup.test.js, server/test/lib/gateway/gateway.restoreBackupEvent.test.js
Gateway restore tests use cloned configurations with process-specific temporary SQLite paths. Shared database cleanup and post-construction storage mutation were removed.

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

Possibly related PRs

Suggested reviewers: cursor

Poem

I’m a rabbit guarding snapshots bright,
Rebuilding schemas through the night.
SQLite paths now hop apart,
Fresh test databases make a start.
Reset counters thump: one, two—
Safe restore work is what we do!

🚥 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 clearly and concisely describes the primary fix: preventing gateway restore tests from corrupting the shared test database.
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 claude/slow-unit-tests-ci-4j4nks

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.

@cursor cursor Bot 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.

Looks good — this correctly fixes the intermittent t_session column-count CI flake.

Why this works

  • Root cause is accurate: gateway.restoreBackupEvent.test.js was restoring fixture DBs (with leftover t_session.client_id) over the worker’s live SQLite file; db.umzug.up() cannot drop that column, so the positional INSERT … SELECT * snapshot reset from #2802/#2818 then fails for every later test on that worker.
  • Restoring into a throwaway /tmp path (matching gateway.restoreBackup.test.js) stops the poison at the source.
  • Spreading getConfig() before overriding storage is the right fix — getConfig() returns the shared config[env] object, so the previous in-place mutation leaked across the process.
  • The resetDb rescue path + regression test are solid defense-in-depth for ADD/DROP-column drift; DETACH before rebuilding the snapshot file is necessary.

Not flagged

  • Production / Gladys runtime untouched (test helpers only).
  • No device categories/types.
  • Not risk:high, not needs:human-review.

One soft limitation noted inline on the rebuild path (non-blocking).

Open in Web View Automation 

Sent by Cursor Automation: Automatic PR review

Comment on lines +130 to +136
// The fast copy assumes the live schema still matches the snapshot's.
// A test that alters the schema (e.g. restoring a real backup over the
// database file) would otherwise fail every later test of this worker:
// rebuild the seeded state and a fresh snapshot instead of giving up.
logger.warn(`resetDb: snapshot reset failed (${e.message}), rebuilding the seed snapshot`);
resetDbStats.snapshotRebuilds += 1;
await rebuildSeededSnapshot();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Soft note (non-blocking): this rescue re-seeds data then VACUUM INTOs whatever schema is currently live. Extra columns (the original backup-poison case) stay baked into the new snapshot — later resets succeed because column counts match again, but the schema is not returned to pristine migrations.

That’s fine here because the primary fix stops restore tests from touching the live DB; this path mainly covers in-process DDL drift like the new regression test. Worth keeping in mind if anything else starts mutating schema mid-suite.

@github-actions

Copy link
Copy Markdown
Contributor

🐳 A Docker image has been built for this branch and pushed to the GitHub Container Registry.

You can test this pull request (AMD64 only) by pulling the image below:

ghcr.io/gladysassistant/gladys-preview:claude-slow-unit-tests-ci-4j4nks

For example, run it with:

sudo docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --cgroupns=host \
  --restart=always \
  --privileged \
  --network=host \
  --name gladys-claude-slow-unit-tests-ci-4j4nks \
  -e NODE_ENV=production \
  -e SERVER_PORT=80 \
  -e TZ=Europe/Paris \
  -e SQLITE_FILE_PATH=/var/lib/gladysassistant/gladys-production.db \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/gladysassistant:/var/lib/gladysassistant \
  -v /dev:/dev \
  -v /run/udev:/run/udev:ro \
  ghcr.io/gladysassistant/gladys-preview:claude-slow-unit-tests-ci-4j4nks

This comment and the image are automatically updated on every new commit pushed to this pull request.

Need an ARM64 image (Raspberry Pi, Apple Silicon, …)? Comment /build-arm64 on this pull request.

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.48%. Comparing base (12bc5f2) to head (19a921c).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2844   +/-   ##
=======================================
  Coverage   99.48%   99.48%           
=======================================
  Files        1219     1219           
  Lines       85399    85399           
=======================================
  Hits        84963    84963           
  Misses        436      436           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot 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.

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 `@server/test/helpers/db.test.js`:
- Around line 95-112: Update rebuildSeededSnapshot in
server/test/helpers/db.test.js to restore the canonical schema before running
seeders and initSnapshotReset, and assert that the first recovery removes
test_drift_column. Remove the later manual DROP COLUMN cleanup in
server/test/helpers/dbReset.test.js at lines 72-75, and add coverage for
recovering both added and missing columns.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d720b2d4-24f1-41d5-b47e-55cc155b37dd

📥 Commits

Reviewing files that changed from the base of the PR and between 12bc5f2 and 19a921c.

📒 Files selected for processing (4)
  • server/test/helpers/db.test.js
  • server/test/helpers/dbReset.test.js
  • server/test/lib/gateway/gateway.restoreBackup.test.js
  • server/test/lib/gateway/gateway.restoreBackupEvent.test.js

Comment thread server/test/helpers/db.test.js
@Pierre-Gilles
Pierre-Gilles merged commit 57ba074 into master Aug 11, 2026
14 checks passed
@Pierre-Gilles
Pierre-Gilles deleted the claude/slow-unit-tests-ci-4j4nks branch August 11, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:server Node.js server code type:chore Deps, CI, refactoring, docs. Hidden from user changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants