/** * Postgres-backed integration test helper (S4-06 follow-up). * * `withTestDb(fn)` spins up a real Postgres container via `testcontainers`, * yields a Kysely instance connected to it, runs the user's callback, then * tears the container down. Use this when `mockDb` is insufficient — e.g. * when an extension hook chains a real SELECT through `ctx.db`, or when you * want to verify migrations apply cleanly. * * Requires: * - Docker / Podman running on the test host. * - `testcontainers` installed (peer dep — declare as devDep in the * extension's package.json). We import it dynamically so unit-test * workflows that never call `withTestDb` don't take the heavy hit. * * Cost model: * - First call: ~3-5s (image pull + container start). Subsequent calls * reuse the cached image, ~1-2s. Use one container across a `describe()` * block when you can — the wrapper supports nesting via `reuse`. * * Why a peer dep + dynamic import: * - `testcontainers` pulls `dockerode` + a bunch of Node-ish modules. We * don't want every consumer of `@zveltio/sdk/testing` to inherit that * weight just to use `mockDb`. By importing lazily, the cost is * opt-in at the call site. */ import type { Kysely } from 'kysely'; export interface WithTestDbOptions { /** Postgres image. Default: `postgres:18-alpine`. */ image?: string; /** DB name to create. Default: `zveltio_test`. */ database?: string; /** Optional SQL strings to run after the container is ready. */ migrations?: string[]; /** * Container start timeout, ms. Default: 60_000. Cold pulls can be slow * on the first run; CI environments may want 120_000. */ startupTimeoutMs?: number; /** * If passed, the container is started once and reused. Pass `false` * (default) to start + stop per call. */ reuse?: boolean; } export interface TestDb { /** Kysely instance — same shape as `ctx.db` in production. */ db: Kysely; /** Connection string the container exposed. */ connectionString: string; /** Stop + remove the container (also closes the Kysely pool). */ cleanup(): Promise; } /** * Spin up Postgres, optionally run migrations, return a `TestDb` handle. * The caller is responsible for calling `cleanup()` (typically inside an * `afterAll`/`afterEach`). * * For the more common "scope the lifecycle to a test/describe block" * pattern, prefer `withTestDb(fn)` below. */ export declare function startTestDb(opts?: WithTestDbOptions): Promise; /** * Convenience wrapper: starts a TestDb, runs `fn`, always cleans up. The * 95% case for integration tests. Returns whatever `fn` returns. * * @example * import { withTestDb } from '@zveltio/sdk/testing'; * import myExtension from '../engine'; * * test('extension creates contact rows', async () => { * await withTestDb(async (db) => { * await db.schema.createTable('zvd_contacts').addColumn(...).execute(); * const ctx = createTestContext({ db }); * const app = await createTestApp(myExtension, { ctx }); * const res = await app.request('/ext/my/contacts', { method: 'POST', ... }); * expect(res.status).toBe(201); * const rows = await db.selectFrom('zvd_contacts').selectAll().execute(); * expect(rows).toHaveLength(1); * }); * }); */ export declare function withTestDb(fnOrOpts: ((db: Kysely) => Promise) | WithTestDbOptions, maybeFn?: (db: Kysely) => Promise): Promise; /** * Apply an ordered list of SQL strings against the db. Each entry runs in * its own statement; semicolons in the source string split into multiple * statements. Useful for replaying engine + extension migrations against * the test db. * * @example * import { readFileSync } from 'fs'; * import { sync as glob } from 'glob'; * const files = glob('engine/migrations/*.sql').map((p) => readFileSync(p, 'utf8')); * await applyMigrationStrings(db, files); */ export declare function applyMigrationStrings(db: Kysely, sqlStrings: string[]): Promise; /** * Same as `applyMigrationStrings` but takes file paths. Reads each file * synchronously, applies in order. Bun-native — uses `Bun.file()`. */ export declare function applyMigrationFiles(db: Kysely, paths: string[]): Promise; /** * Stop any reuse-cached container. Call this from a `globalTeardown` / * `afterAll` so test runs don't leave dangling containers. No-op when no * reuse container is active. */ export declare function stopReusedTestDb(): Promise; /** * Naive SQL statement splitter: splits on `;` outside of single-quoted * strings and dollar-quoted blocks (`$$...$$`). Handles the migration-file * styles Zveltio ships; not a full SQL parser. * * Public so tests can pin its behavior. Not part of the documented surface. */ export declare function splitStatements(src: string): string[]; export declare const _internalForTests: { splitStatements: typeof splitStatements; }; //# sourceMappingURL=with-test-db.d.ts.map