@jterrazz/test 6.1.0 → 6.3.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 +9 -9
- package/dist/index.cjs +66 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -4
- package/dist/index.d.ts +30 -4
- package/dist/index.js +66 -14
- package/dist/index.js.map +1 -1
- package/dist/intercept.cjs +240 -0
- package/dist/intercept.cjs.map +1 -0
- package/dist/intercept.d.cts +89 -0
- package/dist/intercept.d.ts +89 -0
- package/dist/intercept.js +237 -0
- package/dist/intercept.js.map +1 -0
- package/dist/server.cjs +79 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.js +79 -0
- package/dist/server.js.map +1 -0
- package/dist/sqlite.adapter.cjs +22 -3
- package/dist/sqlite.adapter.cjs.map +1 -1
- package/dist/sqlite.adapter.js +22 -3
- package/dist/sqlite.adapter.js.map +1 -1
- package/dist/types.d.cts +35 -0
- package/dist/types.d.ts +35 -0
- package/package.json +6 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as InterceptResponse, r as InterceptTrigger } from "./types.cjs";
|
|
1
2
|
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
3
|
import { i as mockOfDate, n as mockOf, r as MockDatePort, t as MockPort } from "./mock-of.cjs";
|
|
3
4
|
|
|
@@ -56,7 +57,7 @@ interface ServerResponse {
|
|
|
56
57
|
*/
|
|
57
58
|
interface ServerPort {
|
|
58
59
|
/** Send an HTTP request and return the parsed response. */
|
|
59
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
60
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
60
61
|
}
|
|
61
62
|
//#endregion
|
|
62
63
|
//#region src/builder/directory-accessor.d.ts
|
|
@@ -210,10 +211,12 @@ declare class SpecificationBuilder {
|
|
|
210
211
|
private commandEnv;
|
|
211
212
|
private config;
|
|
212
213
|
private fixtures;
|
|
214
|
+
private intercepts;
|
|
213
215
|
private label;
|
|
214
216
|
private mocks;
|
|
215
217
|
private projectName;
|
|
216
218
|
private request;
|
|
219
|
+
private requestHeaders;
|
|
217
220
|
private seeds;
|
|
218
221
|
private spawnConfig;
|
|
219
222
|
private testDir;
|
|
@@ -244,6 +247,29 @@ declare class SpecificationBuilder {
|
|
|
244
247
|
* spec("...").env({ HOME: "$WORKDIR", TZ: "UTC" }).exec("status").run();
|
|
245
248
|
*/
|
|
246
249
|
env(env: CommandEnv): this;
|
|
250
|
+
/**
|
|
251
|
+
* Set HTTP headers for the request. Multiple calls merge.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
|
|
255
|
+
*/
|
|
256
|
+
headers(headers: Record<string, string>): this;
|
|
257
|
+
/**
|
|
258
|
+
* Intercept an outgoing HTTP request and return a controlled response.
|
|
259
|
+
* Uses MSW under the hood. Intercepts are queued — multiple calls with the
|
|
260
|
+
* same trigger fire sequentially (first match consumed first).
|
|
261
|
+
*
|
|
262
|
+
* @param trigger - What to match (use openai.chat(), anthropic.messages(), http.get(), etc.)
|
|
263
|
+
* @param response - What to return (use openai.response(), http.json(), etc.)
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* spec('pipeline')
|
|
267
|
+
* .intercept(openai.chat(), openai.response({ categories: ['TECH'] }))
|
|
268
|
+
* .intercept(openai.chat(), openai.response({ headline: 'AI News' }))
|
|
269
|
+
* .exec('process')
|
|
270
|
+
* .run();
|
|
271
|
+
*/
|
|
272
|
+
intercept(trigger: InterceptTrigger, response: InterceptResponse): this;
|
|
247
273
|
/**
|
|
248
274
|
* Send a GET request to the server adapter.
|
|
249
275
|
*
|
|
@@ -440,8 +466,8 @@ declare class Orchestrator {
|
|
|
440
466
|
}
|
|
441
467
|
//#endregion
|
|
442
468
|
//#region src/runner/targets.d.ts
|
|
469
|
+
/** Any object with a request method compatible with Hono's app.request(). */
|
|
443
470
|
type HonoApp$1 = {
|
|
444
|
-
fetch: (...args: any[]) => any;
|
|
445
471
|
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
446
472
|
};
|
|
447
473
|
/** Services map passed to the app factory after infrastructure starts. */
|
|
@@ -582,7 +608,7 @@ declare class ExecAdapter implements CommandPort {
|
|
|
582
608
|
declare class FetchAdapter implements ServerPort {
|
|
583
609
|
private baseUrl;
|
|
584
610
|
constructor(url: string);
|
|
585
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
611
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
586
612
|
}
|
|
587
613
|
//#endregion
|
|
588
614
|
//#region src/adapters/hono.adapter.d.ts
|
|
@@ -595,7 +621,7 @@ declare class HonoAdapter implements ServerPort {
|
|
|
595
621
|
constructor(app: {
|
|
596
622
|
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
597
623
|
});
|
|
598
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
624
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
599
625
|
}
|
|
600
626
|
//#endregion
|
|
601
627
|
//#region src/runner/integration.d.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as InterceptResponse, r as InterceptTrigger } from "./types.js";
|
|
1
2
|
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
3
|
import { i as mockOfDate, n as mockOf, r as MockDatePort, t as MockPort } from "./mock-of.js";
|
|
3
4
|
|
|
@@ -56,7 +57,7 @@ interface ServerResponse {
|
|
|
56
57
|
*/
|
|
57
58
|
interface ServerPort {
|
|
58
59
|
/** Send an HTTP request and return the parsed response. */
|
|
59
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
60
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
60
61
|
}
|
|
61
62
|
//#endregion
|
|
62
63
|
//#region src/builder/directory-accessor.d.ts
|
|
@@ -210,10 +211,12 @@ declare class SpecificationBuilder {
|
|
|
210
211
|
private commandEnv;
|
|
211
212
|
private config;
|
|
212
213
|
private fixtures;
|
|
214
|
+
private intercepts;
|
|
213
215
|
private label;
|
|
214
216
|
private mocks;
|
|
215
217
|
private projectName;
|
|
216
218
|
private request;
|
|
219
|
+
private requestHeaders;
|
|
217
220
|
private seeds;
|
|
218
221
|
private spawnConfig;
|
|
219
222
|
private testDir;
|
|
@@ -244,6 +247,29 @@ declare class SpecificationBuilder {
|
|
|
244
247
|
* spec("...").env({ HOME: "$WORKDIR", TZ: "UTC" }).exec("status").run();
|
|
245
248
|
*/
|
|
246
249
|
env(env: CommandEnv): this;
|
|
250
|
+
/**
|
|
251
|
+
* Set HTTP headers for the request. Multiple calls merge.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
|
|
255
|
+
*/
|
|
256
|
+
headers(headers: Record<string, string>): this;
|
|
257
|
+
/**
|
|
258
|
+
* Intercept an outgoing HTTP request and return a controlled response.
|
|
259
|
+
* Uses MSW under the hood. Intercepts are queued — multiple calls with the
|
|
260
|
+
* same trigger fire sequentially (first match consumed first).
|
|
261
|
+
*
|
|
262
|
+
* @param trigger - What to match (use openai.chat(), anthropic.messages(), http.get(), etc.)
|
|
263
|
+
* @param response - What to return (use openai.response(), http.json(), etc.)
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* spec('pipeline')
|
|
267
|
+
* .intercept(openai.chat(), openai.response({ categories: ['TECH'] }))
|
|
268
|
+
* .intercept(openai.chat(), openai.response({ headline: 'AI News' }))
|
|
269
|
+
* .exec('process')
|
|
270
|
+
* .run();
|
|
271
|
+
*/
|
|
272
|
+
intercept(trigger: InterceptTrigger, response: InterceptResponse): this;
|
|
247
273
|
/**
|
|
248
274
|
* Send a GET request to the server adapter.
|
|
249
275
|
*
|
|
@@ -440,8 +466,8 @@ declare class Orchestrator {
|
|
|
440
466
|
}
|
|
441
467
|
//#endregion
|
|
442
468
|
//#region src/runner/targets.d.ts
|
|
469
|
+
/** Any object with a request method compatible with Hono's app.request(). */
|
|
443
470
|
type HonoApp$1 = {
|
|
444
|
-
fetch: (...args: any[]) => any;
|
|
445
471
|
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
446
472
|
};
|
|
447
473
|
/** Services map passed to the app factory after infrastructure starts. */
|
|
@@ -582,7 +608,7 @@ declare class ExecAdapter implements CommandPort {
|
|
|
582
608
|
declare class FetchAdapter implements ServerPort {
|
|
583
609
|
private baseUrl;
|
|
584
610
|
constructor(url: string);
|
|
585
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
611
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
586
612
|
}
|
|
587
613
|
//#endregion
|
|
588
614
|
//#region src/adapters/hono.adapter.d.ts
|
|
@@ -595,7 +621,7 @@ declare class HonoAdapter implements ServerPort {
|
|
|
595
621
|
constructor(app: {
|
|
596
622
|
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
597
623
|
});
|
|
598
|
-
request(method: string, path: string, body?: unknown): Promise<ServerResponse>;
|
|
624
|
+
request(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<ServerResponse>;
|
|
599
625
|
}
|
|
600
626
|
//#endregion
|
|
601
627
|
//#region src/runner/integration.d.ts
|
package/dist/index.js
CHANGED
|
@@ -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: {
|
|
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
|
|
127
|
+
const responseHeaders = {};
|
|
125
128
|
response.headers.forEach((value, key) => {
|
|
126
|
-
|
|
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: {
|
|
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
|
|
160
|
+
const responseHeaders = {};
|
|
155
161
|
response.headers.forEach((value, key) => {
|
|
156
|
-
|
|
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
|
};
|
|
@@ -584,10 +590,12 @@ var SpecificationBuilder = class {
|
|
|
584
590
|
commandEnv = {};
|
|
585
591
|
config;
|
|
586
592
|
fixtures = [];
|
|
593
|
+
intercepts = [];
|
|
587
594
|
label;
|
|
588
595
|
mocks = [];
|
|
589
596
|
projectName = null;
|
|
590
597
|
request = null;
|
|
598
|
+
requestHeaders = {};
|
|
591
599
|
seeds = [];
|
|
592
600
|
spawnConfig = null;
|
|
593
601
|
testDir;
|
|
@@ -642,6 +650,41 @@ var SpecificationBuilder = class {
|
|
|
642
650
|
return this;
|
|
643
651
|
}
|
|
644
652
|
/**
|
|
653
|
+
* Set HTTP headers for the request. Multiple calls merge.
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* spec("french").headers({ 'Accept-Language': 'fr' }).get("/articles").run();
|
|
657
|
+
*/
|
|
658
|
+
headers(headers) {
|
|
659
|
+
this.requestHeaders = {
|
|
660
|
+
...this.requestHeaders,
|
|
661
|
+
...headers
|
|
662
|
+
};
|
|
663
|
+
return this;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Intercept an outgoing HTTP request and return a controlled response.
|
|
667
|
+
* Uses MSW under the hood. Intercepts are queued — multiple calls with the
|
|
668
|
+
* same trigger fire sequentially (first match consumed first).
|
|
669
|
+
*
|
|
670
|
+
* @param trigger - What to match (use openai.chat(), anthropic.messages(), http.get(), etc.)
|
|
671
|
+
* @param response - What to return (use openai.response(), http.json(), etc.)
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* spec('pipeline')
|
|
675
|
+
* .intercept(openai.chat(), openai.response({ categories: ['TECH'] }))
|
|
676
|
+
* .intercept(openai.chat(), openai.response({ headline: 'AI News' }))
|
|
677
|
+
* .exec('process')
|
|
678
|
+
* .run();
|
|
679
|
+
*/
|
|
680
|
+
intercept(trigger, response) {
|
|
681
|
+
this.intercepts.push({
|
|
682
|
+
trigger,
|
|
683
|
+
response
|
|
684
|
+
});
|
|
685
|
+
return this;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
645
688
|
* Send a GET request to the server adapter.
|
|
646
689
|
*
|
|
647
690
|
* @example
|
|
@@ -735,9 +778,17 @@ var SpecificationBuilder = class {
|
|
|
735
778
|
await db.seed(sql);
|
|
736
779
|
}
|
|
737
780
|
if (this.fixtures.length > 0 && workDir) for (const entry of this.fixtures) cpSync(resolve(this.testDir, "fixtures", entry.file), resolve(workDir, entry.file), { recursive: true });
|
|
738
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
781
|
+
let cleanupIntercepts = null;
|
|
782
|
+
if (this.intercepts.length > 0) {
|
|
783
|
+
const { registerIntercepts } = await import("./server.js");
|
|
784
|
+
cleanupIntercepts = await registerIntercepts(this.intercepts);
|
|
785
|
+
}
|
|
786
|
+
try {
|
|
787
|
+
if (hasHttpAction) return await this.runHttpAction();
|
|
788
|
+
return await this.runCliAction(workDir);
|
|
789
|
+
} finally {
|
|
790
|
+
if (cleanupIntercepts) cleanupIntercepts();
|
|
791
|
+
}
|
|
741
792
|
}
|
|
742
793
|
resolveEnv(workDir) {
|
|
743
794
|
const keys = Object.keys(this.commandEnv);
|
|
@@ -762,7 +813,8 @@ var SpecificationBuilder = class {
|
|
|
762
813
|
if (!this.config.server) throw new Error("HTTP actions require a server adapter (use integration() or e2e())");
|
|
763
814
|
let body;
|
|
764
815
|
if (this.request.bodyFile) body = JSON.parse(readFileSync(resolve(this.testDir, "requests", this.request.bodyFile), "utf8"));
|
|
765
|
-
const
|
|
816
|
+
const headers = Object.keys(this.requestHeaders).length > 0 ? this.requestHeaders : void 0;
|
|
817
|
+
const response = await this.config.server.request(this.request.method, this.request.path, body, headers);
|
|
766
818
|
return new SpecificationResult({
|
|
767
819
|
config: this.config,
|
|
768
820
|
requestInfo: {
|