api-tests-coverage 1.0.17 → 1.0.19

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.
Files changed (128) hide show
  1. package/README.md +7 -3
  2. package/dist/dashboard/dist/assets/_basePickBy-CYB1KXah.js +1 -0
  3. package/dist/dashboard/dist/assets/_basePickBy-DUQHbXda.js +1 -0
  4. package/dist/dashboard/dist/assets/_baseUniq-Bwm426M6.js +1 -0
  5. package/dist/dashboard/dist/assets/_baseUniq-Ct8XEXnH.js +1 -0
  6. package/dist/dashboard/dist/assets/arc-B7p8x22e.js +1 -0
  7. package/dist/dashboard/dist/assets/arc-CjFGY63A.js +1 -0
  8. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-Boahc5dR.js +36 -0
  9. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-wVr1_uNB.js +36 -0
  10. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-BBXc88fn.js +122 -0
  11. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-CavSRNuP.js +122 -0
  12. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-BsgzPfQ3.js +10 -0
  13. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-w18S5AEN.js +10 -0
  14. package/dist/dashboard/dist/assets/channel-BgeGdqQG.js +1 -0
  15. package/dist/dashboard/dist/assets/channel-psxgcQ_j.js +1 -0
  16. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BF8loPLD.js +1 -0
  17. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-IN53WLTx.js +1 -0
  18. package/dist/dashboard/dist/assets/chunk-55IACEB6-C3HNF-UF.js +1 -0
  19. package/dist/dashboard/dist/assets/chunk-55IACEB6-kJkjQYxk.js +1 -0
  20. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-B7YfMggR.js +165 -0
  21. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-wQ6TCEMq.js +165 -0
  22. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-B7xHuqZu.js +220 -0
  23. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-DfslhtXS.js +220 -0
  24. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-BBMfQbw1.js +15 -0
  25. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-K3PC79JF.js +15 -0
  26. package/dist/dashboard/dist/assets/chunk-QN33PNHL-CmeZ1h1Z.js +1 -0
  27. package/dist/dashboard/dist/assets/chunk-QN33PNHL-DFgUs0T8.js +1 -0
  28. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-Cyg7Km90.js +1 -0
  29. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-DKgOcPif.js +1 -0
  30. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BoJFBewj.js +1 -0
  31. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-C8KNXDi7.js +1 -0
  32. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-AMwn99HP.js +1 -0
  33. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-CM6Qs-Qs.js +1 -0
  34. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-AMwn99HP.js +1 -0
  35. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-CM6Qs-Qs.js +1 -0
  36. package/dist/dashboard/dist/assets/clone-DEYRVSAn.js +1 -0
  37. package/dist/dashboard/dist/assets/clone-KEkbvJY9.js +1 -0
  38. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DMGRGhwB.js +1 -0
  39. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-YL9kFxCl.js +1 -0
  40. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-CJT7lofP.js +4 -0
  41. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-NZWnQN_Y.js +4 -0
  42. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-DGtyS7lD.js +24 -0
  43. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-DtE0cTIs.js +24 -0
  44. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BHyZd544.js +43 -0
  45. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CSCGZUfr.js +43 -0
  46. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DdqZVGN1.js +24 -0
  47. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-qvXlTDud.js +24 -0
  48. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-D0MbudeO.js +60 -0
  49. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-Dhb_VQMS.js +60 -0
  50. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-DRAD4OG7.js +162 -0
  51. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-gKUH-GJ2.js +162 -0
  52. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-DK_45K6s.js +267 -0
  53. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-Dm_lLo9y.js +267 -0
  54. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DM9AW1aP.js +65 -0
  55. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-DYrdM8tK.js +65 -0
  56. package/dist/dashboard/dist/assets/graph-CD7-npU0.js +1 -0
  57. package/dist/dashboard/dist/assets/graph-Clj85F2M.js +1 -0
  58. package/dist/dashboard/dist/assets/index-CqEIqNus.js +781 -0
  59. package/dist/dashboard/dist/assets/index-DbUdNJca.js +781 -0
  60. package/dist/dashboard/dist/assets/index-xecKLQ58.css +1 -0
  61. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BMp4C5wf.js +2 -0
  62. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-DyT5Fs8R.js +2 -0
  63. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BC0GSZ7W.js +139 -0
  64. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-nYZBlgTD.js +139 -0
  65. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-COTfX74l.js +89 -0
  66. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-D6aRd_q1.js +89 -0
  67. package/dist/dashboard/dist/assets/layout-6njVG9Ld.js +1 -0
  68. package/dist/dashboard/dist/assets/layout-BbJNDkTr.js +1 -0
  69. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B93XW27v.js +68 -0
  70. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-CkyYtMaD.js +68 -0
  71. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-9G1tEuaq.js +30 -0
  72. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-uWFQFMEe.js +30 -0
  73. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-i3-JTN3e.js +7 -0
  74. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-jDtdB4Ws.js +7 -0
  75. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-Dw260IiT.js +64 -0
  76. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-WIJ0qiJG.js +64 -0
  77. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-Cb4WB9UB.js +10 -0
  78. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-LR8T4Hv0.js +10 -0
  79. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-BqGJWVUS.js +145 -0
  80. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-DBqchhlr.js +145 -0
  81. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-0Wd-KmOv.js +1 -0
  82. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-Bl16d4W5.js +1 -0
  83. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-B05ygO34.js +1 -0
  84. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-BlwaoFEG.js +1 -0
  85. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-CAmQOjBu.js +61 -0
  86. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-D6JNee_P.js +61 -0
  87. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CCvvSJBX.js +162 -0
  88. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CRP-WvE-.js +162 -0
  89. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-5DoR2_q5.js +7 -0
  90. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-B72UwDAP.js +7 -0
  91. package/dist/dashboard/dist/index.html +2 -2
  92. package/dist/src/config/defaultConfig.d.ts.map +1 -1
  93. package/dist/src/config/defaultConfig.js +37 -0
  94. package/dist/src/config/types.d.ts +42 -0
  95. package/dist/src/config/types.d.ts.map +1 -1
  96. package/dist/src/config/validateConfig.d.ts.map +1 -1
  97. package/dist/src/config/validateConfig.js +3 -0
  98. package/dist/src/generation/ai-flow-exporter.d.ts +7 -0
  99. package/dist/src/generation/ai-flow-exporter.d.ts.map +1 -0
  100. package/dist/src/generation/ai-flow-exporter.js +260 -0
  101. package/dist/src/generation/context-builder.d.ts +16 -0
  102. package/dist/src/generation/context-builder.d.ts.map +1 -0
  103. package/dist/src/generation/context-builder.js +170 -0
  104. package/dist/src/generation/engine.d.ts +19 -0
  105. package/dist/src/generation/engine.d.ts.map +1 -0
  106. package/dist/src/generation/engine.js +204 -0
  107. package/dist/src/generation/file-router.d.ts +8 -0
  108. package/dist/src/generation/file-router.d.ts.map +1 -0
  109. package/dist/src/generation/file-router.js +98 -0
  110. package/dist/src/generation/gap-extractor.d.ts +7 -0
  111. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  112. package/dist/src/generation/gap-extractor.js +291 -0
  113. package/dist/src/generation/index.d.ts +9 -0
  114. package/dist/src/generation/index.d.ts.map +1 -0
  115. package/dist/src/generation/index.js +15 -0
  116. package/dist/src/generation/quality-scorer.d.ts +15 -0
  117. package/dist/src/generation/quality-scorer.d.ts.map +1 -0
  118. package/dist/src/generation/quality-scorer.js +273 -0
  119. package/dist/src/generation/template-renderer.d.ts +12 -0
  120. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  121. package/dist/src/generation/template-renderer.js +546 -0
  122. package/dist/src/generation/types.d.ts +269 -0
  123. package/dist/src/generation/types.d.ts.map +1 -0
  124. package/dist/src/generation/types.js +6 -0
  125. package/dist/src/index.js +113 -0
  126. package/dist/src/inference/routeInference.d.ts.map +1 -1
  127. package/dist/src/inference/routeInference.js +54 -8
  128. package/package.json +1 -1
