@webpieces/http-server 0.3.256 → 0.3.258
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/http-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.258",
|
|
4
4
|
"description": "WebPieces server with filter chain and dependency injection",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/http-routing": "0.3.
|
|
26
|
-
"@webpieces/wp-logging": "0.3.
|
|
25
|
+
"@webpieces/http-routing": "0.3.258",
|
|
26
|
+
"@webpieces/wp-logging": "0.3.258",
|
|
27
27
|
"cors": "2.8.5"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
import { RecordedTestCase } from '@webpieces/http-api';
|
|
2
|
-
/**
|
|
3
|
-
* SpecGenerator - Deterministic template that turns a RecordedTestCase into a
|
|
4
|
-
* small vitest spec (~30 lines) which loads the fixture JSON, primes a
|
|
5
|
-
* createMock per downstream api, invokes the endpoint in-process, and
|
|
6
|
-
* deep-equal asserts the response + the requests each mock received.
|
|
7
|
-
*
|
|
8
|
-
* Deliberately NO deep reflection over DTOs (the fragile part of the Java
|
|
9
|
-
* TestCaseRecorderImpl codegen) - all data lives in the fixture; the spec is
|
|
10
|
-
* pure plumbing. The fixture is also a stable artifact for an AI to write a
|
|
11
|
-
* richer spec from.
|
|
12
|
-
*/
|
|
13
2
|
export declare class SpecGenerator {
|
|
14
3
|
generate(testCase: RecordedTestCase, fixtureFileName: string): string;
|
|
15
4
|
private uniqueApiNames;
|
|
@@ -3,15 +3,73 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SpecGenerator = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* SpecGenerator - Deterministic template that turns a RecordedTestCase into a
|
|
6
|
-
* small vitest spec (~30 lines) which loads the fixture JSON, primes
|
|
7
|
-
* createMock per downstream api, invokes the endpoint in-process, and
|
|
8
|
-
*
|
|
6
|
+
* small vitest spec (~30 lines) which loads the fixture JSON, primes an inline
|
|
7
|
+
* createMock per downstream api, invokes the endpoint in-process, and deep-equal
|
|
8
|
+
* asserts the response + the requests each mock received.
|
|
9
|
+
*
|
|
10
|
+
* The mock test-double is emitted inline into the generated spec (see
|
|
11
|
+
* INLINE_MOCK_SOURCE) so the generated file is fully self-contained and does not
|
|
12
|
+
* depend on any external mock package.
|
|
9
13
|
*
|
|
10
14
|
* Deliberately NO deep reflection over DTOs (the fragile part of the Java
|
|
11
15
|
* TestCaseRecorderImpl codegen) - all data lives in the fixture; the spec is
|
|
12
16
|
* pure plumbing. The fixture is also a stable artifact for an AI to write a
|
|
13
17
|
* richer spec from.
|
|
14
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* Self-contained recording test-double emitted into every generated spec. Mirrors
|
|
21
|
+
* the small slice of the former @webpieces/core-mock API the template relies on:
|
|
22
|
+
* createMock<T>(name) -> T & { mock: MockControls }
|
|
23
|
+
* .mock.addValueToReturn(method, value) queue a return value (FIFO)
|
|
24
|
+
* .mock.addExceptionToThrow(method, ()=>Err) queue an exception (FIFO)
|
|
25
|
+
* .mock.getSingleRequestList(method) first argument of each recorded call
|
|
26
|
+
*/
|
|
27
|
+
const INLINE_MOCK_SOURCE = `type MockControls = {
|
|
28
|
+
addValueToReturn(method: string, value: unknown): void;
|
|
29
|
+
addExceptionToThrow(method: string, errorFactory: () => Error): void;
|
|
30
|
+
getSingleRequestList(method: string): unknown[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function createMock<T>(_name: string): T & { mock: MockControls } {
|
|
34
|
+
const returns = new Map<string, unknown[]>();
|
|
35
|
+
const throwers = new Map<string, Array<() => Error>>();
|
|
36
|
+
const requests = new Map<string, unknown[]>();
|
|
37
|
+
const controls: MockControls = {
|
|
38
|
+
addValueToReturn(method, value) {
|
|
39
|
+
const queue = returns.get(method) ?? [];
|
|
40
|
+
queue.push(value);
|
|
41
|
+
returns.set(method, queue);
|
|
42
|
+
},
|
|
43
|
+
addExceptionToThrow(method, errorFactory) {
|
|
44
|
+
const queue = throwers.get(method) ?? [];
|
|
45
|
+
queue.push(errorFactory);
|
|
46
|
+
throwers.set(method, queue);
|
|
47
|
+
},
|
|
48
|
+
getSingleRequestList(method) {
|
|
49
|
+
return requests.get(method) ?? [];
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
return new Proxy({} as Record<string, unknown>, {
|
|
53
|
+
get(_target, prop: string | symbol): unknown {
|
|
54
|
+
if (prop === 'mock') return controls;
|
|
55
|
+
const method = String(prop);
|
|
56
|
+
return (...args: unknown[]): unknown => {
|
|
57
|
+
const recorded = requests.get(method) ?? [];
|
|
58
|
+
recorded.push(args[0]);
|
|
59
|
+
requests.set(method, recorded);
|
|
60
|
+
const throwQueue = throwers.get(method);
|
|
61
|
+
if (throwQueue && throwQueue.length > 0) {
|
|
62
|
+
throw throwQueue.shift()!();
|
|
63
|
+
}
|
|
64
|
+
const returnQueue = returns.get(method);
|
|
65
|
+
if (returnQueue && returnQueue.length > 0) {
|
|
66
|
+
return returnQueue.shift();
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
}) as T & { mock: MockControls };
|
|
72
|
+
}`;
|
|
15
73
|
class SpecGenerator {
|
|
16
74
|
generate(testCase, fixtureFileName) {
|
|
17
75
|
const endpoint = testCase.serverEndpoint;
|
|
@@ -30,11 +88,12 @@ class SpecGenerator {
|
|
|
30
88
|
return `import 'reflect-metadata';
|
|
31
89
|
import * as fs from 'fs';
|
|
32
90
|
import * as path from 'path';
|
|
33
|
-
import { createMock } from '@webpieces/core-mock';
|
|
34
91
|
import { RecordedTestCase } from '@webpieces/http-api';
|
|
35
92
|
// TODO(generated): import your ServerMeta, the api class, DI tokens, and the
|
|
36
93
|
// downstream api types, then wire the appOverrides ContainerModule below.
|
|
37
94
|
|
|
95
|
+
${INLINE_MOCK_SOURCE}
|
|
96
|
+
|
|
38
97
|
describe('${endpoint.apiName}.${endpoint.methodName} (recorded ${testCase.recordedAt})', () => {
|
|
39
98
|
const fixture: RecordedTestCase = JSON.parse(
|
|
40
99
|
fs.readFileSync(path.join(__dirname, '${fixtureFileName}'), 'utf-8'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecGenerator.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/recorder/SpecGenerator.ts"],"names":[],"mappings":";;;AAEA
|
|
1
|
+
{"version":3,"file":"SpecGenerator.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/recorder/SpecGenerator.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CzB,CAAC;AAEH,MAAa,aAAa;IACtB,QAAQ,CAAC,QAA0B,EAAE,eAAuB;QACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC;QACzC,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAErE,MAAM,SAAS,GAAG,cAAc;aAC3B,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,GAAG,MAAM,GAAG,KAAK,CAAC;aAC5E,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,UAAU,GAAG,QAAQ,CAAC,eAAe;aACtC,GAAG,CAAC,CAAC,IAAsB,EAAE,CAAS,EAAE,EAAE,CACvC,IAAI,CAAC,eAAe;YAChB,CAAC,CAAC,WAAW,IAAI,CAAC,OAAO,8BAA8B,IAAI,CAAC,UAAU,8CAA8C,CAAC,+BAA+B;YACpJ,CAAC,CAAC,WAAW,IAAI,CAAC,OAAO,2BAA2B,IAAI,CAAC,UAAU,8BAA8B,CAAC,qBAAqB,CAAC;aAC/H,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,WAAW,GAAG,QAAQ,CAAC,eAAe;aACvC,GAAG,CAAC,CAAC,IAAsB,EAAE,CAAS,EAAE,EAAE,CACvC,kBAAkB,IAAI,CAAC,OAAO,+BAA+B,IAAI,CAAC,UAAU,0CAA0C,CAAC,aAAa,CAAC;aACxI,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhB,OAAO;;;;;;;EAOb,kBAAkB;;YAER,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,UAAU,cAAc,QAAQ,CAAC,UAAU;;gDAEpC,eAAe;;;;EAI7D,SAAS;EACT,UAAU;;;;;;gDAMoC,QAAQ,CAAC,OAAO;wCACxB,QAAQ,CAAC,UAAU;;EAEzD,WAAW;;;CAGZ,CAAC;IACE,CAAC;IAEO,cAAc,CAAC,KAAyB;QAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AA3DD,sCA2DC","sourcesContent":["import { RecordedTestCase, RecordedEndpoint } from '@webpieces/http-api';\n\n/**\n * SpecGenerator - Deterministic template that turns a RecordedTestCase into a\n * small vitest spec (~30 lines) which loads the fixture JSON, primes an inline\n * createMock per downstream api, invokes the endpoint in-process, and deep-equal\n * asserts the response + the requests each mock received.\n *\n * The mock test-double is emitted inline into the generated spec (see\n * INLINE_MOCK_SOURCE) so the generated file is fully self-contained and does not\n * depend on any external mock package.\n *\n * Deliberately NO deep reflection over DTOs (the fragile part of the Java\n * TestCaseRecorderImpl codegen) - all data lives in the fixture; the spec is\n * pure plumbing. The fixture is also a stable artifact for an AI to write a\n * richer spec from.\n */\n\n/**\n * Self-contained recording test-double emitted into every generated spec. Mirrors\n * the small slice of the former @webpieces/core-mock API the template relies on:\n * createMock<T>(name) -> T & { mock: MockControls }\n * .mock.addValueToReturn(method, value) queue a return value (FIFO)\n * .mock.addExceptionToThrow(method, ()=>Err) queue an exception (FIFO)\n * .mock.getSingleRequestList(method) first argument of each recorded call\n */\nconst INLINE_MOCK_SOURCE = `type MockControls = {\n addValueToReturn(method: string, value: unknown): void;\n addExceptionToThrow(method: string, errorFactory: () => Error): void;\n getSingleRequestList(method: string): unknown[];\n};\n\nfunction createMock<T>(_name: string): T & { mock: MockControls } {\n const returns = new Map<string, unknown[]>();\n const throwers = new Map<string, Array<() => Error>>();\n const requests = new Map<string, unknown[]>();\n const controls: MockControls = {\n addValueToReturn(method, value) {\n const queue = returns.get(method) ?? [];\n queue.push(value);\n returns.set(method, queue);\n },\n addExceptionToThrow(method, errorFactory) {\n const queue = throwers.get(method) ?? [];\n queue.push(errorFactory);\n throwers.set(method, queue);\n },\n getSingleRequestList(method) {\n return requests.get(method) ?? [];\n },\n };\n return new Proxy({} as Record<string, unknown>, {\n get(_target, prop: string | symbol): unknown {\n if (prop === 'mock') return controls;\n const method = String(prop);\n return (...args: unknown[]): unknown => {\n const recorded = requests.get(method) ?? [];\n recorded.push(args[0]);\n requests.set(method, recorded);\n const throwQueue = throwers.get(method);\n if (throwQueue && throwQueue.length > 0) {\n throw throwQueue.shift()!();\n }\n const returnQueue = returns.get(method);\n if (returnQueue && returnQueue.length > 0) {\n return returnQueue.shift();\n }\n return undefined;\n };\n },\n }) as T & { mock: MockControls };\n}`;\n\nexport class SpecGenerator {\n generate(testCase: RecordedTestCase, fixtureFileName: string): string {\n const endpoint = testCase.serverEndpoint;\n const downstreamApis = this.uniqueApiNames(testCase.downstreamCalls);\n\n const mockDecls = downstreamApis\n .map((api: string) => ` const mock${api} = createMock<${api}>('${api}');`)\n .join('\\n');\n const mockPrimes = testCase.downstreamCalls\n .map((call: RecordedEndpoint, i: number) =>\n call.failureResponse\n ? ` mock${call.apiName}.mock.addExceptionToThrow('${call.methodName}', () => new Error(fixture.downstreamCalls[${i}].failureResponse!.message));`\n : ` mock${call.apiName}.mock.addValueToReturn('${call.methodName}', fixture.downstreamCalls[${i}].successResponse);`)\n .join('\\n');\n const mockAsserts = testCase.downstreamCalls\n .map((call: RecordedEndpoint, i: number) =>\n ` expect(mock${call.apiName}.mock.getSingleRequestList('${call.methodName}')[0]).toEqual(fixture.downstreamCalls[${i}].args[0]);`)\n .join('\\n');\n\n return `import 'reflect-metadata';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { RecordedTestCase } from '@webpieces/http-api';\n// TODO(generated): import your ServerMeta, the api class, DI tokens, and the\n// downstream api types, then wire the appOverrides ContainerModule below.\n\n${INLINE_MOCK_SOURCE}\n\ndescribe('${endpoint.apiName}.${endpoint.methodName} (recorded ${testCase.recordedAt})', () => {\n const fixture: RecordedTestCase = JSON.parse(\n fs.readFileSync(path.join(__dirname, '${fixtureFileName}'), 'utf-8'),\n );\n\n it('replays the recorded request against mocked downstream apis', async () => {\n${mockDecls}\n${mockPrimes}\n // TODO(generated): boot the server with the mocks rebound, e.g.:\n // const overrides = new ContainerModule(async (options) => {\n // (await options.rebind(TYPES.RemoteApi)).toConstantValue(mockRemoteApi);\n // });\n // const server = await WebpiecesFactory.create(new ProdServerMeta(), new WebpiecesConfig(), overrides);\n // const api = server.createApiClient(${endpoint.apiName});\n // const response = await api.${endpoint.methodName}(fixture.serverEndpoint.args[0]);\n // expect(response).toEqual(fixture.serverEndpoint.successResponse);\n${mockAsserts}\n });\n});\n`;\n }\n\n private uniqueApiNames(calls: RecordedEndpoint[]): string[] {\n const names: string[] = [];\n for (const call of calls) {\n if (!names.includes(call.apiName)) {\n names.push(call.apiName);\n }\n }\n return names;\n }\n}\n"]}
|