diff --git a/TESTING.md b/TESTING.md index 122ee50b8..aa936de85 100644 --- a/TESTING.md +++ b/TESTING.md @@ -91,9 +91,9 @@ Subprocess and provisioning-lock unit tests start alongside the platform project 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. Unit files and most API integration files use isolated worker threads to reduce startup time. On Linux, -API integration files 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 integration files and the PGlite unit files (`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. This keeps database engines from competing with all framework compilers at startup. Keep module isolation enabled: these suites mock globals, keep database state, and exercise real process/lock boundaries. diff --git a/packages/void/test/unit/test-projects.test.ts b/packages/void/test/unit/test-projects.test.ts index 56194a41a..e6a416ce8 100644 --- a/packages/void/test/unit/test-projects.test.ts +++ b/packages/void/test/unit/test-projects.test.ts @@ -46,6 +46,26 @@ 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 diff --git a/test/affected.mjs b/test/affected.mjs index 21164d89f..423a9b58e 100644 --- a/test/affected.mjs +++ b/test/affected.mjs @@ -58,7 +58,9 @@ export async function runAffected(root, values) { watch: false, changed: initial.full ? false : changed, experimental: { vcsProvider }, - ...(values.unit ? { project: ['unit', 'cli-unit', 'platform-unit'] } : {}), + ...(values.unit + ? { project: ['unit', 'cli-unit', 'platform-unit', 'platform-unit-pglite'] } + : {}), }); try { const all = await vitest.globTestSpecifications(); diff --git a/vite.config.ts b/vite.config.ts index f46ac501b..fe9afc2b9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,6 +9,14 @@ const apiTimezoneTests = [ 'platform/packages/api/test/integration/api-delete-accounting.test.ts', 'platform/packages/api/test/integration/email-connections.test.ts', ]; +// Platform unit files that start PGlite. Add new PGlite unit files here. +const pgliteUnitTests = [ + 'platform/packages/api/test/unit/admin-upgrade-compatibility.test.ts', + 'platform/packages/api/test/unit/email-migration.test.ts', + 'platform/packages/api/test/unit/email-shared-recipient-migration.test.ts', + 'platform/packages/api/test/unit/github-authority-constraints-migration.test.ts', + 'platform/packages/api/test/unit/github-authority-migration.test.ts', +]; const cliUnitTests = [ 'packages/void/test/unit/cf-*.test.ts', 'packages/void/test/unit/provision-*.test.ts', @@ -443,7 +451,20 @@ export default defineConfig({ fsModuleCache: true, pool: 'threads', include: ['platform/packages/*/test/**/*.test.ts'], - exclude: ['platform/packages/api/test/integration/**'], + exclude: ['platform/packages/api/test/integration/**', ...pgliteUnitTests], + maxWorkers, + }, + }, + { + extends: false, + resolve: { alias: platformAlias }, + test: { + name: 'platform-unit-pglite', + fsModuleCache: true, + // 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', + include: pgliteUnitTests, maxWorkers, }, },