@@ -0,0 +1,546 @@
1
+ "use strict";
2
+ /**
3
+ * Feature 28 — TemplateRenderer
4
+ * Renders test scaffolds from GenerationContext.
5
+ * Uses string-based templates (no external dependency required).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.renderTemplate = renderTemplate;
9
+ // ─── Header comment (RULE-GEN01) ──────────────────────────────────────────────
10
+ function autoGeneratedHeader(ctx) {
11
+ return [
12
+ `// AUTO-GENERATED by api-test-coverage-analyzer (Feature 28)`,
13
+ `// Gap: ${ctx.gap.id} | Risk: ${ctx.gap.riskScore} | Priority: ${ctx.gap.priority}`,
14
+ `// TODO: Review generated assertions and replace placeholder values`,
15
+ ``,
16
+ ].join('\n');
17
+ }
18
+ // ─── TypeScript / Jest unit + integration ─────────────────────────────────────
19
+ function renderTypescriptJestUnit(ctx) {
20
+ var _a, _b, _c, _d;
21
+ const { endpoint, fixtures, project } = ctx;
22
+ const method = endpoint.method.toLowerCase();
23
+ const descName = (_a = endpoint.operationId) !== null && _a !== void 0 ? _a : `${endpoint.method} ${endpoint.path}`;
24
+ const authRequired = endpoint.auth.required;
25
+ const scheme = (_b = endpoint.auth.scheme) !== null && _b !== void 0 ? _b : 'Bearer';
26
+ const lines = [
27
+ autoGeneratedHeader(ctx),
28
+ `import request from 'supertest';`,
29
+ `import { app } from '${project.importPrefix}/app';`,
30
+ ``,
31
+ `const BASE_URL = process.env.TEST_BASE_URL ?? '${project.baseUrl}';`,
32
+ ``,
33
+ `// TODO: Replace with a valid JWT token from your test auth setup`,
34
+ `const VALID_AUTH_TOKEN = '${scheme} <your-test-token>';`,
35
+ ``,
36
+ `describe('${descName}', () => {`,
37
+ ];
38
+ // Happy path
39
+ lines.push(` describe('happy path', () => {`);
40
+ lines.push(` it('should succeed and return the expected response', async () => {`);
41
+ if (Object.keys(fixtures.validPayload).length > 0) {
42
+ lines.push(` const payload = ${JSON.stringify(fixtures.validPayload, null, 8).replace(/\n/g, '\n ')};`);
43
+ lines.push(` // TODO: Replace with realistic test data`);
44
+ lines.push(``);
45
+ }
46
+ lines.push(` const response = await request(app)`);
47
+ lines.push(` .${method}('${endpoint.pathNormalized}')`);
48
+ if (authRequired) {
49
+ lines.push(` .set('Authorization', VALID_AUTH_TOKEN)`);
50
+ }
51
+ if (Object.keys(fixtures.validPayload).length > 0) {
52
+ lines.push(` .send(payload);`);
53
+ }
54
+ else {
55
+ lines.push(` ;`);
56
+ }
57
+ lines.push(``);
58
+ // Find success status code
59
+ const successStatus = (_d = (_c = ctx.responses.find(r => r.statusCode >= 200 && r.statusCode < 300)) === null || _c === void 0 ? void 0 : _c.statusCode) !== null && _d !== void 0 ? _d : 200;
60
+ lines.push(` expect(response.status).toBe(${successStatus});`);
61
+ lines.push(` expect(response.body).toBeDefined();`);
62
+ lines.push(` });`);
63
+ lines.push(` });`);
64
+ lines.push(``);
65
+ // Auth tests (RULE-GEN03)
66
+ if (authRequired) {
67
+ lines.push(` describe('authentication', () => {`);
68
+ lines.push(` it('should return 401 when no auth token is provided', async () => {`);
69
+ lines.push(` const response = await request(app)`);
70
+ lines.push(` .${method}('${endpoint.pathNormalized}');`);
71
+ lines.push(``);
72
+ lines.push(` expect(response.status).toBe(401);`);
73
+ lines.push(` });`);
74
+ lines.push(``);
75
+ lines.push(` it('should return 401 when an invalid auth token is provided', async () => {`);
76
+ lines.push(` const response = await request(app)`);
77
+ lines.push(` .${method}('${endpoint.pathNormalized}')`);
78
+ lines.push(` .set('Authorization', '${scheme} invalid-token-value');`);
79
+ lines.push(``);
80
+ lines.push(` expect(response.status).toBe(401);`);
81
+ lines.push(` });`);
82
+ lines.push(` });`);
83
+ lines.push(``);
84
+ }
85
+ // Validation / error tests
86
+ const requiredParams = ctx.parameters.filter(p => p.required && p.in === 'body');
87
+ if (requiredParams.length > 0) {
88
+ lines.push(` describe('validation', () => {`);
89
+ for (const param of requiredParams.slice(0, 3)) {
90
+ lines.push(` it('should return 422 when ${param.name} is missing', async () => {`);
91
+ const partialPayload = {};
92
+ for (const other of requiredParams) {
93
+ if (other.name !== param.name) {
94
+ partialPayload[other.name] = 'test-value';
95
+ }
96
+ }
97
+ lines.push(` const response = await request(app)`);
98
+ lines.push(` .${method}('${endpoint.pathNormalized}')`);
99
+ if (authRequired) {
100
+ lines.push(` .set('Authorization', VALID_AUTH_TOKEN)`);
101
+ }
102
+ lines.push(` .send(${JSON.stringify(partialPayload)});`);
103
+ lines.push(``);
104
+ lines.push(` expect(response.status).toBe(422);`);
105
+ lines.push(` expect(response.body).toHaveProperty('errors');`);
106
+ lines.push(` });`);
107
+ lines.push(``);
108
+ }
109
+ lines.push(` });`);
110
+ }
111
+ lines.push(`});`);
112
+ lines.push(``);
113
+ return lines.join('\n');
114
+ }
115
+ // ─── TypeScript / Jest security ───────────────────────────────────────────────
116
+ function renderTypescriptJestSecurity(ctx) {
117
+ var _a;
118
+ const { endpoint, project } = ctx;
119
+ const method = endpoint.method.toLowerCase();
120
+ const scheme = (_a = endpoint.auth.scheme) !== null && _a !== void 0 ? _a : 'Bearer';
121
+ const lines = [
122
+ autoGeneratedHeader(ctx),
123
+ `import request from 'supertest';`,
124
+ `import { app } from '${project.importPrefix}/app';`,
125
+ ``,
126
+ `// TODO: Replace with a valid JWT token from your test auth setup`,
127
+ `const VALID_AUTH_TOKEN = '${scheme} <your-test-token>';`,
128
+ ``,
129
+ `describe('Security: ${endpoint.method} ${endpoint.path} — Auth Controls', () => {`,
130
+ ` it('[SEC-AUTH-01] should reject requests with no Authorization header', async () => {`,
131
+ ` const response = await request(app)`,
132
+ ` .${method}('${endpoint.pathNormalized}');`,
133
+ ``,
134
+ ` expect(response.status).toBe(401);`,
135
+ ` });`,
136
+ ``,
137
+ ` it('[SEC-AUTH-02] should reject requests with malformed token', async () => {`,
138
+ ` const response = await request(app)`,
139
+ ` .${method}('${endpoint.pathNormalized}')`,
140
+ ` .set('Authorization', 'malformed-token-without-scheme');`,
141
+ ``,
142
+ ` expect(response.status).toBe(401);`,
143
+ ` });`,
144
+ ``,
145
+ ` it('[SEC-AUTH-03] should reject requests with expired token', async () => {`,
146
+ ` // TODO: Generate or use a known-expired JWT for your test environment`,
147
+ ` const expiredToken = '${scheme} eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDAwMDAwMDAsImV4cCI6MTYwMDAwMDAwMX0.TODO';`,
148
+ ``,
149
+ ` const response = await request(app)`,
150
+ ` .${method}('${endpoint.pathNormalized}')`,
151
+ ` .set('Authorization', expiredToken);`,
152
+ ``,
153
+ ` expect(response.status).toBe(401);`,
154
+ ` });`,
155
+ ``,
156
+ ` it('[SEC-AUTH-04] should reject requests with a token for a different user role', async () => {`,
157
+ ` // TODO: Get a token for a user WITHOUT the required permissions`,
158
+ ` const lowPrivToken = '${scheme} <low-privilege-token>';`,
159
+ ``,
160
+ ` const response = await request(app)`,
161
+ ` .${method}('${endpoint.pathNormalized}')`,
162
+ ` .set('Authorization', lowPrivToken);`,
163
+ ``,
164
+ ` // Expect either 401 (unauthenticated) or 403 (unauthorized)`,
165
+ ` expect([401, 403]).toContain(response.status);`,
166
+ ` });`,
167
+ `});`,
168
+ ``,
169
+ `describe('Security: ${endpoint.method} ${endpoint.path} — Injection Prevention', () => {`,
170
+ ` it('[SEC-INJ-01] should not execute SQL injection in request body', async () => {`,
171
+ ` const response = await request(app)`,
172
+ ` .${method}('${endpoint.pathNormalized}')`,
173
+ ` .set('Authorization', VALID_AUTH_TOKEN)`,
174
+ ` .send({ title: "'; DROP TABLE articles; --", body: 'Body', description: 'Desc' });`,
175
+ ``,
176
+ ` // Should either succeed (sanitized) or return 422 (rejected) — never 500`,
177
+ ` expect(response.status).not.toBe(500);`,
178
+ ` expect(response.status).not.toBe(503);`,
179
+ ` });`,
180
+ ``,
181
+ ` it('[SEC-INJ-02] should not execute XSS in request fields', async () => {`,
182
+ ` const response = await request(app)`,
183
+ ` .${method}('${endpoint.pathNormalized}')`,
184
+ ` .set('Authorization', VALID_AUTH_TOKEN)`,
185
+ ` .send({ title: '<script>alert("xss")</script>', body: 'Body', description: 'Desc' });`,
186
+ ``,
187
+ ` expect(response.status).not.toBe(500);`,
188
+ ``,
189
+ ` if (response.status >= 200 && response.status < 300) {`,
190
+ ` // If the server accepted it, it must have been sanitized`,
191
+ ` const body = JSON.stringify(response.body);`,
192
+ ` expect(body).not.toContain('<script>');`,
193
+ ` }`,
194
+ ` });`,
195
+ ``,
196
+ ` it('[SEC-INJ-03] should handle oversized payload gracefully', async () => {`,
197
+ ` const oversizedBody = 'x'.repeat(10_000_000); // 10 MB`,
198
+ ``,
199
+ ` const response = await request(app)`,
200
+ ` .${method}('${endpoint.pathNormalized}')`,
201
+ ` .set('Authorization', VALID_AUTH_TOKEN)`,
202
+ ` .send({ title: 'Test', body: oversizedBody, description: 'Desc' });`,
203
+ ``,
204
+ ` // Must not crash — 413 or 422 are acceptable`,
205
+ ` expect([413, 422, 400]).toContain(response.status);`,
206
+ ` });`,
207
+ `});`,
208
+ ``,
209
+ ];
210
+ return lines.join('\n');
211
+ }
212
+ // ─── TypeScript / Jest integration flow ───────────────────────────────────────
213
+ function renderTypescriptJestIntegration(ctx) {
214
+ const { endpoint, project, flow } = ctx;
215
+ if (!flow) {
216
+ return renderTypescriptJestUnit(ctx);
217
+ }
218
+ const lines = [
219
+ autoGeneratedHeader(ctx),
220
+ `import request from 'supertest';`,
221
+ `import { app } from '${project.importPrefix}/app';`,
222
+ ``,
223
+ `describe('Integration Flow: ${flow.name}', () => {`,
224
+ ` let authToken: string;`,
225
+ ``,
226
+ ` // RULE-INT04: beforeAll (not beforeEach) for auth setup`,
227
+ ` beforeAll(async () => {`,
228
+ ` // TODO: Replace with your test user credentials`,
229
+ ` const loginResponse = await request(app)`,
230
+ ` .post('/api/users/login')`,
231
+ ` .send({ user: { email: 'test@example.com', password: 'testpassword' } });`,
232
+ ``,
233
+ ` expect(loginResponse.status).toBe(200);`,
234
+ ` authToken = \`Token \${loginResponse.body.user.token}\`;`,
235
+ ` });`,
236
+ ``,
237
+ ];
238
+ for (let i = 0; i < flow.steps.length; i++) {
239
+ const step = flow.steps[i];
240
+ const isMissing = i === flow.missingStepIndex;
241
+ const stepMethod = step.method.toLowerCase();
242
+ const stepPath = step.path;
243
+ if (isMissing) {
244
+ lines.push(` // ⚠️ MISSING STEP — This step was detected as uncovered by the analyzer`);
245
+ }
246
+ lines.push(` it('Step ${i + 1}: ${step.description}${isMissing ? ' (MISSING COVERAGE)' : ''}', async () => {`);
247
+ if (isMissing) {
248
+ lines.push(` // TODO: This step was identified as missing. Implement the test.`);
249
+ }
250
+ lines.push(` const response = await request(app)`);
251
+ lines.push(` .${stepMethod}('${stepPath}')`);
252
+ lines.push(` .set('Authorization', authToken);`);
253
+ lines.push(``);
254
+ lines.push(` expect(response.status).toBe(200);`);
255
+ lines.push(` });`);
256
+ lines.push(``);
257
+ }
258
+ lines.push(`});`);
259
+ lines.push(``);
260
+ return lines.join('\n');
261
+ }
262
+ // ─── TypeScript / Jest error scenario ────────────────────────────────────────
263
+ function renderTypescriptJestErrorScenario(ctx) {
264
+ var _a, _b;
265
+ const { endpoint, project } = ctx;
266
+ const method = endpoint.method.toLowerCase();
267
+ const scheme = (_a = endpoint.auth.scheme) !== null && _a !== void 0 ? _a : 'Bearer';
268
+ const lines = [
269
+ autoGeneratedHeader(ctx),
270
+ `import request from 'supertest';`,
271
+ `import { app } from '${project.importPrefix}/app';`,
272
+ ``,
273
+ `// TODO: Replace with a valid JWT token from your test auth setup`,
274
+ `const VALID_AUTH_TOKEN = '${scheme} <your-test-token>';`,
275
+ ``,
276
+ `describe('Error Scenarios: ${endpoint.method} ${endpoint.path}', () => {`,
277
+ ];
278
+ for (const resp of ctx.responses.filter(r => r.statusCode >= 400)) {
279
+ lines.push(` it('should return ${resp.statusCode} when ${(_b = resp.description) !== null && _b !== void 0 ? _b : `error ${resp.statusCode}`}', async () => {`);
280
+ lines.push(` const response = await request(app)`);
281
+ lines.push(` .${method}('${endpoint.pathNormalized}')`);
282
+ if (endpoint.auth.required) {
283
+ lines.push(` .set('Authorization', VALID_AUTH_TOKEN)`);
284
+ }
285
+ lines.push(` .send({});`);
286
+ lines.push(``);
287
+ lines.push(` expect(response.status).toBe(${resp.statusCode});`);
288
+ lines.push(` });`);
289
+ lines.push(``);
290
+ }
291
+ lines.push(`});`);
292
+ lines.push(``);
293
+ return lines.join('\n');
294
+ }
295
+ // ─── TypeScript / Jest business rule ─────────────────────────────────────────
296
+ function renderTypescriptJestBusinessRule(ctx) {
297
+ var _a;
298
+ const { endpoint, project, businessRule } = ctx;
299
+ const method = endpoint.method.toLowerCase();
300
+ const scheme = (_a = endpoint.auth.scheme) !== null && _a !== void 0 ? _a : 'Bearer';
301
+ const rule = businessRule !== null && businessRule !== void 0 ? businessRule : { id: 'rule-1', description: 'Business rule', condition: 'When condition is met', acceptanceCriteria: ['Expected behavior is verified'] };
302
+ const lines = [
303
+ autoGeneratedHeader(ctx),
304
+ `import request from 'supertest';`,
305
+ `import { app } from '${project.importPrefix}/app';`,
306
+ ``,
307
+ `// TODO: Replace with a valid JWT token from your test auth setup`,
308
+ `const VALID_AUTH_TOKEN = '${scheme} <your-test-token>';`,
309
+ ``,
310
+ `describe('Business Rule: ${rule.id} — ${rule.description}', () => {`,
311
+ ` // Rule condition: ${rule.condition}`,
312
+ ];
313
+ for (const criterion of rule.acceptanceCriteria) {
314
+ lines.push(` it('${criterion}', async () => {`);
315
+ lines.push(` // TODO: Implement test for acceptance criterion: ${criterion}`);
316
+ lines.push(` const response = await request(app)`);
317
+ lines.push(` .${method}('${endpoint.pathNormalized}')`);
318
+ if (endpoint.auth.required) {
319
+ lines.push(` .set('Authorization', VALID_AUTH_TOKEN)`);
320
+ }
321
+ lines.push(` .send({});`);
322
+ lines.push(``);
323
+ lines.push(` expect(response.status).toBeDefined();`);
324
+ lines.push(` // TODO: Add specific assertions for this acceptance criterion`);
325
+ lines.push(` });`);
326
+ lines.push(``);
327
+ }
328
+ lines.push(`});`);
329
+ lines.push(``);
330
+ return lines.join('\n');
331
+ }
332
+ // ─── Python / pytest ──────────────────────────────────────────────────────────
333
+ function renderPythonPytestUnit(ctx) {
334
+ var _a, _b;
335
+ const { endpoint } = ctx;
336
+ const method = endpoint.method.toLowerCase();
337
+ const authRequired = endpoint.auth.required;
338
+ const lines = [
339
+ `# AUTO-GENERATED by api-test-coverage-analyzer (Feature 28)`,
340
+ `# Gap: ${ctx.gap.id} | Risk: ${ctx.gap.riskScore} | Priority: ${ctx.gap.priority}`,
341
+ `# TODO: Review generated assertions and replace placeholder values`,
342
+ ``,
343
+ `import pytest`,
344
+ ``,
345
+ `# TODO: Import your app factory and configure for testing`,
346
+ `# from app import create_app`,
347
+ ``,
348
+ `VALID_AUTH_TOKEN = 'Token <your-test-token>' # TODO: Replace`,
349
+ ``,
350
+ ``,
351
+ `@pytest.fixture`,
352
+ `def client(app):`,
353
+ ` """TODO: Configure this fixture for your test setup."""`,
354
+ ` return app.test_client()`,
355
+ ``,
356
+ ``,
357
+ `class Test${toPascalCase(endpoint.method + '_' + endpoint.path.replace(/[^a-zA-Z0-9]/g, '_'))}:`,
358
+ ` """Tests for ${endpoint.method} ${endpoint.path}"""`,
359
+ ``,
360
+ ];
361
+ lines.push(` def test_happy_path(self, client):`);
362
+ lines.push(` """Should succeed with valid input."""`);
363
+ if (authRequired) {
364
+ lines.push(` response = client.${method}(`);
365
+ lines.push(` '${endpoint.path}',`);
366
+ lines.push(` json={},`);
367
+ lines.push(` headers={'Authorization': VALID_AUTH_TOKEN},`);
368
+ lines.push(` )`);
369
+ }
370
+ else {
371
+ lines.push(` response = client.${method}('${endpoint.path}')`);
372
+ }
373
+ lines.push(``);
374
+ const successStatus = (_b = (_a = ctx.responses.find(r => r.statusCode >= 200 && r.statusCode < 300)) === null || _a === void 0 ? void 0 : _a.statusCode) !== null && _b !== void 0 ? _b : 200;
375
+ lines.push(` assert response.status_code == ${successStatus}`);
376
+ lines.push(``);
377
+ if (authRequired) {
378
+ lines.push(` def test_requires_auth(self, client):`);
379
+ lines.push(` """Should return 401 when no auth token is provided."""`);
380
+ lines.push(` response = client.${method}('${endpoint.path}', json={})`);
381
+ lines.push(``);
382
+ lines.push(` assert response.status_code == 401`);
383
+ lines.push(``);
384
+ lines.push(` def test_invalid_token(self, client):`);
385
+ lines.push(` """Should return 401 when an invalid token is provided."""`);
386
+ lines.push(` response = client.${method}(`);
387
+ lines.push(` '${endpoint.path}',`);
388
+ lines.push(` json={},`);
389
+ lines.push(` headers={'Authorization': 'Token invalid-token'},`);
390
+ lines.push(` )`);
391
+ lines.push(``);
392
+ lines.push(` assert response.status_code == 401`);
393
+ lines.push(``);
394
+ }
395
+ return lines.join('\n');
396
+ }
397
+ function toPascalCase(s) {
398
+ return s
399
+ .replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
400
+ .replace(/^(.)/, (c) => c.toUpperCase());
401
+ }
402
+ // ─── Java / JUnit5 ────────────────────────────────────────────────────────────
403
+ function renderJavaJunit5Unit(ctx) {
404
+ var _a, _b;
405
+ const { endpoint } = ctx;
406
+ const method = endpoint.method;
407
+ const authRequired = endpoint.auth.required;
408
+ const successStatus = (_b = (_a = ctx.responses.find(r => r.statusCode >= 200 && r.statusCode < 300)) === null || _a === void 0 ? void 0 : _a.statusCode) !== null && _b !== void 0 ? _b : 200;
409
+ const statusMatcher = successStatus === 201 ? 'isCreated()' : successStatus === 204 ? 'isNoContent()' : 'isOk()';
410
+ const lines = [
411
+ `// AUTO-GENERATED by api-test-coverage-analyzer (Feature 28)`,
412
+ `// Gap: ${ctx.gap.id} | Risk: ${ctx.gap.riskScore} | Priority: ${ctx.gap.priority}`,
413
+ `// TODO: Review generated assertions and replace placeholder values`,
414
+ ``,
415
+ `package generated;`,
416
+ ``,
417
+ `import org.junit.jupiter.api.Test;`,
418
+ `import org.springframework.beans.factory.annotation.Autowired;`,
419
+ `import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;`,
420
+ `import org.springframework.boot.test.context.SpringBootTest;`,
421
+ `import org.springframework.http.MediaType;`,
422
+ `import org.springframework.test.web.servlet.MockMvc;`,
423
+ ``,
424
+ `import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.${method.toLowerCase()};`,
425
+ `import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;`,
426
+ ``,
427
+ `@SpringBootTest`,
428
+ `@AutoConfigureMockMvc`,
429
+ `public class ${toPascalCase(method + '_' + endpoint.path.replace(/[^a-zA-Z0-9]/g, '_') + '_Test')} {`,
430
+ ``,
431
+ ` @Autowired`,
432
+ ` private MockMvc mockMvc;`,
433
+ ``,
434
+ ` // TODO: Replace with a valid JWT from your test auth setup`,
435
+ ` private static final String VALID_TOKEN = "Token <your-test-token>";`,
436
+ ``,
437
+ ` @Test`,
438
+ ` void happyPath_returns${successStatus}() throws Exception {`,
439
+ ` mockMvc.perform(${method.toLowerCase()}("${endpoint.path}")`,
440
+ ...(authRequired ? [` .header("Authorization", VALID_TOKEN)`] : []),
441
+ ` .contentType(MediaType.APPLICATION_JSON)`,
442
+ ` .content("{}")) // TODO: Replace with valid payload`,
443
+ ` .andExpect(status().${statusMatcher});`,
444
+ ` }`,
445
+ ``,
446
+ ];
447
+ if (authRequired) {
448
+ lines.push(` @Test`);
449
+ lines.push(` void noAuth_returns401() throws Exception {`);
450
+ lines.push(` mockMvc.perform(${method.toLowerCase()}("${endpoint.path}")`);
451
+ lines.push(` .contentType(MediaType.APPLICATION_JSON)`);
452
+ lines.push(` .content("{}"))`);
453
+ lines.push(` .andExpect(status().isUnauthorized());`);
454
+ lines.push(` }`);
455
+ lines.push(``);
456
+ }
457
+ lines.push(`}`);
458
+ lines.push(``);
459
+ return lines.join('\n');
460
+ }
461
+ // ─── Cypress E2E ──────────────────────────────────────────────────────────────
462
+ function renderCypressEndpoint(ctx) {
463
+ var _a, _b;
464
+ const { endpoint } = ctx;
465
+ const method = endpoint.method;
466
+ const authRequired = endpoint.auth.required;
467
+ const successStatus = (_b = (_a = ctx.responses.find(r => r.statusCode >= 200 && r.statusCode < 300)) === null || _a === void 0 ? void 0 : _a.statusCode) !== null && _b !== void 0 ? _b : 200;
468
+ const lines = [
469
+ `// AUTO-GENERATED by api-test-coverage-analyzer (Feature 28)`,
470
+ `// Gap: ${ctx.gap.id} | Risk: ${ctx.gap.riskScore} | Priority: ${ctx.gap.priority}`,
471
+ `// NOTE: add generated/ to your specPattern in cypress.config.js if not already included`,
472
+ ``,
473
+ `describe('${method} ${endpoint.path} — E2E', () => {`,
474
+ ];
475
+ if (authRequired) {
476
+ lines.push(` let authToken;`);
477
+ lines.push(``);
478
+ lines.push(` before(() => {`);
479
+ lines.push(` // TODO: Replace with your Cypress login command or fixture`);
480
+ lines.push(` cy.request('POST', '/api/users/login', {`);
481
+ lines.push(` user: { email: Cypress.env('TEST_USER_EMAIL'), password: Cypress.env('TEST_USER_PASSWORD') },`);
482
+ lines.push(` }).then((response) => {`);
483
+ lines.push(` expect(response.status).to.eq(200);`);
484
+ lines.push(` authToken = \`Token \${response.body.user.token}\`;`);
485
+ lines.push(` });`);
486
+ lines.push(` });`);
487
+ lines.push(``);
488
+ }
489
+ lines.push(` it('should succeed with valid request', () => {`);
490
+ lines.push(` cy.request({`);
491
+ lines.push(` method: '${method}',`);
492
+ lines.push(` url: '${endpoint.path}',`);
493
+ if (authRequired) {
494
+ lines.push(` headers: { Authorization: authToken },`);
495
+ }
496
+ lines.push(` body: {}, // TODO: Replace with valid payload`);
497
+ lines.push(` }).then((response) => {`);
498
+ lines.push(` expect(response.status).to.eq(${successStatus});`);
499
+ lines.push(` expect(response.body).to.be.an('object');`);
500
+ lines.push(` });`);
501
+ lines.push(` });`);
502
+ lines.push(``);
503
+ if (authRequired) {
504
+ lines.push(` it('should return 401 without auth token', () => {`);
505
+ lines.push(` cy.request({`);
506
+ lines.push(` method: '${method}',`);
507
+ lines.push(` url: '${endpoint.path}',`);
508
+ lines.push(` body: {},`);
509
+ lines.push(` failOnStatusCode: false, // RULE-CY03`);
510
+ lines.push(` }).then((response) => {`);
511
+ lines.push(` expect(response.status).to.eq(401);`);
512
+ lines.push(` });`);
513
+ lines.push(` });`);
514
+ }
515
+ lines.push(`});`);
516
+ lines.push(``);
517
+ return lines.join('\n');
518
+ }
519
+ function renderTemplate(ctx, opts) {
520
+ const { language, testType } = opts;
521
+ if (language === 'python') {
522
+ return renderPythonPytestUnit(ctx);
523
+ }
524
+ if (language === 'java' || language === 'kotlin') {
525
+ return renderJavaJunit5Unit(ctx);
526
+ }
527
+ // Default: TypeScript
528
+ switch (testType) {
529
+ case 'security':
530
+ return renderTypescriptJestSecurity(ctx);
531
+ case 'integration':
532
+ return renderTypescriptJestIntegration(ctx);
533
+ case 'cypress':
534
+ return renderCypressEndpoint(ctx);
535
+ default:
536
+ // Differentiate by gap type
537
+ switch (ctx.gap.type) {
538
+ case 'error':
539
+ return renderTypescriptJestErrorScenario(ctx);
540
+ case 'business':
541
+ return renderTypescriptJestBusinessRule(ctx);
542
+ default:
543
+ return renderTypescriptJestUnit(ctx);
544
+ }
545
+ }
546
+ }