@jterrazz/test 8.0.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.
Files changed (48) hide show
  1. package/README.md +209 -230
  2. package/dist/checker.d.ts +1 -0
  3. package/dist/checker.js +741 -0
  4. package/dist/index.d.ts +1249 -728
  5. package/dist/index.js +3133 -1538
  6. package/dist/intercept.js +129 -299
  7. package/dist/match.js +153 -0
  8. package/dist/oxlint.cjs +2931 -0
  9. package/dist/oxlint.d.cts +150 -0
  10. package/dist/oxlint.d.ts +150 -0
  11. package/dist/oxlint.js +2925 -0
  12. package/package.json +47 -41
  13. package/dist/chunk.cjs +0 -28
  14. package/dist/index.cjs +0 -2264
  15. package/dist/index.cjs.map +0 -1
  16. package/dist/index.d.cts +0 -980
  17. package/dist/index.js.map +0 -1
  18. package/dist/intercept.cjs +0 -323
  19. package/dist/intercept.cjs.map +0 -1
  20. package/dist/intercept.d.cts +0 -115
  21. package/dist/intercept.d.ts +0 -115
  22. package/dist/intercept.js.map +0 -1
  23. package/dist/intercept2.cjs +0 -81
  24. package/dist/intercept2.cjs.map +0 -1
  25. package/dist/intercept2.js +0 -81
  26. package/dist/intercept2.js.map +0 -1
  27. package/dist/mock-of.cjs +0 -32
  28. package/dist/mock-of.cjs.map +0 -1
  29. package/dist/mock-of.d.cts +0 -27
  30. package/dist/mock-of.d.ts +0 -27
  31. package/dist/mock-of.js +0 -19
  32. package/dist/mock-of.js.map +0 -1
  33. package/dist/mock.cjs +0 -4
  34. package/dist/mock.d.cts +0 -2
  35. package/dist/mock.d.ts +0 -2
  36. package/dist/mock.js +0 -2
  37. package/dist/services.cjs +0 -5
  38. package/dist/services.d.cts +0 -2
  39. package/dist/services.d.ts +0 -2
  40. package/dist/services.js +0 -2
  41. package/dist/sqlite.cjs +0 -350
  42. package/dist/sqlite.cjs.map +0 -1
  43. package/dist/sqlite.d.cts +0 -209
  44. package/dist/sqlite.d.ts +0 -209
  45. package/dist/sqlite.js +0 -331
  46. package/dist/sqlite.js.map +0 -1
  47. package/dist/types.d.cts +0 -42
  48. package/dist/types.d.ts +0 -42
