import { describe, it, expect, vi } from 'vitest'; // The module under test imports @mosaicstack/db at module scope; we replace only the // pieces DatabaseModule uses (partial mock — the real module also exports the // schema the storage adapter's import chain needs) so the test pins the #1392 // contract (refuse to start on an incomplete schema) without a live database. vi.mock('@mosaicstack/db', async (importOriginal) => { const actual: object = await importOriginal(); return { ...actual, createDb: vi.fn(), createPgliteDb: vi.fn(), getMigrationStatus: vi.fn(), runPgliteMigrations: vi.fn(), }; }); import { DatabaseModule } from './database.module.js'; import { getMigrationStatus } from '@mosaicstack/db'; import type { DbHandle } from '@mosaicstack/db'; import type { StorageAdapter } from '@mosaicstack/storage'; import type { MosaicConfig } from '@mosaicstack/config'; function makeModule(storageType: 'postgres' | 'pglite', tier: string) { const storageAdapter = { name: storageType, migrate: vi.fn(), close: vi.fn(), } as unknown as StorageAdapter; const handle = { close: vi.fn() } as unknown as DbHandle; const config = { tier, storage: { type: storageType, url: 'postgresql://x' }, } as unknown as MosaicConfig; return { mod: new DatabaseModule(handle, storageAdapter, config), storageAdapter, }; } describe('DatabaseModule.onModuleInit — #1392 schema verification', () => { it('refuses to start when the postgres schema is incomplete', async () => { const { mod, storageAdapter } = makeModule('postgres', 'standalone'); vi.mocked(getMigrationStatus).mockResolvedValue({ appliedCount: 15, expectedCount: 17, expectedLastTag: '0016_salty_morlocks', complete: false, }); await expect(mod.onModuleInit()).rejects.toThrow('Database schema incomplete: 15/17'); expect(storageAdapter.migrate).toHaveBeenCalled(); // migrations attempted first }); it('starts normally when the schema is complete', async () => { const { mod } = makeModule('postgres', 'standalone'); vi.mocked(getMigrationStatus).mockResolvedValue({ appliedCount: 17, expectedCount: 17, expectedLastTag: '0016_salty_morlocks', complete: true, }); await expect(mod.onModuleInit()).resolves.toBeUndefined(); }); it('does not verify postgres status for the local tier (PGlite migrates itself)', async () => { const { mod } = makeModule('pglite', 'local'); vi.mocked(getMigrationStatus).mockClear(); await expect(mod.onModuleInit()).resolves.toBeUndefined(); expect(getMigrationStatus).not.toHaveBeenCalled(); }); });