@jterrazz/test 6.0.0 → 6.2.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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ServiceHandle, i as postgres, n as redis, o as IsolationStrategy, r as PostgresOptions, s as DatabasePort, t as RedisOptions } from "./redis.adapter.cjs";
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.adapter.cjs";
2
2
  import { i as mockOfDate, n as mockOf, r as MockDatePort, t as MockPort } from "./mock-of.cjs";
3
3
 
4
4
  //#region src/ports/command.port.d.ts
@@ -56,7 +56,7 @@ interface ServerResponse {
56
56
  */
57
57
  interface ServerPort {
58
58
  /** Send an HTTP request and return the parsed response. */
59
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
59
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
60
60
  }
61
61
  //#endregion
62
62
  //#region src/builder/directory-accessor.d.ts
@@ -214,6 +214,7 @@ declare class SpecificationBuilder {
214
214
  private mocks;
215
215
  private projectName;
216
216
  private request;
217
+ private requestHeaders;
217
218
  private seeds;
218
219
  private spawnConfig;
219
220
  private testDir;
@@ -244,6 +245,13 @@ declare class SpecificationBuilder {
244
245
  * spec("...").env({ HOME: "$WORKDIR", TZ: "UTC" }).exec("status").run();
245
246
  */
246
247
  env(env: CommandEnv): this;
248
+ /**
249
+ * Set HTTP headers for the request. Multiple calls merge.
250
+ *
251
+ * @example
252
+ * spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
253
+ */
254
+ headers(headers: Record<string, string>): this;
247
255
  /**
248
256
  * Send a GET request to the server adapter.
249
257
  *
@@ -440,18 +448,16 @@ declare class Orchestrator {
440
448
  }
441
449
  //#endregion
442
450
  //#region src/runner/targets.d.ts
443
- /**
444
- * Target factories for {@link spec}. Each target describes what is being tested
445
- * and how the specification runner should connect to it.
446
- */
451
+ /** Any object with a request method compatible with Hono's app.request(). */
447
452
  type HonoApp$1 = {
448
- fetch: (...args: any[]) => any;
449
453
  request: (path: string, init?: RequestInit) => Promise<Response> | Response;
450
454
  };
455
+ /** Services map passed to the app factory after infrastructure starts. */
456
+ type AppServices = Record<string, ServiceHandle>;
451
457
  /** In-process Hono app target. Created by {@link app}. */
452
458
  interface AppTarget {
453
459
  readonly kind: 'app';
454
- readonly factory: () => HonoApp$1;
460
+ readonly factory: (services: AppServices) => HonoApp$1;
455
461
  }
456
462
  /** Docker compose stack target. Created by {@link stack}. */
457
463
  interface StackTarget {
@@ -468,15 +474,19 @@ type HttpTarget = AppTarget | StackTarget;
468
474
  /** Any valid spec target. */
469
475
  type SpecTarget = AppTarget | CommandTarget | StackTarget;
470
476
  /**
471
- * Test against an in-process Hono app. Services are started via testcontainers
472
- * and the app runs without network overhead.
477
+ * Test against an in-process Hono app. The factory receives started services
478
+ * so you can wire connection strings into your app/DI container.
473
479
  *
474
- * @param factory - Function that returns a Hono app instance (called after services start).
480
+ * @param factory - Function that receives services and returns a Hono app instance.
475
481
  *
476
482
  * @example
477
- * await spec(app(() => createApp(db)), { services: [db] });
483
+ * const db = postgres({ compose: 'db' });
484
+ * await spec(
485
+ * app((services) => createApp({ databaseUrl: services.db.connectionString })),
486
+ * { services: [db] },
487
+ * );
478
488
  */
479
- declare function app(factory: () => HonoApp$1): AppTarget;
489
+ declare function app(factory: (services: AppServices) => HonoApp$1): AppTarget;
480
490
  /**
481
491
  * Test against a full docker compose stack. The stack is started with
482
492
  * `docker compose up` and real HTTP requests are sent to the app service.
@@ -580,7 +590,7 @@ declare class ExecAdapter implements CommandPort {
580
590
  declare class FetchAdapter implements ServerPort {
581
591
  private baseUrl;
582
592
  constructor(url: string);
583
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
593
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
584
594
  }
585
595
  //#endregion
586
596
  //#region src/adapters/hono.adapter.d.ts
@@ -593,7 +603,7 @@ declare class HonoAdapter implements ServerPort {
593
603
  constructor(app: {
594
604
  request: (path: string, init?: RequestInit) => Promise<Response> | Response;
595
605
  });
596
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
606
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
597
607
  }
598
608
  //#endregion
599
609
  //#region src/runner/integration.d.ts
@@ -670,5 +680,5 @@ declare function normalizeOutput(str: string): string;
670
680
  /** Create a {@link DockerContainerPort} bound to an existing container by ID. */
671
681
  declare function dockerContainer(containerId: string): DockerContainerPort;
672
682
  //#endregion
673
- export { type AppTarget, type CliOptions, type CommandEnv, type CommandPort, type CommandResult, type CommandTarget, type ContainerPort, type DatabasePort, DirectoryAccessor, type DirectorySnapshotOptions, DockerAssertion, type DockerContainerPort, type DockerInspectResult, type E2eOptions, ExecAdapter, FetchAdapter, type FileAccessor, HonoAdapter, type HttpTarget, type IntegrationOptions, type IsolationStrategy, type MockDatePort, type MockPort, Orchestrator, type PostgresOptions, type RedisOptions, ResponseAccessor, type ServerPort, type ServerResponse, type ServiceHandle, type SpawnOptions, type SpecOptions, type SpecRunner, type SpecTarget, SpecificationBuilder, type SpecificationConfig, SpecificationResult, type SpecificationRunner, type SpecificationRunnerWithCleanup, type StackTarget, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, stack, stripAnsi };
683
+ export { type AppServices, type AppTarget, type CliOptions, type CommandEnv, type CommandPort, type CommandResult, type CommandTarget, type ContainerPort, type DatabasePort, DirectoryAccessor, type DirectorySnapshotOptions, DockerAssertion, type DockerContainerPort, type DockerInspectResult, type E2eOptions, ExecAdapter, FetchAdapter, type FileAccessor, HonoAdapter, type HttpTarget, type IntegrationOptions, type IsolationStrategy, type MockDatePort, type MockPort, Orchestrator, type PostgresOptions, type RedisOptions, ResponseAccessor, type ServerPort, type ServerResponse, type ServiceHandle, type SpawnOptions, type SpecOptions, type SpecRunner, type SpecTarget, SpecificationBuilder, type SpecificationConfig, SpecificationResult, type SpecificationRunner, type SpecificationRunnerWithCleanup, type SqliteOptions, type StackTarget, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, sqlite, stack, stripAnsi };
674
684
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as ServiceHandle, i as postgres, n as redis, o as IsolationStrategy, r as PostgresOptions, s as DatabasePort, t as RedisOptions } from "./redis.adapter.js";
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.adapter.js";
2
2
  import { i as mockOfDate, n as mockOf, r as MockDatePort, t as MockPort } from "./mock-of.js";
3
3
 
4
4
  //#region src/ports/command.port.d.ts
@@ -56,7 +56,7 @@ interface ServerResponse {
56
56
  */
57
57
  interface ServerPort {
58
58
  /** Send an HTTP request and return the parsed response. */
59
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
59
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
60
60
  }
61
61
  //#endregion
62
62
  //#region src/builder/directory-accessor.d.ts
@@ -214,6 +214,7 @@ declare class SpecificationBuilder {
214
214
  private mocks;
215
215
  private projectName;
216
216
  private request;
217
+ private requestHeaders;
217
218
  private seeds;
218
219
  private spawnConfig;
219
220
  private testDir;
@@ -244,6 +245,13 @@ declare class SpecificationBuilder {
244
245
  * spec("...").env({ HOME: "$WORKDIR", TZ: "UTC" }).exec("status").run();
245
246
  */
246
247
  env(env: CommandEnv): this;
248
+ /**
249
+ * Set HTTP headers for the request. Multiple calls merge.
250
+ *
251
+ * @example
252
+ * spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
253
+ */
254
+ headers(headers: Record<string, string>): this;
247
255
  /**
248
256
  * Send a GET request to the server adapter.
249
257
  *
@@ -440,18 +448,16 @@ declare class Orchestrator {
440
448
  }
441
449
  //#endregion
442
450
  //#region src/runner/targets.d.ts
443
- /**
444
- * Target factories for {@link spec}. Each target describes what is being tested
445
- * and how the specification runner should connect to it.
446
- */
451
+ /** Any object with a request method compatible with Hono's app.request(). */
447
452
  type HonoApp$1 = {
448
- fetch: (...args: any[]) => any;
449
453
  request: (path: string, init?: RequestInit) => Promise<Response> | Response;
450
454
  };
455
+ /** Services map passed to the app factory after infrastructure starts. */
456
+ type AppServices = Record<string, ServiceHandle>;
451
457
  /** In-process Hono app target. Created by {@link app}. */
452
458
  interface AppTarget {
453
459
  readonly kind: 'app';
454
- readonly factory: () => HonoApp$1;
460
+ readonly factory: (services: AppServices) => HonoApp$1;
455
461
  }
456
462
  /** Docker compose stack target. Created by {@link stack}. */
457
463
  interface StackTarget {
@@ -468,15 +474,19 @@ type HttpTarget = AppTarget | StackTarget;
468
474
  /** Any valid spec target. */
469
475
  type SpecTarget = AppTarget | CommandTarget | StackTarget;
470
476
  /**
471
- * Test against an in-process Hono app. Services are started via testcontainers
472
- * and the app runs without network overhead.
477
+ * Test against an in-process Hono app. The factory receives started services
478
+ * so you can wire connection strings into your app/DI container.
473
479
  *
474
- * @param factory - Function that returns a Hono app instance (called after services start).
480
+ * @param factory - Function that receives services and returns a Hono app instance.
475
481
  *
476
482
  * @example
477
- * await spec(app(() => createApp(db)), { services: [db] });
483
+ * const db = postgres({ compose: 'db' });
484
+ * await spec(
485
+ * app((services) => createApp({ databaseUrl: services.db.connectionString })),
486
+ * { services: [db] },
487
+ * );
478
488
  */
479
- declare function app(factory: () => HonoApp$1): AppTarget;
489
+ declare function app(factory: (services: AppServices) => HonoApp$1): AppTarget;
480
490
  /**
481
491
  * Test against a full docker compose stack. The stack is started with
482
492
  * `docker compose up` and real HTTP requests are sent to the app service.
@@ -580,7 +590,7 @@ declare class ExecAdapter implements CommandPort {
580
590
  declare class FetchAdapter implements ServerPort {
581
591
  private baseUrl;
582
592
  constructor(url: string);
583
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
593
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
584
594
  }
585
595
  //#endregion
586
596
  //#region src/adapters/hono.adapter.d.ts
@@ -593,7 +603,7 @@ declare class HonoAdapter implements ServerPort {
593
603
  constructor(app: {
594
604
  request: (path: string, init?: RequestInit) => Promise<Response> | Response;
595
605
  });
596
- request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
606
+ request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
597
607
  }
598
608
  //#endregion
599
609
  //#region src/runner/integration.d.ts
@@ -670,5 +680,5 @@ declare function normalizeOutput(str: string): string;
670
680
  /** Create a {@link DockerContainerPort} bound to an existing container by ID. */
671
681
  declare function dockerContainer(containerId: string): DockerContainerPort;
672
682
  //#endregion
673
- export { type AppTarget, type CliOptions, type CommandEnv, type CommandPort, type CommandResult, type CommandTarget, type ContainerPort, type DatabasePort, DirectoryAccessor, type DirectorySnapshotOptions, DockerAssertion, type DockerContainerPort, type DockerInspectResult, type E2eOptions, ExecAdapter, FetchAdapter, type FileAccessor, HonoAdapter, type HttpTarget, type IntegrationOptions, type IsolationStrategy, type MockDatePort, type MockPort, Orchestrator, type PostgresOptions, type RedisOptions, ResponseAccessor, type ServerPort, type ServerResponse, type ServiceHandle, type SpawnOptions, type SpecOptions, type SpecRunner, type SpecTarget, SpecificationBuilder, type SpecificationConfig, SpecificationResult, type SpecificationRunner, type SpecificationRunnerWithCleanup, type StackTarget, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, stack, stripAnsi };
683
+ export { type AppServices, type AppTarget, type CliOptions, type CommandEnv, type CommandPort, type CommandResult, type CommandTarget, type ContainerPort, type DatabasePort, DirectoryAccessor, type DirectorySnapshotOptions, DockerAssertion, type DockerContainerPort, type DockerInspectResult, type E2eOptions, ExecAdapter, FetchAdapter, type FileAccessor, HonoAdapter, type HttpTarget, type IntegrationOptions, type IsolationStrategy, type MockDatePort, type MockPort, Orchestrator, type PostgresOptions, type RedisOptions, ResponseAccessor, type ServerPort, type ServerResponse, type ServiceHandle, type SpawnOptions, type SpecOptions, type SpecRunner, type SpecTarget, SpecificationBuilder, type SpecificationConfig, SpecificationResult, type SpecificationRunner, type SpecificationRunnerWithCleanup, type SqliteOptions, type StackTarget, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, sqlite, stack, stripAnsi };
674
684
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as postgres, t as redis } from "./redis.adapter.js";
1
+ import { n as redis, r as postgres, t as sqlite } from "./sqlite.adapter.js";
2
2
  import { n as mockOfDate, t as mockOf } from "./mock-of.js";
3
3
  import { execSync, spawn } from "node:child_process";
4
4
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync } from "node:fs";
@@ -113,22 +113,25 @@ var FetchAdapter = class {
113
113
  constructor(url) {
114
114
  this.baseUrl = url.replace(/\/$/, "");
115
115
  }
116
- async request(method, path, body) {
116
+ async request(method, path, body, headers) {
117
117
  const init = {
118
118
  method,
119
- headers: { "Content-Type": "application/json" }
119
+ headers: {
120
+ "Content-Type": "application/json",
121
+ ...headers
122
+ }
120
123
  };
121
124
  if (body !== void 0) init.body = JSON.stringify(body);
122
125
  const response = await fetch(`${this.baseUrl}${path}`, init);
123
126
  const responseBody = await response.json().catch(() => null);
124
- const headers = {};
127
+ const responseHeaders = {};
125
128
  response.headers.forEach((value, key) => {
126
- headers[key] = value;
129
+ responseHeaders[key] = value;
127
130
  });
128
131
  return {
129
132
  status: response.status,
130
133
  body: responseBody,
131
- headers
134
+ headers: responseHeaders
132
135
  };
133
136
  }
134
137
  };
@@ -143,22 +146,25 @@ var HonoAdapter = class {
143
146
  constructor(app) {
144
147
  this.app = app;
145
148
  }
146
- async request(method, path, body) {
149
+ async request(method, path, body, headers) {
147
150
  const init = {
148
151
  method,
149
- headers: { "Content-Type": "application/json" }
152
+ headers: {
153
+ "Content-Type": "application/json",
154
+ ...headers
155
+ }
150
156
  };
151
157
  if (body !== void 0) init.body = JSON.stringify(body);
152
158
  const response = await this.app.request(path, init);
153
159
  const responseBody = await response.json().catch(() => null);
154
- const headers = {};
160
+ const responseHeaders = {};
155
161
  response.headers.forEach((value, key) => {
156
- headers[key] = value;
162
+ responseHeaders[key] = value;
157
163
  });
158
164
  return {
159
165
  status: response.status,
160
166
  body: responseBody,
161
- headers
167
+ headers: responseHeaders
162
168
  };
163
169
  }
164
170
  };
@@ -588,6 +594,7 @@ var SpecificationBuilder = class {
588
594
  mocks = [];
589
595
  projectName = null;
590
596
  request = null;
597
+ requestHeaders = {};
591
598
  seeds = [];
592
599
  spawnConfig = null;
593
600
  testDir;
@@ -642,6 +649,19 @@ var SpecificationBuilder = class {
642
649
  return this;
643
650
  }
644
651
  /**
652
+ * Set HTTP headers for the request. Multiple calls merge.
653
+ *
654
+ * @example
655
+ * spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
656
+ */
657
+ headers(headers) {
658
+ this.requestHeaders = {
659
+ ...this.requestHeaders,
660
+ ...headers
661
+ };
662
+ return this;
663
+ }
664
+ /**
645
665
  * Send a GET request to the server adapter.
646
666
  *
647
667
  * @example
@@ -762,7 +782,8 @@ var SpecificationBuilder = class {
762
782
  if (!this.config.server) throw new Error("HTTP actions require a server adapter (use integration() or e2e())");
763
783
  let body;
764
784
  if (this.request.bodyFile) body = JSON.parse(readFileSync(resolve(this.testDir, "requests", this.request.bodyFile), "utf8"));
765
- const response = await this.config.server.request(this.request.method, this.request.path, body);
785
+ const headers = Object.keys(this.requestHeaders).length > 0 ? this.requestHeaders : void 0;
786
+ const response = await this.config.server.request(this.request.method, this.request.path, body, headers);
766
787
  return new SpecificationResult({
767
788
  config: this.config,
768
789
  requestInfo: {
@@ -1210,7 +1231,13 @@ var Orchestrator = class {
1210
1231
  const composePath = findComposeFile(this.root);
1211
1232
  const composeDir = composePath ? dirname(composePath) : this.root;
1212
1233
  const composeConfig = composePath ? parseComposeFile(composePath) : null;
1213
- const containerTasks = this.services.map((handle) => {
1234
+ const containerServices = [];
1235
+ const embeddedServices = [];
1236
+ for (const handle of this.services) {
1237
+ if (handle.defaultPort === 0) {
1238
+ embeddedServices.push(handle);
1239
+ continue;
1240
+ }
1214
1241
  let image = handle.defaultImage;
1215
1242
  let env = { ...handle.environment };
1216
1243
  if (handle.composeName && composeConfig) {
@@ -1224,18 +1251,26 @@ var Orchestrator = class {
1224
1251
  Object.assign(handle.environment, composeService.environment);
1225
1252
  }
1226
1253
  }
1227
- return {
1228
- container: new TestcontainersAdapter({
1229
- image,
1230
- port: handle.defaultPort,
1231
- env
1232
- }),
1254
+ const container = new TestcontainersAdapter({
1255
+ image,
1256
+ port: handle.defaultPort,
1257
+ env
1258
+ });
1259
+ containerServices.push({
1260
+ container,
1233
1261
  handle
1234
- };
1235
- });
1236
- await Promise.all(containerTasks.map(({ container }) => container.start()));
1262
+ });
1263
+ }
1264
+ await Promise.all([...containerServices.map(({ container }) => container.start()), ...embeddedServices.map(async (handle) => {
1265
+ await handle.initialize(composeDir);
1266
+ handle.started = true;
1267
+ this.running.push({
1268
+ handle,
1269
+ container: null
1270
+ });
1271
+ })]);
1237
1272
  const reports = [];
1238
- for (const { container, handle } of containerTasks) {
1273
+ for (const { container, handle } of containerServices) {
1239
1274
  const serviceStartTime = Date.now();
1240
1275
  try {
1241
1276
  const host = container.getHost();
@@ -1449,7 +1484,12 @@ async function startApp(target, options) {
1449
1484
  });
1450
1485
  await orchestrator.start();
1451
1486
  await acquireIsolation(services);
1452
- const honoApp = target.factory();
1487
+ const servicesMap = {};
1488
+ for (const svc of services) {
1489
+ const key = svc.composeName ?? svc.type;
1490
+ servicesMap[key] = svc;
1491
+ }
1492
+ const honoApp = target.factory(servicesMap);
1453
1493
  const database = orchestrator.getDatabase() ?? void 0;
1454
1494
  const databases = orchestrator.getDatabases();
1455
1495
  const runner = createSpecificationRunner({
@@ -1525,13 +1565,17 @@ async function startCommand(target, options) {
1525
1565
  //#endregion
1526
1566
  //#region src/runner/targets.ts
1527
1567
  /**
1528
- * Test against an in-process Hono app. Services are started via testcontainers
1529
- * and the app runs without network overhead.
1568
+ * Test against an in-process Hono app. The factory receives started services
1569
+ * so you can wire connection strings into your app/DI container.
1530
1570
  *
1531
- * @param factory - Function that returns a Hono app instance (called after services start).
1571
+ * @param factory - Function that receives services and returns a Hono app instance.
1532
1572
  *
1533
1573
  * @example
1534
- * await spec(app(() => createApp(db)), { services: [db] });
1574
+ * const db = postgres({ compose: 'db' });
1575
+ * await spec(
1576
+ * app((services) => createApp({ databaseUrl: services.db.connectionString })),
1577
+ * { services: [db] },
1578
+ * );
1535
1579
  */
1536
1580
  function app(factory) {
1537
1581
  return {
@@ -1655,6 +1699,6 @@ async function integration(options) {
1655
1699
  return runner;
1656
1700
  }
1657
1701
  //#endregion
1658
- export { DirectoryAccessor, DockerAssertion, ExecAdapter, FetchAdapter, HonoAdapter, Orchestrator, ResponseAccessor, SpecificationBuilder, SpecificationResult, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, stack, stripAnsi };
1702
+ export { DirectoryAccessor, DockerAssertion, ExecAdapter, FetchAdapter, HonoAdapter, Orchestrator, ResponseAccessor, SpecificationBuilder, SpecificationResult, TableAssertion, app, cli, command, createSpecificationRunner, dockerContainer, e2e, grep, integration, mockOf, mockOfDate, normalizeOutput, postgres, redis, spec, sqlite, stack, stripAnsi };
1659
1703
 
1660
1704
  //# sourceMappingURL=index.js.map