From 418bd1c6b549f1a1856aa342c2f6de33ef817055 Mon Sep 17 00:00:00 2001 From: jubnl Date: Mon, 21 Sep 2026 10:35:16 +0200 Subject: [PATCH] fix(test): route MikroORM's dynamic imports through vitest from the setup file, not the ORM config --- server/src/mikro-orm.config.ts | 7 ------- server/tests/helpers/db-mock.ts | 7 ++++++- server/tests/helpers/test-db.ts | 10 +++++++--- server/tests/setup.ts | 13 +++++++++++++ 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/server/src/mikro-orm.config.ts b/server/src/mikro-orm.config.ts index 668e84db6..6f3220b6e 100644 --- a/server/src/mikro-orm.config.ts +++ b/server/src/mikro-orm.config.ts @@ -27,11 +27,4 @@ export default defineConfig({ // itself, but the CLI and the migrator's logging still read it, and naming a // different file here than the one actually open is a trap. dbName: resolveDbPath(), - // MikroORM loads migrations and seeders through its own dynamic import(). Under - // vitest that native import() sits outside the transform pipeline, so a - // seeder's extensionless `'../../app-config'` import fails with - // ERR_UNSUPPORTED_DIR_IMPORT. Declared here, the import() runs inside a module - // vitest transforms; in production it resolves the compiled dist files as before. - // Same reason tests/unit/db/restore-migrates-forward.test.ts sets it. - dynamicImportProvider: (id: string) => import(id), }); diff --git a/server/tests/helpers/db-mock.ts b/server/tests/helpers/db-mock.ts index ebc3c72ff..6cf1cc015 100644 --- a/server/tests/helpers/db-mock.ts +++ b/server/tests/helpers/db-mock.ts @@ -36,6 +36,10 @@ import { readSchemaSnapshot } from '../../src/db/schema-snapshot'; * which only works on a database the ORM's migration table already knows. * The legacy `createTestDb()` (in test-db.ts) stays for unit suites that never * boot the app. + * + * @returns a normalised copy: the snapshot's seeded first-run `admin` row and + * the `users` autoincrement sequence have already been removed (see below), so + * the first user any caller creates lands on id 1. */ export function createSnapshotTestDb(): Database.Database { const snapshot = readSchemaSnapshot(); @@ -43,7 +47,8 @@ export function createSnapshotTestDb(): Database.Database { throw new Error('No schema snapshot: run under vitest (tests/global-setup.ts writes it once per run).'); } const db = new Database(snapshot); - db.exec('PRAGMA journal_mode = WAL'); + // No journal_mode PRAGMA here: this is a deserialised in-memory database, so + // WAL mode (a disk-file concept) is a no-op on it. db.exec('PRAGMA busy_timeout = 5000'); db.exec('PRAGMA foreign_keys = ON'); // The snapshot carries the first-run seeded `admin` row (id 1); the legacy diff --git a/server/tests/helpers/test-db.ts b/server/tests/helpers/test-db.ts index ef94f5132..23e9f9dd6 100644 --- a/server/tests/helpers/test-db.ts +++ b/server/tests/helpers/test-db.ts @@ -35,9 +35,13 @@ import type { RateLimitService } from '../../src/nest/common/rate-limit.service' // file imports AuthPublicController (for resetRateLimits below), so a vi.mock // factory MUST import from db-mock.ts directly, never from here, or it // re-enters src/db/database while its own mock for that module is still being -// built and captures the real one. Re-exported so existing non-factory -// importers of test-db.ts keep working unchanged. -export { CAN_ACCESS_TRIP_SQL, createSnapshotTestDb, buildDbMock } from './db-mock'; +// built and captures the real one. Only buildDbMock/CAN_ACCESS_TRIP_SQL are +// re-exported (the two a couple of unit suites use against their own +// createTestDb()) — createSnapshotTestDb is deliberately NOT re-exported here, +// so the re-entrant-import foot-gun above isn't reachable through this file at +// all: nothing importing test-db.ts can accidentally build a vi.mock factory +// that re-enters src/db/database. +export { CAN_ACCESS_TRIP_SQL, buildDbMock } from './db-mock'; // Tables to clear on reset, child-before-parent to be safe (FK checks are OFF during reset). // Keep in sync with schema.ts + migrations.ts. Intentionally excluded: categories, addons, diff --git a/server/tests/setup.ts b/server/tests/setup.ts index 3aeef511c..995e352da 100644 --- a/server/tests/setup.ts +++ b/server/tests/setup.ts @@ -24,6 +24,19 @@ process.env.NODE_ENV = 'test'; process.env.COOKIE_SECURE = 'false'; process.env.LOG_LEVEL = 'error'; // suppress info/debug logs in test output +// MikroORM loads migrations and seeders through its own dynamic import() +// (core/utils/fs-utils.js: `globalThis.dynamicImportProvider ?? (id => import(id))`, +// read at call time). Under vitest that native import() sits outside the +// transform pipeline, so a seeder's extensionless `'../../app-config'` import +// fails with ERR_UNSUPPORTED_DIR_IMPORT. Routed through this module's import() +// it goes through vite-node instead. Test-only on purpose: set from the ORM +// config it would compile to `require('file://…')` under the server's CommonJS +// target and crash every production boot at the seeder step. +interface DynamicImportGlobal { + dynamicImportProvider?: (id: string) => Promise; +} +(globalThis as DynamicImportGlobal).dynamicImportProvider = (id) => import(id); + // Several services fire notification sends as unawaited dynamic-import chains // (`import('…/notificationService').then(({ send }) => send(…).catch(…))`). // Give those chains one macrotask turn to settle after every test, while the