From 6ff45be0320c209b07bde5b7a0fb70622b862930 Mon Sep 17 00:00:00 2001 From: jubnl Date: Thu, 24 Sep 2026 04:47:29 +0200 Subject: [PATCH] =?UTF-8?q?test(server):=20plan=204=20=E2=80=94=20collapse?= =?UTF-8?q?=20nest=20service=20test=20harnesses=20onto=20the=20ORM=20snaps?= =?UTF-8?q?hot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unit/nest/accommodations.service.test.ts | 27 ++++++------------ .../unit/nest/assignments.service.test.ts | 22 +++++---------- server/tests/unit/nest/atlas.service.test.ts | 19 +++++-------- server/tests/unit/nest/audit.service.test.ts | 11 ++------ .../tests/unit/nest/calendar.service.test.ts | 24 +++++----------- .../unit/nest/categories.service.test.ts | 11 ++------ .../tests/unit/nest/channel-override.test.ts | 16 +++++------ server/tests/unit/nest/collab.service.test.ts | 22 +++++---------- .../unit/nest/collections.service.test.ts | 22 ++++++--------- .../nest/dawarich-suggestions.service.test.ts | 17 ++--------- .../unit/nest/dawarich-sync.service.test.ts | 24 ++++++---------- .../tests/unit/nest/dawarich.service.test.ts | 14 ++-------- server/tests/unit/nest/days.service.test.ts | 22 +++++---------- .../doc-sync/doc-sync-config.service.test.ts | 25 ++++++----------- .../nest/doc-sync/doc-sync.controller.test.ts | 24 ++++++---------- .../unit/nest/doc-sync/doc-sync.job.test.ts | 8 ++---- .../nest/doc-sync/doc-sync.service.test.ts | 25 ++++++----------- server/tests/unit/nest/files.service.test.ts | 28 ++++++++----------- server/tests/unit/nest/immich.service.test.ts | 20 +++++-------- .../unit/nest/journey-book.service.test.ts | 24 ++++++---------- .../unit/nest/journey-domain.service.test.ts | 20 +++++-------- .../unit/nest/journey-share.service.test.ts | 19 +++++-------- .../unit/nest/journey-stats.service.test.ts | 24 ++++++---------- server/tests/unit/nest/mailer.service.test.ts | 19 +++++-------- .../unit/nest/memories-access.service.test.ts | 19 +++++-------- .../notification-preferences.service.test.ts | 19 +++++-------- .../unit/nest/notification-transports.test.ts | 12 ++------ .../nest/notifications.inapp-prefs.test.ts | 18 +++++------- .../unit/nest/notifications.service.test.ts | 18 +++++------- server/tests/unit/nest/oauth.service.test.ts | 19 +++++-------- .../tests/unit/nest/packing.service.test.ts | 19 +++++-------- .../unit/nest/permissions.service.test.ts | 22 +++++---------- .../unit/nest/photo-resolver.service.test.ts | 20 +++++-------- .../tests/unit/nest/places.directions.test.ts | 17 +++++------ server/tests/unit/nest/places.service.test.ts | 23 +++++---------- .../unit/nest/reminder-jobs.service.test.ts | 22 +++++---------- .../unit/nest/reservations.service.test.ts | 19 +++++-------- .../unit/nest/school-holidays.service.test.ts | 11 ++------ .../tests/unit/nest/settings.service.test.ts | 19 +++++-------- server/tests/unit/nest/share.service.test.ts | 19 +++++-------- .../storage/storage-admin.service.test.ts | 21 +++++--------- .../nest/storage/storage-jobs.service.test.ts | 21 +++++--------- .../storage/storage-registry.service.test.ts | 22 +++++---------- .../storage/storage-stats.service.test.ts | 21 +++++--------- .../tests/unit/nest/synology.service.test.ts | 20 +++++-------- server/tests/unit/nest/tags.service.test.ts | 11 ++------ server/tests/unit/nest/todo.service.test.ts | 22 +++++---------- server/tests/unit/nest/token.service.test.ts | 19 +++++-------- .../unit/nest/travel-stats.service.test.ts | 19 +++++-------- .../nest/unified-memories.service.test.ts | 19 +++++-------- .../unit/nest/user-profile.service.test.ts | 19 +++++-------- server/tests/unit/nest/vacay.service.test.ts | 19 +++++-------- 52 files changed, 337 insertions(+), 679 deletions(-) diff --git a/server/tests/unit/nest/accommodations.service.test.ts b/server/tests/unit/nest/accommodations.service.test.ts index 4c9f6dfbe..c496527e3 100644 --- a/server/tests/unit/nest/accommodations.service.test.ts +++ b/server/tests/unit/nest/accommodations.service.test.ts @@ -5,15 +5,11 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, @@ -28,16 +24,14 @@ const { testDb, dbMock } = vi.hoisted(() => { `).get(userId, tripId, userId), isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), - }, - }; + }; }); -vi.mock('../../../src/db/database', () => dbMock); + const { broadcast } = vi.hoisted(() => ({ broadcast: vi.fn() })); vi.mock('../../../src/websocket', () => ({ broadcast })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createDay, createPlace, createDayAccommodation, createDayAssignment, addTripMember } from '../../helpers/factories'; import { PermissionsService } from '../../../src/nest/permissions/permissions.service'; @@ -56,11 +50,6 @@ beforeAll(async () => { svc = await makeAccommodationsService(testDb); }); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); diff --git a/server/tests/unit/nest/assignments.service.test.ts b/server/tests/unit/nest/assignments.service.test.ts index cb9aff276..3c25d8b5f 100644 --- a/server/tests/unit/nest/assignments.service.test.ts +++ b/server/tests/unit/nest/assignments.service.test.ts @@ -13,12 +13,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -33,10 +31,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: unknown, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -44,8 +42,7 @@ vi.mock('../../../src/config', () => ({ })); vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn() })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, addTripMember, createDay, createPlace, createDayAssignment, createTag } from '../../helpers/factories'; import { type TripAccess } from '../../../src/nest/database/database.service'; @@ -109,11 +106,6 @@ function tripOwnedBy(userId: number): TripAccess { return { user_id: userId } as TripAccess; } -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); diff --git a/server/tests/unit/nest/atlas.service.test.ts b/server/tests/unit/nest/atlas.service.test.ts index 64f94c3d0..570f15d63 100644 --- a/server/tests/unit/nest/atlas.service.test.ts +++ b/server/tests/unit/nest/atlas.service.test.ts @@ -10,12 +10,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup (real in-memory SQLite — same pattern as mcp unit tests) ──────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -30,13 +28,12 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; + +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createReservation } from '../../helpers/factories'; import { getCountryFromCoords, getCountryFromAddress, isPointInCountryBox, reverseGeocodeCountry, getRegionGeo, getCountryGeo } from '../../../src/nest/atlas/atlas-geo'; @@ -81,8 +78,6 @@ function insertPlace(db: any, tripId: number, name: string, address: string | nu } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); atlas = new AtlasService( await createTestBucketListRepo(testDb), await createTestHiddenCountriesRepo(testDb), diff --git a/server/tests/unit/nest/audit.service.test.ts b/server/tests/unit/nest/audit.service.test.ts index eee71b859..4482be48c 100644 --- a/server/tests/unit/nest/audit.service.test.ts +++ b/server/tests/unit/nest/audit.service.test.ts @@ -20,9 +20,7 @@ vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ import type { Request } from 'express'; import { ValidationError } from '@mikro-orm/core'; -import Database from 'better-sqlite3'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; import { createTestOrm, type TestOrm } from '../../helpers/test-orm'; import { AuditLog } from '../../../src/db/entities/AuditLog.entity'; import { Users } from '../../../src/db/entities/Users.entity'; @@ -32,10 +30,7 @@ import { AuditService } from '../../../src/nest/audit/audit.service'; import { getClientIp } from '../../../src/nest/audit/client-ip'; import { logInfo, logDebug, logError } from '../../../src/nest/audit/audit-log.logger'; -const testDb = new Database(':memory:'); -testDb.exec('PRAGMA journal_mode = WAL'); -testDb.exec('PRAGMA foreign_keys = ON'); -testDb.exec('PRAGMA busy_timeout = 5000'); +const testDb = createSnapshotTestDb(); let t: TestOrm; let auditLogRepo: AuditLogRepository; @@ -43,8 +38,6 @@ let usersRepo: UsersRepository; let svc: AuditService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await createTestOrm(testDb); auditLogRepo = t.repo(AuditLog); usersRepo = t.repo(Users); diff --git a/server/tests/unit/nest/calendar.service.test.ts b/server/tests/unit/nest/calendar.service.test.ts index 13a65aa15..59805c23b 100644 --- a/server/tests/unit/nest/calendar.service.test.ts +++ b/server/tests/unit/nest/calendar.service.test.ts @@ -9,12 +9,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -29,10 +27,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -41,8 +39,7 @@ vi.mock('../../../src/config', () => ({ const { broadcast } = vi.hoisted(() => ({ broadcast: vi.fn() })); vi.mock('../../../src/websocket', () => ({ broadcast })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createReservation, createPlace, createDay, createDayAssignment, createDayNote } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -63,7 +60,6 @@ import { budgetRepoArgs } from '../../helpers/budget-repos'; const dbs = () => new DatabaseService(testDb); - // Named `svc` so the moved cases below read exactly as they did on TripsService. let budgetSvc: BudgetService; let svc: CalendarService; @@ -75,11 +71,6 @@ beforeAll(async () => { ); }); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); @@ -1597,7 +1588,6 @@ describe('folded quirk branches', () => { }); }); - // foldICS is the last thing that touches the bytes of both the download and the // feed, and it is exported, so it is pinned directly instead of through a trip. describe('foldICS', () => { diff --git a/server/tests/unit/nest/categories.service.test.ts b/server/tests/unit/nest/categories.service.test.ts index 6bf1efa44..0bca744bf 100644 --- a/server/tests/unit/nest/categories.service.test.ts +++ b/server/tests/unit/nest/categories.service.test.ts @@ -9,9 +9,7 @@ * now injects CategoriesService directly.) */ import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest'; -import Database from 'better-sqlite3'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import { createTestCategoriesRepo, sharedTestOrm } from '../../helpers/test-uow'; @@ -20,17 +18,12 @@ import { CategoriesService } from '../../../src/nest/categories/categories.servi // ── DB setup ────────────────────────────────────────────────────────────────── -const testDb = new Database(':memory:'); -testDb.exec('PRAGMA journal_mode = WAL'); -testDb.exec('PRAGMA foreign_keys = ON'); -testDb.exec('PRAGMA busy_timeout = 5000'); +const testDb = createSnapshotTestDb(); let t: TestOrm; let svc: CategoriesService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); svc = new CategoriesService(await createTestCategoriesRepo(testDb)); }); diff --git a/server/tests/unit/nest/channel-override.test.ts b/server/tests/unit/nest/channel-override.test.ts index 7e0bd6981..5bfd307d9 100644 --- a/server/tests/unit/nest/channel-override.test.ts +++ b/server/tests/unit/nest/channel-override.test.ts @@ -9,12 +9,13 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - return { testDb: db, dbMock: { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null } }; +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'x'.repeat(40), ENCRYPTION_KEY: 'a'.repeat(64), updateJwtSecret: () => {} })); vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ decrypt_api_key: (v: string) => v, maybe_encrypt_api_key: (v: string) => v, encrypt_api_key: (v: string) => v })); const { sendMailMock } = vi.hoisted(() => ({ sendMailMock: vi.fn().mockResolvedValue({ accepted: ['a@b.c'] }) })); @@ -45,8 +46,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => { }; }); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin, setAppSetting, setNotificationChannels } from '../../helpers/factories'; // The send dispatcher is DI-native since the notifications fold @@ -89,8 +89,6 @@ const getPreferencesMatrix = (...a: Parameters { - createTables(testDb); - runMigrations(testDb); prefsSvc = new NotificationPreferencesService( new MailerService(await createTestUsersRepo(testDb), await createTestSettingsRepo(testDb), await createTestAppSettingsRepo(testDb)), await createTestUnitOfWork(testDb), diff --git a/server/tests/unit/nest/collab.service.test.ts b/server/tests/unit/nest/collab.service.test.ts index e29ba4763..6e12384f3 100644 --- a/server/tests/unit/nest/collab.service.test.ts +++ b/server/tests/unit/nest/collab.service.test.ts @@ -12,12 +12,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } // ── DB setup ───────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -32,10 +30,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-jwt-secret-for-trek-testing-only', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -75,8 +73,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => { }; }); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip } from '../../helpers/factories'; import { avatarUrl } from '../../../src/nest/common/avatarUrl'; @@ -148,11 +145,6 @@ beforeAll(async () => { /** A CollabService with its own preview cache and budget, for the tests that fill either. */ const freshSvc = async () => buildCollabService(new DatabaseService(testDb), collabFx.storage, new RateLimitService()); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); mockCheckSsrf.mockResolvedValue({ allowed: true, isPrivate: false, resolvedIp: '93.184.216.34' }); diff --git a/server/tests/unit/nest/collections.service.test.ts b/server/tests/unit/nest/collections.service.test.ts index 0b9e56421..7d4e52c6d 100644 --- a/server/tests/unit/nest/collections.service.test.ts +++ b/server/tests/unit/nest/collections.service.test.ts @@ -11,14 +11,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock, broadcastToUser } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - const broadcastToUser = vi.fn(); - const mock = { +vi.mock('../../../src/db/database', async () => { + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, @@ -31,10 +27,12 @@ const { testDb, dbMock, broadcastToUser } = vi.hoisted(() => { .get(userId, tripId, userId), isOwner: () => false, }; - return { testDb: db, dbMock: mock, broadcastToUser }; }); -vi.mock('../../../src/db/database', () => dbMock); +const { broadcastToUser } = vi.hoisted(() => ({ broadcastToUser: vi.fn() })); + +import { db as testDb } from '../../../src/db/database'; + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -45,8 +43,6 @@ const notifSend = vi.fn().mockResolvedValue(undefined); import fs from 'fs'; import path from 'path'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { createUser, createTrip, createPlace, createCategory, createTag, addTripMember, createDay, createDayAssignment } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; import { PermissionsService } from '../../../src/nest/permissions/permissions.service'; @@ -126,8 +122,6 @@ function clearCollections() { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); }); beforeEach(() => { diff --git a/server/tests/unit/nest/dawarich-suggestions.service.test.ts b/server/tests/unit/nest/dawarich-suggestions.service.test.ts index 1852f4ef2..22f7e6243 100644 --- a/server/tests/unit/nest/dawarich-suggestions.service.test.ts +++ b/server/tests/unit/nest/dawarich-suggestions.service.test.ts @@ -11,17 +11,11 @@ * user's stored credentials. */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; // ── DB setup (real in-memory SQLite — same pattern as the other service tests) ── -const { testDb } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { testDb: db }; -}); +const testDb = createSnapshotTestDb(); vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', @@ -32,8 +26,6 @@ vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn() })); import { DAWARICH_BUCKET_SCAN_LIMIT } from '@trek/shared'; import type { DawarichConnection } from '@trek/shared'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createDay, addTripMember } from '../../helpers/factories'; import { @@ -299,11 +291,6 @@ async function asyncRefusalFrom(fn: () => Promise): Promise { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); // dawarich_visit_suggestions is not in the shared helper's reset list, so it diff --git a/server/tests/unit/nest/dawarich-sync.service.test.ts b/server/tests/unit/nest/dawarich-sync.service.test.ts index 35912c0c5..0cac7c7ab 100644 --- a/server/tests/unit/nest/dawarich-sync.service.test.ts +++ b/server/tests/unit/nest/dawarich-sync.service.test.ts @@ -19,29 +19,23 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup (real in-memory SQLite — same vi.hoisted pattern as atlas/immich) ── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => null, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../src/db/database', () => dbMock); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; + +import { db as testDb } from '../../../src/db/database'; import { resetTestDb, setAddonEnabled } from '../../helpers/test-db'; import { createUser, createTrip } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -237,8 +231,6 @@ function withVisits(...visits: DawarichVisitRaw[]): void { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); suggestions = await createTestDawarichVisitSuggestionsRepo(testDb); trips = await createTestTripsRepo(testDb); diff --git a/server/tests/unit/nest/dawarich.service.test.ts b/server/tests/unit/nest/dawarich.service.test.ts index b5a7a1317..a855862e7 100644 --- a/server/tests/unit/nest/dawarich.service.test.ts +++ b/server/tests/unit/nest/dawarich.service.test.ts @@ -41,17 +41,11 @@ * yet", never a crash on the settings page. */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; // ── DB setup (real in-memory SQLite, the pattern the other Dawarich service tests use) ── -const { testDb } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { testDb: db }; -}); +const testDb = createSnapshotTestDb(); // Same fixed key the global setup exports, pinned here so the at-rest round // trip cannot depend on what is lying in server/data. @@ -76,8 +70,6 @@ vi.mock('../../../src/utils/ssrfGuard', () => ({ })); import { DAWARICH_KEY_MASK, type DawarichCapabilities } from '@trek/shared'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import type { DawarichConnectionsRepository } from '../../../src/db/repositories/DawarichConnections.repository'; @@ -245,8 +237,6 @@ const countryCodeAbsentCases: Array<[string, Partial]> = [ ]; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); connections = await createTestDawarichConnectionsRepo(testDb); svc = new DawarichService( diff --git a/server/tests/unit/nest/days.service.test.ts b/server/tests/unit/nest/days.service.test.ts index 2b81c3b68..5a481202f 100644 --- a/server/tests/unit/nest/days.service.test.ts +++ b/server/tests/unit/nest/days.service.test.ts @@ -14,12 +14,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -38,10 +36,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -49,8 +47,7 @@ vi.mock('../../../src/config', () => ({ })); vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn() })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createDay, createPlace, createDayAssignment, createDayAccommodation, createDayNote, createTag } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -96,11 +93,6 @@ beforeAll(async () => { accommodations = await makeAccommodationsService(testDb); }); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); diff --git a/server/tests/unit/nest/doc-sync/doc-sync-config.service.test.ts b/server/tests/unit/nest/doc-sync/doc-sync-config.service.test.ts index 6ad32f487..d1ab9d630 100644 --- a/server/tests/unit/nest/doc-sync/doc-sync-config.service.test.ts +++ b/server/tests/unit/nest/doc-sync/doc-sync-config.service.test.ts @@ -15,27 +15,21 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => undefined, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../../src/db/database', () => dbMock); + // The whole guard surface, not only `checkSsrf`: modules on the import graph // pull other names off it, and a factory mock that omits one throws on access @@ -51,9 +45,8 @@ vi.mock('../../../../src/utils/ssrfGuard', () => ({ SsrfBlockedError: class extends Error {}, })); +import { db as testDb } from '../../../../src/db/database'; import { DOCSYNC_SECRET_MASK, type DocsyncConnectionInput, type DocsyncLinkInput } from '@trek/shared'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import { resetTestDb } from '../../../helpers/test-db'; import { createTrip, createUser } from '../../../helpers/factories'; import { @@ -124,8 +117,6 @@ async function storedSecret(connectionId: number): Promise { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); svc = new DocSyncConfigService( await createTestTripsRepo(testDb), await createTestDocumentProvidersRepo(testDb), diff --git a/server/tests/unit/nest/doc-sync/doc-sync.controller.test.ts b/server/tests/unit/nest/doc-sync/doc-sync.controller.test.ts index 56f3e3594..394536784 100644 --- a/server/tests/unit/nest/doc-sync/doc-sync.controller.test.ts +++ b/server/tests/unit/nest/doc-sync/doc-sync.controller.test.ts @@ -18,26 +18,21 @@ import type { Request } from 'express'; * reconciler is a spy: a run is a side effect here, not the subject. */ -const { testDb, dbMock } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => undefined, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../../src/db/database', () => dbMock); + vi.mock('../../../../src/websocket', () => ({ broadcast: vi.fn(), broadcastToUser: vi.fn() })); /** @@ -57,8 +52,7 @@ vi.mock('../../../../src/utils/ssrfGuard', async (importOriginal) => ({ checkSsrf, })); -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; +import { db as testDb } from '../../../../src/db/database'; import { createTrip, createUser } from '../../../helpers/factories'; import { DocSyncController } from '../../../../src/nest/doc-sync/doc-sync.controller'; import { DocSyncConfigService } from '../../../../src/nest/doc-sync/doc-sync-config.service'; @@ -210,8 +204,6 @@ let tripId: number; let otherTripId: number; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); // L1 (Plan 3h Task 7 review): createLink's fire-and-forget syncLink call // is now wrapped in its own withRequestContext fork, which needs a real // MikroORM instance — sync itself stays a spy, this ORM never resolves a diff --git a/server/tests/unit/nest/doc-sync/doc-sync.job.test.ts b/server/tests/unit/nest/doc-sync/doc-sync.job.test.ts index c14d33206..11b667865 100644 --- a/server/tests/unit/nest/doc-sync/doc-sync.job.test.ts +++ b/server/tests/unit/nest/doc-sync/doc-sync.job.test.ts @@ -14,7 +14,7 @@ * service's query, so it runs the tick over the real service and a database. */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } from 'vitest'; -import Database from 'better-sqlite3'; +import { createSnapshotTestDb } from '../../../helpers/db-mock'; const log = vi.hoisted(() => ({ LOG_LEVEL: 'error', @@ -39,8 +39,6 @@ import { AllowedFileTypesService } from '../../../../src/nest/files/allowed-file import type { FilesService } from '../../../../src/nest/files/files.service'; import type { StorageService } from '../../../../src/nest/storage/storage.service'; import type { RealtimeService } from '../../../../src/nest/realtime/realtime.service'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import { createTrip, createUser } from '../../../helpers/factories'; import { createTestUnitOfWork, createTestAppSettingsRepo, createTestTripsRepo } from '../../../helpers/test-uow'; import { @@ -417,7 +415,7 @@ describe('DocSyncJob due-ness', () => { * whatever this file told it. */ describe('DocSyncJob and a provider switched off in the admin panel', () => { - const testDb = new Database(':memory:'); + const testDb = createSnapshotTestDb(); let paperlessLink: number; let nextcloudLink: number; @@ -450,8 +448,6 @@ describe('DocSyncJob and a provider switched off in the admin panel', () => { const linkRow = (id: number) => testDb.prepare('SELECT * FROM trip_document_links WHERE id = ?').get(id); beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); appSettingsRepo = await createTestAppSettingsRepo(testDb); config = new DocSyncConfigService( await createTestTripsRepo(testDb), diff --git a/server/tests/unit/nest/doc-sync/doc-sync.service.test.ts b/server/tests/unit/nest/doc-sync/doc-sync.service.test.ts index 8f0bd7c3e..3068b7ab4 100644 --- a/server/tests/unit/nest/doc-sync/doc-sync.service.test.ts +++ b/server/tests/unit/nest/doc-sync/doc-sync.service.test.ts @@ -20,31 +20,24 @@ import os from 'node:os'; import path from 'node:path'; import { Readable } from 'node:stream'; -const { testDb, dbMock } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => null, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../../src/db/database', () => dbMock); + +import { db as testDb } from '../../../../src/db/database'; import type { DocsyncErrorCode } from '@trek/shared'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import { createTrip, createUser } from '../../../helpers/factories'; import { AllowedFileTypesService } from '../../../../src/nest/files/allowed-file-types.service'; import { MAX_FILE_SIZE } from '../../../../src/nest/files/files.constants'; @@ -313,8 +306,6 @@ const switchProvider = (id: string, on: boolean) => describe('DocSyncService', () => { beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); spoolDir = fs.mkdtempSync(path.join(os.tmpdir(), 'trek-docsync-')); ownerId = createUser(testDb, { username: 'owner', email: 'owner@docsync.test' }).user.id; diff --git a/server/tests/unit/nest/files.service.test.ts b/server/tests/unit/nest/files.service.test.ts index 6cb122f39..8b41c5fb3 100644 --- a/server/tests/unit/nest/files.service.test.ts +++ b/server/tests/unit/nest/files.service.test.ts @@ -11,15 +11,17 @@ import path from 'path'; // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, bareDb, dbMock } = vi.hoisted(() => { +// A second, schema-less DB for the getAllowedExtensions catch branch. +const { bareDb } = vi.hoisted(() => { + // eslint-disable-next-line @typescript-eslint/no-require-imports const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - // A second, schema-less DB for the getAllowedExtensions catch branch. - const bare = new Database(':memory:'); - const mock = { + return { bareDb: new Database(':memory:') }; +}); + +vi.mock('../../../src/db/database', async () => { + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, @@ -33,10 +35,9 @@ const { testDb, bareDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: unknown, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, bareDb: bare, dbMock: mock }; }); -vi.mock('../../../src/db/database', () => dbMock); +import { db as testDb } from '../../../src/db/database'; vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -54,8 +55,6 @@ const { consumeEphemeralToken } = vi.hoisted(() => ({ consumeEphemeralToken: vi. vi.mock('../../../src/nest/auth/ephemeral-tokens', () => ({ consumeEphemeralToken })); import type { Request } from 'express'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, addTripMember, createPlace, createReservation, createDay, createDayAssignment, setAppSetting, createCollabNote } from '../../helpers/factories'; import { DatabaseService, type TripAccess } from '../../../src/nest/database/database.service'; @@ -104,11 +103,6 @@ let svc: FilesService; let tripFilesRepo: TripFilesRepository; let fileLinksRepo: FileLinksRepository; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeAll(async () => { tripFilesRepo = await createTestTripFilesRepo(testDb); fileLinksRepo = await createTestFileLinksRepo(testDb); diff --git a/server/tests/unit/nest/immich.service.test.ts b/server/tests/unit/nest/immich.service.test.ts index 156622ace..356d2058c 100644 --- a/server/tests/unit/nest/immich.service.test.ts +++ b/server/tests/unit/nest/immich.service.test.ts @@ -9,16 +9,13 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - return { - testDb: db, - dbMock: { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }, - }; +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'x'.repeat(40), ENCRYPTION_KEY: 'a'.repeat(64), @@ -43,8 +40,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => ({ SsrfBlockedError: class extends Error {}, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { ImmichService } from '../../../src/nest/memories/immich.service'; import type { AuditService } from '../../../src/nest/audit/audit.service'; import type { MemoriesAccessService } from '../../../src/nest/memories/memories-access.service'; @@ -78,8 +74,6 @@ function upstream(opts: { ok?: boolean; status?: number; json?: unknown; url?: s } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const users = await createTestUsersRepo(testDb); svc = new ImmichService(audit as unknown as AuditService, access as unknown as MemoriesAccessService, journeyFx.storage, users); }); diff --git a/server/tests/unit/nest/journey-book.service.test.ts b/server/tests/unit/nest/journey-book.service.test.ts index e3e762f6f..524fc0ca5 100644 --- a/server/tests/unit/nest/journey-book.service.test.ts +++ b/server/tests/unit/nest/journey-book.service.test.ts @@ -8,26 +8,21 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => null, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -35,8 +30,7 @@ vi.mock('../../../src/config', () => ({ })); vi.mock('../../../src/websocket', () => ({ broadcastToUser: vi.fn() })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createJourney, addJourneyContributor } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -69,8 +63,6 @@ function doc(title = 'one') { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const uow = await createTestUnitOfWork(testDb); const t = await sharedTestOrm(testDb); domain = new JourneyDomainService( diff --git a/server/tests/unit/nest/journey-domain.service.test.ts b/server/tests/unit/nest/journey-domain.service.test.ts index 19799512d..889a02109 100644 --- a/server/tests/unit/nest/journey-domain.service.test.ts +++ b/server/tests/unit/nest/journey-domain.service.test.ts @@ -6,12 +6,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach, afterAll } // -- DB setup ----------------------------------------------------------------- -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); // Plan 3g Task 1 (Part A, R9): `getPlaceWithTags`/`canAccessTrip`/`isOwner` // dropped from this mock — none of the three are exports of // `src/db/database.ts` any more (it exports only `db`/`closeDb`/ @@ -23,10 +21,10 @@ const { testDb, dbMock } = vi.hoisted(() => { // guard now goes through a REAL `TripsRepository.findAccessible` below // (`createTestTripsRepo`), not a hand-written closure. const mock = { db, closeDb: () => {}, reinitialize: () => {} }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -34,8 +32,7 @@ vi.mock('../../../src/config', () => ({ })); vi.mock('../../../src/websocket', () => ({ broadcastToUser: vi.fn() })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, @@ -97,8 +94,6 @@ let placesRepoDirect: Awaited>; // R9's "fix the constructor call for the entire file in one pass" edit; nothing // below this block changes. beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const t = await sharedTestOrm(testDb); dbs = new DatabaseService(dbConn, t.em); journeysRepoDirect = await createTestJourneysRepo(testDb); @@ -2773,7 +2768,6 @@ describe('addTripToJourney guards', () => { }); }); - // -- Dismissing a suggestion (discussion #2299) -------------------------------- describe('dismissed suggestions', () => { diff --git a/server/tests/unit/nest/journey-share.service.test.ts b/server/tests/unit/nest/journey-share.service.test.ts index de749efbc..d5fe44293 100644 --- a/server/tests/unit/nest/journey-share.service.test.ts +++ b/server/tests/unit/nest/journey-share.service.test.ts @@ -6,12 +6,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // -- DB setup ----------------------------------------------------------------- -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -20,18 +18,17 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => null, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', updateJwtSecret: () => {}, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createJourney, createJourneyEntry, addJourneyContributor } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -63,8 +60,6 @@ let t: TestOrm; // call here would be unsafe once `SettingsService`'s writes go through // `uow.transactional(...)`. beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const uow = await createTestUnitOfWork(testDb); t = await sharedTestOrm(testDb); const journeysRepo = await createTestJourneysRepo(testDb); diff --git a/server/tests/unit/nest/journey-stats.service.test.ts b/server/tests/unit/nest/journey-stats.service.test.ts index 53739f26c..5ffa55bd8 100644 --- a/server/tests/unit/nest/journey-stats.service.test.ts +++ b/server/tests/unit/nest/journey-stats.service.test.ts @@ -8,26 +8,21 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { - testDb: db, - dbMock: { +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => null, isOwner: () => false, - }, - }; + }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -54,8 +49,7 @@ vi.mock('../../../src/nest/atlas/atlas-geo', () => ({ }, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createJourney, createJourneyEntry, createPlace, @@ -93,8 +87,6 @@ function placeEntry( } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const t = await sharedTestOrm(testDb); svc = new JourneyDomainService( dbs, new RealtimeService(), new TrekPhotoRegistrationService(t.repo(TrekPhotos), t.repo(TripPhotos), await createTestJourneyPhotosRepo(testDb), dbs), await createTestUnitOfWork(testDb), diff --git a/server/tests/unit/nest/mailer.service.test.ts b/server/tests/unit/nest/mailer.service.test.ts index 9227178de..5cfbee284 100644 --- a/server/tests/unit/nest/mailer.service.test.ts +++ b/server/tests/unit/nest/mailer.service.test.ts @@ -10,12 +10,10 @@ * Constructed directly (no TestingModule, repo convention). */ -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -23,9 +21,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => undefined, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); + const { sendMail, createTransport } = vi.hoisted(() => { const send = vi.fn().mockResolvedValue({ messageId: 'test' }); return { @@ -34,7 +33,6 @@ const { sendMail, createTransport } = vi.hoisted(() => { }; }); -vi.mock('../../../src/db/database', () => dbMock); vi.mock('nodemailer', () => ({ default: { createTransport } })); vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ logInfo: vi.fn(), @@ -43,9 +41,8 @@ vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ logWarn: vi.fn(), })); +import { db as testDb } from '../../../src/db/database'; import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { MailerService } from '../../../src/nest/notifications/mailer/mailer.service'; import { logError, logInfo, logWarn } from '../../../src/nest/audit/audit-log.logger'; @@ -78,8 +75,6 @@ function lastTransportOptions(): Record { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); testOrm = await createTestOrm(testDb); }); diff --git a/server/tests/unit/nest/memories-access.service.test.ts b/server/tests/unit/nest/memories-access.service.test.ts index 5d4f88afa..7efcb7391 100644 --- a/server/tests/unit/nest/memories-access.service.test.ts +++ b/server/tests/unit/nest/memories-access.service.test.ts @@ -8,12 +8,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ───────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -28,10 +26,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -53,8 +51,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => { }; }); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip } from '../../helpers/factories'; import { mapDbError, pipeAsset, type ServiceResult } from '../../../src/nest/memories/memories.helpers'; @@ -84,8 +81,6 @@ const getAlbumIdFromLink = (...a: Parameters { - createTables(testDb); - runMigrations(testDb); const t = await sharedTestOrm(testDb); const dbs = new DatabaseService(testDb, t.em); // `DatabaseService.prototype.canAccessTrip` is patched on the PROTOTYPE diff --git a/server/tests/unit/nest/notification-preferences.service.test.ts b/server/tests/unit/nest/notification-preferences.service.test.ts index 1473de459..12c86c0fd 100644 --- a/server/tests/unit/nest/notification-preferences.service.test.ts +++ b/server/tests/unit/nest/notification-preferences.service.test.ts @@ -4,11 +4,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -17,10 +16,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => null, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-jwt-secret-for-trek-testing-only', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -32,8 +31,7 @@ vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ encrypt_api_key: (v: string) => v, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin, setAppSetting, setNotificationChannels, disableNotificationPref } from '../../helpers/factories'; import { MailerService } from '../../../src/nest/notifications/mailer/mailer.service'; @@ -65,10 +63,7 @@ const getActiveChannels = (...a: Parameters) => svc.ge const isSmtpConfigured = (...a: Parameters) => svc.isSmtpConfigured(...a); const isWebhookConfigured = (...a: Parameters) => svc.isWebhookConfigured(...a); - beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const usersRepo = await createTestUsersRepo(testDb); const settingsRepo = await createTestSettingsRepo(testDb); const appSettingsRepoForChannels = await createTestAppSettingsRepo(testDb); diff --git a/server/tests/unit/nest/notification-transports.test.ts b/server/tests/unit/nest/notification-transports.test.ts index 5db742c9f..400cfc29e 100644 --- a/server/tests/unit/nest/notification-transports.test.ts +++ b/server/tests/unit/nest/notification-transports.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, afterEach, afterAll, beforeAll, beforeEach } from 'vitest'; -import Database from 'better-sqlite3'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; vi.mock('../../../src/db/database', () => ({ db: { prepare: () => ({ get: vi.fn(() => undefined), all: vi.fn(() => []) }) }, @@ -52,8 +52,6 @@ import { NtfyService, resolveNtfyUrl, resolveAdminNtfyUrl, resolveNtfyToken, typ import { buildBuiltinChannels } from '../../../src/nest/notifications/channels/builtins'; import { checkSsrf } from '../../../src/utils/ssrfGuard'; import { logError } from '../../../src/nest/audit/audit-log.logger'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { createTestSettingsRepo, createTestAppSettingsRepo } from '../../helpers/test-uow'; // The transports are providers now, taking SettingsRepository/ @@ -72,9 +70,7 @@ let sendWebhook: WebhookService['sendWebhook']; let sendNtfy: NtfyService['sendNtfy']; beforeAll(async () => { - const transportsDb = new Database(':memory:'); - createTables(transportsDb); - runMigrations(transportsDb); + const transportsDb = createSnapshotTestDb(); const settingsRepo = await createTestSettingsRepo(transportsDb); const appSettingsRepo = await createTestAppSettingsRepo(transportsDb); webhookSvc = new WebhookService(settingsRepo, appSettingsRepo); @@ -464,9 +460,7 @@ describe('GHSA-7pqc-fj3c-9346: ntfy token is only attached when the target is th // the same call NotificationsService.send() makes. describe('GHSA-7pqc-fj3c-9346 (live path): buildBuiltinChannels sendToUser resolves configs through the repositories', () => { it('operator-server user gets the admin token; foreign-server user does not', async () => { - const liveDb = new Database(':memory:'); - createTables(liveDb); - runMigrations(liveDb); + const liveDb = createSnapshotTestDb(); liveDb .prepare("INSERT INTO users (id, username, email, password_hash, role) VALUES (1,'op','op@x','x','admin'),(2,'u2','u2@x','x','user')") .run(); diff --git a/server/tests/unit/nest/notifications.inapp-prefs.test.ts b/server/tests/unit/nest/notifications.inapp-prefs.test.ts index 3abccd103..da668e7bc 100644 --- a/server/tests/unit/nest/notifications.inapp-prefs.test.ts +++ b/server/tests/unit/nest/notifications.inapp-prefs.test.ts @@ -7,11 +7,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -20,10 +19,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => null, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-jwt-secret-for-trek-testing-only', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -37,8 +36,7 @@ const { broadcastMock } = vi.hoisted(() => ({ broadcastMock: vi.fn() })); // export both even though only broadcastToUser is asserted here. vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn(), broadcastToUser: broadcastMock })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin, disableNotificationPref } from '../../helpers/factories'; import { registerAction } from '../../../src/nest/notifications/in-app-actions'; @@ -58,8 +56,6 @@ const createNotificationForRecipient = (...a: Parameters) => notifications.respond(...a); beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); notifications = await makeNotificationsService(new DatabaseService(testDb)); }); diff --git a/server/tests/unit/nest/notifications.service.test.ts b/server/tests/unit/nest/notifications.service.test.ts index 05d9d238c..074b3b851 100644 --- a/server/tests/unit/nest/notifications.service.test.ts +++ b/server/tests/unit/nest/notifications.service.test.ts @@ -9,11 +9,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -22,10 +21,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => null, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-jwt-secret-for-trek-testing-only', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -88,8 +87,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => { }; }); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin, setAppSetting, setNotificationChannels, disableNotificationPref } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -140,8 +138,6 @@ function countAllNotifications(): number { // ── Setup ────────────────────────────────────────────────────────────────── beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); notifications = await makeNotificationsService(new DatabaseService(testDb)); }); diff --git a/server/tests/unit/nest/oauth.service.test.ts b/server/tests/unit/nest/oauth.service.test.ts index f784fc096..b2a2cda77 100644 --- a/server/tests/unit/nest/oauth.service.test.ts +++ b/server/tests/unit/nest/oauth.service.test.ts @@ -11,12 +11,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; import crypto from 'crypto'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -27,10 +25,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-jwt-secret-for-trek-testing-only', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -42,13 +40,12 @@ vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ maybe_encrypt_api_key: (v: string) => v, })); vi.mock('../../../src/mcp/sessionManager', () => ({ revokeUserSessions: vi.fn(), revokeUserSessionsForClient: vi.fn(), sessions: new Map() })); +import { db as testDb } from '../../../src/db/database'; import { revokeUserSessionsForClient } from '../../../src/mcp/sessionManager'; vi.mock('../../../src/demo/demo-reset', () => ({ saveBaseline: vi.fn() })); const { isAddonEnabled } = vi.hoisted(() => ({ isAddonEnabled: vi.fn().mockReturnValue(true) })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; // PKCE helper — generates a valid code_verifier + code_challenge pair (RFC 7636) @@ -111,8 +108,6 @@ const getConsent = (...args: Parameters) => svc.getC const isConsentSufficient = (...args: Parameters) => svc.isConsentSufficient(...args); beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await createTestOrm(testDb); auditLogRepo = t.repo(AuditLog); usersRepo = t.repo(Users); diff --git a/server/tests/unit/nest/packing.service.test.ts b/server/tests/unit/nest/packing.service.test.ts index e76e2e1d5..f3e786b8a 100644 --- a/server/tests/unit/nest/packing.service.test.ts +++ b/server/tests/unit/nest/packing.service.test.ts @@ -18,12 +18,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -38,10 +36,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -56,8 +54,7 @@ const permissionsStub = { checkPermission } as unknown as PermissionsService; const { send } = vi.hoisted(() => ({ send: vi.fn(() => Promise.resolve()) })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin, createTrip, addTripMember } from '../../helpers/factories'; import type { PermissionsService } from '../../../src/nest/permissions/permissions.service'; @@ -88,8 +85,6 @@ let packingItemContributorsRepoDirect: Awaited { - createTables(testDb); - runMigrations(testDb); packingItemsRepoDirect = await createTestPackingItemsRepo(testDb); packingBagsRepoDirect = await createTestPackingBagsRepo(testDb); packingTemplatesRepoDirect = await createTestPackingTemplatesRepo(testDb); diff --git a/server/tests/unit/nest/permissions.service.test.ts b/server/tests/unit/nest/permissions.service.test.ts index 302affb23..19e4073ad 100644 --- a/server/tests/unit/nest/permissions.service.test.ts +++ b/server/tests/unit/nest/permissions.service.test.ts @@ -21,21 +21,19 @@ import { ValidationError } from '@mikro-orm/core'; // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, reinitialize: () => {}, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + // The service logs unexpected load failures via the plain audit logger. vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', @@ -45,8 +43,7 @@ vi.mock('../../../src/nest/audit/audit-log.logger', () => ({ logWarn: vi.fn(), })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { logError } from '../../../src/nest/audit/audit-log.logger'; import { PermissionsService, PERMISSION_ACTIONS } from '../../../src/nest/permissions/permissions.service'; import { @@ -62,11 +59,6 @@ let svc: PermissionsService; let t: TestOrm; let appSettings: AppSettingsRepository; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeAll(async () => { t = await createTestOrm(testDb); appSettings = t.repo(AppSettings); diff --git a/server/tests/unit/nest/photo-resolver.service.test.ts b/server/tests/unit/nest/photo-resolver.service.test.ts index 3b90cb979..92a3db65a 100644 --- a/server/tests/unit/nest/photo-resolver.service.test.ts +++ b/server/tests/unit/nest/photo-resolver.service.test.ts @@ -10,16 +10,13 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - return { - testDb: db, - dbMock: { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }, - }; +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'x'.repeat(40), ENCRYPTION_KEY: 'a'.repeat(64), @@ -31,8 +28,7 @@ vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ maybe_encrypt_api_key: (v: string) => v, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { DatabaseService } from '../../../src/nest/database/database.service'; import { TrekPhotoRegistrationService } from '../../../src/nest/photos/trek-photos.repository'; import { TrekPhotos } from '../../../src/db/entities/TrekPhotos.entity'; @@ -98,8 +94,6 @@ function insertPhoto(cols: Record): number { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await createTestOrm(testDb); const repo = new TrekPhotoRegistrationService(t.repo(TrekPhotos), t.repo(TripPhotos), t.repo(JourneyPhotos), dbs); svc = new PhotoResolverService( diff --git a/server/tests/unit/nest/places.directions.test.ts b/server/tests/unit/nest/places.directions.test.ts index cd12b6d7f..69979fa52 100644 --- a/server/tests/unit/nest/places.directions.test.ts +++ b/server/tests/unit/nest/places.directions.test.ts @@ -8,10 +8,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA foreign_keys = ON'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); // DatabaseService delegates the joined read straight through to the module, so the // mock has to answer it: without this the insert half of every import throws. const mock = { @@ -23,9 +23,9 @@ const { testDb, dbMock } = vi.hoisted(() => { return place ? { ...place, category: null, tags: [] } : null; }, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + const { checkSsrf, safeFetchFollow } = vi.hoisted(() => ({ checkSsrf: vi.fn(async () => ({ allowed: true, resolvedIp: '1.2.3.4' })), @@ -38,8 +38,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => ({ SsrfBlockedError: class SsrfBlockedError extends Error {}, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { createUser, createTrip, createPlace } from '../../helpers/factories'; import { accommodationsOver } from '../../helpers/accommodations-service'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -131,8 +130,6 @@ const geocoder = () => vi.fn(async (query: string) => { let tripId: string; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); // Plan 3c Task 0b: `dbs` is constructed at module load, before any // `beforeAll` can resolve a real `EntityManager` — the four // repository-backed methods are spied directly on this instance instead, diff --git a/server/tests/unit/nest/places.service.test.ts b/server/tests/unit/nest/places.service.test.ts index 9e2e9fb60..14068b5c9 100644 --- a/server/tests/unit/nest/places.service.test.ts +++ b/server/tests/unit/nest/places.service.test.ts @@ -18,12 +18,10 @@ import { ADDRESS_BACKFILL_MAX_PLACES } from '../../../src/nest/places/places.hel // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -41,10 +39,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: (tripId: any, userId: number) => db.prepare(`SELECT t.id, t.user_id FROM trips t LEFT JOIN trip_members m ON m.trip_id = t.id AND m.user_id = ? WHERE t.id = ? AND (t.user_id = ? OR m.user_id IS NOT NULL)`).get(userId, tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -61,8 +59,7 @@ vi.mock('../../../src/config', () => ({ const removeIfUnreferencedSpy = vi.fn(); const photoCacheStub = { removeIfUnreferenced: removeIfUnreferencedSpy } as unknown as PlacePhotoCacheService; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { accommodationsOver } from '../../helpers/accommodations-service'; import { createUser, createTrip, createPlace, createDay, createCategory, createTag, addTripMember } from '../../helpers/factories'; @@ -160,7 +157,6 @@ async function makePlacesService( ); } - let accommodations: Awaited>; let svc: Awaited>; beforeAll(async () => { @@ -190,11 +186,6 @@ beforeAll(async () => { svc = await makePlacesService(); }); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); diff --git a/server/tests/unit/nest/reminder-jobs.service.test.ts b/server/tests/unit/nest/reminder-jobs.service.test.ts index 95879951a..e08166425 100644 --- a/server/tests/unit/nest/reminder-jobs.service.test.ts +++ b/server/tests/unit/nest/reminder-jobs.service.test.ts @@ -7,21 +7,16 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - return { - testDb: db, - dbMock: { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => undefined, isOwner: () => false }, - }; +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: () => undefined, isOwner: () => false }; }); + const logMock = vi.hoisted(() => ({ LOG_LEVEL: 'error', logInfo: vi.fn(), logError: vi.fn(), logWarn: vi.fn(), logDebug: vi.fn() })); -vi.mock('../../../src/db/database', () => dbMock); vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn(), broadcastToUser: vi.fn() })); vi.mock('../../../src/nest/audit/audit-log.logger', () => logMock); vi.mock('../../../src/config', () => ({ @@ -30,8 +25,7 @@ vi.mock('../../../src/config', () => ({ updateJwtSecret: () => {}, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { createUser, createTrip, createTodoItem, setAppSetting, setNotificationChannels } from '../../helpers/factories'; import { ReminderJobsService } from '../../../src/nest/notifications/reminder-jobs.service'; import { notificationsStub } from '../../helpers/notifications'; @@ -87,8 +81,6 @@ function tripWithReminder(userId: number, days: number, title = 'Lisbon'): numbe } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); appSettingsRepo = await createTestAppSettingsRepo(testDb); tripsRepo = await createTestTripsRepo(testDb); todoItemsRepo = await createTestTodoItemsRepo(testDb); diff --git a/server/tests/unit/nest/reservations.service.test.ts b/server/tests/unit/nest/reservations.service.test.ts index 3c8c01063..7e778e5dd 100644 --- a/server/tests/unit/nest/reservations.service.test.ts +++ b/server/tests/unit/nest/reservations.service.test.ts @@ -9,13 +9,10 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, reinitialize: () => {}, getPlaceWithTags: () => null, canAccessTrip: (tripId: unknown, userId: number) => db.prepare(` @@ -26,10 +23,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: unknown, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + const { broadcast } = vi.hoisted(() => ({ broadcast: vi.fn() })); vi.mock('../../../src/websocket', () => ({ broadcast })); @@ -43,9 +40,8 @@ const budget = { createBudgetItem: vi.fn(), updateBudgetItem: vi.fn(), deleteBud const { notif } = vi.hoisted(() => ({ notif: { send: vi.fn().mockResolvedValue(undefined) } })); +import { db as testDb } from '../../../src/db/database'; import { HttpException } from '@nestjs/common'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createReservation, createBudgetItem, createPlace, createDay, createDayAccommodation, createDayAssignment, addTripMember } from '../../helpers/factories'; import type { PermissionsService } from '../../../src/nest/permissions/permissions.service'; @@ -84,7 +80,6 @@ beforeAll(async () => { svc = new ReservationsService(dbs, permissionsStub, budget as unknown as BudgetService, new RealtimeService(), notificationsStub(notif.send), new ReservationsReadService(await createTestReservationsRepo(testDb), await createTestReservationEndpointsRepo(testDb), await createTestReservationTravelersRepo(testDb)), await makeAccommodationsService(testDb), await createTestUnitOfWork(testDb), await createTestReservationsRepo(testDb), await createTestReservationEndpointsRepo(testDb), await createTestReservationTravelersRepo(testDb), await createTestReservationDayPositionsRepo(testDb), await createTestDayAccommodationsRepo(testDb), await createTestDaysRepo(testDb), await createTestPlacesRepo(testDb), await createTestDayAssignmentsRepo(testDb), await createTestTripMembersRepo(testDb), await createTestUsersRepo(testDb), await createTestTripsRepo(testDb), await createTestBudgetItemsRepo(testDb)); }); -beforeAll(() => { createTables(testDb); runMigrations(testDb); }); beforeEach(() => { resetTestDb(testDb); vi.clearAllMocks(); diff --git a/server/tests/unit/nest/school-holidays.service.test.ts b/server/tests/unit/nest/school-holidays.service.test.ts index a4ddcc8b8..09998c989 100644 --- a/server/tests/unit/nest/school-holidays.service.test.ts +++ b/server/tests/unit/nest/school-holidays.service.test.ts @@ -11,10 +11,8 @@ * service replaced, on the same seeded rows, and compare full-key. */ import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest'; -import Database from 'better-sqlite3'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; import { ConflictException } from '@nestjs/common'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import { sharedTestOrm, createTestUnitOfWork } from '../../helpers/test-uow'; @@ -25,17 +23,12 @@ import { import type { TestOrm } from '../../helpers/test-orm'; import { SchoolHolidaysService } from '../../../src/nest/school-holidays/school-holidays.service'; -const testDb = new Database(':memory:'); -testDb.exec('PRAGMA journal_mode = WAL'); -testDb.exec('PRAGMA foreign_keys = ON'); -testDb.exec('PRAGMA busy_timeout = 5000'); +const testDb = createSnapshotTestDb(); let t: TestOrm; let svc: SchoolHolidaysService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); svc = new SchoolHolidaysService( await createTestSchoolHolidayCountriesRepo(testDb), diff --git a/server/tests/unit/nest/settings.service.test.ts b/server/tests/unit/nest/settings.service.test.ts index 337d34a5b..e5f7b5e4c 100644 --- a/server/tests/unit/nest/settings.service.test.ts +++ b/server/tests/unit/nest/settings.service.test.ts @@ -11,12 +11,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB + apiKeyCrypto mock ──────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -25,10 +23,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => null, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -41,8 +39,7 @@ vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ decrypt_api_key: (v: string) => v, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import type { TestOrm } from '../../helpers/test-orm'; @@ -59,8 +56,6 @@ let t: TestOrm; // bulkUpsertSettings's transaction would then run outside it (or contend on a second // mutex over the same connection), exactly the trap the shared-ORM helpers exist to avoid. beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); svc = new SettingsService( await createTestUnitOfWork(testDb), diff --git a/server/tests/unit/nest/share.service.test.ts b/server/tests/unit/nest/share.service.test.ts index 09e1274e0..543c76d49 100644 --- a/server/tests/unit/nest/share.service.test.ts +++ b/server/tests/unit/nest/share.service.test.ts @@ -11,12 +11,10 @@ import type { PlacePhotoCacheService } from '../../../src/nest/place-photos/plac // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -31,10 +29,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -48,8 +46,7 @@ const permissionsStub = { checkPermission } as unknown as PermissionsService; const serveKey = vi.fn(); const photoCacheStub = { serveKey } as unknown as PlacePhotoCacheService; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, addTripMember, createDay, createPlace, createDayAssignment, @@ -70,8 +67,6 @@ let t: TestOrm; // `sharedTestOrm(testDb)` (task-2-review.md I2) — see settings.service.test.ts's // own comment on this pattern. beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); svc = new ShareService( new SettingsService(await createTestUnitOfWork(testDb), await createTestAppSettingsRepo(testDb), await createTestSettingsRepo(testDb)), diff --git a/server/tests/unit/nest/storage/storage-admin.service.test.ts b/server/tests/unit/nest/storage/storage-admin.service.test.ts index 88df3c647..30c0f7e8a 100644 --- a/server/tests/unit/nest/storage/storage-admin.service.test.ts +++ b/server/tests/unit/nest/storage/storage-admin.service.test.ts @@ -1,22 +1,20 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - return { testDb: db, dbMock: { db, closeDb: () => {}, reinitialize: () => {} } }; +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {} }; }); -vi.mock('../../../../src/db/database', () => dbMock); + vi.mock('../../../../src/config', () => ({ ENCRYPTION_KEY: 'storage-admin-test-key' })); +import { db as testDb } from '../../../../src/db/database'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { Logger } from '@nestjs/common'; import { MASKED_SETTING_VALUE, type StorageConfig } from '@trek/shared'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import type { RuntimeEnvService } from '../../../../src/nest/app-config/runtime-env.service'; import { encrypt_api_key } from '../../../../src/nest/common/crypto/apiKeyCrypto'; import { StorageAdminService } from '../../../../src/nest/storage/storage-admin.service'; @@ -28,11 +26,6 @@ import { StorageService } from '../../../../src/nest/storage/storage.service'; import { StorageConflictError } from '../../../../src/nest/storage/storage.types'; import { createTestUnitOfWork, createTestAppSettingsRepo, sharedTestOrm } from '../../../helpers/test-uow'; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - const tmpDirs: string[] = []; function makeTmpDir(): string { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'trek-storage-admin-')); diff --git a/server/tests/unit/nest/storage/storage-jobs.service.test.ts b/server/tests/unit/nest/storage/storage-jobs.service.test.ts index c285de122..6c6f47f2e 100644 --- a/server/tests/unit/nest/storage/storage-jobs.service.test.ts +++ b/server/tests/unit/nest/storage/storage-jobs.service.test.ts @@ -1,22 +1,20 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - return { testDb: db, dbMock: { db, closeDb: () => {}, reinitialize: () => {} } }; +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {} }; }); -vi.mock('../../../../src/db/database', () => dbMock); + vi.mock('../../../../src/config', () => ({ ENCRYPTION_KEY: 'storage-jobs-test-key' })); +import { db as testDb } from '../../../../src/db/database'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { Readable } from 'node:stream'; import { Logger } from '@nestjs/common'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import type { RuntimeEnvService } from '../../../../src/nest/app-config/runtime-env.service'; import { MirrorDriver } from '../../../../src/nest/storage/drivers/mirror.driver'; import { StorageEventsService } from '../../../../src/nest/storage/storage-events.service'; @@ -31,11 +29,6 @@ import { } from '../../../../src/nest/storage/storage-jobs.service'; import { createTestUnitOfWork, createTestAppSettingsRepo, sharedTestOrm } from '../../../helpers/test-uow'; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - const tmpDirs: string[] = []; function makeTmpDir(): string { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'trek-jobs-')); diff --git a/server/tests/unit/nest/storage/storage-registry.service.test.ts b/server/tests/unit/nest/storage/storage-registry.service.test.ts index 5b3619024..00c6f45a2 100644 --- a/server/tests/unit/nest/storage/storage-registry.service.test.ts +++ b/server/tests/unit/nest/storage/storage-registry.service.test.ts @@ -4,24 +4,21 @@ import { Logger } from '@nestjs/common'; // ── DB setup (the permissions.service.test.ts pattern: real in-memory SQLite // so the app_settings SQL is exercised faithfully) ──────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); - return { testDb: db, dbMock: { db, closeDb: () => {}, reinitialize: () => {} } }; +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {} }; }); -vi.mock('../../../../src/db/database', () => dbMock); + vi.mock('../../../../src/config', () => ({ ENCRYPTION_KEY: 'storage-registry-test-key' })); +import { db as testDb } from '../../../../src/db/database'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { Readable } from 'node:stream'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import type { RuntimeEnvService } from '../../../../src/nest/app-config/runtime-env.service'; import { encrypt_api_key } from '../../../../src/nest/common/crypto/apiKeyCrypto'; import { StorageEventsService } from '../../../../src/nest/storage/storage-events.service'; @@ -39,11 +36,6 @@ import { import { STORAGE_CATEGORIES } from '../../../../src/nest/storage/storage.types'; import { createTestUnitOfWork, createTestAppSettingsRepo, sharedTestOrm } from '../../../helpers/test-uow'; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - // ── helpers ─────────────────────────────────────────────────────────────────── const tmpDirs: string[] = []; diff --git a/server/tests/unit/nest/storage/storage-stats.service.test.ts b/server/tests/unit/nest/storage/storage-stats.service.test.ts index 88f6895f0..bc8cb0356 100644 --- a/server/tests/unit/nest/storage/storage-stats.service.test.ts +++ b/server/tests/unit/nest/storage/storage-stats.service.test.ts @@ -1,21 +1,19 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - return { testDb: db, dbMock: { db, closeDb: () => {}, reinitialize: () => {} } }; +vi.mock('../../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {} }; }); -vi.mock('../../../../src/db/database', () => dbMock); + vi.mock('../../../../src/config', () => ({ ENCRYPTION_KEY: 'storage-stats-test-key' })); +import { db as testDb } from '../../../../src/db/database'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { Readable } from 'node:stream'; -import { createTables } from '../../../../src/db/schema'; -import { runMigrations } from '../../../../src/db/migrations'; import type { RuntimeEnvService } from '../../../../src/nest/app-config/runtime-env.service'; import { StorageEventsService } from '../../../../src/nest/storage/storage-events.service'; import { StorageRegistryService } from '../../../../src/nest/storage/storage-registry.service'; @@ -23,11 +21,6 @@ import { StorageService } from '../../../../src/nest/storage/storage.service'; import { StatsBusyError, StorageStatsService } from '../../../../src/nest/storage/storage-stats.service'; import { createTestUnitOfWork, createTestAppSettingsRepo, sharedTestOrm } from '../../../helpers/test-uow'; -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - const tmpDirs: string[] = []; function makeTmpDir(): string { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'trek-stats-')); diff --git a/server/tests/unit/nest/synology.service.test.ts b/server/tests/unit/nest/synology.service.test.ts index 135423d5a..689fe43d7 100644 --- a/server/tests/unit/nest/synology.service.test.ts +++ b/server/tests/unit/nest/synology.service.test.ts @@ -11,16 +11,13 @@ */ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest'; -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - return { - testDb: db, - dbMock: { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }, - }; +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); + return { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, isOwner: () => false, getPlaceWithTags: () => null }; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'x'.repeat(40), ENCRYPTION_KEY: 'a'.repeat(64), @@ -47,8 +44,7 @@ vi.mock('../../../src/utils/ssrfGuard', () => ({ // The service fires the session-cleared notice and .catch()es it, so the stub // has to be a promise. -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { SynologyService } from '../../../src/nest/memories/synology.service'; import type { MemoriesAccessService } from '../../../src/nest/memories/memories-access.service'; import { notificationsStub } from '../../helpers/notifications'; @@ -80,8 +76,6 @@ function httpError(status: number) { } beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); const users = await createTestUsersRepo(testDb); svc = new SynologyService(access as unknown as MemoriesAccessService, notificationsStub(), users); }); diff --git a/server/tests/unit/nest/tags.service.test.ts b/server/tests/unit/nest/tags.service.test.ts index a6ee9ba2c..ad77fb5e8 100644 --- a/server/tests/unit/nest/tags.service.test.ts +++ b/server/tests/unit/nest/tags.service.test.ts @@ -9,9 +9,7 @@ * injects TagsService directly.) */ import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest'; -import Database from 'better-sqlite3'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { createSnapshotTestDb } from '../../helpers/db-mock'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import { createTestTagsRepo, sharedTestOrm } from '../../helpers/test-uow'; @@ -20,17 +18,12 @@ import { TagsService } from '../../../src/nest/tags/tags.service'; // ── DB setup ────────────────────────────────────────────────────────────────── -const testDb = new Database(':memory:'); -testDb.exec('PRAGMA journal_mode = WAL'); -testDb.exec('PRAGMA foreign_keys = ON'); -testDb.exec('PRAGMA busy_timeout = 5000'); +const testDb = createSnapshotTestDb(); let t: TestOrm; let svc: TagsService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); t = await sharedTestOrm(testDb); svc = new TagsService(await createTestTagsRepo(testDb)); }); diff --git a/server/tests/unit/nest/todo.service.test.ts b/server/tests/unit/nest/todo.service.test.ts index 56ae20f99..feb7807da 100644 --- a/server/tests/unit/nest/todo.service.test.ts +++ b/server/tests/unit/nest/todo.service.test.ts @@ -9,12 +9,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ────────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -29,10 +27,10 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -40,8 +38,7 @@ vi.mock('../../../src/config', () => ({ })); vi.mock('../../../src/websocket', () => ({ broadcast: vi.fn() })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, addTripMember } from '../../helpers/factories'; import { DatabaseService } from '../../../src/nest/database/database.service'; @@ -62,11 +59,6 @@ beforeAll(async () => { ); }); -beforeAll(() => { - createTables(testDb); - runMigrations(testDb); -}); - beforeEach(() => { resetTestDb(testDb); }); diff --git a/server/tests/unit/nest/token.service.test.ts b/server/tests/unit/nest/token.service.test.ts index e072ae0d9..3a96c137e 100644 --- a/server/tests/unit/nest/token.service.test.ts +++ b/server/tests/unit/nest/token.service.test.ts @@ -11,12 +11,10 @@ // vi.hoisted: build the real in-memory DB and the module mock before any import // --------------------------------------------------------------------------- -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -24,10 +22,10 @@ const { testDb, dbMock } = vi.hoisted(() => { canAccessTrip: () => undefined, isOwner: () => false, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/nest/auth/ephemeral-tokens', () => ({ createEphemeralToken: vi.fn() })); vi.mock('../../../src/mcp/sessionManager', () => ({ revokeUserSessions: vi.fn() })); @@ -35,9 +33,8 @@ vi.mock('../../../src/mcp/sessionManager', () => ({ revokeUserSessions: vi.fn() // Imports (after mocks) // --------------------------------------------------------------------------- +import { db as testDb } from '../../../src/db/database'; import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; import { TokenService } from '../../../src/nest/tokens/token.service'; @@ -55,8 +52,6 @@ import { createTestMcpTokensRepo, createTestUsersRepo } from '../../helpers/test let svc: TokenService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); svc = new TokenService(await createTestMcpTokensRepo(testDb), await createTestUsersRepo(testDb), new EphemeralTokenService()); }); diff --git a/server/tests/unit/nest/travel-stats.service.test.ts b/server/tests/unit/nest/travel-stats.service.test.ts index fe3d4b0ea..45b6b3ac9 100644 --- a/server/tests/unit/nest/travel-stats.service.test.ts +++ b/server/tests/unit/nest/travel-stats.service.test.ts @@ -11,21 +11,18 @@ * are preserved so the history stays greppable. */ -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => undefined, isOwner: () => false }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + +import { db as testDb } from '../../../src/db/database'; import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createTrip, createPlace, createReservation, addTripMember } from '../../helpers/factories'; import { AtlasService } from '../../../src/nest/atlas/atlas.service'; @@ -42,8 +39,6 @@ import { let atlas: AtlasService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); atlas = new AtlasService( await createTestBucketListRepo(testDb), await createTestHiddenCountriesRepo(testDb), diff --git a/server/tests/unit/nest/unified-memories.service.test.ts b/server/tests/unit/nest/unified-memories.service.test.ts index b4bb8445d..836a07350 100644 --- a/server/tests/unit/nest/unified-memories.service.test.ts +++ b/server/tests/unit/nest/unified-memories.service.test.ts @@ -7,12 +7,10 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup ───────────────────────────────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, @@ -27,18 +25,17 @@ const { testDb, dbMock } = vi.hoisted(() => { isOwner: (tripId: any, userId: number) => !!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId), }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', updateJwtSecret: () => {}, })); -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb, setAddonEnabled } from '../../helpers/test-db'; import { createUser, createTrip } from '../../helpers/factories'; import { ADDON_IDS } from '../../../src/addons'; @@ -105,8 +102,6 @@ const createTripAlbumLink = (...a: Parameters) => sv const removeAlbumLink = (...a: Parameters) => svc.removeAlbumLink(...a); beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); // Plan 3c Task 0b: `dbs` is constructed at module load, before any // `beforeAll` can resolve a real `EntityManager` — `canAccessTrip` (the // only one of the four primitives this suite's code paths reach) is spied diff --git a/server/tests/unit/nest/user-profile.service.test.ts b/server/tests/unit/nest/user-profile.service.test.ts index 4ff59707f..7c791f5e8 100644 --- a/server/tests/unit/nest/user-profile.service.test.ts +++ b/server/tests/unit/nest/user-profile.service.test.ts @@ -11,17 +11,15 @@ // vi.hoisted: build the real in-memory DB and the module mock before any import // --------------------------------------------------------------------------- -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => undefined, isOwner: () => false }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ decrypt_api_key: vi.fn((v) => v), maybe_encrypt_api_key: vi.fn((v) => v), @@ -33,9 +31,8 @@ vi.mock('../../../src/nest/common/crypto/apiKeyCrypto', () => ({ // Imports (after mocks) // --------------------------------------------------------------------------- +import { db as testDb } from '../../../src/db/database'; import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from 'vitest'; -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; import { resetTestDb } from '../../helpers/test-db'; import { createUser, createAdmin } from '../../helpers/factories'; import fs from 'node:fs'; @@ -49,8 +46,6 @@ const avatarsFx = makeStorageFixture('avatars/'); let profile: UserProfileService; beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); profile = new UserProfileService( avatarsFx.storage, await createTestUnitOfWork(testDb), diff --git a/server/tests/unit/nest/vacay.service.test.ts b/server/tests/unit/nest/vacay.service.test.ts index d6b10502f..ade3a53a6 100644 --- a/server/tests/unit/nest/vacay.service.test.ts +++ b/server/tests/unit/nest/vacay.service.test.ts @@ -2,22 +2,20 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vites // ── DB setup (real in-memory SQLite) ───────────────────────────────────────── -const { testDb, dbMock } = vi.hoisted(() => { - const Database = require('better-sqlite3'); - const db = new Database(':memory:'); - db.exec('PRAGMA journal_mode = WAL'); - db.exec('PRAGMA foreign_keys = ON'); - db.exec('PRAGMA busy_timeout = 5000'); +vi.mock('../../../src/db/database', async () => { + + const { createSnapshotTestDb } = await import('../../helpers/db-mock'); + const db = createSnapshotTestDb(); const mock = { db, closeDb: () => {}, reinitialize: () => {}, canAccessTrip: () => null, }; - return { testDb: db, dbMock: mock }; + return mock; }); -vi.mock('../../../src/db/database', () => dbMock); + vi.mock('../../../src/config', () => ({ JWT_SECRET: 'test-secret', ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2', @@ -27,8 +25,7 @@ vi.mock('../../../src/config', () => ({ vi.mock('../../../src/websocket', () => ({ broadcastToUser: vi.fn() })); // shareCalendar fires a notification after inserting — keep that out of unit scope -import { createTables } from '../../../src/db/schema'; -import { runMigrations } from '../../../src/db/migrations'; +import { db as testDb } from '../../../src/db/database'; import { resetTestDb } from '../../helpers/test-db'; import { createUser } from '../../helpers/factories'; @@ -54,8 +51,6 @@ let svc: VacayService; // ── Lifecycle ───────────────────────────────────────────────────────────────── beforeAll(async () => { - createTables(testDb); - runMigrations(testDb); svc = new VacayService( await createTestVacayPlansRepo(testDb), await createTestVacayPlanMembersRepo(testDb), await createTestVacayYearsRepo(testDb), await createTestVacayUserYearsRepo(testDb),