@jterrazz/test 7.1.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -230
- package/dist/checker.d.ts +1 -0
- package/dist/checker.js +741 -0
- package/dist/index.d.ts +1296 -529
- package/dist/index.js +3303 -1261
- package/dist/intercept.js +129 -290
- package/dist/match.js +153 -0
- package/dist/oxlint.cjs +2931 -0
- package/dist/oxlint.d.cts +150 -0
- package/dist/oxlint.d.ts +150 -0
- package/dist/oxlint.js +2925 -0
- package/package.json +47 -41
- package/dist/chunk.cjs +0 -28
- package/dist/index.cjs +0 -1814
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -734
- package/dist/index.js.map +0 -1
- package/dist/intercept.cjs +0 -313
- package/dist/intercept.cjs.map +0 -1
- package/dist/intercept.d.cts +0 -105
- package/dist/intercept.d.ts +0 -105
- package/dist/intercept.js.map +0 -1
- package/dist/intercept2.cjs +0 -81
- package/dist/intercept2.cjs.map +0 -1
- package/dist/intercept2.js +0 -81
- package/dist/intercept2.js.map +0 -1
- package/dist/mock-of.cjs +0 -32
- package/dist/mock-of.cjs.map +0 -1
- package/dist/mock-of.d.cts +0 -27
- package/dist/mock-of.d.ts +0 -27
- package/dist/mock-of.js +0 -19
- package/dist/mock-of.js.map +0 -1
- package/dist/mock.cjs +0 -4
- package/dist/mock.d.cts +0 -2
- package/dist/mock.d.ts +0 -2
- package/dist/mock.js +0 -2
- package/dist/services.cjs +0 -5
- package/dist/services.d.cts +0 -2
- package/dist/services.d.ts +0 -2
- package/dist/services.js +0 -2
- package/dist/sqlite.adapter.cjs +0 -350
- package/dist/sqlite.adapter.cjs.map +0 -1
- package/dist/sqlite.adapter.d.cts +0 -209
- package/dist/sqlite.adapter.d.ts +0 -209
- package/dist/sqlite.adapter.js +0 -331
- package/dist/sqlite.adapter.js.map +0 -1
- package/dist/types.d.cts +0 -42
- package/dist/types.d.ts +0 -42
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["sep","grepUtil","postgres","redis"],"sources":["../src/builder/cli/adapters/exec.adapter.ts","../src/builder/http/adapters/fetch.adapter.ts","../src/builder/http/adapters/hono.adapter.ts","../src/builder/common/directory.ts","../src/builder/common/reporter.ts","../src/builder/common/directory-accessor.ts","../src/builder/common/grep.ts","../src/builder/common/response-accessor.ts","../src/builder/common/table-assertion.ts","../src/builder/common/result.ts","../src/builder/specification-builder.ts","../src/docker/docker-adapter.ts","../src/docker/docker-assertion.ts","../src/infra/adapters/compose.adapter.ts","../src/infra/adapters/testcontainers.adapter.ts","../src/infra/compose-parser.ts","../src/infra/orchestrator.ts","../src/spec/resolve.ts","../src/spec/spec.ts","../src/spec/targets.ts","../src/spec/legacy-cli.ts","../src/spec/legacy-e2e.ts","../src/spec/legacy-integration.ts"],"sourcesContent":["import { execSync, spawn } from 'node:child_process';\n\nimport type { CommandEnv, CommandPort, CommandResult, SpawnOptions } from '../command.port.js';\n\n/**\n * Build a child-process env from the parent env plus user overrides.\n * `null` overrides delete keys (e.g. `INIT_CWD: null`).\n */\nfunction buildEnv(extra?: CommandEnv): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env, INIT_CWD: undefined };\n if (extra) {\n for (const [key, value] of Object.entries(extra)) {\n if (value === null) {\n delete env[key];\n } else {\n env[key] = value;\n }\n }\n }\n return env;\n}\n\n/**\n * Executes CLI commands via Node.js child_process.\n * Uses `execSync` for one-shot commands and `spawn` for long-running processes.\n * Used by the `cli()` specification runner.\n */\nexport class ExecAdapter implements CommandPort {\n private command: string;\n\n constructor(command: string) {\n this.command = command;\n }\n\n async exec(args: string, cwd: string, extraEnv?: CommandEnv): Promise<CommandResult> {\n const env = buildEnv(extraEnv);\n\n try {\n const stdout = execSync(`${this.command} ${args}`, {\n cwd,\n encoding: 'utf8',\n env,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return { exitCode: 0, stdout, stderr: '' };\n } catch (error: any) {\n return {\n exitCode: error.status ?? 1,\n stdout: error.stdout?.toString() ?? '',\n stderr: error.stderr?.toString() ?? '',\n };\n }\n }\n\n async spawn(\n args: string,\n cwd: string,\n options: SpawnOptions,\n extraEnv?: CommandEnv,\n ): Promise<CommandResult> {\n const env = buildEnv(extraEnv);\n\n return new Promise((resolve) => {\n let stdout = '';\n let stderr = '';\n let resolved = false;\n\n const child = spawn(this.command, args.split(/\\s+/).filter(Boolean), {\n cwd,\n env,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n const finish = (exitCode: number) => {\n if (resolved) {\n return;\n }\n resolved = true;\n child.kill('SIGTERM');\n resolve({ exitCode, stdout, stderr });\n };\n\n let patternMatched = false;\n\n const checkPattern = () => {\n if (\n !patternMatched &&\n (stdout.includes(options.waitFor) || stderr.includes(options.waitFor))\n ) {\n patternMatched = true;\n finish(0);\n }\n };\n\n child.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n checkPattern();\n });\n\n child.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n checkPattern();\n });\n\n // Process exited before pattern matched\n child.on('exit', (code) => {\n if (!patternMatched) {\n finish(code === 0 ? 1 : (code ?? 1));\n }\n });\n\n setTimeout(() => finish(124), options.timeout);\n });\n }\n}\n","import type { ServerPort, ServerResponse } from '../server.port.js';\n\n/**\n * Server adapter that sends real HTTP requests via the Fetch API.\n * Used by the `e2e()` specification runner to hit a live server.\n */\nexport class FetchAdapter implements ServerPort {\n private baseUrl: string;\n\n constructor(url: string) {\n this.baseUrl = url.replace(/\\/$/, '');\n }\n\n async request(\n method: string,\n path: string,\n body?: unknown,\n headers?: Record<string, string>,\n ): Promise<ServerResponse> {\n const init: RequestInit = {\n method,\n headers: { 'Content-Type': 'application/json', ...headers },\n };\n\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n\n const response = await fetch(`${this.baseUrl}${path}`, init);\n const responseBody = await response.json().catch(() => null);\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n return {\n status: response.status,\n body: responseBody,\n headers: responseHeaders,\n };\n }\n}\n","import type { ServerPort, ServerResponse } from '../server.port.js';\n\n/**\n * Server adapter that dispatches requests in-process through a Hono app instance.\n * Used by the `integration()` specification runner -- no network overhead.\n */\nexport class HonoAdapter implements ServerPort {\n private app: {\n request: (path: string, init?: RequestInit) => Promise<Response> | Response;\n };\n\n constructor(app: {\n request: (path: string, init?: RequestInit) => Promise<Response> | Response;\n }) {\n this.app = app;\n }\n\n async request(\n method: string,\n path: string,\n body?: unknown,\n headers?: Record<string, string>,\n ): Promise<ServerResponse> {\n const init: RequestInit = {\n method,\n headers: { 'Content-Type': 'application/json', ...headers },\n };\n\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n\n const response = await this.app.request(path, init);\n const responseBody = await response.json().catch(() => null);\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n return {\n status: response.status,\n body: responseBody,\n headers: responseHeaders,\n };\n }\n}\n","import { readFileSync, statSync } from 'node:fs';\nimport { readdir } from 'node:fs/promises';\nimport { relative, resolve, sep } from 'node:path';\n\n/**\n * Default ignore patterns — paths that should never appear in a tracked snapshot.\n * Each entry is matched against any path segment OR a path prefix.\n */\nconst DEFAULT_IGNORES = ['.git', '.DS_Store', 'node_modules', '.next', 'dist', '.turbo', '.cache'];\n\nexport interface DirectoryDiff {\n added: string[];\n changed: { path: string; expected: string; actual: string }[];\n removed: string[];\n}\n\ninterface WalkOptions {\n ignore?: string[];\n}\n\n/**\n * Recursively walk a directory, returning sorted relative paths of files only.\n * Ignored entries (default + caller-supplied) are skipped.\n */\nexport async function walkDirectory(root: string, options: WalkOptions = {}): Promise<string[]> {\n const ignores = new Set([...DEFAULT_IGNORES, ...(options.ignore ?? [])]);\n const out: string[] = [];\n\n async function walk(current: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(current);\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (ignores.has(entry)) {\n continue;\n }\n const abs = resolve(current, entry);\n const stat = statSync(abs);\n if (stat.isDirectory()) {\n await walk(abs);\n } else if (stat.isFile()) {\n out.push(relative(root, abs).split(sep).join('/'));\n }\n }\n }\n\n await walk(root);\n out.sort();\n return out;\n}\n\n/**\n * Compare two directory trees file-by-file.\n * Binary files are compared by byte equality but reported without inline diff.\n */\nexport async function diffDirectories(\n expectedRoot: string,\n actualRoot: string,\n options: WalkOptions = {},\n): Promise<DirectoryDiff> {\n const expectedFiles = await walkDirectory(expectedRoot, options);\n const actualFiles = await walkDirectory(actualRoot, options);\n\n const expectedSet = new Set(expectedFiles);\n const actualSet = new Set(actualFiles);\n\n const added = actualFiles.filter((f) => !expectedSet.has(f));\n const removed = expectedFiles.filter((f) => !actualSet.has(f));\n const changed: DirectoryDiff['changed'] = [];\n\n for (const file of expectedFiles) {\n if (!actualSet.has(file)) {\n continue;\n }\n const expected = readFileSync(resolve(expectedRoot, file), 'utf8');\n const actual = readFileSync(resolve(actualRoot, file), 'utf8');\n if (expected !== actual) {\n changed.push({ actual, expected, path: file });\n }\n }\n\n return { added, changed, removed };\n}\n","// ── Colors ──\n\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst DIM = '\\x1b[2m';\nconst BOLD = '\\x1b[1m';\nconst RESET = '\\x1b[0m';\nconst BG_CYAN = '\\x1b[46m';\nconst BLACK = '\\x1b[30m';\n\n// ── Symbols (vitest-native) ──\n\nconst CHECK = '✓';\nconst CROSS = '×';\nconst ARROW = '→';\nconst DASH = '⎯';\n\n// ── Types ──\n\n/** Status report for a single infrastructure service after startup. */\nexport interface ServiceReport {\n name: string;\n type: string;\n connectionString?: string;\n durationMs: number;\n error?: string;\n logs?: string;\n}\n\n/** Describes the application under test for startup reporting. */\nexport interface AppInfo {\n type: 'http' | 'in-process';\n url?: string;\n}\n\n// ── Startup report ──\n\nexport function formatStartupReport(\n mode: 'e2e' | 'integration',\n services: ServiceReport[],\n app?: AppInfo,\n): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(`${BG_CYAN}${BLACK}${BOLD} INFRA ${RESET} Starting infrastructure...`);\n lines.push('');\n\n for (const service of services) {\n if (service.error) {\n lines.push(\n ` ${RED}${CROSS}${RESET} ${service.type} (${service.name}) ${RED}${service.error}${RESET} ${DIM}${service.durationMs}ms${RESET}`,\n );\n if (service.logs) {\n const logLines = service.logs.trim().split('\\n').slice(-10);\n for (const logLine of logLines) {\n lines.push(` ${DIM}${logLine}${RESET}`);\n }\n }\n } else {\n const conn = service.connectionString\n ? `${DIM}${service.connectionString}${RESET}`\n : '';\n lines.push(\n ` ${GREEN}${CHECK}${RESET} ${service.type} (${service.name}) ${conn} ${DIM}${service.durationMs}ms${RESET}`,\n );\n }\n }\n\n if (app) {\n lines.push('');\n if (app.type === 'in-process') {\n lines.push(` ${DIM}${ARROW} app: in-process (Hono)${RESET}`);\n } else {\n lines.push(` ${DIM}${ARROW} app: ${app.url}${RESET}`);\n }\n }\n\n lines.push('');\n\n return lines.join('\\n');\n}\n\n// ── Error divider ──\n\nexport function formatErrorDivider(label: string): string {\n const dashes = DASH.repeat(30);\n return `\\n${RED}${dashes} ${label} ${dashes}${RESET}\\n`;\n}\n\n// ── Status error ──\n\nexport function formatStatusError(\n expectedStatus: number,\n receivedStatus: number,\n request: { method: string; path: string; body?: unknown },\n responseBody: unknown,\n): string {\n const lines: string[] = [];\n\n lines.push(`Expected status: ${GREEN}${expectedStatus}${RESET}`);\n lines.push(`Received status: ${RED}${receivedStatus}${RESET}`);\n lines.push('');\n lines.push(`${DIM}${request.method} ${request.path}${RESET}`);\n\n if (request.body) {\n lines.push(formatJson(request.body, DIM));\n }\n\n if (responseBody) {\n lines.push('');\n lines.push(`${DIM}Response:${RESET}`);\n lines.push(formatJson(responseBody, RED));\n }\n\n return lines.join('\\n');\n}\n\n// ── Table diff ──\n\nexport function formatTableDiff(\n table: string,\n columns: string[],\n expected: unknown[][],\n actual: unknown[][],\n): string {\n const lines: string[] = [];\n\n lines.push(`Table \"${table}\" mismatch`);\n lines.push(`${DIM} query: ${columns.join(', ')}${RESET}`);\n lines.push(`${DIM} expected: ${rowLabel(expected.length)}${RESET}`);\n lines.push(`${DIM} received: ${rowLabel(actual.length)}${RESET}`);\n lines.push('');\n lines.push(`${GREEN}- Expected${RESET}`);\n lines.push(`${RED}+ Received${RESET}`);\n lines.push('');\n\n const header = columns.join(' | ');\n lines.push(`${DIM} ${header}${RESET}`);\n\n const maxRows = Math.max(expected.length, actual.length);\n\n for (let i = 0; i < maxRows; i++) {\n const exp = expected[i];\n const act = actual[i];\n\n if (exp && !act) {\n lines.push(`${GREEN}- ${formatRow(exp)}${RESET}`);\n } else if (!exp && act) {\n lines.push(`${RED}+ ${formatRow(act)}${RESET}`);\n } else if (exp && act) {\n const same = JSON.stringify(exp) === JSON.stringify(act);\n if (same) {\n lines.push(` ${formatRow(act)}`);\n } else {\n lines.push(`${GREEN}- ${formatRow(exp)}${RESET}`);\n lines.push(`${RED}+ ${formatRow(act)}${RESET}`);\n }\n }\n }\n\n if (expected.length === 0 && actual.length === 0) {\n lines.push(` (empty)`);\n }\n\n return lines.join('\\n');\n}\n\n// ── Response diff ──\n\nexport function formatResponseDiff(file: string, expected: unknown, actual: unknown): string {\n const lines: string[] = [];\n\n lines.push(`Response mismatch (${file})`);\n lines.push('');\n lines.push(`${GREEN}- Expected${RESET}`);\n lines.push(`${RED}+ Received${RESET}`);\n lines.push('');\n\n const expectedLines = JSON.stringify(expected, null, 2).split('\\n');\n const actualLines = JSON.stringify(actual, null, 2).split('\\n');\n const maxLines = Math.max(expectedLines.length, actualLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const exp = expectedLines[i];\n const act = actualLines[i];\n\n if (exp === act) {\n lines.push(` ${exp}`);\n } else {\n if (exp !== undefined) {\n lines.push(`${GREEN}- ${exp}${RESET}`);\n }\n if (act !== undefined) {\n lines.push(`${RED}+ ${act}${RESET}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n// ── Exit code error ──\n\nexport function formatExitCodeError(\n expected: number,\n received: number,\n stdout: string,\n stderr: string,\n): string {\n const lines: string[] = [];\n\n lines.push(`Expected exit code: ${GREEN}${expected}${RESET}`);\n lines.push(`Received exit code: ${RED}${received}${RESET}`);\n\n if (stdout.trim()) {\n lines.push('');\n lines.push(`${DIM}stdout:${RESET}`);\n for (const line of stdout.trim().split('\\n').slice(-15)) {\n lines.push(` ${DIM}${line}${RESET}`);\n }\n }\n\n if (stderr.trim()) {\n lines.push('');\n lines.push(`${DIM}stderr:${RESET}`);\n for (const line of stderr.trim().split('\\n').slice(-15)) {\n lines.push(` ${RED}${line}${RESET}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n// ── Stdout/stderr diff ──\n\nexport function formatStdoutDiff(file: string, expected: string, actual: string): string {\n const lines: string[] = [];\n\n lines.push(`Output mismatch (${file})`);\n lines.push('');\n lines.push(`${GREEN}- Expected${RESET}`);\n lines.push(`${RED}+ Received${RESET}`);\n lines.push('');\n\n const expectedLines = expected.split('\\n');\n const actualLines = actual.split('\\n');\n const maxLines = Math.max(expectedLines.length, actualLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const exp = expectedLines[i];\n const act = actualLines[i];\n\n if (exp === act) {\n lines.push(` ${exp}`);\n } else {\n if (exp !== undefined) {\n lines.push(`${GREEN}- ${exp}${RESET}`);\n }\n if (act !== undefined) {\n lines.push(`${RED}+ ${act}${RESET}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n// ── Directory diff ──\n\ninterface DirectoryDiffData {\n added: string[];\n changed: { path: string; expected: string; actual: string }[];\n removed: string[];\n}\n\nexport function formatDirectoryDiff(\n fixtureName: string,\n diff: DirectoryDiffData,\n hint: string,\n): string {\n const lines: string[] = [];\n\n const total = diff.added.length + diff.removed.length + diff.changed.length;\n lines.push(`Directory mismatch: ${BOLD}${fixtureName}${RESET}`);\n lines.push(\n `${DIM} ${total} difference${total === 1 ? '' : 's'}: ${diff.added.length} added, ${diff.removed.length} removed, ${diff.changed.length} changed${RESET}`,\n );\n lines.push('');\n lines.push(`${GREEN}- Expected (fixture)${RESET}`);\n lines.push(`${RED}+ Received (generated)${RESET}`);\n lines.push('');\n\n for (const path of diff.added) {\n lines.push(`${RED}+ added ${path}${RESET} ${DIM}(not in fixture)${RESET}`);\n }\n for (const path of diff.removed) {\n lines.push(`${GREEN}- removed ${path}${RESET} ${DIM}(in fixture, not generated)${RESET}`);\n }\n for (const { path, expected, actual } of diff.changed) {\n const expectedLines = expected.split('\\n');\n const actualLines = actual.split('\\n');\n const changedCount = countLineDifferences(expectedLines, actualLines);\n lines.push(\n `${BOLD}~ changed ${path}${RESET} ${DIM}(${changedCount} line${changedCount === 1 ? '' : 's'} differ)${RESET}`,\n );\n\n let shown = 0;\n const maxShown = 5;\n const maxLines = Math.max(expectedLines.length, actualLines.length);\n for (let i = 0; i < maxLines && shown < maxShown; i++) {\n const exp = expectedLines[i];\n const act = actualLines[i];\n if (exp !== act) {\n lines.push(`${DIM} line ${i + 1}:${RESET}`);\n if (exp !== undefined) {\n lines.push(` ${GREEN}- ${exp}${RESET}`);\n }\n if (act !== undefined) {\n lines.push(` ${RED}+ ${act}${RESET}`);\n }\n shown++;\n }\n }\n if (changedCount > maxShown) {\n lines.push(` ${DIM}... ${changedCount - maxShown} more line(s)${RESET}`);\n }\n }\n\n lines.push('');\n lines.push(`${DIM}${hint}${RESET}`);\n\n return lines.join('\\n');\n}\n\nfunction countLineDifferences(expected: string[], actual: string[]): number {\n let count = 0;\n const max = Math.max(expected.length, actual.length);\n for (let i = 0; i < max; i++) {\n if (expected[i] !== actual[i]) {\n count++;\n }\n }\n return count;\n}\n\n// ── File assertions ──\n\nexport function formatFileMissing(path: string): string {\n return `Expected file to exist: ${RED}${path}${RESET}`;\n}\n\nexport function formatFileUnexpected(path: string): string {\n return `Expected file NOT to exist: ${RED}${path}${RESET}`;\n}\n\nexport function formatFileContentMismatch(path: string, expected: string, actual: string): string {\n const lines: string[] = [];\n lines.push(`File \"${path}\" does not contain expected content`);\n lines.push('');\n lines.push(`${GREEN}Expected to contain:${RESET}`);\n lines.push(` ${GREEN}${expected}${RESET}`);\n lines.push('');\n lines.push(`${RED}Actual content (first 20 lines):${RESET}`);\n for (const line of actual.split('\\n').slice(0, 20)) {\n lines.push(` ${DIM}${line}${RESET}`);\n }\n return lines.join('\\n');\n}\n\n// ── Service logs section ──\n\nexport function formatServiceLogs(services: { name: string; logs: string }[]): string {\n const lines: string[] = [];\n\n for (const { name, logs } of services) {\n if (!logs.trim()) {\n continue;\n }\n\n lines.push('');\n lines.push(`${DIM}${name} logs (last 10 lines):${RESET}`);\n\n const logLines = logs.trim().split('\\n').slice(-10);\n for (const line of logLines) {\n lines.push(` ${DIM}${line}${RESET}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n// ── Helpers ──\n\nfunction rowLabel(n: number): string {\n return n === 1 ? '1 row' : `${n} rows`;\n}\n\nfunction formatJson(value: unknown, color: string): string {\n return JSON.stringify(value, null, 2)\n .split('\\n')\n .map((line) => `${color}${line}${RESET}`)\n .join('\\n');\n}\n\nfunction formatRow(row: unknown[]): string {\n return row.map((v) => String(v ?? 'null')).join(' | ');\n}\n\n// ── Test utilities ──\n\n/** Strip ANSI escape codes from a string. */\nexport function stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n\n/** Strip ANSI codes and replace volatile tokens (ports, durations) with placeholders. */\nexport function normalizeOutput(str: string): string {\n return stripAnsi(str)\n .replace(/localhost:\\d+/g, 'localhost:PORT')\n .replace(/\\d+ms/g, 'Xms')\n .replace(/\\d+\\.\\d+s/g, 'X.Xs')\n .trim();\n}\n","import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { diffDirectories, type DirectoryDiff, walkDirectory } from './directory.js';\nimport { formatDirectoryDiff } from './reporter.js';\n\nexport interface DirectorySnapshotOptions {\n /** Extra path segments to ignore (in addition to default: .git, node_modules, etc.). */\n ignore?: string[];\n /**\n * Force update mode regardless of vitest flags / env vars.\n * `true` writes the fixture, `false` always asserts. Defaults to auto-detect.\n */\n update?: boolean;\n}\n\n/**\n * Detect whether the user wants to update snapshots — `true` for any of:\n * - vitest run with `-u` / `--update`\n * - JTERRAZZ_TEST_UPDATE=1\n * - UPDATE_SNAPSHOTS=1\n */\nfunction shouldUpdateSnapshots(): boolean {\n if (process.env.JTERRAZZ_TEST_UPDATE === '1') {\n return true;\n }\n if (process.env.UPDATE_SNAPSHOTS === '1') {\n return true;\n }\n if (process.argv.includes('-u') || process.argv.includes('--update')) {\n return true;\n }\n return false;\n}\n\n/**\n * Handle to a directory produced by a specification run.\n * Supports snapshot-based assertions via {@link toMatchFixture} and file listing via {@link files}.\n */\nexport class DirectoryAccessor {\n private absPath: string;\n private testDir: string;\n\n constructor(absPath: string, testDir: string) {\n this.absPath = absPath;\n this.testDir = testDir;\n }\n\n /**\n * Compare the directory tree against `expected/{name}/` (relative to the test file).\n * On mismatch, throws with a structured diff. With update mode enabled, the\n * fixture is overwritten with the current contents instead.\n */\n async toMatchFixture(name: string, options: DirectorySnapshotOptions = {}): Promise<void> {\n const fixtureDir = resolve(this.testDir, 'expected', name);\n const update = options.update ?? shouldUpdateSnapshots();\n\n if (update) {\n rmSync(fixtureDir, { force: true, recursive: true });\n mkdirSync(fixtureDir, { recursive: true });\n cpSync(this.absPath, fixtureDir, { recursive: true });\n return;\n }\n\n if (!existsSync(fixtureDir)) {\n throw new Error(\n `Directory fixture \"${name}\" does not exist at ${fixtureDir}.\\n` +\n `Run with JTERRAZZ_TEST_UPDATE=1 (or vitest -u) to create it.`,\n );\n }\n\n const diff: DirectoryDiff = await diffDirectories(fixtureDir, this.absPath, {\n ignore: options.ignore,\n });\n\n if (diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0) {\n return;\n }\n\n throw new Error(\n formatDirectoryDiff(\n name,\n diff,\n 'Run with JTERRAZZ_TEST_UPDATE=1 to update the fixture.',\n ),\n );\n }\n\n /**\n * List all files in the directory (recursive, sorted, ignoring defaults).\n * Useful for ad-hoc assertions when you don't want a full snapshot.\n */\n async files(options: { ignore?: string[] } = {}): Promise<string[]> {\n return walkDirectory(this.absPath, options);\n }\n}\n","/**\n * Extract text blocks from output that contain a pattern.\n * Splits by blank lines (how linter/compiler output is structured),\n * returns only blocks matching the pattern.\n *\n * @example\n * expect(grep(result.stdout, \"unused-var.ts\")).toContain(\"no-unused-vars\")\n * expect(grep(result.stdout, \"valid/sorted.ts\")).not.toContain(\"sort-imports\")\n */\nexport function grep(output: string, pattern: string): string {\n // eslint-disable-next-line no-control-regex\n const clean = output.replace(/\\x1b\\[[0-9;]*m/g, '');\n const blocks = clean.split(/\\n\\s*\\n/);\n return blocks.filter((block) => block.includes(pattern)).join('\\n\\n');\n}\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { formatResponseDiff } from './reporter.js';\n\n/** Accessor for an HTTP response body with file-based assertion support. */\nexport class ResponseAccessor {\n readonly body: unknown;\n private testDir: string;\n\n constructor(body: unknown, testDir: string) {\n this.body = body;\n this.testDir = testDir;\n }\n\n /**\n * Assert that the response body matches the JSON in `responses/{file}`.\n *\n * @example\n * result.response.toMatchFile(\"expected-items.json\");\n */\n toMatchFile(file: string): void {\n const expected = JSON.parse(readFileSync(resolve(this.testDir, 'responses', file), 'utf8'));\n if (JSON.stringify(this.body) !== JSON.stringify(expected)) {\n throw new Error(formatResponseDiff(file, expected, this.body));\n }\n }\n}\n","import type { DatabasePort } from '../../adapters/ports/database.port.js';\nimport { formatTableDiff } from './reporter.js';\n\n/** Assertion helper for verifying database table contents after a specification run. */\nexport class TableAssertion {\n private tableName: string;\n private db: DatabasePort;\n\n constructor(tableName: string, db: DatabasePort) {\n this.tableName = tableName;\n this.db = db;\n }\n\n /**\n * Assert that the table contains exactly the expected rows for the given columns.\n *\n * @example\n * await result.table(\"users\").toMatch({\n * columns: [\"name\", \"email\"],\n * rows: [[\"Alice\", \"alice@example.com\"]],\n * });\n */\n async toMatch(expected: { columns: string[]; rows: unknown[][] }): Promise<void> {\n const actual = await this.db.query(this.tableName, expected.columns);\n if (JSON.stringify(actual) !== JSON.stringify(expected.rows)) {\n throw new Error(\n formatTableDiff(this.tableName, expected.columns, expected.rows, actual),\n );\n }\n }\n\n /** Assert that the table has zero rows. */\n async toBeEmpty(): Promise<void> {\n const actual = await this.db.query(this.tableName, ['*']);\n if (actual.length !== 0) {\n throw new Error(\n `Expected table \"${this.tableName}\" to be empty, but it has ${actual.length} rows`,\n );\n }\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport type { DatabasePort } from '../../adapters/ports/database.port.js';\nimport type { CommandResult } from '../cli/command.port.js';\nimport type { ServerResponse } from '../http/server.port.js';\nimport type { SpecificationConfig } from '../specification-builder.js';\nimport { DirectoryAccessor } from './directory-accessor.js';\nimport { grep as grepUtil } from './grep.js';\nimport { ResponseAccessor } from './response-accessor.js';\nimport { TableAssertion } from './table-assertion.js';\n\n/** Read-only handle to a single file produced by a CLI action. */\nexport interface FileAccessor {\n /** The UTF-8 text content. Throws if the file does not exist. */\n readonly content: string;\n readonly exists: boolean;\n}\n\n/**\n * The outcome of a single specification run.\n * Provides accessors for CLI output, HTTP responses, files, directories, and database tables.\n */\nexport class SpecificationResult {\n private commandResult?: CommandResult;\n private config: SpecificationConfig;\n private requestInfo?: { body?: unknown; method: string; path: string };\n private responseData?: ServerResponse;\n private testDir: string;\n private workDir?: string;\n\n constructor(options: {\n commandResult?: CommandResult;\n config: SpecificationConfig;\n requestInfo?: { body?: unknown; method: string; path: string };\n response?: ServerResponse;\n testDir: string;\n workDir?: string;\n }) {\n this.responseData = options.response;\n this.commandResult = options.commandResult;\n this.config = options.config;\n this.testDir = options.testDir;\n this.requestInfo = options.requestInfo;\n this.workDir = options.workDir;\n }\n\n // ── Raw value accessors ──\n\n /** The process exit code. Only available after a CLI action. */\n get exitCode(): number {\n if (!this.commandResult) {\n throw new Error('.exitCode requires a CLI action (.exec())');\n }\n return this.commandResult.exitCode;\n }\n\n /** The HTTP response status code. Only available after an HTTP action. */\n get status(): number {\n if (!this.responseData) {\n throw new Error('.status requires an HTTP action (.get(), .post(), etc.)');\n }\n return this.responseData.status;\n }\n\n /** The captured standard output. Only available after a CLI action. */\n get stdout(): string {\n if (!this.commandResult) {\n throw new Error('.stdout requires a CLI action (.exec())');\n }\n return this.commandResult.stdout;\n }\n\n /** The captured standard error. Only available after a CLI action. */\n get stderr(): string {\n if (!this.commandResult) {\n throw new Error('.stderr requires a CLI action (.exec())');\n }\n return this.commandResult.stderr;\n }\n\n /**\n * Extract text blocks from stdout that contain a pattern.\n * Useful for parsing structured CLI output (linters, compilers).\n *\n * @example\n * expect(result.grep('error.ts')).toContain('no-unused-vars');\n */\n grep(pattern: string): string {\n return grepUtil(this.stdout, pattern);\n }\n\n // ── Structured accessors ──\n\n /** Access the HTTP response body for assertions. Only available after an HTTP action. */\n get response(): ResponseAccessor {\n if (!this.responseData) {\n throw new Error('.response requires an HTTP action (.get(), .post(), etc.)');\n }\n return new ResponseAccessor(this.responseData.body, this.testDir);\n }\n\n /** Access a directory (relative to the working directory) for snapshot assertions. */\n directory(path: string = '.'): DirectoryAccessor {\n const baseDir = this.workDir ?? this.testDir;\n return new DirectoryAccessor(resolve(baseDir, path), this.testDir);\n }\n\n /** Access a single file (relative to the working directory) for content assertions. */\n file(path: string): FileAccessor {\n const baseDir = this.workDir ?? this.testDir;\n const resolvedPath = resolve(baseDir, path);\n const exists = existsSync(resolvedPath);\n return {\n get content(): string {\n if (!exists) {\n throw new Error(`File not found: ${path}`);\n }\n return readFileSync(resolvedPath, 'utf8');\n },\n exists,\n };\n }\n\n /** Access a database table for row-level assertions. */\n table(tableName: string, options?: { service?: string }): TableAssertion {\n const db = this.resolveDatabase(options?.service);\n if (!db) {\n throw new Error(\n options?.service\n ? `table(\"${tableName}\") requires database \"${options.service}\" but it was not found`\n : `table(\"${tableName}\") requires a database adapter`,\n );\n }\n return new TableAssertion(tableName, db);\n }\n\n // ── Private ──\n\n private resolveDatabase(serviceName?: string): DatabasePort | undefined {\n if (serviceName && this.config.databases) {\n return this.config.databases.get(serviceName);\n }\n return this.config.database;\n }\n}\n","import { cpSync, existsSync, mkdtempSync, readFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { resolve } from 'node:path';\n\nimport type { DatabasePort } from '../adapters/ports/database.port.js';\nimport type { CommandEnv, CommandPort, CommandResult, SpawnOptions } from './cli/command.port.js';\nimport type {\n InterceptEntry,\n InterceptResponse,\n InterceptTrigger,\n} from './common/intercept/types.js';\nimport { SpecificationResult } from './common/result.js';\nimport type { ServerPort } from './http/server.port.js';\n\n// ── Types ──\n\n/** A named job that can be triggered via .job(). */\nexport interface JobHandle {\n name: string;\n execute: () => Promise<void>;\n}\n\n/** Adapter configuration passed to the specification runner at setup time. */\nexport interface SpecificationConfig {\n command?: CommandPort;\n database?: DatabasePort;\n databases?: Map<string, DatabasePort>;\n fixturesRoot?: string;\n jobs?: JobHandle[];\n server?: ServerPort;\n}\n\n/** A SQL seed file to execute before the action, optionally targeting a named database service. */\nexport interface SeedEntry {\n file: string;\n service?: string;\n}\n\n/** A fixture file or directory to copy into the working directory before execution. */\nexport interface FixtureEntry {\n file: string;\n}\n\n/** An MSW mock definition file to register before the action. */\nexport interface MockEntry {\n file: string;\n}\n\n/** An HTTP request to perform against the server adapter. */\nexport interface RequestEntry {\n bodyFile?: string;\n headers?: Record<string, string>;\n method: string;\n path: string;\n}\n\n// ── Builder (before .run()) ──\n\n/**\n * Fluent builder for declaring a single test specification.\n *\n * Chain setup methods ({@link seed}, {@link fixture}, {@link env}), an action\n * ({@link get}, {@link post}, {@link exec}), then call {@link run} to execute\n * and receive a {@link SpecificationResult} for assertions.\n */\nexport class SpecificationBuilder {\n private commandArgs: null | string | string[] = null;\n private commandEnv: CommandEnv = {};\n private config: SpecificationConfig;\n private fixtures: FixtureEntry[] = [];\n private intercepts: InterceptEntry[] = [];\n private jobName: null | string = null;\n private label: string;\n private mocks: MockEntry[] = [];\n private projectName: null | string = null;\n private request: null | RequestEntry = null;\n private requestHeaders: Record<string, string> = {};\n private seeds: SeedEntry[] = [];\n private spawnConfig: null | { args: string; options: SpawnOptions } = null;\n private testDir: string;\n\n constructor(config: SpecificationConfig, testDir: string, label: string) {\n this.config = config;\n this.testDir = testDir;\n this.label = label;\n }\n\n // ── Setup ──\n\n /**\n * Queue a SQL seed file to run before the action.\n *\n * @example\n * spec(\"creates user\").seed(\"users.sql\").exec(\"list-users\").run();\n */\n seed(file: string, options?: { service?: string }): this {\n this.seeds.push({ file, service: options?.service });\n return this;\n }\n\n /** Copy a file or directory from `fixtures/` into the working directory before execution. */\n fixture(file: string): this {\n this.fixtures.push({ file });\n return this;\n }\n\n /** Copy an entire project fixture from `fixturesRoot` into the working directory. */\n project(name: string): this {\n this.projectName = name;\n return this;\n }\n\n /** Register an MSW mock definition file from `mock/` to intercept HTTP calls. */\n mock(file: string): this {\n this.mocks.push({ file });\n return this;\n }\n\n /**\n * Set environment variables for the CLI process. Merged on top of process.env.\n * Use `null` to unset a variable. Multiple calls merge.\n *\n * The token `$WORKDIR` (in any value) is replaced with the actual working\n * directory at run-time — useful for tests that need a fully isolated `HOME`.\n *\n * @example\n * spec(\"...\").env({ HOME: \"$WORKDIR\", TZ: \"UTC\" }).exec(\"status\").run();\n */\n env(env: CommandEnv): this {\n this.commandEnv = { ...this.commandEnv, ...env };\n return this;\n }\n\n /**\n * Set HTTP headers for the request. Multiple calls merge.\n *\n * @example\n * spec(\"french\").headers({ 'Accept-Language': 'fr' }).get(\"/articles\").run();\n */\n headers(headers: Record<string, string>): this {\n this.requestHeaders = { ...this.requestHeaders, ...headers };\n return this;\n }\n\n /**\n * Intercept an outgoing HTTP request and return a controlled response.\n * Uses MSW under the hood. Intercepts are queued — multiple calls with the\n * same trigger fire sequentially (first match consumed first).\n *\n * @param trigger - What to match (use openai.agent(), http.get(), etc.)\n * @param response - What to return: an InterceptResponse object, or a filename\n * from `intercepts/` (JSON loaded and auto-wrapped by the trigger).\n *\n * @example\n * // With inline response\n * .intercept(openai.agent({...}), openai.reply({ categories: ['TECH'] }))\n *\n * // With JSON fixture file (loaded from intercepts/ directory)\n * .intercept(openai.agent({...}), 'ingest-tech.json')\n * .intercept(http.get(url), 'world-news-tech.json')\n */\n intercept(trigger: InterceptTrigger, response: InterceptResponse | string): this {\n if (typeof response === 'string') {\n // File path format: 'adapter/filename.json'\n const slashIndex = response.indexOf('/');\n if (slashIndex === -1) {\n throw new Error(\n `.intercept(): file path must be 'adapter/filename.json' (e.g. 'openai/ingest-tech.json'), got '${response}'`,\n );\n }\n\n const adapterName = response.slice(0, slashIndex);\n if (adapterName !== trigger.adapter) {\n throw new Error(\n `.intercept(): adapter mismatch - trigger uses '${trigger.adapter}' but file path starts with '${adapterName}/'`,\n );\n }\n\n const filePath = resolve(this.testDir, 'intercepts', response);\n const data = JSON.parse(readFileSync(filePath, 'utf8'));\n const resolved = trigger.wrap(data);\n this.intercepts.push({ trigger, response: resolved });\n } else {\n this.intercepts.push({ trigger, response });\n }\n return this;\n }\n\n // ── HTTP actions ──\n\n /**\n * Send a GET request to the server adapter.\n *\n * @example\n * spec(\"list items\").get(\"/api/items\").run();\n */\n get(path: string): this {\n this.request = { method: 'GET', path };\n return this;\n }\n\n /**\n * Send a POST request to the server adapter.\n *\n * @param bodyFile - Optional JSON file from `requests/` to use as the request body.\n * @example\n * spec(\"create item\").post(\"/api/items\", \"new-item.json\").run();\n */\n post(path: string, bodyFile?: string): this {\n this.request = { bodyFile, method: 'POST', path };\n return this;\n }\n\n /** Send a PUT request to the server adapter. */\n put(path: string, bodyFile?: string): this {\n this.request = { bodyFile, method: 'PUT', path };\n return this;\n }\n\n /** Send a DELETE request to the server adapter. */\n delete(path: string): this {\n this.request = { method: 'DELETE', path };\n return this;\n }\n\n // ── CLI actions ──\n\n /**\n * Execute a CLI command (or a sequence of commands) in an isolated working directory.\n * When an array is passed, commands run sequentially and stop on the first non-zero exit code.\n *\n * @example\n * spec(\"init project\").exec(\"init --name demo\").run();\n * spec(\"multi-step\").exec([\"init\", \"build\"]).run();\n */\n exec(args: string | string[]): this {\n this.commandArgs = args;\n return this;\n }\n\n /** Spawn a long-running CLI process with custom spawn options (e.g. timeout, signal). */\n spawn(args: string, options: SpawnOptions): this {\n this.spawnConfig = { args, options };\n return this;\n }\n\n // ── Job actions ──\n\n /**\n * Execute a named job registered via the app() factory.\n *\n * @param name - The job name to trigger (must match a registered JobHandle.name).\n *\n * @example\n * spec('pipeline').intercept(openai.chat(), openai.response({...})).job('report-refresh').run();\n */\n job(name: string): this {\n this.jobName = name;\n return this;\n }\n\n // ── Run ──\n\n /**\n * Execute the specification: run seeds, copy fixtures, then perform the\n * configured action (HTTP or CLI).\n *\n * @returns The result object used for assertions.\n * @example\n * const result = await spec(\"test\").exec(\"status\").run();\n * expect(result.exitCode).toBe(0);\n */\n async run(): Promise<SpecificationResult> {\n const hasHttpAction = this.request !== null;\n const hasCliAction = this.commandArgs !== null || this.spawnConfig !== null;\n const hasJobAction = this.jobName !== null;\n\n const actionCount = [hasHttpAction, hasCliAction, hasJobAction].filter(Boolean).length;\n\n if (actionCount === 0) {\n throw new Error(\n `Specification \"${this.label}\": no action defined. Call .get(), .post(), .exec(), .job(), etc. before .run()`,\n );\n }\n\n if (actionCount > 1) {\n throw new Error(\n `Specification \"${this.label}\": cannot mix action types (.get/.post, .exec/.spawn, .job)`,\n );\n }\n\n let workDir: null | string = null;\n if (hasCliAction) {\n workDir = this.prepareWorkDir();\n }\n\n // Reset all databases\n if (this.config.databases) {\n for (const db of this.config.databases.values()) {\n await db.reset();\n }\n } else if (this.config.database) {\n await this.config.database.reset();\n }\n\n // Execute seeds\n for (const entry of this.seeds) {\n let db: DatabasePort | undefined;\n if (entry.service && this.config.databases) {\n db = this.config.databases.get(entry.service);\n if (!db) {\n throw new Error(\n `seed() targets database \"${entry.service}\" but it was not found. Available: ${[...this.config.databases.keys()].join(', ')}`,\n );\n }\n } else {\n db = this.config.database;\n }\n\n if (!db) {\n throw new Error('seed() requires a database adapter');\n }\n\n const sql = readFileSync(resolve(this.testDir, 'seeds', entry.file), 'utf8');\n await db.seed(sql);\n }\n\n // Copy fixture files into working directory\n if (this.fixtures.length > 0 && workDir) {\n for (const entry of this.fixtures) {\n const src = resolve(this.testDir, 'fixtures', entry.file);\n const dest = resolve(workDir, entry.file);\n cpSync(src, dest, { recursive: true });\n }\n }\n\n // Register HTTP intercepts via MSW\n let cleanupIntercepts: (() => void) | null = null;\n if (this.intercepts.length > 0) {\n const { registerIntercepts } = await import('./common/intercept/intercept.js');\n cleanupIntercepts = await registerIntercepts(this.intercepts);\n }\n\n // Execute action\n try {\n if (hasHttpAction) {\n return await this.runHttpAction();\n }\n if (hasJobAction) {\n return await this.runJobAction();\n }\n return await this.runCliAction(workDir!);\n } finally {\n if (cleanupIntercepts) {\n cleanupIntercepts();\n }\n }\n }\n\n // ── Private ──\n\n private resolveEnv(workDir: string): CommandEnv | undefined {\n const keys = Object.keys(this.commandEnv);\n if (keys.length === 0) {\n return undefined;\n }\n\n const resolved: CommandEnv = {};\n for (const key of keys) {\n const value = this.commandEnv[key];\n resolved[key] =\n typeof value === 'string' ? value.replace(/\\$WORKDIR/g, workDir) : value;\n }\n return resolved;\n }\n\n private prepareWorkDir(): string {\n // Every CLI spec runs in a fresh, empty temp directory unless a project\n // Fixture is explicitly copied in via .project() (or files via .fixture()).\n // This guarantees isolation — the runner never writes into fixturesRoot.\n const tempDir = mkdtempSync(resolve(tmpdir(), 'spec-cli-'));\n\n if (this.projectName && this.config.fixturesRoot) {\n const projectDir = resolve(this.config.fixturesRoot, this.projectName);\n if (!existsSync(projectDir)) {\n throw new Error(\n `project(\"${this.projectName}\"): fixture project not found at ${projectDir}`,\n );\n }\n cpSync(projectDir, tempDir, { recursive: true });\n }\n\n return tempDir;\n }\n\n private async runHttpAction(): Promise<SpecificationResult> {\n if (!this.config.server) {\n throw new Error('HTTP actions require a server adapter (use integration() or e2e())');\n }\n\n let body: unknown;\n if (this.request!.bodyFile) {\n body = JSON.parse(\n readFileSync(resolve(this.testDir, 'requests', this.request!.bodyFile), 'utf8'),\n );\n }\n\n const headers =\n Object.keys(this.requestHeaders).length > 0 ? this.requestHeaders : undefined;\n const response = await this.config.server.request(\n this.request!.method,\n this.request!.path,\n body,\n headers,\n );\n\n return new SpecificationResult({\n config: this.config,\n requestInfo: { body, method: this.request!.method, path: this.request!.path },\n response,\n testDir: this.testDir,\n });\n }\n\n private async runJobAction(): Promise<SpecificationResult> {\n if (!this.config.jobs?.length) {\n throw new Error(\n 'Job actions require jobs registered via app(() => ({ server, jobs }))',\n );\n }\n\n const job = this.config.jobs.find((j) => j.name === this.jobName);\n if (!job) {\n const available = this.config.jobs.map((j) => j.name).join(', ');\n throw new Error(`job(\"${this.jobName}\"): not found. Available: ${available}`);\n }\n\n await job.execute();\n\n return new SpecificationResult({\n config: this.config,\n testDir: this.testDir,\n });\n }\n\n private async runCliAction(workDir: string): Promise<SpecificationResult> {\n if (!this.config.command) {\n throw new Error('CLI actions require a command adapter (use cli())');\n }\n\n const env = this.resolveEnv(workDir);\n let commandResult: CommandResult;\n\n if (this.spawnConfig) {\n commandResult = await this.config.command.spawn(\n this.spawnConfig.args,\n workDir,\n this.spawnConfig.options,\n env,\n );\n } else if (Array.isArray(this.commandArgs)) {\n commandResult = { exitCode: 0, stderr: '', stdout: '' };\n for (const args of this.commandArgs) {\n commandResult = await this.config.command.exec(args, workDir, env);\n if (commandResult.exitCode !== 0) {\n break;\n }\n }\n } else {\n commandResult = await this.config.command.exec(this.commandArgs!, workDir, env);\n }\n\n return new SpecificationResult({\n commandResult,\n config: this.config,\n testDir: this.testDir,\n workDir,\n });\n }\n}\n\n// ── Caller detection ──\n\nfunction getCallerDir(): string {\n const stack = new Error('caller detection').stack;\n if (!stack) {\n throw new Error('Cannot detect caller directory: no stack trace');\n }\n\n const lines = stack.split('\\n');\n for (const line of lines) {\n const match = line.match(/at\\s+(?:.*?\\()?(?:file:\\/\\/)?([^:)]+):\\d+:\\d+/);\n if (!match) {\n continue;\n }\n\n const filePath = match[1];\n\n if (filePath.includes('node_modules')) {\n continue;\n }\n if (filePath.includes('/src/builder/') || filePath.includes('/src/spec/')) {\n continue;\n }\n\n return resolve(filePath, '..');\n }\n\n throw new Error('Cannot detect caller directory from stack trace');\n}\n\n// ── Factory functions ──\n\n/** Factory function returned by `cli()`, `integration()`, or `e2e()` that starts a new spec. */\nexport type SpecificationRunner = (label: string) => SpecificationBuilder;\n\n/**\n * Create a {@link SpecificationRunner} bound to the given adapter configuration.\n * The test file directory is auto-detected from the call stack.\n */\nexport function createSpecificationRunner(config: SpecificationConfig): SpecificationRunner {\n return (label: string) => {\n const testDir = getCallerDir();\n return new SpecificationBuilder(config, testDir, label);\n };\n}\n","import { execSync } from 'node:child_process';\n\nimport type { DockerContainerPort, DockerInspectResult } from './docker-port.js';\n\n/**\n * Implements {@link DockerContainerPort} by shelling out to the Docker CLI.\n * Each instance is bound to a single container ID.\n */\nexport class DockerAdapter implements DockerContainerPort {\n private containerId: string;\n\n constructor(containerId: string) {\n this.containerId = containerId;\n }\n\n async exec(cmd: string[]): Promise<string> {\n const result = execSync(\n `docker exec ${this.containerId} ${cmd.map((c) => `'${c}'`).join(' ')}`,\n { encoding: 'utf8', timeout: 10_000 },\n );\n return result.trim();\n }\n\n async file(path: string): Promise<{ exists: boolean; content: string }> {\n try {\n const content = await this.exec(['cat', path]);\n return { exists: true, content };\n } catch {\n return { exists: false, content: '' };\n }\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const result = execSync(\n `docker inspect --format='{{.State.Running}}' ${this.containerId}`,\n {\n encoding: 'utf8',\n timeout: 5000,\n },\n );\n return result.trim() === 'true';\n } catch {\n return false;\n }\n }\n\n async logs(tail?: number): Promise<string> {\n const tailFlag = tail ? `--tail ${tail}` : '';\n const result = execSync(`docker logs ${tailFlag} ${this.containerId}`, {\n encoding: 'utf8',\n timeout: 10_000,\n });\n return result;\n }\n\n async inspect(): Promise<DockerInspectResult> {\n const raw = execSync(`docker inspect ${this.containerId}`, {\n encoding: 'utf8',\n timeout: 5000,\n });\n const data = JSON.parse(raw)[0];\n return {\n id: data.Id,\n name: data.Name,\n state: {\n running: data.State.Running,\n exitCode: data.State.ExitCode,\n status: data.State.Status,\n },\n config: {\n image: data.Config.Image,\n env: data.Config.Env || [],\n },\n hostConfig: {\n memory: data.HostConfig.Memory || 0,\n cpuQuota: data.HostConfig.CpuQuota || 0,\n networkMode: data.HostConfig.NetworkMode || '',\n mounts: (data.Mounts || []).map((m: any) => ({\n source: m.Source,\n destination: m.Destination,\n type: m.Type,\n })),\n },\n networkSettings: {\n networks: Object.fromEntries(\n Object.entries(data.NetworkSettings.Networks || {}).map(\n ([name, net]: [string, any]) => [\n name,\n { gateway: net.Gateway, ipAddress: net.IPAddress },\n ],\n ),\n ),\n },\n };\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await this.exec(['test', '-e', path]);\n return true;\n } catch {\n return false;\n }\n }\n}\n\n/** Create a {@link DockerContainerPort} bound to an existing container by ID. */\nexport function dockerContainer(containerId: string): DockerContainerPort {\n return new DockerAdapter(containerId);\n}\n","import type { DockerContainerPort } from './docker-port.js';\n\n/**\n * Fluent assertion builder for Docker containers.\n * Chain async assertions like `toBeRunning()`, `toHaveFile()`, `toHaveMount()`.\n */\nexport class DockerAssertion {\n private container: DockerContainerPort;\n\n constructor(container: DockerContainerPort) {\n this.container = container;\n }\n\n /** Assert the container is running */\n async toBeRunning(): Promise<this> {\n const running = await this.container.isRunning();\n if (!running) {\n throw new Error('Expected container to be running');\n }\n return this;\n }\n\n /** Assert the container is NOT running / doesn't exist */\n async toNotExist(): Promise<this> {\n const running = await this.container.isRunning();\n if (running) {\n throw new Error('Expected container to not exist or not be running');\n }\n return this;\n }\n\n /** Assert a file exists inside the container */\n async toHaveFile(path: string, opts?: { containing?: string }): Promise<this> {\n const file = await this.container.file(path);\n if (!file.exists) {\n throw new Error(`Expected file ${path} to exist in container`);\n }\n if (opts?.containing && !file.content.includes(opts.containing)) {\n throw new Error(\n `Expected file ${path} to contain \"${opts.containing}\", got: ${file.content.slice(0, 200)}`,\n );\n }\n return this;\n }\n\n /** Assert a file does NOT exist */\n async toNotHaveFile(path: string): Promise<this> {\n const exists = await this.container.exists(path);\n if (exists) {\n throw new Error(`Expected ${path} to not exist in container`);\n }\n return this;\n }\n\n /** Assert a directory exists */\n async toHaveDirectory(path: string): Promise<this> {\n const exists = await this.container.exists(path);\n if (!exists) {\n throw new Error(`Expected directory ${path} to exist in container`);\n }\n return this;\n }\n\n /** Assert a mount exists */\n async toHaveMount(destination: string): Promise<this> {\n const info = await this.container.inspect();\n const mount = info.hostConfig.mounts.find((m) => m.destination === destination);\n if (!mount) {\n const available = info.hostConfig.mounts.map((m) => m.destination).join(', ');\n throw new Error(`Expected mount at ${destination}, found: [${available}]`);\n }\n return this;\n }\n\n /** Assert network mode */\n async toHaveNetwork(mode: string): Promise<this> {\n const info = await this.container.inspect();\n if (!info.hostConfig.networkMode.includes(mode)) {\n throw new Error(\n `Expected network mode \"${mode}\", got \"${info.hostConfig.networkMode}\"`,\n );\n }\n return this;\n }\n\n /** Assert memory limit */\n async toHaveMemoryLimit(bytes: number): Promise<this> {\n const info = await this.container.inspect();\n if (info.hostConfig.memory !== bytes) {\n throw new Error(`Expected memory limit ${bytes}, got ${info.hostConfig.memory}`);\n }\n return this;\n }\n\n /** Assert CPU quota */\n async toHaveCpuQuota(quota: number): Promise<this> {\n const info = await this.container.inspect();\n if (info.hostConfig.cpuQuota !== quota) {\n throw new Error(`Expected CPU quota ${quota}, got ${info.hostConfig.cpuQuota}`);\n }\n return this;\n }\n\n /** Execute a command and return output for custom assertions */\n async exec(cmd: string[]): Promise<string> {\n return this.container.exec(cmd);\n }\n\n /** Read a file for custom assertions */\n async readFile(path: string): Promise<string> {\n const file = await this.container.file(path);\n if (!file.exists) {\n throw new Error(`File ${path} does not exist`);\n }\n return file.content;\n }\n\n /** Get logs for custom assertions */\n async getLogs(tail?: number): Promise<string> {\n return this.container.logs(tail);\n }\n}\n","import { execSync } from 'node:child_process';\nimport { dirname } from 'node:path';\n\nimport type { ContainerPort } from '../ports/container.port.js';\n\n/**\n * Container adapter using docker compose — runs full compose stack.\n * Used by e2e() to start all services including the app.\n */\nexport class ComposeAdapter implements ContainerPort {\n private composeFile: string;\n private serviceName: string;\n private started = false;\n\n constructor(composeFile: string, serviceName: string) {\n this.composeFile = composeFile;\n this.serviceName = serviceName;\n }\n\n private exec(command: string): string {\n return execSync(command, {\n cwd: dirname(this.composeFile),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'inherit'],\n }).trim();\n }\n\n async start(): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.exec(`docker compose -f ${this.composeFile} up -d --wait ${this.serviceName}`);\n this.started = true;\n }\n\n async stop(): Promise<void> {\n if (!this.started) {\n return;\n }\n\n this.exec(`docker compose -f ${this.composeFile} rm -fsv ${this.serviceName}`);\n this.started = false;\n }\n\n getMappedPort(containerPort: number): number {\n const output = this.exec(\n `docker compose -f ${this.composeFile} port ${this.serviceName} ${containerPort}`,\n );\n // Output: 0.0.0.0:54321\n const port = output.split(':').pop();\n return Number(port);\n }\n\n getHost(): string {\n return 'localhost';\n }\n\n getConnectionString(): string {\n return `${this.getHost()}:${this.getMappedPort(0)}`;\n }\n\n async getLogs(): Promise<string> {\n try {\n return this.exec(\n `docker compose -f ${this.composeFile} logs ${this.serviceName} --tail=50`,\n );\n } catch {\n return '';\n }\n }\n}\n\n/**\n * Start the full compose stack and stop it all on cleanup.\n * Supports per-worker project names for parallel execution.\n */\nexport class ComposeStackAdapter {\n private composeFile: string;\n private projectName: null | string;\n private started = false;\n\n constructor(composeFile: string, projectName?: string) {\n this.composeFile = composeFile;\n this.projectName = projectName ?? null;\n }\n\n private get projectFlag(): string {\n return this.projectName ? `-p ${this.projectName}` : '';\n }\n\n private run(command: string): string {\n try {\n return execSync(command, {\n cwd: dirname(this.composeFile),\n encoding: 'utf8',\n timeout: 120_000,\n }).trim();\n } catch (error: any) {\n const stderr = error.stderr?.toString().trim() ?? error.message;\n throw new Error(`docker compose failed: ${stderr}`, { cause: error });\n }\n }\n\n async start(): Promise<void> {\n if (this.started) {\n return;\n }\n\n this.run(`docker compose ${this.projectFlag} -f ${this.composeFile} up -d --wait`);\n this.started = true;\n }\n\n async stop(): Promise<void> {\n if (!this.started) {\n return;\n }\n\n this.run(`docker compose ${this.projectFlag} -f ${this.composeFile} down -v`);\n this.started = false;\n }\n\n getMappedPort(serviceName: string, containerPort: number): number {\n const output = this.run(\n `docker compose ${this.projectFlag} -f ${this.composeFile} port ${serviceName} ${containerPort}`,\n );\n const port = output.split(':').pop();\n return Number(port);\n }\n\n getHost(): string {\n return 'localhost';\n }\n}\n","import type { ContainerPort } from '../ports/container.port.js';\n\n/**\n * Container adapter using testcontainers.\n * Wraps a GenericContainer for programmatic container lifecycle.\n */\nexport class TestcontainersAdapter implements ContainerPort {\n private image: string;\n private containerPort: number;\n private env: Record<string, string>;\n private reuse: boolean;\n private container: any = null;\n\n constructor(options: {\n image: string;\n port: number;\n env?: Record<string, string>;\n reuse?: boolean;\n }) {\n this.image = options.image;\n this.containerPort = options.port;\n this.env = options.env ?? {};\n this.reuse = options.reuse ?? false;\n }\n\n async start(): Promise<void> {\n const { GenericContainer, Wait } = await import('testcontainers');\n\n let builder = new GenericContainer(this.image).withExposedPorts(this.containerPort);\n\n for (const [key, value] of Object.entries(this.env)) {\n builder = builder.withEnvironment({ [key]: value });\n }\n\n if (this.image.startsWith('postgres')) {\n builder = builder.withWaitStrategy(\n Wait.forLogMessage(/database system is ready to accept connections/, 2),\n );\n }\n\n if (this.reuse) {\n builder = builder.withReuse();\n }\n\n this.container = await builder.start();\n }\n\n async stop(): Promise<void> {\n if (this.container && !this.reuse) {\n await this.container.stop();\n this.container = null;\n }\n }\n\n getMappedPort(containerPort: number): number {\n if (!this.container) {\n throw new Error('Container not started');\n }\n return this.container.getMappedPort(containerPort);\n }\n\n getHost(): string {\n if (!this.container) {\n throw new Error('Container not started');\n }\n return this.container.getHost();\n }\n\n getConnectionString(): string {\n return `${this.getHost()}:${this.getMappedPort(this.containerPort)}`;\n }\n\n async getLogs(): Promise<string> {\n if (!this.container) {\n return '';\n }\n\n const stream = await this.container.logs();\n return new Promise((resolve) => {\n let output = '';\n stream.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n stream.on('end', () => {\n resolve(output);\n });\n // Timeout after 1s if stream doesn't end\n setTimeout(() => {\n resolve(output);\n }, 1000);\n });\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\n\n/**\n * A parsed service from docker-compose.test.yaml.\n */\nexport interface ComposeService {\n name: string;\n image?: string;\n build?: string;\n ports: { container: number; host?: number }[];\n environment: Record<string, string>;\n volumes: string[];\n dependsOn: string[];\n}\n\n/**\n * Result of parsing a compose file.\n */\nexport interface ComposeConfig {\n services: ComposeService[];\n appService: ComposeService | null;\n infraServices: ComposeService[];\n}\n\n/**\n * Detect the service type from the image name.\n */\nexport function detectServiceType(\n image: string | undefined,\n): 'app' | 'postgres' | 'redis' | 'unknown' {\n if (!image) {\n return 'app';\n }\n\n const lower = image.toLowerCase();\n\n if (lower.startsWith('postgres')) {\n return 'postgres';\n }\n if (lower.startsWith('redis')) {\n return 'redis';\n }\n\n return 'unknown';\n}\n\n/**\n * Find the compose file in the project.\n * Looks for docker/compose.test.yaml or docker-compose.test.yaml.\n */\nexport function findComposeFile(projectRoot: string): null | string {\n const candidates = [\n resolve(projectRoot, 'docker/compose.test.yaml'),\n resolve(projectRoot, 'docker/compose.test.yml'),\n resolve(projectRoot, 'docker-compose.test.yaml'),\n resolve(projectRoot, 'docker-compose.test.yml'),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\n/**\n * Parse a docker-compose file and extract service definitions.\n */\nexport function parseComposeFile(filePath: string): ComposeConfig {\n const content = readFileSync(filePath, 'utf8');\n const doc = parseYaml(content);\n\n if (!doc?.services) {\n return { services: [], appService: null, infraServices: [] };\n }\n\n const services: ComposeService[] = Object.entries(doc.services).map(\n ([name, def]: [string, any]) => {\n const ports: { container: number; host?: number }[] = [];\n if (def.ports) {\n for (const port of def.ports) {\n const str = String(port);\n if (str.includes(':')) {\n const [host, container] = str.split(':');\n ports.push({ container: Number(container), host: Number(host) });\n } else {\n ports.push({ container: Number(str) });\n }\n }\n }\n\n const environment: Record<string, string> = {};\n if (def.environment) {\n if (Array.isArray(def.environment)) {\n for (const env of def.environment) {\n const [key, ...rest] = String(env).split('=');\n environment[key] = rest.join('=');\n }\n } else {\n Object.assign(environment, def.environment);\n }\n }\n\n const volumes: string[] = def.volumes ? def.volumes.map((v: string) => String(v)) : [];\n\n let dependsOn: string[] = [];\n if (def.depends_on) {\n dependsOn = Array.isArray(def.depends_on)\n ? def.depends_on\n : Object.keys(def.depends_on);\n }\n\n return {\n name,\n image: def.image,\n build: def.build,\n ports,\n environment,\n volumes,\n dependsOn,\n };\n },\n );\n\n const appService = services.find((s) => s.build !== undefined) ?? null;\n const infraServices = services.filter((s) => s.build === undefined);\n\n return { services, appService, infraServices };\n}\n","import { dirname } from 'node:path';\n\nimport type { DatabasePort } from '../adapters/ports/database.port.js';\nimport { postgres } from '../adapters/postgres.adapter.js';\nimport { redis } from '../adapters/redis.adapter.js';\nimport {\n type AppInfo,\n formatStartupReport,\n type ServiceReport,\n} from '../builder/common/reporter.js';\nimport { ComposeStackAdapter } from './adapters/compose.adapter.js';\nimport { TestcontainersAdapter } from './adapters/testcontainers.adapter.js';\nimport { detectServiceType, findComposeFile, parseComposeFile } from './compose-parser.js';\nimport type { ContainerPort } from './ports/container.port.js';\nimport type { ServiceHandle } from './ports/service.port.js';\n\ninterface RunningService {\n handle: ServiceHandle;\n container: ContainerPort | null;\n}\n\nexport interface OrchestratorOptions {\n services: ServiceHandle[];\n mode: 'e2e' | 'integration';\n root?: string;\n /** Compose project name — used for per-worker stack isolation. */\n projectName?: string;\n}\n\n/**\n * Orchestrator for test infrastructure.\n * Integration: starts services via testcontainers.\n * E2E: runs full docker compose up.\n */\nexport class Orchestrator {\n private services: ServiceHandle[];\n private mode: 'e2e' | 'integration';\n private root: string;\n private projectName: string | undefined;\n private running: RunningService[] = [];\n private composeStack: ComposeStackAdapter | null = null;\n private composeHandles: ServiceHandle[] = [];\n private started = false;\n\n constructor(options: OrchestratorOptions) {\n this.services = options.services;\n this.mode = options.mode;\n this.root = options.root ?? process.cwd();\n this.projectName = options.projectName;\n }\n\n /**\n * Start declared services via testcontainers (integration mode).\n * Phase 1: start all containers in parallel (the slow part).\n * Phase 2: wire connections, healthcheck, and init sequentially (fast).\n */\n async start(): Promise<void> {\n if (this.started) {\n return;\n }\n\n const composePath = findComposeFile(this.root);\n const composeDir = composePath ? dirname(composePath) : this.root;\n const composeConfig = composePath ? parseComposeFile(composePath) : null;\n\n // Separate services that need containers from embedded ones (e.g. SQLite)\n const containerServices: { container: TestcontainersAdapter; handle: ServiceHandle }[] = [];\n const embeddedServices: ServiceHandle[] = [];\n\n for (const handle of this.services) {\n if (handle.defaultPort === 0) {\n // Embedded service (no container needed)\n embeddedServices.push(handle);\n continue;\n }\n\n let image = handle.defaultImage;\n let env = { ...handle.environment };\n\n if (handle.composeName && composeConfig) {\n const composeService = composeConfig.services.find(\n (s) => s.name === handle.composeName,\n );\n if (composeService) {\n image = composeService.image ?? image;\n env = { ...env, ...composeService.environment };\n Object.assign(handle.environment, composeService.environment);\n }\n }\n\n const container = new TestcontainersAdapter({ image, port: handle.defaultPort, env });\n containerServices.push({ container, handle });\n }\n\n // Phase 1: start containers in parallel + initialize embedded services\n await Promise.all([\n ...containerServices.map(({ container }) => container.start()),\n ...embeddedServices.map(async (handle) => {\n await handle.initialize(composeDir);\n handle.started = true;\n this.running.push({ handle, container: null });\n }),\n ]);\n\n // Phase 2: wire connections, healthcheck, init (fast — containers already running)\n const reports: ServiceReport[] = [];\n\n for (const { container, handle } of containerServices) {\n const serviceStartTime = Date.now();\n\n try {\n const host = container.getHost();\n const port = container.getMappedPort(handle.defaultPort);\n handle.connectionString = handle.buildConnectionString(host, port);\n\n await handle.healthcheck();\n await handle.initialize(composeDir);\n handle.started = true;\n\n reports.push({\n name: handle.composeName ?? handle.type,\n type: handle.type,\n connectionString: handle.connectionString,\n durationMs: Date.now() - serviceStartTime,\n });\n this.running.push({ handle, container });\n } catch (error: any) {\n let logs = '';\n try {\n logs = await container.getLogs();\n } catch {\n /* Ignore log fetch errors */\n }\n try {\n await container.stop();\n } catch {\n /* Ignore stop errors */\n }\n\n reports.push({\n name: handle.composeName ?? handle.type,\n type: handle.type,\n durationMs: Date.now() - serviceStartTime,\n error: error.message,\n logs,\n });\n\n const output = formatStartupReport('integration', reports, { type: 'in-process' });\n console.error(output);\n throw error;\n }\n }\n\n this.started = true;\n\n const appInfo: AppInfo = { type: 'in-process' };\n const output = formatStartupReport('integration', reports, appInfo);\n console.log(output);\n }\n\n /**\n * Stop testcontainers (integration mode).\n */\n async stop(): Promise<void> {\n for (const { container } of this.running) {\n if (container) {\n await container.stop();\n }\n }\n this.running = [];\n this.started = false;\n }\n\n /**\n * Start full docker compose stack (e2e mode).\n * Auto-detects infra services and creates handles for them.\n */\n async startCompose(): Promise<void> {\n const composePath = findComposeFile(this.root);\n if (!composePath) {\n throw new Error(`E2E: no compose file found in ${this.root}`);\n }\n\n const startTime = Date.now();\n const composeDir = dirname(composePath);\n const composeConfig = parseComposeFile(composePath);\n\n this.composeStack = new ComposeStackAdapter(composePath, this.projectName);\n await this.composeStack.start();\n\n // Create handles for detected infra services\n for (const service of composeConfig.infraServices) {\n const type = detectServiceType(service.image);\n\n if (type === 'postgres') {\n const handle = postgres({ compose: service.name, env: service.environment });\n const port = this.composeStack.getMappedPort(service.name, 5432);\n handle.connectionString = handle.buildConnectionString('localhost', port);\n\n await handle.initialize(composeDir);\n handle.started = true;\n\n this.composeHandles.push(handle);\n } else if (type === 'redis') {\n const handle = redis({ compose: service.name });\n const port = this.composeStack.getMappedPort(service.name, 6379);\n handle.connectionString = handle.buildConnectionString('localhost', port);\n handle.started = true;\n\n this.composeHandles.push(handle);\n }\n }\n\n const durationMs = Date.now() - startTime;\n const reports: ServiceReport[] = this.composeHandles.map((h) => ({\n name: h.composeName ?? h.type,\n type: h.type,\n connectionString: h.connectionString,\n durationMs,\n }));\n\n const appUrl = this.getAppUrl();\n const appInfo: AppInfo = { type: 'http', url: appUrl ?? undefined };\n const output = formatStartupReport('e2e', reports, appInfo);\n console.log(output);\n }\n\n /**\n * Stop docker compose stack (e2e mode).\n */\n async stopCompose(): Promise<void> {\n if (this.composeStack) {\n await this.composeStack.stop();\n this.composeStack = null;\n }\n this.composeHandles = [];\n }\n\n /**\n * Get a database service by compose name, or the first one if no name given.\n */\n getDatabase(serviceName?: string): DatabasePort | null {\n for (const handle of [...this.services, ...this.composeHandles]) {\n if (serviceName && handle.composeName !== serviceName) {\n continue;\n }\n const adapter = handle.createDatabaseAdapter();\n if (adapter) {\n return adapter;\n }\n }\n return null;\n }\n\n /**\n * Get all database services keyed by compose name.\n */\n getDatabases(): Map<string, DatabasePort> {\n const map = new Map<string, DatabasePort>();\n for (const handle of [...this.services, ...this.composeHandles]) {\n const adapter = handle.createDatabaseAdapter();\n if (adapter && handle.composeName) {\n map.set(handle.composeName, adapter);\n }\n }\n return map;\n }\n\n /**\n * Get app URL from compose (e2e mode).\n */\n getAppUrl(): null | string {\n const composePath = findComposeFile(this.root);\n if (!composePath || !this.composeStack) {\n return null;\n }\n\n const config = parseComposeFile(composePath);\n const appService = config.appService;\n\n if (!appService || appService.ports.length === 0) {\n return null;\n }\n\n const port = this.composeStack.getMappedPort(\n appService.name,\n appService.ports[0].container,\n );\n return `http://localhost:${port}`;\n }\n}\n","import { existsSync } from 'node:fs';\nimport { isAbsolute, resolve } from 'node:path';\n\n/**\n * Resolve root — if relative, resolves from the caller's directory.\n */\nexport function resolveProjectRoot(root: string | undefined): string {\n if (!root) {\n return process.cwd();\n }\n\n if (isAbsolute(root)) {\n return root;\n }\n\n const stack = new Error('resolve root').stack;\n if (stack) {\n const lines = stack.split('\\n');\n for (const line of lines) {\n const match = line.match(/at\\s+(?:.*?\\()?(?:file:\\/\\/)?([^:)]+):\\d+:\\d+/);\n if (!match) {\n continue;\n }\n\n const filePath = match[1];\n if (filePath.includes('node_modules') || filePath.includes('/src/spec/')) {\n continue;\n }\n\n return resolve(filePath, '..', root);\n }\n }\n\n return resolve(process.cwd(), root);\n}\n\n/**\n * Resolve a CLI command — checks node_modules/.bin, then treats as absolute/PATH.\n */\nexport function resolveCommand(command: string, root: string): string {\n if (isAbsolute(command)) {\n return command;\n }\n\n const binPath = resolve(root, 'node_modules/.bin', command);\n if (existsSync(binPath)) {\n return binPath;\n }\n\n const cwdBinPath = resolve(process.cwd(), 'node_modules/.bin', command);\n if (existsSync(cwdBinPath)) {\n return cwdBinPath;\n }\n\n return command;\n}\n","import type { DatabasePort } from '../adapters/ports/database.port.js';\nimport { ExecAdapter } from '../builder/cli/adapters/exec.adapter.js';\nimport { FetchAdapter } from '../builder/http/adapters/fetch.adapter.js';\nimport { HonoAdapter } from '../builder/http/adapters/hono.adapter.js';\nimport {\n createSpecificationRunner,\n type SpecificationBuilder,\n} from '../builder/specification-builder.js';\nimport { dockerContainer } from '../docker/docker-adapter.js';\nimport { DockerAssertion } from '../docker/docker-assertion.js';\nimport { Orchestrator } from '../infra/orchestrator.js';\nimport type { ServiceHandle } from '../infra/ports/service.port.js';\nimport { resolveCommand, resolveProjectRoot } from './resolve.js';\nimport type { AppTarget, CommandTarget, SpecTarget, StackTarget } from './targets.js';\n\n/** Shared options for all spec targets. */\nexport interface SpecOptions {\n /** Project root for fixture lookup and compose detection (relative paths supported). */\n root?: string;\n /** Infrastructure services to start via testcontainers. */\n services?: ServiceHandle[];\n}\n\n/** A specification runner with teardown support and orchestrator access. */\nexport interface SpecRunner {\n /** The function to create individual specs: `runner('label').get('/path').run()`. */\n (label: string): SpecificationBuilder;\n /** Stop all infrastructure started by this runner. */\n cleanup: () => Promise<void>;\n /**\n * Get a Docker assertion builder for a running container.\n *\n * @example\n * await runner.docker('my-container').toBeRunning();\n */\n docker: (containerId: string) => DockerAssertion;\n /** The orchestrator managing the test infrastructure lifecycle. */\n orchestrator: Orchestrator;\n}\n\n/**\n * Create a specification runner for the given target.\n *\n * @param target - What to test against: {@link app}, {@link stack}, or {@link command}.\n * @param options - Shared options: root directory, infrastructure services.\n *\n * @example\n * // HTTP — in-process app with testcontainers\n * const s = await spec(app(() => createApp(db)), { services: [db] });\n *\n * // HTTP — full docker compose stack\n * const s = await spec(stack('../../'));\n *\n * // CLI — command binary\n * const s = await spec(command('my-cli'), { root: '../fixtures' });\n */\nexport async function spec(target: SpecTarget, options: SpecOptions = {}): Promise<SpecRunner> {\n switch (target.kind) {\n case 'app': {\n return startApp(target, options);\n }\n case 'stack': {\n return startStack(target, options);\n }\n case 'command': {\n return startCommand(target, options);\n }\n }\n}\n\n// ── Isolation helpers ──\n\nfunction getWorkerId(): string {\n return process.env.VITEST_POOL_ID ?? '0';\n}\n\nasync function acquireIsolation(services: ServiceHandle[]): Promise<void> {\n const workerId = getWorkerId();\n for (const service of services) {\n await service.isolation().acquire(workerId);\n }\n}\n\nasync function releaseIsolation(services: ServiceHandle[]): Promise<void> {\n for (const service of services) {\n await service.isolation().release();\n }\n}\n\n// ── Internal dispatchers ──\n\nasync function startApp(target: AppTarget, options: SpecOptions): Promise<SpecRunner> {\n const services = options.services ?? [];\n const orchestrator = new Orchestrator({\n mode: 'integration',\n root: resolveProjectRoot(options.root),\n services,\n });\n\n await orchestrator.start();\n await acquireIsolation(services);\n\n // Build services map keyed by composeName or type\n const servicesMap: Record<string, ServiceHandle> = {};\n for (const svc of services) {\n const key = svc.composeName ?? svc.type;\n servicesMap[key] = svc;\n }\n\n const factoryResult = target.factory(servicesMap);\n\n // Normalize: bare HonoApp or { server, jobs }\n // Check for 'jobs' array (not 'server') to discriminate — bare apps may have a 'server' property\n const isComposite = Array.isArray((factoryResult as any).jobs);\n const honoApp = isComposite ? (factoryResult as any).server : factoryResult;\n const jobs = isComposite ? (factoryResult as any).jobs : undefined;\n\n const database = orchestrator.getDatabase() ?? undefined;\n const databases = orchestrator.getDatabases();\n\n const runner = createSpecificationRunner({\n database,\n databases: databases.size > 0 ? databases : undefined,\n jobs,\n server: new HonoAdapter(honoApp),\n }) as SpecRunner;\n\n runner.cleanup = async () => {\n await releaseIsolation(services);\n await orchestrator.stop();\n };\n runner.docker = (id: string) => new DockerAssertion(dockerContainer(id));\n runner.orchestrator = orchestrator;\n\n return runner;\n}\n\nasync function startStack(target: StackTarget, options: SpecOptions): Promise<SpecRunner> {\n const root = resolveProjectRoot(target.root ?? options.root);\n const workerId = getWorkerId();\n const projectName = `test-worker-${workerId}`;\n\n const orchestrator = new Orchestrator({\n mode: 'e2e',\n root,\n services: options.services ?? [],\n projectName,\n });\n\n await orchestrator.startCompose();\n\n const appUrl = orchestrator.getAppUrl();\n if (!appUrl) {\n throw new Error(\n 'stack(): could not detect app URL from compose. Ensure an app service with ports is defined.',\n );\n }\n\n const database = orchestrator.getDatabase() ?? undefined;\n const databases = orchestrator.getDatabases();\n\n const runner = createSpecificationRunner({\n database,\n databases: databases.size > 0 ? databases : undefined,\n server: new FetchAdapter(appUrl),\n }) as SpecRunner;\n\n runner.cleanup = () => orchestrator.stopCompose();\n runner.docker = (id: string) => new DockerAssertion(dockerContainer(id));\n runner.orchestrator = orchestrator;\n\n return runner;\n}\n\nasync function startCommand(target: CommandTarget, options: SpecOptions): Promise<SpecRunner> {\n const root = resolveProjectRoot(options.root);\n const bin = resolveCommand(target.bin, root);\n const services = options.services ?? [];\n\n let orchestrator: null | Orchestrator = null;\n let database: DatabasePort | undefined;\n let databases: Map<string, DatabasePort> | undefined;\n\n if (services.length) {\n orchestrator = new Orchestrator({\n mode: 'integration',\n root,\n services,\n });\n await orchestrator.start();\n await acquireIsolation(services);\n database = orchestrator.getDatabase() ?? undefined;\n const dbMap = orchestrator.getDatabases();\n databases = dbMap.size > 0 ? dbMap : undefined;\n }\n\n const runner = createSpecificationRunner({\n command: new ExecAdapter(bin),\n database,\n databases,\n fixturesRoot: root,\n }) as SpecRunner;\n\n runner.cleanup = async () => {\n await releaseIsolation(services);\n if (orchestrator) {\n await orchestrator.stop();\n }\n };\n runner.docker = (id: string) => new DockerAssertion(dockerContainer(id));\n runner.orchestrator = orchestrator!;\n\n return runner;\n}\n","/**\n * Target factories for {@link spec}. Each target describes what is being tested\n * and how the specification runner should connect to it.\n */\n\nimport type { ServiceHandle } from '../infra/ports/service.port.js';\n\n/** Any object with a request method compatible with Hono's app.request(). */\ntype HonoApp = {\n request: (path: string, init?: RequestInit) => Promise<Response> | Response;\n};\n\n/** A named job that can be triggered via .job(). */\nexport interface JobHandle {\n name: string;\n execute: () => Promise<void>;\n}\n\n/** Services map passed to the app factory after infrastructure starts. */\nexport type AppServices = Record<string, ServiceHandle>;\n\n/**\n * Return value from the app() factory. Either a bare server (backward compat)\n * or an object with server + jobs for job-based testing.\n */\nexport type AppFactoryResult = HonoApp | { server: HonoApp; jobs: JobHandle[] };\n\n// ── Target types ──\n\n/** In-process Hono app target. Created by {@link app}. */\nexport interface AppTarget {\n readonly kind: 'app';\n readonly factory: (services: AppServices) => AppFactoryResult;\n}\n\n/** Docker compose stack target. Created by {@link stack}. */\nexport interface StackTarget {\n readonly kind: 'stack';\n readonly root: string;\n}\n\n/** CLI command target. Created by {@link command}. */\nexport interface CommandTarget {\n readonly kind: 'command';\n readonly bin: string;\n}\n\n/** Any target that produces an HTTP interface (app or stack). */\nexport type HttpTarget = AppTarget | StackTarget;\n\n/** Any valid spec target. */\nexport type SpecTarget = AppTarget | CommandTarget | StackTarget;\n\n// ── Target factories ──\n\n/**\n * Test against an in-process Hono app. The factory receives started services\n * so you can wire connection strings into your app/DI container.\n *\n * @param factory - Function that receives services and returns a Hono app instance.\n *\n * @example\n * const db = postgres({ compose: 'db' });\n * await spec(\n * app((services) => createApp({ databaseUrl: services.db.connectionString })),\n * { services: [db] },\n * );\n */\nexport function app(factory: (services: AppServices) => HonoApp): AppTarget {\n return { kind: 'app', factory };\n}\n\n/**\n * Test against a full docker compose stack. The stack is started with\n * `docker compose up` and real HTTP requests are sent to the app service.\n *\n * @param root - Project root containing `docker/compose.test.yaml`.\n *\n * @example\n * await spec(stack('../../'));\n */\nexport function stack(root: string): StackTarget {\n return { kind: 'stack', root };\n}\n\n/**\n * Test a CLI binary. Each spec runs in a fresh temp directory.\n *\n * @param bin - Path to the CLI binary or command name (resolved from node_modules/.bin or PATH).\n *\n * @example\n * await spec(command('my-cli'), { root: '../fixtures' });\n */\nexport function command(bin: string): CommandTarget {\n return { kind: 'command', bin };\n}\n","import type { DatabasePort } from '../adapters/ports/database.port.js';\nimport { ExecAdapter } from '../builder/cli/adapters/exec.adapter.js';\nimport { createSpecificationRunner } from '../builder/specification-builder.js';\nimport { Orchestrator } from '../infra/orchestrator.js';\nimport type { ServiceHandle } from '../infra/ports/service.port.js';\nimport type { SpecificationRunnerWithCleanup } from './legacy-integration.js';\nimport { resolveCommand, resolveProjectRoot } from './resolve.js';\n\nexport interface CliOptions {\n /** CLI command to run (resolved from node_modules/.bin or PATH). */\n command: string;\n /** Project root — base dir for .project() fixture lookup (relative paths supported). */\n root?: string;\n /** Optional infrastructure services (started via testcontainers). */\n services?: ServiceHandle[];\n}\n\n/**\n * Create a CLI specification runner.\n * Runs CLI commands against fixture projects. Optionally starts infrastructure.\n */\nexport async function cli(options: CliOptions): Promise<SpecificationRunnerWithCleanup> {\n const root = resolveProjectRoot(options.root);\n const command = resolveCommand(options.command, root);\n\n let orchestrator: null | Orchestrator = null;\n let database: DatabasePort | undefined;\n let databases: Map<string, DatabasePort> | undefined;\n\n if (options.services?.length) {\n orchestrator = new Orchestrator({\n mode: 'integration',\n root,\n services: options.services,\n });\n await orchestrator.start();\n database = orchestrator.getDatabase() ?? undefined;\n const dbMap = orchestrator.getDatabases();\n databases = dbMap.size > 0 ? dbMap : undefined;\n }\n\n const runner = createSpecificationRunner({\n command: new ExecAdapter(command),\n database,\n databases,\n fixturesRoot: root,\n }) as SpecificationRunnerWithCleanup;\n\n runner.cleanup = async () => {\n if (orchestrator) {\n await orchestrator.stop();\n }\n };\n runner.orchestrator = orchestrator!;\n\n return runner;\n}\n","import { FetchAdapter } from '../builder/http/adapters/fetch.adapter.js';\nimport { createSpecificationRunner } from '../builder/specification-builder.js';\nimport { Orchestrator } from '../infra/orchestrator.js';\nimport type { SpecificationRunnerWithCleanup } from './legacy-integration.js';\nimport { resolveProjectRoot } from './resolve.js';\n\nexport interface E2eOptions {\n /** Project root — must contain docker/compose.test.yaml. */\n root?: string;\n}\n\n/**\n * Create an E2E specification runner.\n * Starts full docker compose stack. App URL and database auto-detected.\n */\nexport async function e2e(options: E2eOptions = {}): Promise<SpecificationRunnerWithCleanup> {\n const orchestrator = new Orchestrator({\n mode: 'e2e',\n root: resolveProjectRoot(options.root),\n services: [],\n });\n\n await orchestrator.startCompose();\n\n const appUrl = orchestrator.getAppUrl();\n if (!appUrl) {\n throw new Error(\n 'E2E: could not detect app URL from compose. Ensure an app service with ports is defined.',\n );\n }\n\n const database = orchestrator.getDatabase() ?? undefined;\n const databases = orchestrator.getDatabases();\n\n const runner = createSpecificationRunner({\n database,\n databases: databases.size > 0 ? databases : undefined,\n server: new FetchAdapter(appUrl),\n }) as SpecificationRunnerWithCleanup;\n\n runner.cleanup = () => orchestrator.stopCompose();\n runner.orchestrator = orchestrator;\n\n return runner;\n}\n","import { HonoAdapter } from '../builder/http/adapters/hono.adapter.js';\nimport {\n createSpecificationRunner,\n type SpecificationRunner,\n} from '../builder/specification-builder.js';\nimport { Orchestrator } from '../infra/orchestrator.js';\nimport type { ServiceHandle } from '../infra/ports/service.port.js';\nimport { resolveProjectRoot } from './resolve.js';\n\ntype HonoApp = {\n fetch: (...args: any[]) => any;\n request: (path: string, init?: RequestInit) => Promise<Response> | Response;\n};\n\nexport interface IntegrationOptions {\n /** Factory that returns a Hono app — called after services start. */\n app: () => HonoApp;\n /** Project root for compose detection (relative paths supported). */\n root?: string;\n /** Declared services — started via testcontainers. */\n services: ServiceHandle[];\n}\n\n/** A specification runner that also exposes teardown and the underlying orchestrator. */\nexport interface SpecificationRunnerWithCleanup extends SpecificationRunner {\n /** Stop all infrastructure containers started by this runner. */\n cleanup: () => Promise<void>;\n /** The orchestrator managing the test infrastructure lifecycle. */\n orchestrator: Orchestrator;\n}\n\n/**\n * Create an integration specification runner.\n * Starts infra containers via testcontainers, app runs in-process.\n */\nexport async function integration(\n options: IntegrationOptions,\n): Promise<SpecificationRunnerWithCleanup> {\n const orchestrator = new Orchestrator({\n mode: 'integration',\n root: resolveProjectRoot(options.root),\n services: options.services,\n });\n\n await orchestrator.start();\n\n const app = options.app();\n const database = orchestrator.getDatabase() ?? undefined;\n const databases = orchestrator.getDatabases();\n\n const runner = createSpecificationRunner({\n database,\n databases: databases.size > 0 ? databases : undefined,\n server: new HonoAdapter(app),\n }) as SpecificationRunnerWithCleanup;\n\n runner.cleanup = () => orchestrator.stop();\n runner.orchestrator = orchestrator;\n\n return runner;\n}\n"],"mappings":"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"}
|