/** * Testing helpers for Zveltio extensions (S4-06). * * Goal: give extension authors a one-liner to spin up a fake * `ExtensionContext` and a Hono test app, so unit tests run without a * real database or auth setup. For integration tests against a real * Postgres + engine, use Bun's `testcontainers` directly — this module * is the offline-fast path. * * Three primitives: * - `createTestContext(overrides?)` — minimal `ExtensionContext` with * no-op stubs for everything (db, auth, events, services, * queryAlter, entityAccess, registerPublicRoute). Override per-test. * - `createTestApp(extension, ctx?)` — runs `extension.register()` * against a fresh Hono and returns the app, ready for `.request()`. * - `mockDb(presets?)` — records query-builder calls; returns presets * when an `.execute()` / `.executeTakeFirst()` lands. Enough to * fake CRUD paths; falls back to empty arrays / undefined. */ import { Hono } from 'hono'; import type { ZveltioExtension, ExtensionContext } from '../extension/index.js'; export interface MockCall { /** Method name chain, e.g. `'selectFrom.where.execute'`. */ chain: string; /** Args of the terminal call. */ args: unknown[]; } export interface MockDbPresets { /** * Preset a return value for a specific chain. Match is by suffix — * `'selectFrom.zvd_users.execute'` matches the chain ending in that. * Use exact chains for precision. * * @example * mockDb({ * 'selectFrom.zvd_users.selectAll.execute': [{ id: '1', name: 'A' }], * 'selectFrom.zvd_users.executeTakeFirst': { id: '1', name: 'A' }, * }) */ [chain: string]: unknown; } export interface MockDb { /** Recorded calls, oldest first. Reset between tests. */ readonly calls: MockCall[]; /** Reset call history (presets stay). */ reset(): void; /** Add or override a preset after construction. */ preset(chain: string, value: unknown): void; } /** * Build a recording mock Kysely-shaped object. Any method chain is * accepted; the terminal `.execute()` / `.executeTakeFirst()` / * `.executeTakeFirstOrThrow()` returns a preset if matched, otherwise * an empty list (for execute) or undefined. */ export declare function mockDb(presets?: MockDbPresets): any & MockDb; export interface MockEventBus { emit(event: string, payload: unknown): void; on(event: string, handler: (payload: any) => void): () => void; onBefore(event: string, handler: (payload: any) => any): () => void; runBefore(event: string, seed: Record): Promise; /** Recorded emits, oldest first. */ readonly emitted: Array<{ event: string; payload: unknown; }>; /** Reset emit log. */ reset(): void; } export declare function mockEventBus(): MockEventBus; export declare function mockServiceRegistry(): { register(name: string, value: unknown): void; unregister(name: string): void; get(name: string): T | null; has(name: string): boolean; list(): string[]; waitFor(name: string, timeoutMs?: number): Promise; }; export interface MockAuthOptions { /** If set, getSession returns `{ user: this }`. If null, getSession returns null. */ user?: { id: string; email?: string; name?: string; roles?: string[]; } | null; } export declare function mockAuth(opts?: MockAuthOptions): any; export interface CreateTestContextOptions { /** Override the mocked db. Default: `mockDb()`. */ db?: any; /** Override the mocked auth. Default: signed-in test user. */ auth?: any; /** Override the mocked event bus. Default: in-memory. */ events?: MockEventBus; /** Provide additional fields. They go onto the returned ctx as-is. */ extra?: Partial>; } export declare function createTestContext(opts?: CreateTestContextOptions): ExtensionContext; export interface CreateTestAppOptions { /** Override the context handed to register(). Default: `createTestContext()`. */ ctx?: ExtensionContext; /** * When the extension uses `mountStrategy: 'subapp'`, the engine normally * mounts the sub-app at `/ext/`. For tests we replicate that so * `.request('/ext//...')` works. Pass `false` to skip the wrapper * and mount the sub-app at root for cleaner test URLs. */ mountSubappAt?: string | false; } /** * Spin up a Hono test app with the extension's routes registered. Returns * the outer Hono so callers can use `.request(path, init)` for assertions. * * Honors the extension's `mountStrategy`. For `'subapp'`, the outer app * mounts the sub-app at `/ext/` by default — same shape * the engine produces in production. */ export declare function createTestApp(extension: ZveltioExtension, opts?: CreateTestAppOptions): Promise; export { withTestDb, startTestDb, applyMigrationStrings, applyMigrationFiles, stopReusedTestDb, splitStatements, type TestDb, type WithTestDbOptions, } from './with-test-db.js'; //# sourceMappingURL=index.d.ts.map