@techspokes/typescript-wsdl-client 0.11.0 → 0.11.3
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/app/generateApp.d.ts.map +1 -1
- package/dist/app/generateApp.js +3 -26
- package/dist/cli.js +22 -0
- package/dist/gateway/generators.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/pipeline.d.ts +4 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +17 -0
- package/dist/test/generateTests.d.ts +20 -0
- package/dist/test/generateTests.d.ts.map +1 -0
- package/dist/test/generateTests.js +136 -0
- package/dist/test/generators.d.ts +69 -0
- package/dist/test/generators.d.ts.map +1 -0
- package/dist/test/generators.js +630 -0
- package/dist/test/mockData.d.ts +68 -0
- package/dist/test/mockData.d.ts.map +1 -0
- package/dist/test/mockData.js +133 -0
- package/dist/util/imports.d.ts +17 -0
- package/dist/util/imports.d.ts.map +1 -0
- package/dist/util/imports.js +42 -0
- package/docs/cli-reference.md +21 -0
- package/docs/decisions/001-test-generation.md +36 -0
- package/docs/testing.md +70 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generateApp.d.ts","sourceRoot":"","sources":["../../src/app/generateApp.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAklBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDzE"}
|
package/dist/app/generateApp.js
CHANGED
|
@@ -25,6 +25,7 @@ import fs from "node:fs";
|
|
|
25
25
|
import path from "node:path";
|
|
26
26
|
import { deriveClientName } from "../util/tools.js";
|
|
27
27
|
import { info, success } from "../util/cli.js";
|
|
28
|
+
import { computeRelativeImport, getImportExtension } from "../util/imports.js";
|
|
28
29
|
/**
|
|
29
30
|
* Validates that all required files and directories exist
|
|
30
31
|
*
|
|
@@ -73,11 +74,7 @@ function validateRequiredFiles(opts) {
|
|
|
73
74
|
* @returns {string} - File extension with leading dot or empty string for bare
|
|
74
75
|
*/
|
|
75
76
|
function getExtension(imports) {
|
|
76
|
-
|
|
77
|
-
return ".js";
|
|
78
|
-
if (imports === "ts")
|
|
79
|
-
return ".ts";
|
|
80
|
-
return "";
|
|
77
|
+
return getImportExtension(imports);
|
|
81
78
|
}
|
|
82
79
|
/**
|
|
83
80
|
* Returns the file extension for scaffold app files (server, config).
|
|
@@ -88,27 +85,7 @@ function getExtension(imports) {
|
|
|
88
85
|
function getAppFileExtension() {
|
|
89
86
|
return ".ts";
|
|
90
87
|
}
|
|
91
|
-
|
|
92
|
-
* Computes a relative import path from source to target
|
|
93
|
-
*
|
|
94
|
-
* @param {string} from - Source directory
|
|
95
|
-
* @param {string} to - Target file or directory
|
|
96
|
-
* @param {string} imports - Import mode (js, ts, or bare)
|
|
97
|
-
* @returns {string} - Relative import specifier with proper extension
|
|
98
|
-
*/
|
|
99
|
-
function computeRelativeImport(from, to, imports) {
|
|
100
|
-
const rel = path.relative(from, to);
|
|
101
|
-
// Normalize to POSIX separators
|
|
102
|
-
const posix = rel.split(path.sep).join("/");
|
|
103
|
-
// Ensure it starts with ./ or ../
|
|
104
|
-
const prefixed = posix.startsWith(".") ? posix : `./${posix}`;
|
|
105
|
-
// Apply import extension rules
|
|
106
|
-
const ext = getExtension(imports);
|
|
107
|
-
if (ext) {
|
|
108
|
-
return prefixed + ext;
|
|
109
|
-
}
|
|
110
|
-
return prefixed;
|
|
111
|
-
}
|
|
88
|
+
// computeRelativeImport is now imported from ../util/imports.js
|
|
112
89
|
/**
|
|
113
90
|
* Checks if a string is a URL (http:// or https://)
|
|
114
91
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -665,6 +665,16 @@ if (rawArgs[0] === "pipeline") {
|
|
|
665
665
|
type: "string",
|
|
666
666
|
default: "",
|
|
667
667
|
desc: "Route prefix for app scaffold"
|
|
668
|
+
})
|
|
669
|
+
// Test generation flags
|
|
670
|
+
.option("test-dir", {
|
|
671
|
+
type: "string",
|
|
672
|
+
desc: "Output directory for generated Vitest test suite"
|
|
673
|
+
})
|
|
674
|
+
.option("force-test", {
|
|
675
|
+
type: "boolean",
|
|
676
|
+
default: false,
|
|
677
|
+
desc: "Overwrite existing test files when using --test-dir"
|
|
668
678
|
})
|
|
669
679
|
.strict()
|
|
670
680
|
.help()
|
|
@@ -741,6 +751,14 @@ if (rawArgs[0] === "pipeline") {
|
|
|
741
751
|
handleCLIError("--init-app requires --client-dir, --gateway-dir, and --openapi-file to be set");
|
|
742
752
|
}
|
|
743
753
|
}
|
|
754
|
+
// Validate test generation requirements
|
|
755
|
+
const testDir = pipelineArgv["test-dir"];
|
|
756
|
+
const forceTest = pipelineArgv["force-test"];
|
|
757
|
+
if (testDir) {
|
|
758
|
+
if (!clientOut || !gatewayOut) {
|
|
759
|
+
handleCLIError("--test-dir requires --client-dir and --gateway-dir to be set");
|
|
760
|
+
}
|
|
761
|
+
}
|
|
744
762
|
await runGenerationPipeline({
|
|
745
763
|
wsdl: pipelineArgv["wsdl-source"],
|
|
746
764
|
clientOutDir: clientOut ? path.resolve(clientOut) : undefined,
|
|
@@ -771,6 +789,10 @@ if (rawArgs[0] === "pipeline") {
|
|
|
771
789
|
port: pipelineArgv["app-port"],
|
|
772
790
|
prefix: pipelineArgv["app-prefix"],
|
|
773
791
|
} : undefined,
|
|
792
|
+
test: testDir ? {
|
|
793
|
+
testDir: path.resolve(testDir),
|
|
794
|
+
force: forceTest,
|
|
795
|
+
} : undefined,
|
|
774
796
|
});
|
|
775
797
|
process.exit(0);
|
|
776
798
|
}
|
|
@@ -467,7 +467,7 @@ export function classifyError(err: unknown): ClassifiedError {
|
|
|
467
467
|
httpStatus: 400,
|
|
468
468
|
code: "VALIDATION_ERROR",
|
|
469
469
|
message: "Request validation failed",
|
|
470
|
-
details: (err as Record<string, unknown>).validation,
|
|
470
|
+
details: { validationErrors: (err as Record<string, unknown>).validation },
|
|
471
471
|
};
|
|
472
472
|
}
|
|
473
473
|
|
|
@@ -494,7 +494,7 @@ export function classifyError(err: unknown): ClassifiedError {
|
|
|
494
494
|
httpStatus: 503,
|
|
495
495
|
code: "SERVICE_UNAVAILABLE",
|
|
496
496
|
message: "Unable to connect to SOAP service",
|
|
497
|
-
details: err.message,
|
|
497
|
+
details: { message: err.message },
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
500
|
if (err.message.includes("ETIMEDOUT") || err.message.includes("timeout")) {
|
|
@@ -502,7 +502,7 @@ export function classifyError(err: unknown): ClassifiedError {
|
|
|
502
502
|
httpStatus: 504,
|
|
503
503
|
code: "GATEWAY_TIMEOUT",
|
|
504
504
|
message: "SOAP service request timed out",
|
|
505
|
-
details: err.message,
|
|
505
|
+
details: { message: err.message },
|
|
506
506
|
};
|
|
507
507
|
}
|
|
508
508
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CompilerOptions } from "./config.js";
|
|
2
2
|
export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
3
3
|
export { generateGateway } from "./gateway/generateGateway.js";
|
|
4
|
+
export { generateTests } from "./test/generateTests.js";
|
|
4
5
|
export { runGenerationPipeline } from "./pipeline.js";
|
|
5
6
|
/**
|
|
6
7
|
* Compiles a WSDL file to TypeScript client code
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CAwDf"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,aAAa,CAAC;AAWjD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAC,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GACjE,OAAO,CAAC,IAAI,CAAC,CAwDf"}
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { generateOperations } from "./client/generateOperations.js";
|
|
|
24
24
|
import { info } from "./util/cli.js";
|
|
25
25
|
export { generateOpenAPI } from "./openapi/generateOpenAPI.js";
|
|
26
26
|
export { generateGateway } from "./gateway/generateGateway.js";
|
|
27
|
+
export { generateTests } from "./test/generateTests.js";
|
|
27
28
|
export { runGenerationPipeline } from "./pipeline.js";
|
|
28
29
|
// noinspection JSUnusedGlobalSymbols
|
|
29
30
|
/**
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -40,6 +40,10 @@ export interface PipelineOptions {
|
|
|
40
40
|
port?: number;
|
|
41
41
|
prefix?: string;
|
|
42
42
|
};
|
|
43
|
+
test?: {
|
|
44
|
+
testDir: string;
|
|
45
|
+
force?: boolean;
|
|
46
|
+
};
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* Runs the complete generation pipeline from WSDL to TypeScript artifacts and optionally OpenAPI/Gateway/App
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAGzE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAkB,KAAK,sBAAsB,EAAC,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAC,KAAK,eAAe,EAAyB,MAAM,aAAa,CAAC;AAGzE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1G,OAAO,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,GAAG,iBAAiB,CAAC,GAAG;QAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,CAAC;CAAE,CAAC,CAgJhH"}
|
package/dist/pipeline.js
CHANGED
|
@@ -128,6 +128,23 @@ export async function runGenerationPipeline(opts) {
|
|
|
128
128
|
prefix: opts.app.prefix,
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
|
+
// Step 7: Optionally generate test suite
|
|
132
|
+
if (opts.test) {
|
|
133
|
+
if (!opts.clientOutDir || !opts.gateway?.outDir) {
|
|
134
|
+
throw new Error("Test generation requires client and gateway to be generated in the pipeline");
|
|
135
|
+
}
|
|
136
|
+
const { generateTests } = await import("./test/generateTests.js");
|
|
137
|
+
await generateTests({
|
|
138
|
+
testDir: path.resolve(opts.test.testDir),
|
|
139
|
+
gatewayDir: path.resolve(opts.gateway.outDir),
|
|
140
|
+
clientDir: path.resolve(opts.clientOutDir),
|
|
141
|
+
catalogFile: path.resolve(opts.catalogOut),
|
|
142
|
+
imports: finalCompiler.imports,
|
|
143
|
+
force: opts.test.force,
|
|
144
|
+
versionSlug: opts.gateway.versionSlug,
|
|
145
|
+
serviceSlug: opts.gateway.serviceSlug,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
131
148
|
// Return the compiled catalog and OpenAPI doc for potential further processing
|
|
132
149
|
return { compiled, openapiDoc };
|
|
133
150
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for test suite generation
|
|
3
|
+
*/
|
|
4
|
+
export interface GenerateTestsOptions {
|
|
5
|
+
testDir: string;
|
|
6
|
+
gatewayDir: string;
|
|
7
|
+
clientDir: string;
|
|
8
|
+
catalogFile: string;
|
|
9
|
+
imports?: "js" | "ts" | "bare";
|
|
10
|
+
force?: boolean;
|
|
11
|
+
versionSlug?: string;
|
|
12
|
+
serviceSlug?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates a complete Vitest test suite for the generated gateway artifacts.
|
|
16
|
+
*
|
|
17
|
+
* @param opts - Test generation options
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateTests(opts: GenerateTestsOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=generateTests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateTests.d.ts","sourceRoot":"","sources":["../../src/test/generateTests.ts"],"names":[],"mappings":"AAgCA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAuBD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4J7E"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Suite Generator Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Generates a complete, runnable Vitest test suite that validates all
|
|
5
|
+
* generated gateway artifacts. Opt-in via --test-dir flag.
|
|
6
|
+
*
|
|
7
|
+
* Steps:
|
|
8
|
+
* 1. Validate gatewayDir, clientDir, catalogFile exist
|
|
9
|
+
* 2. Read and parse catalog.json
|
|
10
|
+
* 3. Resolve ClientMeta and OperationMetadata
|
|
11
|
+
* 4. Create output directories
|
|
12
|
+
* 5. For each file: skip-if-exists check, then write
|
|
13
|
+
*/
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { info, success } from "../util/cli.js";
|
|
17
|
+
import { resolveClientMeta, resolveOperationMeta } from "../gateway/helpers.js";
|
|
18
|
+
import { emitVitestConfig, emitMockClientHelper, emitTestAppHelper, emitRoutesTest, emitErrorsTest, emitEnvelopeTest, emitValidationTest, emitClassifyErrorTest, emitEnvelopeBuildersTest, emitUnwrapTest, } from "./generators.js";
|
|
19
|
+
/**
|
|
20
|
+
* Checks whether a test file should be written.
|
|
21
|
+
* Returns true if the file does not exist or force is enabled.
|
|
22
|
+
* Logs an info message and returns false if the file exists and force is disabled.
|
|
23
|
+
*/
|
|
24
|
+
function shouldWriteTestFile(filePath, force) {
|
|
25
|
+
if (!fs.existsSync(filePath))
|
|
26
|
+
return true;
|
|
27
|
+
if (force)
|
|
28
|
+
return true;
|
|
29
|
+
info(`Skipping ${path.basename(filePath)} (already exists, use --force-test to overwrite)`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Writes a test file if it should be written (skip-if-exists with force override).
|
|
34
|
+
*/
|
|
35
|
+
function writeTestFile(filePath, content, force) {
|
|
36
|
+
if (!shouldWriteTestFile(filePath, force))
|
|
37
|
+
return;
|
|
38
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
39
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generates a complete Vitest test suite for the generated gateway artifacts.
|
|
43
|
+
*
|
|
44
|
+
* @param opts - Test generation options
|
|
45
|
+
*/
|
|
46
|
+
export async function generateTests(opts) {
|
|
47
|
+
const testDir = path.resolve(opts.testDir);
|
|
48
|
+
const gatewayDir = path.resolve(opts.gatewayDir);
|
|
49
|
+
const clientDir = path.resolve(opts.clientDir);
|
|
50
|
+
const catalogFile = path.resolve(opts.catalogFile);
|
|
51
|
+
const importsMode = opts.imports ?? "js";
|
|
52
|
+
const force = opts.force ?? false;
|
|
53
|
+
// Validate required files exist
|
|
54
|
+
if (!fs.existsSync(gatewayDir)) {
|
|
55
|
+
throw new Error(`Gateway directory does not exist: ${gatewayDir}`);
|
|
56
|
+
}
|
|
57
|
+
if (!fs.existsSync(clientDir)) {
|
|
58
|
+
throw new Error(`Client directory does not exist: ${clientDir}`);
|
|
59
|
+
}
|
|
60
|
+
if (!fs.existsSync(catalogFile)) {
|
|
61
|
+
throw new Error(`Catalog file does not exist: ${catalogFile}`);
|
|
62
|
+
}
|
|
63
|
+
// Read and parse catalog
|
|
64
|
+
const catalogRaw = fs.readFileSync(catalogFile, "utf-8");
|
|
65
|
+
const catalog = JSON.parse(catalogRaw);
|
|
66
|
+
// Resolve client metadata
|
|
67
|
+
const clientMeta = resolveClientMeta({
|
|
68
|
+
clientDir,
|
|
69
|
+
catalogFile,
|
|
70
|
+
serviceSlug: opts.serviceSlug ?? "service",
|
|
71
|
+
importsMode,
|
|
72
|
+
}, catalog);
|
|
73
|
+
// Build operation metadata from catalog + OpenAPI paths
|
|
74
|
+
const operations = [];
|
|
75
|
+
if (catalog.operations) {
|
|
76
|
+
// Read OpenAPI doc paths from the gateway's generated routes to get URL paths
|
|
77
|
+
// We use the catalog operations and derive paths using the same pattern as gateway generation
|
|
78
|
+
for (const op of catalog.operations) {
|
|
79
|
+
const operationSlug = op.name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
80
|
+
// Try to find the path from the generated operation schema
|
|
81
|
+
const opSchemaPath = path.join(gatewayDir, "schemas", "operations", `${operationSlug}.json`);
|
|
82
|
+
let urlPath = `/${operationSlug.replace(/_/g, "-")}`;
|
|
83
|
+
if (fs.existsSync(opSchemaPath)) {
|
|
84
|
+
// Read the route file to extract the actual URL path
|
|
85
|
+
const routeFilePath = path.join(gatewayDir, "routes", `${operationSlug}.ts`);
|
|
86
|
+
if (fs.existsSync(routeFilePath)) {
|
|
87
|
+
const routeContent = fs.readFileSync(routeFilePath, "utf-8");
|
|
88
|
+
const urlMatch = routeContent.match(/url:\s*"([^"]+)"/);
|
|
89
|
+
if (urlMatch) {
|
|
90
|
+
urlPath = urlMatch[1];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const resolved = resolveOperationMeta(op.name, operationSlug, "post", // Default method for SOAP operations
|
|
95
|
+
urlPath, catalog.operations);
|
|
96
|
+
// Try to read method from the route file
|
|
97
|
+
const routeFilePath = path.join(gatewayDir, "routes", `${operationSlug}.ts`);
|
|
98
|
+
if (fs.existsSync(routeFilePath)) {
|
|
99
|
+
const routeContent = fs.readFileSync(routeFilePath, "utf-8");
|
|
100
|
+
const methodMatch = routeContent.match(/method:\s*"([^"]+)"/);
|
|
101
|
+
if (methodMatch) {
|
|
102
|
+
resolved.method = methodMatch[1].toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
operations.push(resolved);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Create output directories
|
|
109
|
+
fs.mkdirSync(path.join(testDir, "helpers"), { recursive: true });
|
|
110
|
+
fs.mkdirSync(path.join(testDir, "gateway"), { recursive: true });
|
|
111
|
+
fs.mkdirSync(path.join(testDir, "runtime"), { recursive: true });
|
|
112
|
+
// Emit vitest.config.ts
|
|
113
|
+
writeTestFile(path.join(testDir, "vitest.config.ts"), emitVitestConfig(), force);
|
|
114
|
+
// Emit helpers/mock-client.ts
|
|
115
|
+
writeTestFile(path.join(testDir, "helpers", "mock-client.ts"), emitMockClientHelper(testDir, clientDir, importsMode, clientMeta, operations, catalog), force);
|
|
116
|
+
// Emit helpers/test-app.ts
|
|
117
|
+
writeTestFile(path.join(testDir, "helpers", "test-app.ts"), emitTestAppHelper(testDir, gatewayDir, importsMode, clientMeta), force);
|
|
118
|
+
// Emit gateway/routes.test.ts
|
|
119
|
+
writeTestFile(path.join(testDir, "gateway", "routes.test.ts"), emitRoutesTest(testDir, importsMode, operations, catalog), force);
|
|
120
|
+
// Emit gateway/errors.test.ts
|
|
121
|
+
writeTestFile(path.join(testDir, "gateway", "errors.test.ts"), emitErrorsTest(testDir, importsMode, operations, catalog), force);
|
|
122
|
+
// Emit gateway/envelope.test.ts
|
|
123
|
+
writeTestFile(path.join(testDir, "gateway", "envelope.test.ts"), emitEnvelopeTest(testDir, importsMode, operations, catalog), force);
|
|
124
|
+
// Emit gateway/validation.test.ts
|
|
125
|
+
writeTestFile(path.join(testDir, "gateway", "validation.test.ts"), emitValidationTest(testDir, importsMode, operations), force);
|
|
126
|
+
// Emit runtime/classify-error.test.ts
|
|
127
|
+
writeTestFile(path.join(testDir, "runtime", "classify-error.test.ts"), emitClassifyErrorTest(testDir, gatewayDir, importsMode), force);
|
|
128
|
+
// Emit runtime/envelope-builders.test.ts
|
|
129
|
+
writeTestFile(path.join(testDir, "runtime", "envelope-builders.test.ts"), emitEnvelopeBuildersTest(testDir, gatewayDir, importsMode), force);
|
|
130
|
+
// Emit runtime/unwrap.test.ts (conditional: only when ArrayOf* wrappers exist)
|
|
131
|
+
const unwrapContent = emitUnwrapTest(testDir, gatewayDir, importsMode, catalog);
|
|
132
|
+
if (unwrapContent) {
|
|
133
|
+
writeTestFile(path.join(testDir, "runtime", "unwrap.test.ts"), unwrapContent, force);
|
|
134
|
+
}
|
|
135
|
+
success(`Test suite generated in ${testDir}`);
|
|
136
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ClientMeta, ResolvedOperationMeta } from "../gateway/helpers.js";
|
|
2
|
+
import type { CatalogForMocks } from "./mockData.js";
|
|
3
|
+
/**
|
|
4
|
+
* Emits vitest.config.ts content.
|
|
5
|
+
*
|
|
6
|
+
* Sets root to __dirname so test glob patterns resolve relative to the
|
|
7
|
+
* config file location, not the working directory.
|
|
8
|
+
*/
|
|
9
|
+
export declare function emitVitestConfig(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Emits helpers/mock-client.ts content with full default responses per operation.
|
|
12
|
+
*
|
|
13
|
+
* @param testDir - Absolute path to test output directory
|
|
14
|
+
* @param clientDir - Absolute path to client directory
|
|
15
|
+
* @param importsMode - Import extension mode
|
|
16
|
+
* @param clientMeta - Client metadata
|
|
17
|
+
* @param operations - Resolved operation metadata
|
|
18
|
+
* @param catalog - Compiled catalog for mock data generation
|
|
19
|
+
*/
|
|
20
|
+
export declare function emitMockClientHelper(testDir: string, clientDir: string, importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta, operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
|
|
21
|
+
/**
|
|
22
|
+
* Emits helpers/test-app.ts content.
|
|
23
|
+
*
|
|
24
|
+
* @param testDir - Absolute path to test output directory
|
|
25
|
+
* @param gatewayDir - Absolute path to gateway directory
|
|
26
|
+
* @param importsMode - Import extension mode
|
|
27
|
+
* @param clientMeta - Client metadata
|
|
28
|
+
*/
|
|
29
|
+
export declare function emitTestAppHelper(testDir: string, gatewayDir: string, importsMode: "js" | "ts" | "bare", clientMeta: ClientMeta): string;
|
|
30
|
+
/**
|
|
31
|
+
* Emits gateway/routes.test.ts with one test per operation.
|
|
32
|
+
*
|
|
33
|
+
* @param testDir - Absolute path to test output directory
|
|
34
|
+
* @param importsMode - Import extension mode
|
|
35
|
+
* @param operations - Resolved operation metadata
|
|
36
|
+
* @param catalog - Compiled catalog for mock data generation
|
|
37
|
+
*/
|
|
38
|
+
export declare function emitRoutesTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
|
|
39
|
+
/**
|
|
40
|
+
* Emits gateway/errors.test.ts with error classification tests through Fastify.
|
|
41
|
+
*
|
|
42
|
+
* @param testDir - Absolute path to test output directory
|
|
43
|
+
* @param importsMode - Import extension mode
|
|
44
|
+
* @param operations - Resolved operation metadata (uses first operation for error tests)
|
|
45
|
+
* @param catalog - Compiled catalog for mock data generation
|
|
46
|
+
*/
|
|
47
|
+
export declare function emitErrorsTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
|
|
48
|
+
/**
|
|
49
|
+
* Emits gateway/envelope.test.ts with SUCCESS/ERROR structure assertions.
|
|
50
|
+
*/
|
|
51
|
+
export declare function emitEnvelopeTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[], catalog: CatalogForMocks): string;
|
|
52
|
+
/**
|
|
53
|
+
* Emits gateway/validation.test.ts with invalid payload tests per route.
|
|
54
|
+
*/
|
|
55
|
+
export declare function emitValidationTest(testDir: string, importsMode: "js" | "ts" | "bare", operations: ResolvedOperationMeta[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Emits runtime/classify-error.test.ts with direct classifyError() unit tests.
|
|
58
|
+
*/
|
|
59
|
+
export declare function emitClassifyErrorTest(testDir: string, gatewayDir: string, importsMode: "js" | "ts" | "bare"): string;
|
|
60
|
+
/**
|
|
61
|
+
* Emits runtime/envelope-builders.test.ts with buildSuccessEnvelope/buildErrorEnvelope tests.
|
|
62
|
+
*/
|
|
63
|
+
export declare function emitEnvelopeBuildersTest(testDir: string, gatewayDir: string, importsMode: "js" | "ts" | "bare"): string;
|
|
64
|
+
/**
|
|
65
|
+
* Emits runtime/unwrap.test.ts with unwrapArrayWrappers tests.
|
|
66
|
+
* Returns null if no array wrappers exist in the catalog.
|
|
67
|
+
*/
|
|
68
|
+
export declare function emitUnwrapTest(testDir: string, gatewayDir: string, importsMode: "js" | "ts" | "bare", catalog: CatalogForMocks): string | null;
|
|
69
|
+
//# sourceMappingURL=generators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/test/generators.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,UAAU,EAAE,qBAAqB,EAAC,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAGnD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAiBzC;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CAiDR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,UAAU,GACrB,MAAM,CAsCR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CA8CR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CAkIR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,EACnC,OAAO,EAAE,eAAe,GACvB,MAAM,CAuER;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,UAAU,EAAE,qBAAqB,EAAE,GAClC,MAAM,CAoCR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgFR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAChC,MAAM,CAgDR;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACjC,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,IAAI,CAyCf"}
|