Skip to content

Replace DATABASE_URL_TEST with testcontainers for isolated E2E tests#149

Draft
kahlstrm wants to merge 5 commits intomainfrom
claude/refactor-e2e-test-containers-I7MXe
Draft

Replace DATABASE_URL_TEST with testcontainers for isolated E2E tests#149
kahlstrm wants to merge 5 commits intomainfrom
claude/refactor-e2e-test-containers-I7MXe

Conversation

@kahlstrm
Copy link
Copy Markdown
Contributor

@kahlstrm kahlstrm commented Feb 3, 2026

Summary

Replaces the static DATABASE_URL_TEST environment variable with a dynamic PostgreSQL testcontainer approach, providing completely isolated and reproducible test databases for each test run.

Key Changes

  • Testcontainer Integration: Added e2e/testcontainer.ts module that manages PostgreSQL container lifecycle using @testcontainers/postgresql

    • createTestDatabase(): Starts a container and runs migrations
    • getDatabaseUrl(): Retrieves connection URI from state file
    • stopTestDatabase(): Cleans up container and connections
  • Global Setup/Teardown: Refactored test lifecycle management

    • global-setup.ts: Now starts the container, runs migrations, seeds data, and saves container state
    • global-teardown.ts: New file that stops the container and cleans up state
  • Server Startup: Created e2e/start-server.ts script that reads database URL from testcontainer state and starts the preview server with proper environment variables

  • Test Files: Updated all E2E test files to use getDatabaseUrl() instead of process.env.DATABASE_URL_TEST

    • bulk-actions.test.ts
    • csv-import.test.ts
    • emails.test.ts
    • passkeys.test.ts
    • user-merge.test.ts
    • fixtures/db.ts
  • Configuration: Updated playwright.config.ts to use the new server startup script and added global teardown configuration

  • CI/CD: Removed DATABASE_URL_TEST from GitHub Actions workflow since it's now dynamically created

  • Gitignore: Added e2e/.testcontainer-state.json to track container state during test runs

Implementation Details

  • Container state is persisted to e2e/.testcontainer-state.json during setup and read by fixtures and the server startup script
  • Migrations are run programmatically using Drizzle's migrate() function
  • Database seeding happens after container startup via the existing seed script
  • Increased Playwright timeouts (180s for server startup, 60s for tests) to accommodate container initialization
  • Container cleanup uses docker stop directly since testcontainers doesn't provide a direct stop method after the process ends

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6

Replace DATABASE_URL_TEST with testcontainers for e2e tests, following
the same pattern used in vitest integration tests.

Changes:
- Add e2e/testcontainer.ts with utilities for PostgreSQL container
- Add e2e/global-teardown.ts to stop container after tests
- Add e2e/start-server.ts to read container URL and start preview server
- Update global-setup.ts to start testcontainer and run migrations
- Update playwright.config.ts to use globalTeardown and new server script
- Update all test files to use getDatabaseUrl() from testcontainer utils
- Update fixtures/db.ts to read URL from container state
- Remove DATABASE_URL_TEST from CI workflow (no longer needed)
- Update PLAYWRIGHT.md documentation

This makes e2e tests fully self-contained and eliminates the need for
a pre-existing PostgreSQL server.

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6
Increase globalTimeout in CI to allow testcontainers more time to
start the PostgreSQL container, run migrations, and seed the database.

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6
Add try-catch wrapper and initial logging to help diagnose why
global-setup might be failing in CI without creating the state file.

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6
Add console.log at module load time to verify if the globalSetup
module is being loaded by Playwright at all.

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6
Instead of failing immediately when state file doesn't exist, the
webServer now polls for the file with a 2-minute timeout. This will
help diagnose whether globalSetup is running at all or just slow.

https://claude.ai/code/session_01TrZ8pg34hfW2ndRK4oXpn6
@MikaelSiidorow MikaelSiidorow marked this pull request as draft February 10, 2026 19:25
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