@typokit/testing 0.1.4
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/contract-generator.d.ts +65 -0
- package/dist/contract-generator.d.ts.map +1 -0
- package/dist/contract-generator.js +325 -0
- package/dist/contract-generator.js.map +1 -0
- package/dist/factory.d.ts +27 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +194 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/integration-suite.d.ts +66 -0
- package/dist/integration-suite.d.ts.map +1 -0
- package/dist/integration-suite.js +110 -0
- package/dist/integration-suite.js.map +1 -0
- package/dist/schema-matcher.d.ts +78 -0
- package/dist/schema-matcher.d.ts.map +1 -0
- package/dist/schema-matcher.js +99 -0
- package/dist/schema-matcher.js.map +1 -0
- package/package.json +34 -0
- package/src/contract-generator.test.ts +356 -0
- package/src/contract-generator.ts +412 -0
- package/src/factory.test.ts +217 -0
- package/src/factory.ts +248 -0
- package/src/index.test.ts +275 -0
- package/src/index.ts +284 -0
- package/src/integration-suite.test.ts +336 -0
- package/src/integration-suite.ts +191 -0
- package/src/schema-matcher.test.ts +293 -0
- package/src/schema-matcher.ts +160 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { HttpMethod, SchemaTypeMap } from "@typokit/types";
|
|
2
|
+
/** A route definition used for contract test generation */
|
|
3
|
+
export interface ContractTestRoute {
|
|
4
|
+
/** HTTP method */
|
|
5
|
+
method: HttpMethod;
|
|
6
|
+
/** Route path (e.g. "/users/:id") */
|
|
7
|
+
path: string;
|
|
8
|
+
/** Handler reference */
|
|
9
|
+
handlerRef: string;
|
|
10
|
+
/** Validator schema references */
|
|
11
|
+
validators?: {
|
|
12
|
+
params?: string;
|
|
13
|
+
query?: string;
|
|
14
|
+
body?: string;
|
|
15
|
+
};
|
|
16
|
+
/** Response schema name (for toMatchSchema assertions) */
|
|
17
|
+
responseSchema?: string;
|
|
18
|
+
/** Expected success status code (default: 200) */
|
|
19
|
+
expectedStatus?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Supported test runners */
|
|
22
|
+
export type TestRunner = "jest" | "vitest" | "rstest";
|
|
23
|
+
/** Options for contract test generation */
|
|
24
|
+
export interface ContractTestOptions {
|
|
25
|
+
/** Test runner to generate imports for */
|
|
26
|
+
runner: TestRunner;
|
|
27
|
+
/** Import path for the app module (e.g. "../src/app") */
|
|
28
|
+
appImport: string;
|
|
29
|
+
/** Routes to generate tests for */
|
|
30
|
+
routes: ContractTestRoute[];
|
|
31
|
+
/** Schema type metadata for validators */
|
|
32
|
+
schemas: SchemaTypeMap;
|
|
33
|
+
}
|
|
34
|
+
/** A generated contract test file */
|
|
35
|
+
export interface ContractTestOutput {
|
|
36
|
+
/** Relative file path (e.g. "__generated__/users.contract.test.ts") */
|
|
37
|
+
filePath: string;
|
|
38
|
+
/** Generated file content */
|
|
39
|
+
content: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate contract test files from route schemas.
|
|
43
|
+
*
|
|
44
|
+
* Groups routes by path prefix and produces one test file per group.
|
|
45
|
+
* Each file tests: valid input → expected status, missing required
|
|
46
|
+
* fields → 400, invalid field formats → 400.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const outputs = generateContractTests({
|
|
50
|
+
* runner: "vitest",
|
|
51
|
+
* appImport: "../src/app",
|
|
52
|
+
* routes: [{ method: "POST", path: "/users", ... }],
|
|
53
|
+
* schemas: { CreateUserInput: { ... } },
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function generateContractTests(options: ContractTestOptions): ContractTestOutput[];
|
|
58
|
+
/**
|
|
59
|
+
* Detect the test runner used in a project by checking for known
|
|
60
|
+
* config files or dependencies.
|
|
61
|
+
*
|
|
62
|
+
* Returns the detected runner or "vitest" as default.
|
|
63
|
+
*/
|
|
64
|
+
export declare function detectTestRunner(packageJson: Record<string, unknown>): TestRunner;
|
|
65
|
+
//# sourceMappingURL=contract-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-generator.d.ts","sourceRoot":"","sources":["../src/contract-generator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAgB,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI9E,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,kBAAkB;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,6BAA6B;AAC7B,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEtD,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,MAAM,EAAE,UAAU,CAAC;IACnB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,qCAAqC;AACrC,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAuHD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,EAAE,CAgBtB;AAoLD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,UAAU,CAoBZ"}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// @typokit/testing — Contract Test Generation
|
|
2
|
+
//
|
|
3
|
+
// Auto-generates baseline contract tests from route schemas.
|
|
4
|
+
// Output is test-runner-agnostic (Jest, Vitest, Rstest).
|
|
5
|
+
// ─── Helpers ──────────────────────────────────────────────────
|
|
6
|
+
/** Deterministic seed-based data generator for inline test values */
|
|
7
|
+
function generateSampleValue(type, jsdoc) {
|
|
8
|
+
// Check for format constraints
|
|
9
|
+
const format = jsdoc?.["format"] ?? jsdoc?.["@format"];
|
|
10
|
+
if (format === "email")
|
|
11
|
+
return "test@example.com";
|
|
12
|
+
if (format === "url")
|
|
13
|
+
return "https://example.com";
|
|
14
|
+
if (format === "uuid")
|
|
15
|
+
return "550e8400-e29b-41d4-a716-446655440000";
|
|
16
|
+
if (format === "date-time")
|
|
17
|
+
return "2026-01-01T00:00:00.000Z";
|
|
18
|
+
// Check for string unions
|
|
19
|
+
if (type.includes("|") && type.includes('"')) {
|
|
20
|
+
const values = type.split("|").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
21
|
+
return values[0];
|
|
22
|
+
}
|
|
23
|
+
if (type === "string")
|
|
24
|
+
return "test-value";
|
|
25
|
+
if (type === "number")
|
|
26
|
+
return 42;
|
|
27
|
+
if (type === "boolean")
|
|
28
|
+
return true;
|
|
29
|
+
if (type === "string[]")
|
|
30
|
+
return ["test-item"];
|
|
31
|
+
if (type === "number[]")
|
|
32
|
+
return [1];
|
|
33
|
+
if (type.endsWith("[]"))
|
|
34
|
+
return [];
|
|
35
|
+
return "test-value";
|
|
36
|
+
}
|
|
37
|
+
/** Generate an invalid value for a given type */
|
|
38
|
+
function generateInvalidSampleValue(type, jsdoc) {
|
|
39
|
+
const format = jsdoc?.["format"] ?? jsdoc?.["@format"];
|
|
40
|
+
if (format === "email")
|
|
41
|
+
return "not-an-email";
|
|
42
|
+
if (format === "url")
|
|
43
|
+
return "not-a-url";
|
|
44
|
+
if (format === "uuid")
|
|
45
|
+
return "not-a-uuid";
|
|
46
|
+
if (format === "date-time")
|
|
47
|
+
return "not-a-date";
|
|
48
|
+
if (type === "number")
|
|
49
|
+
return "not-a-number";
|
|
50
|
+
if (type === "boolean")
|
|
51
|
+
return "not-a-boolean";
|
|
52
|
+
// String unions — use a value not in the set
|
|
53
|
+
if (type.includes("|") && type.includes('"')) {
|
|
54
|
+
return "__invalid_enum_value__";
|
|
55
|
+
}
|
|
56
|
+
return 12345;
|
|
57
|
+
}
|
|
58
|
+
/** Get the test runner import statement */
|
|
59
|
+
function getImportStatement(runner) {
|
|
60
|
+
switch (runner) {
|
|
61
|
+
case "jest":
|
|
62
|
+
return 'import { describe, it, expect } from "@jest/globals";';
|
|
63
|
+
case "vitest":
|
|
64
|
+
return 'import { describe, it, expect } from "vitest";';
|
|
65
|
+
case "rstest":
|
|
66
|
+
return 'import { describe, it, expect } from "@rstest/core";';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Group routes by path prefix for file organization */
|
|
70
|
+
function groupRoutesByPrefix(routes) {
|
|
71
|
+
const groups = {};
|
|
72
|
+
for (const route of routes) {
|
|
73
|
+
// Extract first path segment as group name
|
|
74
|
+
const segments = route.path.split("/").filter(Boolean);
|
|
75
|
+
const prefix = segments.length > 0 ? segments[0] : "root";
|
|
76
|
+
if (!groups[prefix]) {
|
|
77
|
+
groups[prefix] = [];
|
|
78
|
+
}
|
|
79
|
+
groups[prefix].push(route);
|
|
80
|
+
}
|
|
81
|
+
// Sort routes within each group deterministically
|
|
82
|
+
for (const key of Object.keys(groups)) {
|
|
83
|
+
groups[key].sort((a, b) => {
|
|
84
|
+
const methodOrder = a.method.localeCompare(b.method);
|
|
85
|
+
if (methodOrder !== 0)
|
|
86
|
+
return methodOrder;
|
|
87
|
+
return a.path.localeCompare(b.path);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return groups;
|
|
91
|
+
}
|
|
92
|
+
/** Escape a value for use in generated TypeScript code */
|
|
93
|
+
function toCodeLiteral(value) {
|
|
94
|
+
if (typeof value === "string")
|
|
95
|
+
return JSON.stringify(value);
|
|
96
|
+
if (typeof value === "number")
|
|
97
|
+
return String(value);
|
|
98
|
+
if (typeof value === "boolean")
|
|
99
|
+
return String(value);
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
return `[${value.map(toCodeLiteral).join(", ")}]`;
|
|
102
|
+
}
|
|
103
|
+
return JSON.stringify(value);
|
|
104
|
+
}
|
|
105
|
+
/** Build a valid body object literal as code string */
|
|
106
|
+
function buildBodyLiteral(schema, indent) {
|
|
107
|
+
const entries = [];
|
|
108
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
109
|
+
if (prop.optional)
|
|
110
|
+
continue;
|
|
111
|
+
const value = generateSampleValue(prop.type, prop.jsdoc);
|
|
112
|
+
entries.push(`${indent} ${key}: ${toCodeLiteral(value)},`);
|
|
113
|
+
}
|
|
114
|
+
if (entries.length === 0)
|
|
115
|
+
return "{}";
|
|
116
|
+
return `{\n${entries.join("\n")}\n${indent}}`;
|
|
117
|
+
}
|
|
118
|
+
// ─── Generator ────────────────────────────────────────────────
|
|
119
|
+
/**
|
|
120
|
+
* Generate contract test files from route schemas.
|
|
121
|
+
*
|
|
122
|
+
* Groups routes by path prefix and produces one test file per group.
|
|
123
|
+
* Each file tests: valid input → expected status, missing required
|
|
124
|
+
* fields → 400, invalid field formats → 400.
|
|
125
|
+
*
|
|
126
|
+
* ```ts
|
|
127
|
+
* const outputs = generateContractTests({
|
|
128
|
+
* runner: "vitest",
|
|
129
|
+
* appImport: "../src/app",
|
|
130
|
+
* routes: [{ method: "POST", path: "/users", ... }],
|
|
131
|
+
* schemas: { CreateUserInput: { ... } },
|
|
132
|
+
* });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export function generateContractTests(options) {
|
|
136
|
+
const { runner, appImport, routes, schemas } = options;
|
|
137
|
+
const groups = groupRoutesByPrefix(routes);
|
|
138
|
+
const outputs = [];
|
|
139
|
+
for (const [prefix, groupRoutes] of Object.entries(groups).sort(([a], [b]) => a.localeCompare(b))) {
|
|
140
|
+
const content = generateTestFile(runner, appImport, groupRoutes, schemas);
|
|
141
|
+
outputs.push({
|
|
142
|
+
filePath: `__generated__/${prefix}.contract.test.ts`,
|
|
143
|
+
content,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return outputs;
|
|
147
|
+
}
|
|
148
|
+
/** Generate a single contract test file for a group of routes */
|
|
149
|
+
function generateTestFile(runner, appImport, routes, schemas) {
|
|
150
|
+
const lines = [];
|
|
151
|
+
// Header
|
|
152
|
+
lines.push("// DO NOT EDIT — regenerated on schema change");
|
|
153
|
+
lines.push(`// Generated by @typokit/testing contract-generator`);
|
|
154
|
+
lines.push("");
|
|
155
|
+
// Imports
|
|
156
|
+
lines.push(getImportStatement(runner));
|
|
157
|
+
lines.push(`import { createTestClient } from "@typokit/testing";`);
|
|
158
|
+
// Only import toMatchSchema if any route has a response schema
|
|
159
|
+
const hasResponseSchema = routes.some((r) => r.responseSchema);
|
|
160
|
+
if (hasResponseSchema) {
|
|
161
|
+
lines.push(`import { toMatchSchema } from "@typokit/testing";`);
|
|
162
|
+
}
|
|
163
|
+
lines.push(`import { app } from ${JSON.stringify(appImport)};`);
|
|
164
|
+
lines.push("");
|
|
165
|
+
// Setup
|
|
166
|
+
lines.push("let client: Awaited<ReturnType<typeof createTestClient>>;");
|
|
167
|
+
lines.push("");
|
|
168
|
+
lines.push("beforeAll(async () => {");
|
|
169
|
+
lines.push(" client = await createTestClient(app);");
|
|
170
|
+
lines.push("});");
|
|
171
|
+
lines.push("");
|
|
172
|
+
lines.push("afterAll(async () => {");
|
|
173
|
+
lines.push(" await client.close();");
|
|
174
|
+
lines.push("});");
|
|
175
|
+
lines.push("");
|
|
176
|
+
// Generate test blocks for each route
|
|
177
|
+
for (const route of routes) {
|
|
178
|
+
generateRouteTests(lines, route, schemas);
|
|
179
|
+
lines.push("");
|
|
180
|
+
}
|
|
181
|
+
return lines.join("\n");
|
|
182
|
+
}
|
|
183
|
+
/** Generate describe/it blocks for a single route */
|
|
184
|
+
function generateRouteTests(lines, route, schemas) {
|
|
185
|
+
const { method, path, validators, responseSchema, expectedStatus } = route;
|
|
186
|
+
const successStatus = expectedStatus ?? 200;
|
|
187
|
+
const methodLower = method.toLowerCase();
|
|
188
|
+
lines.push(`describe("${method} ${path}", () => {`);
|
|
189
|
+
// Resolve body schema if validators reference one
|
|
190
|
+
const bodySchemaName = validators?.body;
|
|
191
|
+
const bodySchema = bodySchemaName ? schemas[bodySchemaName] : undefined;
|
|
192
|
+
// Determine if the method typically has a body
|
|
193
|
+
const hasBody = ["POST", "PUT", "PATCH"].includes(method);
|
|
194
|
+
// ── Test 1: Valid input → expected status ──
|
|
195
|
+
if (hasBody && bodySchema) {
|
|
196
|
+
const bodyLiteral = buildBodyLiteral(bodySchema, " ");
|
|
197
|
+
lines.push(` it("accepts valid ${bodySchemaName}", async () => {`);
|
|
198
|
+
lines.push(` const res = await client.${methodLower}("${path}", {`);
|
|
199
|
+
lines.push(` body: ${bodyLiteral},`);
|
|
200
|
+
lines.push(` });`);
|
|
201
|
+
lines.push(` expect(res.status).toBe(${successStatus});`);
|
|
202
|
+
if (responseSchema) {
|
|
203
|
+
lines.push(` expect(res.body).toMatchSchema("${responseSchema}");`);
|
|
204
|
+
}
|
|
205
|
+
lines.push(` });`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
lines.push(` it("responds with ${successStatus}", async () => {`);
|
|
209
|
+
lines.push(` const res = await client.${methodLower}("${path}");`);
|
|
210
|
+
lines.push(` expect(res.status).toBe(${successStatus});`);
|
|
211
|
+
if (responseSchema) {
|
|
212
|
+
lines.push(` expect(res.body).toMatchSchema("${responseSchema}");`);
|
|
213
|
+
}
|
|
214
|
+
lines.push(` });`);
|
|
215
|
+
}
|
|
216
|
+
// ── Test 2: Missing required fields → 400 ──
|
|
217
|
+
if (hasBody && bodySchema) {
|
|
218
|
+
const requiredFields = Object.entries(bodySchema.properties)
|
|
219
|
+
.filter(([, prop]) => !prop.optional)
|
|
220
|
+
.map(([key]) => key)
|
|
221
|
+
.sort();
|
|
222
|
+
if (requiredFields.length > 0) {
|
|
223
|
+
lines.push("");
|
|
224
|
+
lines.push(` it("rejects missing required fields", async () => {`);
|
|
225
|
+
lines.push(` const res = await client.${methodLower}("${path}", { body: {} });`);
|
|
226
|
+
lines.push(` expect(res.status).toBe(400);`);
|
|
227
|
+
lines.push(` });`);
|
|
228
|
+
// Individual field tests for more specific coverage
|
|
229
|
+
for (const field of requiredFields) {
|
|
230
|
+
lines.push("");
|
|
231
|
+
lines.push(` it("rejects missing '${field}' field", async () => {`);
|
|
232
|
+
// Build a body with all required fields except this one
|
|
233
|
+
const partialEntries = [];
|
|
234
|
+
for (const [key, prop] of Object.entries(bodySchema.properties)) {
|
|
235
|
+
if (prop.optional || key === field)
|
|
236
|
+
continue;
|
|
237
|
+
const value = generateSampleValue(prop.type, prop.jsdoc);
|
|
238
|
+
partialEntries.push(` ${key}: ${toCodeLiteral(value)},`);
|
|
239
|
+
}
|
|
240
|
+
const partialBody = partialEntries.length > 0
|
|
241
|
+
? `{\n${partialEntries.join("\n")}\n }`
|
|
242
|
+
: "{}";
|
|
243
|
+
lines.push(` const res = await client.${methodLower}("${path}", {`);
|
|
244
|
+
lines.push(` body: ${partialBody},`);
|
|
245
|
+
lines.push(` });`);
|
|
246
|
+
lines.push(` expect(res.status).toBe(400);`);
|
|
247
|
+
lines.push(` });`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// ── Test 3: Invalid field formats → 400 ──
|
|
252
|
+
if (hasBody && bodySchema) {
|
|
253
|
+
const fieldsWithFormats = Object.entries(bodySchema.properties)
|
|
254
|
+
.filter(([, prop]) => {
|
|
255
|
+
const jsdoc = prop.jsdoc;
|
|
256
|
+
if (!jsdoc)
|
|
257
|
+
return false;
|
|
258
|
+
return !!(jsdoc["format"] || jsdoc["@format"]);
|
|
259
|
+
})
|
|
260
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
261
|
+
// Also include string unions and typed fields that can be invalidated
|
|
262
|
+
const fieldsWithTypes = Object.entries(bodySchema.properties)
|
|
263
|
+
.filter(([, prop]) => {
|
|
264
|
+
// Already covered by format
|
|
265
|
+
if (prop.jsdoc?.["format"] || prop.jsdoc?.["@format"])
|
|
266
|
+
return false;
|
|
267
|
+
return (prop.type === "number" ||
|
|
268
|
+
prop.type === "boolean" ||
|
|
269
|
+
(prop.type.includes("|") && prop.type.includes('"')));
|
|
270
|
+
})
|
|
271
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
272
|
+
const invalidFields = [...fieldsWithFormats, ...fieldsWithTypes];
|
|
273
|
+
for (const [field, prop] of invalidFields) {
|
|
274
|
+
const invalidValue = generateInvalidSampleValue(prop.type, prop.jsdoc);
|
|
275
|
+
lines.push("");
|
|
276
|
+
lines.push(` it("rejects invalid ${field} format", async () => {`);
|
|
277
|
+
// Build a valid body, then replace the field with invalid value
|
|
278
|
+
const fullEntries = [];
|
|
279
|
+
for (const [key, p] of Object.entries(bodySchema.properties)) {
|
|
280
|
+
if (p.optional)
|
|
281
|
+
continue;
|
|
282
|
+
const value = key === field ? invalidValue : generateSampleValue(p.type, p.jsdoc);
|
|
283
|
+
fullEntries.push(` ${key}: ${toCodeLiteral(value)},`);
|
|
284
|
+
}
|
|
285
|
+
const fullBody = fullEntries.length > 0 ? `{\n${fullEntries.join("\n")}\n }` : "{}";
|
|
286
|
+
lines.push(` const res = await client.${methodLower}("${path}", {`);
|
|
287
|
+
lines.push(` body: ${fullBody},`);
|
|
288
|
+
lines.push(` });`);
|
|
289
|
+
lines.push(` expect(res.status).toBe(400);`);
|
|
290
|
+
lines.push(` });`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
lines.push(`});`);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Detect the test runner used in a project by checking for known
|
|
297
|
+
* config files or dependencies.
|
|
298
|
+
*
|
|
299
|
+
* Returns the detected runner or "vitest" as default.
|
|
300
|
+
*/
|
|
301
|
+
export function detectTestRunner(packageJson) {
|
|
302
|
+
const deps = {
|
|
303
|
+
...packageJson["dependencies"],
|
|
304
|
+
...packageJson["devDependencies"],
|
|
305
|
+
};
|
|
306
|
+
if (deps["rstest"])
|
|
307
|
+
return "rstest";
|
|
308
|
+
if (deps["vitest"])
|
|
309
|
+
return "vitest";
|
|
310
|
+
if (deps["jest"] || deps["@jest/globals"])
|
|
311
|
+
return "jest";
|
|
312
|
+
// Check scripts for runner hints
|
|
313
|
+
const scripts = packageJson["scripts"];
|
|
314
|
+
if (scripts) {
|
|
315
|
+
const testScript = scripts["test"] ?? "";
|
|
316
|
+
if (testScript.includes("rstest"))
|
|
317
|
+
return "rstest";
|
|
318
|
+
if (testScript.includes("vitest"))
|
|
319
|
+
return "vitest";
|
|
320
|
+
if (testScript.includes("jest"))
|
|
321
|
+
return "jest";
|
|
322
|
+
}
|
|
323
|
+
return "vitest";
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=contract-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-generator.js","sourceRoot":"","sources":["../src/contract-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TypeMetadata } from "@typokit/types";
|
|
2
|
+
/** Options for creating a factory */
|
|
3
|
+
export interface FactoryOptions {
|
|
4
|
+
/** Seed for deterministic random generation */
|
|
5
|
+
seed?: number;
|
|
6
|
+
}
|
|
7
|
+
/** A test factory that produces typed instances */
|
|
8
|
+
export interface Factory<T> {
|
|
9
|
+
/** Build a single valid instance with optional field overrides */
|
|
10
|
+
build(overrides?: Partial<T>): T;
|
|
11
|
+
/** Build multiple valid instances */
|
|
12
|
+
buildMany(count: number, overrides?: Partial<T>): T[];
|
|
13
|
+
/** Build an instance with a specific field set to an invalid value */
|
|
14
|
+
buildInvalid(field: keyof T & string): T;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a type-safe test factory from TypeMetadata.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* const userFactory = createFactory<User>(userMetadata, { seed: 42 });
|
|
21
|
+
* const user = userFactory.build();
|
|
22
|
+
* const admin = userFactory.build({ role: "admin" });
|
|
23
|
+
* const invalid = userFactory.buildInvalid("email");
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function createFactory<T>(metadata: TypeMetadata, options?: FactoryOptions): Factory<T>;
|
|
27
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAgBnD,qCAAqC;AACrC,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mDAAmD;AACnD,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,kEAAkE;IAClE,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACjC,qCAAqC;IACrC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACtD,sEAAsE;IACtE,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;CAC1C;AAuJD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAgDZ"}
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// @typokit/testing — Test Factories
|
|
2
|
+
//
|
|
3
|
+
// Type-safe test factories that produce valid/invalid fixture data
|
|
4
|
+
// from TypeMetadata. Deterministic when seeded.
|
|
5
|
+
// ─── Seeded PRNG (mulberry32) ─────────────────────────────────
|
|
6
|
+
function mulberry32(seed) {
|
|
7
|
+
let s = seed | 0;
|
|
8
|
+
return () => {
|
|
9
|
+
s = (s + 0x6d2b79f5) | 0;
|
|
10
|
+
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
11
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
12
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// ─── Random Data Generators ───────────────────────────────────
|
|
16
|
+
function randomString(rand, length) {
|
|
17
|
+
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
18
|
+
let result = "";
|
|
19
|
+
for (let i = 0; i < length; i++) {
|
|
20
|
+
result += chars[Math.floor(rand() * chars.length)];
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function randomEmail(rand) {
|
|
25
|
+
return `${randomString(rand, 8)}@${randomString(rand, 5)}.com`;
|
|
26
|
+
}
|
|
27
|
+
function randomUrl(rand) {
|
|
28
|
+
return `https://${randomString(rand, 8)}.com/${randomString(rand, 4)}`;
|
|
29
|
+
}
|
|
30
|
+
function randomUuid(rand) {
|
|
31
|
+
const hex = "0123456789abcdef";
|
|
32
|
+
const segments = [8, 4, 4, 4, 12];
|
|
33
|
+
return segments
|
|
34
|
+
.map((len) => {
|
|
35
|
+
let s = "";
|
|
36
|
+
for (let i = 0; i < len; i++) {
|
|
37
|
+
s += hex[Math.floor(rand() * 16)];
|
|
38
|
+
}
|
|
39
|
+
return s;
|
|
40
|
+
})
|
|
41
|
+
.join("-");
|
|
42
|
+
}
|
|
43
|
+
function randomInt(rand, min, max) {
|
|
44
|
+
return Math.floor(rand() * (max - min + 1)) + min;
|
|
45
|
+
}
|
|
46
|
+
function randomDate(rand) {
|
|
47
|
+
const year = randomInt(rand, 2020, 2030);
|
|
48
|
+
const month = String(randomInt(rand, 1, 12)).padStart(2, "0");
|
|
49
|
+
const day = String(randomInt(rand, 1, 28)).padStart(2, "0");
|
|
50
|
+
return `${year}-${month}-${day}T00:00:00.000Z`;
|
|
51
|
+
}
|
|
52
|
+
// ─── Value Generator ──────────────────────────────────────────
|
|
53
|
+
function generateValue(type, jsdoc, rand) {
|
|
54
|
+
const format = jsdoc?.["format"];
|
|
55
|
+
const minLengthStr = jsdoc?.["minLength"];
|
|
56
|
+
const maxLengthStr = jsdoc?.["maxLength"];
|
|
57
|
+
const minStr = jsdoc?.["minimum"];
|
|
58
|
+
const maxStr = jsdoc?.["maximum"];
|
|
59
|
+
// Check for JSDoc format constraints first
|
|
60
|
+
if (format === "email")
|
|
61
|
+
return randomEmail(rand);
|
|
62
|
+
if (format === "url" || format === "uri")
|
|
63
|
+
return randomUrl(rand);
|
|
64
|
+
if (format === "uuid")
|
|
65
|
+
return randomUuid(rand);
|
|
66
|
+
if (format === "date" || format === "date-time")
|
|
67
|
+
return randomDate(rand);
|
|
68
|
+
// String union types like '"a" | "b" | "c"'
|
|
69
|
+
if (type.includes('" | "') || type.includes("' | '")) {
|
|
70
|
+
const values = type
|
|
71
|
+
.split("|")
|
|
72
|
+
.map((v) => v.trim().replace(/^["']|["']$/g, ""));
|
|
73
|
+
return values[Math.floor(rand() * values.length)];
|
|
74
|
+
}
|
|
75
|
+
// Handle base types
|
|
76
|
+
const baseType = type.replace(/\[\]$/, "");
|
|
77
|
+
const isArray = type.endsWith("[]");
|
|
78
|
+
const gen = () => {
|
|
79
|
+
switch (baseType) {
|
|
80
|
+
case "string": {
|
|
81
|
+
const minLen = minLengthStr ? parseInt(minLengthStr, 10) : 5;
|
|
82
|
+
const maxLen = maxLengthStr ? parseInt(maxLengthStr, 10) : 20;
|
|
83
|
+
const len = randomInt(rand, minLen, maxLen);
|
|
84
|
+
return randomString(rand, len);
|
|
85
|
+
}
|
|
86
|
+
case "number": {
|
|
87
|
+
const min = minStr ? parseInt(minStr, 10) : 1;
|
|
88
|
+
const max = maxStr ? parseInt(maxStr, 10) : 1000;
|
|
89
|
+
return randomInt(rand, min, max);
|
|
90
|
+
}
|
|
91
|
+
case "boolean":
|
|
92
|
+
return rand() > 0.5;
|
|
93
|
+
case "Date":
|
|
94
|
+
return randomDate(rand);
|
|
95
|
+
default:
|
|
96
|
+
return randomString(rand, 10);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
if (isArray) {
|
|
100
|
+
const count = randomInt(rand, 1, 3);
|
|
101
|
+
return Array.from({ length: count }, gen);
|
|
102
|
+
}
|
|
103
|
+
return gen();
|
|
104
|
+
}
|
|
105
|
+
// ─── Invalid Value Generator ──────────────────────────────────
|
|
106
|
+
function generateInvalidValue(type, jsdoc) {
|
|
107
|
+
const format = jsdoc?.["format"];
|
|
108
|
+
const minLengthStr = jsdoc?.["minLength"];
|
|
109
|
+
const maxStr = jsdoc?.["maximum"];
|
|
110
|
+
const minStr = jsdoc?.["minimum"];
|
|
111
|
+
if (format === "email")
|
|
112
|
+
return "not-an-email";
|
|
113
|
+
if (format === "url" || format === "uri")
|
|
114
|
+
return "not a url";
|
|
115
|
+
if (format === "uuid")
|
|
116
|
+
return "not-a-uuid";
|
|
117
|
+
if (format === "date" || format === "date-time")
|
|
118
|
+
return "not-a-date";
|
|
119
|
+
if (type.includes('" | "') || type.includes("' | '")) {
|
|
120
|
+
return "__invalid_enum_value__";
|
|
121
|
+
}
|
|
122
|
+
const baseType = type.replace(/\[\]$/, "");
|
|
123
|
+
switch (baseType) {
|
|
124
|
+
case "string": {
|
|
125
|
+
if (minLengthStr) {
|
|
126
|
+
const minLen = parseInt(minLengthStr, 10);
|
|
127
|
+
return minLen > 1 ? "x" : "";
|
|
128
|
+
}
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
case "number": {
|
|
132
|
+
if (maxStr)
|
|
133
|
+
return parseInt(maxStr, 10) + 100;
|
|
134
|
+
if (minStr)
|
|
135
|
+
return parseInt(minStr, 10) - 100;
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
case "boolean":
|
|
139
|
+
return "not-a-boolean";
|
|
140
|
+
default:
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// ─── createFactory ────────────────────────────────────────────
|
|
145
|
+
/**
|
|
146
|
+
* Create a type-safe test factory from TypeMetadata.
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* const userFactory = createFactory<User>(userMetadata, { seed: 42 });
|
|
150
|
+
* const user = userFactory.build();
|
|
151
|
+
* const admin = userFactory.build({ role: "admin" });
|
|
152
|
+
* const invalid = userFactory.buildInvalid("email");
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function createFactory(metadata, options = {}) {
|
|
156
|
+
const seed = options.seed ?? 12345;
|
|
157
|
+
function buildOne(rand, overrides) {
|
|
158
|
+
const result = {};
|
|
159
|
+
for (const [key, prop] of Object.entries(metadata.properties)) {
|
|
160
|
+
if (prop.optional && rand() > 0.7) {
|
|
161
|
+
continue; // skip some optional fields
|
|
162
|
+
}
|
|
163
|
+
result[key] = generateValue(prop.type, prop.jsdoc, rand);
|
|
164
|
+
}
|
|
165
|
+
if (overrides) {
|
|
166
|
+
Object.assign(result, overrides);
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
build(overrides) {
|
|
172
|
+
const rand = mulberry32(seed);
|
|
173
|
+
return buildOne(rand, overrides);
|
|
174
|
+
},
|
|
175
|
+
buildMany(count, overrides) {
|
|
176
|
+
const rand = mulberry32(seed);
|
|
177
|
+
const results = [];
|
|
178
|
+
for (let i = 0; i < count; i++) {
|
|
179
|
+
results.push(buildOne(rand, overrides));
|
|
180
|
+
}
|
|
181
|
+
return results;
|
|
182
|
+
},
|
|
183
|
+
buildInvalid(field) {
|
|
184
|
+
const rand = mulberry32(seed);
|
|
185
|
+
const instance = buildOne(rand);
|
|
186
|
+
const prop = metadata.properties[field];
|
|
187
|
+
if (prop) {
|
|
188
|
+
instance[field] = generateInvalidValue(prop.type, prop.jsdoc);
|
|
189
|
+
}
|
|
190
|
+
return instance;
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|