diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0154dbd9..e9d2de8ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - run: vp run build:core if: ${{ matrix.shard == 'unit' }} - - run: vp test run --project unit --project cli-unit + - run: vp test run --project unit --project unit-pglite --project cli-unit if: ${{ matrix.shard == 'unit' }} - run: vp run build diff --git a/TESTING.md b/TESTING.md index aa936de85..5c7d38c3b 100644 --- a/TESTING.md +++ b/TESTING.md @@ -58,7 +58,7 @@ changes to metaframework output still need `build:all` and the artifact check. For a leaf-module edit with known import-based consumers, Vitest's native related command is useful: ```sh -vp test related --run packages/void/src/runtime/sse.ts --project unit --project cli-unit +vp test related --run packages/void/src/runtime/sse.ts --project unit --project unit-pglite --project cli-unit vp test list packages/void/test/unit/plugin-routing.test.ts --filesOnly ``` @@ -89,9 +89,9 @@ or hanging tests when they occur instead of routinely increasing timeouts. Subprocess and provisioning-lock unit tests start alongside the platform projects so their real process deadlines can overlap database work. The remaining SDK unit and integration files then -share a scheduling phase. `vp run test:unit` includes both `unit` and `cli-unit`; CI does the same. +share a scheduling phase. `vp run test:unit` includes `unit`, `unit-pglite`, and `cli-unit`; CI does the same. Unit files and most API integration files use isolated worker threads to reduce startup time. On Linux, -API integration files and the PGlite unit files (`platform-unit-pglite`) use forks to isolate PGlite's +API integration files and the PGlite unit files (`unit-pglite`, `platform-unit-pglite`) use forks to isolate PGlite's WebAssembly JIT allocations after native Node 24 cleanup crashes in shared worker processes. API tests that change the process timezone also use forks, because threads cannot change Node's timezone. Select every API file with `vp test run --project 'platform-api-integration*'`. SDK integration files use forks. diff --git a/package.json b/package.json index ea83c04b7..bf887e7ab 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "test:pages:svelte-solid": "vp test run --project parallel-integration packages/svelte packages/solid", "test:platform": "vp test run --project 'platform-*'", "test:profile": "vp test run --reporter=default --experimental.importDurations.print --experimental.importDurations.limit=20", - "test:unit": "vp test run --project unit --project cli-unit", + "test:unit": "vp test run --project unit --project unit-pglite --project cli-unit", "uninstall:void-dev": "node scripts/void-dev-link.mjs uninstall" }, "devDependencies": { diff --git a/packages/void/test/unit/test-projects.test.ts b/packages/void/test/unit/test-projects.test.ts index e6a416ce8..707330ddd 100644 --- a/packages/void/test/unit/test-projects.test.ts +++ b/packages/void/test/unit/test-projects.test.ts @@ -46,30 +46,10 @@ it('keeps process-timezone tests in forks', () => { } }); -it('assigns every platform unit file exactly once and keeps PGlite files in their own project', () => { - const discovered = [ - ...globSync('platform/packages/*/test/**/*.test.ts', { - cwd: root, - exclude: ['platform/packages/api/test/integration/**'], - }), - ]; - const unitProjects = projects.filter((project) => project.test.name.startsWith('platform-unit')); - const selected = unitProjects.flatMap(files); - expect(selected.sort()).toEqual(discovered.sort()); - expect(new Set(selected).size).toBe(discovered.length); - for (const project of unitProjects) { - for (const file of files(project)) { - if (readFileSync(join(root, file), 'utf8').includes('@electric-sql/pglite')) { - expect(project.test.name, file).toBe('platform-unit-pglite'); - } - } - } -}); - it('assigns every SDK unit file exactly once including the subprocess project', () => { const discovered = [...globSync('packages/*/test/unit/**/*.test.ts', { cwd: root })]; const selected = projects - .filter((project) => ['unit', 'cli-unit'].includes(project.test.name)) + .filter((project) => ['unit', 'unit-pglite', 'cli-unit'].includes(project.test.name)) .flatMap(files); expect(selected.sort()).toEqual(discovered.sort()); expect(new Set(selected).size).toBe(discovered.length); diff --git a/test/affected.mjs b/test/affected.mjs index 423a9b58e..040eca6a5 100644 --- a/test/affected.mjs +++ b/test/affected.mjs @@ -59,7 +59,7 @@ export async function runAffected(root, values) { changed: initial.full ? false : changed, experimental: { vcsProvider }, ...(values.unit - ? { project: ['unit', 'cli-unit', 'platform-unit', 'platform-unit-pglite'] } + ? { project: ['unit', 'unit-pglite', 'cli-unit', 'platform-unit', 'platform-unit-pglite'] } : {}), }); try { diff --git a/vite.config.ts b/vite.config.ts index fe9afc2b9..afc29ad77 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -22,6 +22,13 @@ const cliUnitTests = [ 'packages/void/test/unit/provision-*.test.ts', 'packages/void/test/unit/deploy-drift.test.ts', ]; +// SDK unit files that start PGlite. Add new PGlite unit files here. +const sdkPgliteUnitTests = [ + 'packages/void/test/unit/db-export-roundtrip.test.ts', + 'packages/void/test/unit/platform-database-lock.test.ts', + 'packages/void/test/unit/platform-database-migrations.test.ts', + 'packages/void/test/unit/runner-pg.test.ts', +]; // Vitest forwards a FILTERED env to test workers — in CI only VOID_E2E_CF_TOKEN survives, while // VOID_E2E_CF_ACCOUNT_ID and VOID_E2E_RUN_ID are dropped. The gated live Cloudflare deploy e2e @@ -434,7 +441,7 @@ export default defineConfig({ name: 'unit', fsModuleCache: true, include: ['packages/*/test/unit/**/*.test.ts'], - exclude: cliUnitTests, + exclude: [...cliUnitTests, ...sdkPgliteUnitTests], sequence: { groupOrder: 2 }, pool: 'threads', maxWorkers, @@ -443,6 +450,20 @@ export default defineConfig({ ...(process.platform === 'win32' ? { testTimeout: 20_000 } : {}), }, }, + { + extends: false, + test: { + name: 'unit-pglite', + fsModuleCache: true, + include: sdkPgliteUnitTests, + sequence: { groupOrder: 2 }, + // Node 24 on Linux can abort the whole run while unregistering PGlite's Wasm JIT + // allocations across worker threads. Keep each database in its own process. + pool: process.platform === 'linux' ? 'forks' : 'threads', + maxWorkers, + ...(process.platform === 'win32' ? { testTimeout: 20_000 } : {}), + }, + }, { extends: false, resolve: { alias: platformAlias },