package/dist/index.d.cts DELETED
@@ -1,980 +0,0 @@
1
- import { a as PostgresOptions, c as IsolationStrategy, i as redis, l as DatabasePort, n as sqlite, o as postgres, r as RedisOptions, s as ServiceHandle, t as SqliteOptions } from "./sqlite.cjs";
2
- import { n as InterceptResponse, r as InterceptTrigger } from "./types.cjs";
3
- import { i as mockOfDate, n as mockOf, r as MockDatePort, t as MockPort } from "./mock-of.cjs";
4
-
5
- //#region src/infra/docker/docker.port.d.ts
6
- /** Abstract interface for interacting with a Docker container (exec, inspect, file I/O). */
7
- interface DockerContainerPort {
8
- /** Execute a command inside the container, return stdout */
9
- exec(cmd: string[]): Promise<string>;
10
- /** Read a file from inside the container */
11
- file(path: string): Promise<{
12
- exists: boolean;
13
- content: string;
14
- }>;
15
- /** Check if container is running */
16
- isRunning(): Promise<boolean>;
17
- /** Get container logs */
18
- logs(tail?: number): Promise<string>;
19
- /** Get full docker inspect JSON */
20
- inspect(): Promise<DockerInspectResult>;
21
- /** Check if a file/directory exists */
22
- exists(path: string): Promise<boolean>;
23
- }
24
- /** Normalized subset of `docker inspect` output for assertion use. */
25
- interface DockerInspectResult {
26
- id: string;
27
- name: string;
28
- state: {
29
- running: boolean;
30
- exitCode: number;
31
- status: string;
32
- };
33
- config: {
34
- image: string;
35
- env: string[];
36
- };
37
- hostConfig: {
38
- memory: number;
39
- cpuQuota: number;
40
- networkMode: string;
41
- mounts: Array<{
42
- source: string;
43
- destination: string;
44
- type: string;
45
- }>;
46
- };
47
- networkSettings: {
48
- networks: Record<string, {
49
- gateway: string;
50
- ipAddress: string;
51
- }>;
52
- };
53
- }
54
- //#endregion
55
- //#region src/infra/docker/docker-assertion.d.ts
56
- /**
57
- * Fluent assertion builder for Docker containers.
58
- * Chain async assertions like `toBeRunning()`, `toHaveFile()`, `toHaveMount()`.
59
- */
60
- declare class DockerAssertion {
61
- private container;
62
- constructor(container: DockerContainerPort);
63
- /** Assert the container is running */
64
- toBeRunning(): Promise<this>;
65
- /** Assert the container is NOT running / doesn't exist */
66
- toNotExist(): Promise<this>;
67
- /** Assert a file exists inside the container */
68
- toHaveFile(path: string, opts?: {
69
- containing?: string;
70
- }): Promise<this>;
71
- /** Assert a file does NOT exist */
72
- toNotHaveFile(path: string): Promise<this>;
73
- /** Assert a directory exists */
74
- toHaveDirectory(path: string): Promise<this>;
75
- /** Assert a mount exists */
76
- toHaveMount(destination: string): Promise<this>;
77
- /** Assert network mode */
78
- toHaveNetwork(mode: string): Promise<this>;
79
- /** Assert memory limit */
80
- toHaveMemoryLimit(bytes: number): Promise<this>;
81
- /** Assert CPU quota */
82
- toHaveCpuQuota(quota: number): Promise<this>;
83
- /** Execute a command and return output for custom assertions */
84
- exec(cmd: string[]): Promise<string>;
85
- /** Read a file for custom assertions */
86
- readFile(path: string): Promise<string>;
87
- /** Get logs for custom assertions */
88
- getLogs(tail?: number): Promise<string>;
89
- }
90
- //#endregion
91
- //#region src/infra/orchestrator.d.ts
92
- interface OrchestratorOptions {
93
- services: ServiceHandle[];
94
- mode: 'e2e' | 'integration';
95
- root?: string;
96
- /** Compose project name — used for per-worker stack isolation. */
97
- projectName?: string;
98
- }
99
- /**
100
- * Orchestrator for test infrastructure.
101
- * Integration: starts services via testcontainers.
102
- * E2E: runs full docker compose up.
103
- */
104
- declare class Orchestrator {
105
- private services;
106
- private mode;
107
- private root;
108
- private projectName;
109
- private running;
110
- private composeStack;
111
- private composeHandles;
112
- private started;
113
- constructor(options: OrchestratorOptions);
114
- /**
115
- * Start declared services via testcontainers (integration mode).
116
- * Phase 1: start all containers in parallel (the slow part).
117
- * Phase 2: wire connections, healthcheck, and init sequentially (fast).
118
- */
119
- start(): Promise<void>;
120
- /**
121
- * Stop testcontainers (integration mode).
122
- */
123
- stop(): Promise<void>;
124
- /**
125
- * Start full docker compose stack (e2e mode).
126
- * Auto-detects infra services and creates handles for them.
127
- */
128
- startCompose(): Promise<void>;
129
- /**
130
- * Stop docker compose stack (e2e mode).
131
- */
132
- stopCompose(): Promise<void>;
133
- /**
134
- * Get a database service by compose name, or the first one if no name given.
135
- */
136
- getDatabase(serviceName?: string): DatabasePort | null;
137
- /**
138
- * Get all database services keyed by compose name.
139
- */
140
- getDatabases(): Map<string, DatabasePort>;
141
- /**
142
- * Get app URL from compose (e2e mode).
143
- */
144
- getAppUrl(): null | string;
145
- }
146
- //#endregion
147
- //#region src/spec/modes/cli/command.port.d.ts
148
- /**
149
- * Result of executing a CLI command, including exit code and captured output streams.
150
- */
151
- interface CommandResult {
152
- /** Process exit code (0 = success). */
153
- exitCode: number;
154
- /** Captured standard output. */
155
- stdout: string;
156
- /** Captured standard error. */
157
- stderr: string;
158
- }
159
- /**
160
- * Options for spawning a long-running process.
161
- */
162
- interface SpawnOptions {
163
- /** Resolve when stdout/stderr contains this string. */
164
- waitFor: string;
165
- /** Kill the process after this many milliseconds. */
166
- timeout: number;
167
- }
168
- /**
169
- * Extra environment variables to set for the child process.
170
- * Values are merged on top of process.env. A `null` value unsets the variable.
171
- */
172
- type CommandEnv = Record<string, null | string>;
173
- /**
174
- * Abstract CLI interface for specification runners.
175
- * Implement this to plug in your command execution strategy.
176
- */
177
- interface CommandPort {
178
- /** Execute a CLI command with the given arguments in the given working directory. */
179
- exec(args: string, cwd: string, env?: CommandEnv): Promise<CommandResult>;
180
- /** Spawn a long-running process and wait for a pattern or timeout. */
181
- spawn(args: string, cwd: string, options: SpawnOptions, env?: CommandEnv): Promise<CommandResult>;
182
- }
183
- //#endregion
184
- //#region src/spec/result/directory.d.ts
185
- interface DirectorySnapshotOptions {
186
- ignore?: string[];
187
- update?: boolean;
188
- }
189
- declare class DirectoryAccessor {
190
- private absPath;
191
- private testDir;
192
- constructor(absPath: string, testDir: string);
193
- toMatchFixture(name: string, options?: DirectorySnapshotOptions): Promise<void>;
194
- files(options?: {
195
- ignore?: string[];
196
- }): Promise<string[]>;
197
- }
198
- //#endregion
199
- //#region src/spec/result/filesystem.d.ts
200
- /**
201
- * Accessor for the whole temporary working directory used by a CLI spec.
202
- *
203
- * Generalization of {@link DirectoryAccessor} that snapshots the entire CLI
204
- * working directory into `<test-file-dir>/expected/filesystem/<name>/`.
205
- */
206
- declare class FilesystemAccessor {
207
- /** The absolute path of the temporary working directory. */
208
- readonly cwd: string;
209
- private readonly testDir;
210
- constructor(cwd: string, testDir: string);
211
- /** List all files (recursively) under the working directory, sorted. */
212
- files(options?: {
213
- ignore?: string[];
214
- }): Promise<string[]>;
215
- /**
216
- * Assert the working directory matches a convention-based fixture tree:
217
- * `<test-file-dir>/expected/filesystem/<name>/`.
218
- */
219
- toMatch(name: string, options?: DirectorySnapshotOptions): Promise<void>;
220
- }
221
- //#endregion
222
- //#region src/spec/result/json.d.ts
223
- interface JsonSnapshotOptions {
224
- update?: boolean;
225
- }
226
- /**
227
- * Accessor for a JSON payload parsed from a text stream (stdout).
228
- *
229
- * Lazily parses the JSON on first use; parse errors are deferred until an
230
- * assertion is performed so that callers can still read the raw stream text
231
- * without triggering a throw.
232
- *
233
- * When a `transform` is configured on the spec runner, it is applied to the
234
- * raw stdout text BEFORE `JSON.parse`. This lets consumers strip ANSI codes,
235
- * scrub machine-specific paths, etc. before structural comparison.
236
- */
237
- declare class JsonAccessor {
238
- private readonly rawText;
239
- private readonly testDir;
240
- private readonly transform?;
241
- constructor(rawText: string, testDir: string, transform?: (text: string) => string);
242
- /** The parsed JSON value. Throws if the text is not valid JSON. */
243
- get value(): unknown;
244
- /**
245
- * Assert the parsed JSON deep-equals the JSON in the given file on disk.
246
- *
247
- * Path is resolved relative to the test file directory unless absolute.
248
- * If `JTERRAZZ_TEST_UPDATE=1` (or vitest `-u`), the file is (re)written
249
- * with the pretty-printed parsed value.
250
- */
251
- toMatchFile(path: string, options?: JsonSnapshotOptions): void;
252
- /**
253
- * Assert the parsed JSON matches a convention-based fixture:
254
- * `<test-file-dir>/expected/json/<name>`.
255
- *
256
- * The extension is part of the name and must be included by the caller —
257
- * e.g. `toMatch('error.json')`, not `toMatch('error')`. Explicit extensions
258
- * are clearer at the call site and remove magic from the resolution.
259
- */
260
- toMatch(name: string, options?: JsonSnapshotOptions): void;
261
- private parse;
262
- }
263
- //#endregion
264
- //#region src/spec/result/table.d.ts
265
- /** Assertion helper for verifying database table contents after a specification run. */
266
- declare class TableAssertion {
267
- private tableName;
268
- private db;
269
- constructor(tableName: string, db: DatabasePort);
270
- /**
271
- * Assert that the table contains exactly the expected rows for the given columns.
272
- *
273
- * @example
274
- * await result.table("users").toMatch({
275
- * columns: ["name", "email"],
276
- * rows: [["Alice", "alice@example.com"]],
277
- * });
278
- */
279
- toMatch(expected: {
280
- columns: string[];
281
- rows: unknown[][];
282
- }): Promise<void>;
283
- /** Assert that the table has zero rows. */
284
- toBeEmpty(): Promise<void>;
285
- }
286
- //#endregion
287
- //#region src/spec/result/result.d.ts
288
- /** Read-only handle to a single file produced by a spec action. */
289
- interface FileAccessor {
290
- /** The UTF-8 text content. Throws if the file does not exist. */
291
- readonly content: string;
292
- readonly exists: boolean;
293
- }
294
- interface BaseResultOptions {
295
- config: SpecificationConfig;
296
- testDir: string;
297
- workDir?: string;
298
- }
299
- /**
300
- * Base result - common accessors available after any action type.
301
- * Extended by HttpResult, CliResult, and used directly by JobResult.
302
- */
303
- declare class BaseResult {
304
- protected config: SpecificationConfig;
305
- protected testDir: string;
306
- protected workDir?: string;
307
- constructor(options: BaseResultOptions);
308
- /** Access a directory (relative to the working directory) for snapshot assertions. */
309
- directory(path?: string): DirectoryAccessor;
310
- /** Access a single file (relative to the working directory) for content assertions. */
311
- file(path: string): FileAccessor;
312
- /** Access a database table for row-level assertions. */
313
- table(tableName: string, options?: {
314
- service?: string;
315
- }): TableAssertion;
316
- private resolveDatabase;
317
- }
318
- //#endregion
319
- //#region src/spec/result/stream.d.ts
320
- interface StreamSnapshotOptions {
321
- update?: boolean;
322
- }
323
- /**
324
- * Accessor for a captured text stream (stdout/stderr) with file-based
325
- * assertion support.
326
- *
327
- * Backed by a primitive string, exposed via {@link text}, but also provides
328
- * `toString()` / `valueOf()` for string coercion, so common patterns like
329
- * `String(result.stdout)` and template-literal interpolation still work.
330
- */
331
- declare class StreamAccessor {
332
- private readonly streamName;
333
- private readonly testDir;
334
- private readonly transform?;
335
- readonly text: string;
336
- constructor(text: string, streamName: string, testDir: string, transform?: (text: string) => string);
337
- /**
338
- * Assert the captured text matches the given file on disk.
339
- *
340
- * Path is resolved relative to the test file directory unless absolute.
341
- * If `JTERRAZZ_TEST_UPDATE=1` (or vitest `-u`), the file is (re)written
342
- * with the actual text.
343
- *
344
- * When a `transform` is configured on the spec runner, it is applied to
345
- * the actual text before comparison (and before writing in update mode).
346
- * The fixture file is treated as authoritative and is NOT transformed.
347
- */
348
- toMatchFile(path: string, options?: StreamSnapshotOptions): void;
349
- /**
350
- * Assert the captured text matches a convention-based fixture:
351
- * `<test-file-dir>/expected/<stream>/<name>`.
352
- *
353
- * The extension is part of the name and must be included by the caller —
354
- * e.g. `toMatch('valid.txt')`, not `toMatch('valid')`. Explicit extensions
355
- * are clearer at the call site and remove magic from the resolution.
356
- */
357
- toMatch(name: string, options?: StreamSnapshotOptions): void;
358
- /**
359
- * Assert the captured text contains the given substring. The runner's
360
- * `transform` (if any) is applied before the check so callers can rely
361
- * on the same normalised view as {@link toMatch}.
362
- *
363
- * Throws with a tight diff-style error on miss so tests read uniformly
364
- * with the other accessor assertions (no reaching through `.text` into
365
- * `expect(...).toContain(...)`).
366
- */
367
- toContain(substring: string): void;
368
- toString(): string;
369
- valueOf(): string;
370
- }
371
- //#endregion
372
- //#region src/spec/modes/cli/container-accessor.d.ts
373
- /**
374
- * Assertion accessor for a single Docker container captured by the docker()
375
- * spec mode. Mirrors the shape of {@link CliResult} so tests use the same
376
- * vocabulary (`stdout.toContain`, `file(...).content`, etc.) regardless of
377
- * where output came from.
378
- *
379
- * Sync state (`exists`, `running`, `status`) is derived from the `docker
380
- * inspect` payload captured at result-construction time. Logs are fetched
381
- * lazily on first access to `stdout`/`stderr`.
382
- */
383
- declare class ContainerAccessor {
384
- private cachedLogs;
385
- private readonly containerId;
386
- readonly exists: boolean;
387
- private readonly inspectData;
388
- readonly running: boolean;
389
- readonly status: string;
390
- private readonly testDir;
391
- private readonly transform?;
392
- /**
393
- * Underlying Docker container ID, or `null` if no container was captured
394
- * for this name. Useful when a follow-up CLI command needs to reference
395
- * the container by id (e.g. `spwn world inspect <id>`). Prefer the other
396
- * accessors for common reads — pulling the raw id is the escape hatch.
397
- */
398
- get id(): null | string;
399
- constructor(containerId: null | string, inspectData: unknown, testDir: string, transform?: (text: string) => string);
400
- /**
401
- * Raw `docker inspect` object for this container. Throws if the container
402
- * was not captured (i.e. `.exists === false`).
403
- */
404
- get inspect(): JsonAccessor;
405
- /** Captured logs (stdout+stderr combined for v1). */
406
- get stdout(): StreamAccessor;
407
- /** Captured logs (stdout+stderr combined for v1). */
408
- get stderr(): StreamAccessor;
409
- /**
410
- * Read a file from inside the container via `docker exec cat`. The
411
- * returned object satisfies the same {@link FileAccessor} shape used by
412
- * the host filesystem accessor, so tests do not need to learn a new API.
413
- */
414
- file(path: string): FileAccessor;
415
- /**
416
- * Run a shell command inside the container and get back the same
417
- * {@link CliResult} used for host-side executions.
418
- */
419
- exec(cmd: string): Promise<CliResult>;
420
- private requireExists;
421
- private loadLogs;
422
- private containerExec;
423
- }
424
- //#endregion
425
- //#region src/spec/modes/cli/result.d.ts
426
- /**
427
- * Result from a CLI action (.exec(), .spawn()).
428
- *
429
- * When the runner was configured with a `docker` option, the result also
430
- * exposes container accessors and participates in `Symbol.asyncDispose`
431
- * cleanup. The Docker shell-outs are **lazy**: a test that never calls
432
- * `.container(...)` never queries the Docker daemon, so CLI-only tests
433
- * pay zero Docker cost even when the runner is Docker-aware.
434
- *
435
- * Dispose always runs one final label-filtered `docker ps` to catch
436
- * containers that were spawned during `.exec` but never asserted on —
437
- * tests still get leak-free cleanup even if they forget to reach into
438
- * a container.
439
- */
440
- declare class CliResult extends BaseResult {
441
- private readonly commandResult;
442
- private containersCache;
443
- private readonly dockerConfig?;
444
- private readonly testRunId?;
445
- private readonly transform?;
446
- constructor(options: {
447
- commandResult: CommandResult;
448
- config: SpecificationConfig;
449
- dockerConfig?: DockerSpecConfig;
450
- testDir: string;
451
- testRunId?: string;
452
- transform?: (text: string) => string;
453
- workDir: string;
454
- });
455
- /** The process exit code. */
456
- get exitCode(): number;
457
- /** Accessor for the captured standard output with file-based assertions. */
458
- get stdout(): StreamAccessor;
459
- /** Accessor for the captured standard error with file-based assertions. */
460
- get stderr(): StreamAccessor;
461
- /** Accessor for parsing stdout as JSON and asserting against JSON fixtures. */
462
- get json(): JsonAccessor;
463
- /** Accessor for the temporary working directory the command ran in. */
464
- get filesystem(): FilesystemAccessor;
465
- /**
466
- * Look up a container the CLI spawned during this run by the value of
467
- * its name-label (as declared in `SpecOptions.docker.nameLabel`).
468
- * First access triggers a one-shot `docker ps` + `docker inspect`
469
- * query and caches the result for the rest of the result's lifetime.
470
- * Tests that don't call this never touch Docker.
471
- *
472
- * Returns an accessor with `.exists === false` when no container
473
- * carries that name — callers can still assert absence without a try.
474
- */
475
- container(name: string): ContainerAccessor;
476
- /** All captured container IDs. Triggers the same lazy query as `container()`. */
477
- get containerIds(): string[];
478
- [Symbol.asyncDispose](): Promise<void>;
479
- /**
480
- * Extract text blocks from stdout that contain a pattern.
481
- *
482
- * @example
483
- * expect(result.grep('error.ts')).toContain('no-unused-vars');
484
- */
485
- grep(pattern: string): string;
486
- private ensureDockerAware;
487
- private loadContainers;
488
- }
489
- //#endregion
490
- //#region src/spec/result/response.d.ts
491
- /** Accessor for an HTTP response body with file-based assertion support. */
492
- declare class ResponseAccessor {
493
- readonly body: unknown;
494
- private testDir;
495
- constructor(body: unknown, testDir: string);
496
- /**
497
- * Assert that the response body matches the JSON in `responses/{file}`.
498
- *
499
- * @example
500
- * result.response.toMatchFile("expected-items.json");
501
- */
502
- toMatchFile(file: string): void;
503
- }
504
- //#endregion
505
- //#region src/spec/modes/http/server.port.d.ts
506
- /**
507
- * HTTP response returned by a server port, with parsed JSON body.
508
- */
509
- interface ServerResponse {
510
- /** HTTP status code. */
511
- status: number;
512
- /** Parsed JSON response body, or null if parsing failed. */
513
- body: unknown;
514
- /** Response headers as a flat key-value map. */
515
- headers: Record<string, string>;
516
- }
517
- /**
518
- * Abstract server interface for specification runners.
519
- * Integration mode uses an in-process Hono app; E2E mode uses real HTTP via fetch.
520
- */
521
- interface ServerPort {
522
- /** Send an HTTP request and return the parsed response. */
523
- request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
524
- }
525
- //#endregion
526
- //#region src/spec/modes/http/result.d.ts
527
- /** Result from an HTTP action (.get(), .post(), .put(), .delete()). */
528
- declare class HttpResult extends BaseResult {
529
- private responseData;
530
- constructor(options: {
531
- config: SpecificationConfig;
532
- response: ServerResponse;
533
- testDir: string;
534
- });
535
- /** The HTTP response status code. */
536
- get status(): number;
537
- /** Access the HTTP response body for assertions. */
538
- get response(): ResponseAccessor;
539
- }
540
- //#endregion
541
- //#region src/spec/builder.d.ts
542
- /** A named job that can be triggered via .job(). */
543
- interface JobHandle$1 {
544
- name: string;
545
- execute: () => Promise<void>;
546
- }
547
- /**
548
- * Context passed to a {@link SeedHandler} when a non-SQL seed is dispatched.
549
- */
550
- interface SeedHandlerContext {
551
- /** Absolute path of the temporary working directory for the current spec. */
552
- cwd: string;
553
- /** Absolute path of the test file directory (where `seeds/` lives). */
554
- testDir: string;
555
- }
556
- /**
557
- * Handler invoked for a non-SQL seed entry. The first path segment of the
558
- * seed file selects the handler; the handler is given the absolute path of
559
- * the source fragment (file or directory) and the working directory to mutate.
560
- */
561
- type SeedHandler = (ctx: SeedHandlerContext, fragmentPath: string) => Promise<void> | void;
562
- /**
563
- * Configuration for the docker() spec mode. When set on
564
- * {@link SpecificationConfig}, the CLI runner generates a test-run id, injects
565
- * it into the child env under `envVar`, then queries Docker for every
566
- * container carrying `testRunLabel=<id>` after the command exits.
567
- */
568
- interface DockerSpecConfig {
569
- envVar: string;
570
- nameLabel: string;
571
- testRunLabel: string;
572
- }
573
- /** Adapter configuration passed to the specification runner at setup time. */
574
- interface SpecificationConfig {
575
- command?: CommandPort;
576
- database?: DatabasePort;
577
- databases?: Map<string, DatabasePort>;
578
- dockerConfig?: DockerSpecConfig;
579
- /**
580
- * Unique id shared by every `.run()` from this runner instance.
581
- * Stable for the runner's lifetime so multi-step tests (spawn in
582
- * one run, inspect in another) see the same container scope. The
583
- * public `createSpecificationRunner` auto-populates this when
584
- * `dockerConfig` is present — callers should leave it unset.
585
- */
586
- dockerTestRunId?: string;
587
- fixturesRoot?: string;
588
- jobs?: JobHandle$1[];
589
- /**
590
- * Pluggable seed handlers for CLI-mode tests. Keys are leading path
591
- * segments (e.g. `"spwn.yaml/"`, `"agent/"`). When `.seed(relPath)` is
592
- * called with a path whose first segment matches, the handler is invoked
593
- * with the full absolute path of the seed fragment under
594
- * `<test-file-dir>/seeds/<relPath>`.
595
- */
596
- seedHandlers?: Record<string, SeedHandler>;
597
- server?: ServerPort;
598
- /**
599
- * Optional normaliser applied to CLI stdout/stderr before every
600
- * .toMatch / .toMatchFile comparison. Does not mutate the raw
601
- * `.text` accessor.
602
- */
603
- transform?: (text: string) => string;
604
- }
605
- /**
606
- * Fluent builder for declaring a single test specification.
607
- *
608
- * Chain setup methods ({@link seed}, {@link fixture}, {@link env}), an action
609
- * ({@link get}, {@link post}, {@link exec}), then call {@link run} to execute
610
- * and receive a {@link SpecificationResult} for assertions.
611
- */
612
- declare class SpecificationBuilder {
613
- private commandArgs;
614
- private commandEnv;
615
- private config;
616
- private fixtures;
617
- private intercepts;
618
- private jobName;
619
- private label;
620
- private mocks;
621
- private projectName;
622
- private request;
623
- private requestHeaders;
624
- private seeds;
625
- private spawnConfig;
626
- private testDir;
627
- constructor(config: SpecificationConfig, testDir: string, label: string);
628
- /**
629
- * Queue a SQL seed file to run before the action.
630
- *
631
- * @example
632
- * spec("creates user").seed("users.sql").exec("list-users").run();
633
- */
634
- seed(file: string, options?: {
635
- service?: string;
636
- }): this;
637
- /** Copy a file or directory from `fixtures/` into the working directory before execution. */
638
- fixture(file: string): this;
639
- /** Copy an entire project fixture from `fixturesRoot` into the working directory. */
640
- project(name: string): this;
641
- /** Register an MSW mock definition file from `mock/` to intercept HTTP calls. */
642
- mock(file: string): this;
643
- /**
644
- * Set environment variables for the CLI process. Merged on top of process.env.
645
- * Use `null` to unset a variable. Multiple calls merge.
646
- *
647
- * The token `$WORKDIR` (in any value) is replaced with the actual working
648
- * directory at run-time — useful for tests that need a fully isolated `HOME`.
649
- *
650
- * @example
651
- * spec("...").env({ HOME: "$WORKDIR", TZ: "UTC" }).exec("status").run();
652
- */
653
- env(env: CommandEnv): this;
654
- /**
655
- * Set HTTP headers for the request. Multiple calls merge.
656
- *
657
- * @example
658
- * spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
659
- */
660
- headers(headers: Record<string, string>): this;
661
- /**
662
- * Intercept an outgoing HTTP request and return a controlled response.
663
- * Uses MSW under the hood. Intercepts are queued — multiple calls with the
664
- * same trigger fire sequentially (first match consumed first).
665
- *
666
- * @param trigger - What to match (use openai.agent(), http.get(), etc.)
667
- * @param response - What to return: an InterceptResponse object, or a filename
668
- * from `intercepts/` (JSON loaded and auto-wrapped by the trigger).
669
- *
670
- * @example
671
- * // With inline response
672
- * .intercept(openai.agent({...}), openai.reply({ categories: ['TECH'] }))
673
- *
674
- * // With JSON fixture file (loaded from intercepts/ directory)
675
- * .intercept(openai.agent({...}), 'ingest-tech.json')
676
- * .intercept(http.get(url), 'world-news-tech.json')
677
- */
678
- intercept(trigger: InterceptTrigger, response: InterceptResponse | string): this;
679
- /**
680
- * Send a GET request to the server adapter.
681
- *
682
- * @example
683
- * spec("list items").get("/api/items").run();
684
- */
685
- get(path: string): this;
686
- /**
687
- * Send a POST request to the server adapter.
688
- *
689
- * @param bodyFile - Optional JSON file from `requests/` to use as the request body.
690
- * @example
691
- * spec("create item").post("/api/items", "new-item.json").run();
692
- */
693
- post(path: string, bodyFile?: string): this;
694
- /** Send a PUT request to the server adapter. */
695
- put(path: string, bodyFile?: string): this;
696
- /** Send a DELETE request to the server adapter. */
697
- delete(path: string): this;
698
- /**
699
- * Execute a CLI command (or a sequence of commands) in an isolated working directory.
700
- * When an array is passed, commands run sequentially and stop on the first non-zero exit code.
701
- *
702
- * @example
703
- * spec("init project").exec("init --name demo").run();
704
- * spec("multi-step").exec(["init", "build"]).run();
705
- */
706
- exec(args: string | string[]): this;
707
- /** Spawn a long-running CLI process with custom spawn options (e.g. timeout, signal). */
708
- spawn(args: string, options: SpawnOptions): this;
709
- /**
710
- * Execute a named job registered via the app() factory.
711
- *
712
- * @param name - The job name to trigger (must match a registered JobHandle.name).
713
- *
714
- * @example
715
- * spec('pipeline').intercept(openai.chat(), openai.response({...})).job('report-refresh').run();
716
- */
717
- job(name: string): this;
718
- /**
719
- * Execute the specification: run seeds, copy fixtures, then perform the
720
- * configured action (HTTP or CLI).
721
- *
722
- * @returns The result object used for assertions.
723
- * @example
724
- * const result = await spec("test").exec("status").run();
725
- * expect(result.exitCode).toBe(0);
726
- */
727
- run(): Promise<BaseResult | CliResult | HttpResult>;
728
- private resolveSeedHandler;
729
- private resolveEnv;
730
- private prepareWorkDir;
731
- private runHttpAction;
732
- private runJobAction;
733
- private runCliAction;
734
- }
735
- /** Factory function returned by `cli()`, `integration()`, or `e2e()` that starts a new spec. */
736
- type SpecificationRunner = (label: string) => SpecificationBuilder;
737
- /**
738
- * Create a {@link SpecificationRunner} bound to the given adapter configuration.
739
- * The test file directory is auto-detected from the call stack.
740
- */
741
- declare function createSpecificationRunner(config: SpecificationConfig): SpecificationRunner;
742
- //#endregion
743
- //#region src/spec/targets.d.ts
744
- /** Any object with a request method compatible with Hono's app.request(). */
745
- type HonoApp = {
746
- request: (path: string, init?: RequestInit) => Promise<Response> | Response;
747
- };
748
- /** A named job that can be triggered via .job(). */
749
- interface JobHandle {
750
- name: string;
751
- execute: () => Promise<void>;
752
- }
753
- /** Services map passed to the app factory after infrastructure starts. */
754
- type AppServices = Record<string, ServiceHandle>;
755
- /**
756
- * Return value from the app() factory. Either a bare server (backward compat)
757
- * or an object with server + jobs for job-based testing.
758
- */
759
- type AppFactoryResult = HonoApp | {
760
- server: HonoApp;
761
- jobs: JobHandle[];
762
- };
763
- /** In-process Hono app target. Created by {@link app}. */
764
- interface AppTarget {
765
- readonly kind: 'app';
766
- readonly factory: (services: AppServices) => AppFactoryResult;
767
- }
768
- /** Docker compose stack target. Created by {@link stack}. */
769
- interface StackTarget {
770
- readonly kind: 'stack';
771
- readonly root: string;
772
- }
773
- /** CLI command target. Created by {@link command}. */
774
- interface CommandTarget {
775
- readonly kind: 'command';
776
- readonly bin: string;
777
- }
778
- /** Any target that produces an HTTP interface (app or stack). */
779
- type HttpTarget = AppTarget | StackTarget;
780
- /** Any valid spec target. */
781
- type SpecTarget = AppTarget | CommandTarget | StackTarget;
782
- /**
783
- * Test against an in-process Hono app. The factory receives started services
784
- * so you can wire connection strings into your app/DI container.
785
- *
786
- * @param factory - Function that receives services and returns a Hono app instance.
787
- *
788
- * @example
789
- * const db = postgres({ compose: 'db' });
790
- * await spec(
791
- * app((services) => createApp({ databaseUrl: services.db.connectionString })),
792
- * { services: [db] },
793
- * );
794
- */
795
- declare function app(factory: (services: AppServices) => HonoApp): AppTarget;
796
- /**
797
- * Test against a full docker compose stack. The stack is started with
798
- * `docker compose up` and real HTTP requests are sent to the app service.
799
- *
800
- * @param root - Project root containing `docker/compose.test.yaml`.
801
- *
802
- * @example
803
- * await spec(stack('../../'));
804
- */
805
- declare function stack(root: string): StackTarget;
806
- /**
807
- * Test a CLI binary. Each spec runs in a fresh temp directory.
808
- *
809
- * Pass a `docker: { envVar, nameLabel, testRunLabel }` option on
810
- * {@link SpecOptions} to make the runner Docker-aware — the runner
811
- * stamps a unique test-run id into `envVar`, and results expose
812
- * `.container(name)` accessors that lazily query Docker. Tests that
813
- * never call `.container(...)` pay zero Docker cost.
814
- *
815
- * @param bin - Path to the CLI binary or command name (resolved from node_modules/.bin or PATH).
816
- *
817
- * @example
818
- * // CLI-only
819
- * await spec(command('my-cli'), { root: '../fixtures' });
820
- *
821
- * // CLI binary that also spawns containers — same runner, just
822
- * // opt into container accessors via the docker option:
823
- * await spec(command('my-cli'), {
824
- * root: '../fixtures',
825
- * docker: {
826
- * envVar: 'MYCLI_TEST_LABEL',
827
- * nameLabel: 'com.mycli.world.name',
828
- * testRunLabel: 'com.mycli.test.run',
829
- * },
830
- * });
831
- */
832
- declare function command(bin: string): CommandTarget;
833
- //#endregion
834
- //#region src/spec/spec.d.ts
835
- /** Shared options for all spec targets. */
836
- interface SpecOptions {
837
- /**
838
- * Opt-in Docker awareness for CLI-mode runners. When set, every
839
- * `.run()` generates a unique test-run id, injects it into the
840
- * child process env under `envVar`, and exposes `.container(name)`
841
- * accessors on the result that lazily query Docker. A test that
842
- * never calls `.container(...)` never touches the Docker daemon —
843
- * CLI-only and container-asserting tests share one runner.
844
- *
845
- * Always wrap calls that may spawn containers with `await using`
846
- * so leaked containers get force-removed at scope exit (the
847
- * runner cleans up by label filter).
848
- */
849
- docker?: DockerSpecConfig;
850
- /** Project root for fixture lookup and compose detection (relative paths supported). */
851
- root?: string;
852
- /**
853
- * Pluggable seed handlers for CLI-mode tests. Keys are leading path
854
- * segments (e.g. `"spwn.yaml/"`); values receive the seed context and the
855
- * absolute path of the source fragment under `<test-file-dir>/seeds/`.
856
- */
857
- seedHandlers?: Record<string, SeedHandler>;
858
- /** Infrastructure services to start via testcontainers. */
859
- services?: ServiceHandle[];
860
- /**
861
- * Optional normaliser applied to result.stdout.text and
862
- * result.stderr.text before every .toMatch / .toMatchFile
863
- * comparison. Useful for stripping ANSI codes, masking
864
- * machine-specific paths, scrubbing timestamps, etc.
865
- *
866
- * The transform does NOT mutate the raw `.text` accessor —
867
- * callers can still inspect the pristine output.
868
- */
869
- transform?: (text: string) => string;
870
- }
871
- /** A specification runner with teardown support and orchestrator access. */
872
- interface SpecRunner {
873
- /** The function to create individual specs: `runner('label').get('/path').run()`. */
874
- (label: string): SpecificationBuilder;
875
- /** Stop all infrastructure started by this runner. */
876
- cleanup: () => Promise<void>;
877
- /**
878
- * Get a Docker assertion builder for a running container.
879
- *
880
- * @example
881
- * await runner.docker('my-container').toBeRunning();
882
- */
883
- docker: (containerId: string) => DockerAssertion;
884
- /** The orchestrator managing the test infrastructure lifecycle. */
885
- orchestrator: Orchestrator;
886
- }
887
- /**
888
- * Create a specification runner for the given target.
889
- *
890
- * @param target - What to test against: {@link app}, {@link stack}, or {@link command}.
891
- * @param options - Shared options: root directory, infrastructure services.
892
- *
893
- * @example
894
- * // HTTP — in-process app with testcontainers
895
- * const s = await spec(app(() => createApp(db)), { services: [db] });
896
- *
897
- * // HTTP — full docker compose stack
898
- * const s = await spec(stack('../../'));
899
- *
900
- * // CLI — command binary
901
- * const s = await spec(command('my-cli'), { root: '../fixtures' });
902
- */
903
- declare function spec(target: SpecTarget, options?: SpecOptions): Promise<SpecRunner>;
904
- //#endregion
905
- //#region src/spec/modes/cli/docker-lookup.d.ts
906
- /** Return all container IDs (running or stopped) that carry `key=value`. */
907
- declare function findContainersByLabel(key: string, value: string): string[];
908
- /** Return the raw `docker inspect` payload (object, not array) for a container. */
909
- declare function inspectContainer(id: string): unknown;
910
- /** Force-remove the given container IDs in a single call. Errors are swallowed. */
911
- declare function removeContainers(ids: string[]): void;
912
- //#endregion
913
- //#region src/infra/containers/container.port.d.ts
914
- /**
915
- * Abstract container interface.
916
- * Represents a running service (database, cache, etc.)
917
- */
918
- interface ContainerPort {
919
- /** Start the container and wait until ready. */
920
- start(): Promise<void>;
921
- /** Stop and remove the container. */
922
- stop(): Promise<void>;
923
- /** Get the mapped host port for a container port. */
924
- getMappedPort(containerPort: number): number;
925
- /** Get the host to connect to. */
926
- getHost(): string;
927
- /** Get a full connection string for this service. */
928
- getConnectionString(): string;
929
- /** Get container logs (stdout + stderr). */
930
- getLogs(): Promise<string>;
931
- }
932
- //#endregion
933
- //#region src/spec/modes/cli/adapters/exec.adapter.d.ts
934
- /**
935
- * Executes CLI commands via Node.js child_process.
936
- * Uses `spawnSync` for one-shot commands and `spawn` for long-running processes.
937
- * Used by the command() specification runner.
938
- *
939
- * `spawnSync` is used (over the simpler `execSync`) so stdout AND stderr are
940
- * captured regardless of exit code. Many CLIs follow the Unix convention of
941
- * writing status banners to stderr on success — `execSync` would silently
942
- * discard them, leaving snapshot tests with no output to assert on.
943
- */
944
- declare class ExecAdapter implements CommandPort {
945
- private command;
946
- constructor(command: string);
947
- exec(args: string, cwd: string, extraEnv?: CommandEnv): Promise<CommandResult>;
948
- spawn(args: string, cwd: string, options: SpawnOptions, extraEnv?: CommandEnv): Promise<CommandResult>;
949
- }
950
- //#endregion
951
- //#region src/spec/modes/http/adapters/fetch.adapter.d.ts
952
- /**
953
- * Server adapter that sends real HTTP requests via the Fetch API.
954
- * Used by the `e2e()` specification runner to hit a live server.
955
- */
956
- declare class FetchAdapter implements ServerPort {
957
- private baseUrl;
958
- constructor(url: string);
959
- request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
960
- }
961
- //#endregion
962
- //#region src/spec/modes/http/adapters/hono.adapter.d.ts
963
- /**
964
- * Server adapter that dispatches requests in-process through a Hono app instance.
965
- * Used by the `integration()` specification runner -- no network overhead.
966
- */
967
- declare class HonoAdapter implements ServerPort {
968
- private app;
969
- constructor(app: {
970
- request: (path: string, init?: RequestInit) => Promise<Response> | Response;
971
- });
972
- request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
973
- }
974
- //#endregion
975
- //#region src/infra/docker/docker.d.ts
976
- /** Create a {@link DockerContainerPort} bound to an existing container by ID. */
977
- declare function dockerContainer(containerId: string): DockerContainerPort;
978
- //#endregion
979
- export { type AppFactoryResult, type AppServices, type AppTarget, BaseResult, CliResult, type CommandEnv, type CommandPort, type CommandResult, type CommandTarget, ContainerAccessor, type ContainerPort, type DatabasePort, DirectoryAccessor, type DirectorySnapshotOptions, DockerAssertion, type DockerContainerPort, type DockerInspectResult, type DockerSpecConfig, ExecAdapter, FetchAdapter, type FileAccessor, FilesystemAccessor, HonoAdapter, HttpResult, type HttpTarget, type IsolationStrategy, type JobHandle, JsonAccessor, type JsonSnapshotOptions, type MockDatePort, type MockPort, Orchestrator, type PostgresOptions, type RedisOptions, ResponseAccessor, type SeedHandler, type SeedHandlerContext, type ServerPort, type ServerResponse, type ServiceHandle, type SpawnOptions, type SpecOptions, type SpecRunner, type SpecTarget, SpecificationBuilder, type SpecificationConfig, type SpecificationRunner, type SqliteOptions, type StackTarget, StreamAccessor, type StreamSnapshotOptions, TableAssertion, app, command, createSpecificationRunner, dockerContainer, findContainersByLabel, inspectContainer, mockOf, mockOfDate, postgres, redis, removeContainers, spec, sqlite, stack };
980
- //# sourceMappingURL=index.d.cts.map