api-tests-coverage 1.0.21 → 1.0.22

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 (125) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-BKGHUeDJ.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-CtA-DQF7.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-CbXP3Mc9.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-n7QxasMM.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-MXnGwKRn.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-B3yZ5P9k.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-C7QwX7U8.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-Dw3El5KF.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-T8Jf00IL.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-DAJalKYJ.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-p7o_KuDF.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-B0wUAfQR.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BTFwTEw8.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CNXHnjkC.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-BIVywBYp.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-Dkb-G0UK.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-Dkb-G0UK.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-BSdXhKmH.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-DuALhY5a.js +1 -0
  20. package/dist/dashboard/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
  21. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-w6endfy9.js +4 -0
  22. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-BDnUwPQC.js +24 -0
  23. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-Cbb7ctAo.js +43 -0
  24. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-76ascveh.js +24 -0
  25. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BQQnVF0J.js +60 -0
  26. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-dKukiSxD.js +162 -0
  27. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-BqnazZuZ.js +267 -0
  28. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BUFcnXCj.js +65 -0
  29. package/dist/dashboard/dist/assets/graph-CGQIvL3r.js +1 -0
  30. package/dist/dashboard/dist/assets/index-CadOHtae.css +1 -0
  31. package/dist/dashboard/dist/assets/index-DwqfA4mc.js +777 -0
  32. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-BdylFPLI.js +2 -0
  33. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-BzJTBkhp.js +139 -0
  34. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-StKomgio.js +89 -0
  35. package/dist/dashboard/dist/assets/katex-O9d3_IXG.js +261 -0
  36. package/dist/dashboard/dist/assets/layout-BnsX73QS.js +1 -0
  37. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-HqFRhVFX.js +68 -0
  38. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-2I2tFH0C.js +30 -0
  39. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-PlELkdBW.js +7 -0
  40. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-_9eLQNcZ.js +64 -0
  41. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DHl1Ss7O.js +10 -0
  42. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-B9p0m3Uf.js +145 -0
  43. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC--rpDODjT.js +1 -0
  44. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Ca9uGk46.js +1 -0
  45. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-BCHaGBHB.js +61 -0
  46. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-CgiqDY8M.js +162 -0
  47. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-BMvBBbeV.js +7 -0
  48. package/dist/dashboard/dist/index.html +14 -0
  49. package/dist/dashboard/dist/reports/business-coverage.json +201 -0
  50. package/dist/dashboard/dist/reports/coverage-intelligence.json +728 -0
  51. package/dist/dashboard/dist/reports/coverage-summary.json +995 -0
  52. package/dist/dashboard/dist/reports/endpoint-coverage.json +336 -0
  53. package/dist/dashboard/dist/reports/error-coverage.json +367 -0
  54. package/dist/dashboard/dist/reports/missing-tests-recommendations.json +285 -0
  55. package/dist/dashboard/dist/reports/risk-prioritization.json +312 -0
  56. package/dist/dashboard/dist/reports/security-coverage.json +299 -0
  57. package/dist/dashboard/dist/vite.svg +1 -0
  58. package/dist/src/generation/context-builder.d.ts +6 -0
  59. package/dist/src/generation/context-builder.d.ts.map +1 -0
  60. package/dist/src/generation/context-builder.js +202 -0
  61. package/dist/src/generation/engine.d.ts +40 -0
  62. package/dist/src/generation/engine.d.ts.map +1 -0
  63. package/dist/src/generation/engine.js +378 -0
  64. package/dist/src/generation/file-router.d.ts +7 -0
  65. package/dist/src/generation/file-router.d.ts.map +1 -0
  66. package/dist/src/generation/file-router.js +79 -0
  67. package/dist/src/generation/gap-extractor.d.ts +12 -0
  68. package/dist/src/generation/gap-extractor.d.ts.map +1 -0
  69. package/dist/src/generation/gap-extractor.js +281 -0
  70. package/dist/src/generation/template-renderer.d.ts +10 -0
  71. package/dist/src/generation/template-renderer.d.ts.map +1 -0
  72. package/dist/src/generation/template-renderer.js +526 -0
  73. package/dist/src/generation/types.d.ts +73 -0
  74. package/dist/src/generation/types.d.ts.map +1 -0
  75. package/dist/src/generation/types.js +2 -0
  76. package/dist/src/index.js +154 -0
  77. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts +21 -0
  78. package/dist/src/pipeline/detectors/expressMiddlewareDetector.d.ts.map +1 -0
  79. package/dist/src/pipeline/detectors/expressMiddlewareDetector.js +201 -0
  80. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts +23 -0
  81. package/dist/src/pipeline/detectors/flaskBlueprintDetector.d.ts.map +1 -0
  82. package/dist/src/pipeline/detectors/flaskBlueprintDetector.js +263 -0
  83. package/dist/src/pipeline/detectors/springDddDetector.d.ts +23 -0
  84. package/dist/src/pipeline/detectors/springDddDetector.d.ts.map +1 -0
  85. package/dist/src/pipeline/detectors/springDddDetector.js +237 -0
  86. package/dist/src/pipeline/detectors/types.d.ts +97 -0
  87. package/dist/src/pipeline/detectors/types.d.ts.map +1 -0
  88. package/dist/src/pipeline/detectors/types.js +15 -0
  89. package/dist/src/reporting.d.ts.map +1 -1
  90. package/dist/src/reporting.js +2 -1
  91. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts +5 -0
  92. package/dist/src/streaming/detectors/eventBridgeDetector.d.ts.map +1 -0
  93. package/dist/src/streaming/detectors/eventBridgeDetector.js +82 -0
  94. package/dist/src/streaming/detectors/kafkaDetector.d.ts +5 -0
  95. package/dist/src/streaming/detectors/kafkaDetector.d.ts.map +1 -0
  96. package/dist/src/streaming/detectors/kafkaDetector.js +97 -0
  97. package/dist/src/streaming/detectors/natsDetector.d.ts +5 -0
  98. package/dist/src/streaming/detectors/natsDetector.d.ts.map +1 -0
  99. package/dist/src/streaming/detectors/natsDetector.js +96 -0
  100. package/dist/src/streaming/detectors/pubsubDetector.d.ts +5 -0
  101. package/dist/src/streaming/detectors/pubsubDetector.d.ts.map +1 -0
  102. package/dist/src/streaming/detectors/pubsubDetector.js +82 -0
  103. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts +5 -0
  104. package/dist/src/streaming/detectors/rabbitmqDetector.d.ts.map +1 -0
  105. package/dist/src/streaming/detectors/rabbitmqDetector.js +103 -0
  106. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts +5 -0
  107. package/dist/src/streaming/detectors/redisPubsubDetector.d.ts.map +1 -0
  108. package/dist/src/streaming/detectors/redisPubsubDetector.js +81 -0
  109. package/dist/src/streaming/detectors/snsDetector.d.ts +5 -0
  110. package/dist/src/streaming/detectors/snsDetector.d.ts.map +1 -0
  111. package/dist/src/streaming/detectors/snsDetector.js +81 -0
  112. package/dist/src/streaming/detectors/sqsDetector.d.ts +5 -0
  113. package/dist/src/streaming/detectors/sqsDetector.d.ts.map +1 -0
  114. package/dist/src/streaming/detectors/sqsDetector.js +81 -0
  115. package/dist/src/streaming/eventCoverage.d.ts +46 -0
  116. package/dist/src/streaming/eventCoverage.d.ts.map +1 -0
  117. package/dist/src/streaming/eventCoverage.js +270 -0
  118. package/dist/src/streaming/types.d.ts +34 -0
  119. package/dist/src/streaming/types.d.ts.map +1 -0
  120. package/dist/src/streaming/types.js +2 -0
  121. package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
  122. package/dist/src/summary/markdownRenderer.js +1 -0
  123. package/dist/src/summary/summaryTypes.d.ts.map +1 -1
  124. package/dist/src/summary/summaryTypes.js +1 -0
  125. package/package.json +1 -1
@@ -0,0 +1,526 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OWASP_INJECTION_STRINGS = void 0;
4
+ exports.renderFileHeader = renderFileHeader;
5
+ exports.renderTemplate = renderTemplate;
6
+ exports.getTemplateName = getTemplateName;
7
+ /** Common OWASP injection strings for security tests */
8
+ exports.OWASP_INJECTION_STRINGS = [
9
+ "' OR '1'='1",
10
+ "'; DROP TABLE users; --",
11
+ '<script>alert("xss")</script>',
12
+ '../../../etc/passwd',
13
+ '${7*7}',
14
+ '{{7*7}}',
15
+ '; ls -la',
16
+ '\\x00',
17
+ '%00',
18
+ '<img src=x onerror=alert(1)>',
19
+ ];
20
+ /** Standard file header injected into every generated test file */
21
+ function renderFileHeader(gapId, gapType) {
22
+ return [
23
+ '// AUTO-GENERATED by api-test-coverage-analyzer',
24
+ `// Gap ID: ${gapId} | Type: ${gapType}`,
25
+ '// DO NOT EDIT — regenerate with: node dist/src/index.js generate-tests',
26
+ '// TODO: Review and adjust before committing',
27
+ '',
28
+ ].join('\n');
29
+ }
30
+ /** Simple string interpolation — replaces {{key}} tokens with values */
31
+ function interpolate(template, vars) {
32
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => { var _a; return (_a = vars[key]) !== null && _a !== void 0 ? _a : `{{${key}}}`; });
33
+ }
34
+ // ─── TypeScript / Jest templates ─────────────────────────────────────────────
35
+ function renderEndpointMissing(gap, ctx) {
36
+ var _a, _b, _c, _d;
37
+ const method = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.method) !== null && _b !== void 0 ? _b : 'GET';
38
+ const endpointPath = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : '/unknown';
39
+ const methodLower = method.toLowerCase();
40
+ const header = renderFileHeader(gap.id, gap.type);
41
+ const body = `
42
+ import request from 'supertest'; // TODO: adjust import path
43
+ import app from '../../../src/app'; // TODO: adjust import path
44
+
45
+ describe('${method} ${endpointPath}', () => {
46
+ it('should return 200 with valid response', async () => {
47
+ const response = await request(app)
48
+ .${methodLower}('${endpointPath}')
49
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'); // TODO: add valid token
50
+
51
+ expect(response.status).toBe(200);
52
+ expect(response.body).toMatchObject({}); // TODO: add expected shape
53
+ });
54
+
55
+ it('should return 401 when not authenticated', async () => {
56
+ const response = await request(app).${methodLower}('${endpointPath}');
57
+ expect(response.status).toBe(401);
58
+ });
59
+ });
60
+ `;
61
+ return header + body.trimStart();
62
+ }
63
+ function renderParameterMissing(gap, ctx) {
64
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
65
+ const paramName = (_b = (_a = gap.parameter) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'param';
66
+ const paramIn = (_d = (_c = gap.parameter) === null || _c === void 0 ? void 0 : _c.in) !== null && _d !== void 0 ? _d : 'query';
67
+ const required = (_f = (_e = gap.parameter) === null || _e === void 0 ? void 0 : _e.required) !== null && _f !== void 0 ? _f : false;
68
+ const endpointPath = (_h = (_g = gap.endpoint) === null || _g === void 0 ? void 0 : _g.path) !== null && _h !== void 0 ? _h : '/api/endpoint';
69
+ const method = (_k = (_j = gap.endpoint) === null || _j === void 0 ? void 0 : _j.method) !== null && _k !== void 0 ? _k : 'GET';
70
+ const methodLower = method.toLowerCase();
71
+ const header = renderFileHeader(gap.id, gap.type);
72
+ const body = `
73
+ import request from 'supertest'; // TODO: adjust import path
74
+ import app from '../../../src/app'; // TODO: adjust import path
75
+
76
+ describe('${method} ${endpointPath} — parameter: ${paramName}', () => {
77
+ it('should accept a valid ${paramName} ${paramIn} parameter', async () => {
78
+ const response = await request(app)
79
+ .${methodLower}('${endpointPath}')
80
+ ${paramIn === 'query' ? `.query({ ${paramName}: 'valid-value' }) // TODO: provide valid value` : `.send({ ${paramName}: 'valid-value' }) // TODO: provide valid value`}
81
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'); // TODO: add valid token
82
+
83
+ expect(response.status).toBe(200);
84
+ expect(response.body).toMatchObject({}); // TODO: add expected shape
85
+ });
86
+ ${required
87
+ ? `
88
+ it('should return 400 when ${paramName} is missing', async () => {
89
+ const response = await request(app)
90
+ .${methodLower}('${endpointPath}')
91
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'); // TODO: add valid token
92
+
93
+ expect(response.status).toBe(400);
94
+ });
95
+ `
96
+ : ''}
97
+ it('should return 400 when ${paramName} is invalid', async () => {
98
+ const response = await request(app)
99
+ .${methodLower}('${endpointPath}')
100
+ ${paramIn === 'query' ? `.query({ ${paramName}: 'INVALID_VALUE' }) // TODO: provide invalid value` : `.send({ ${paramName}: 'INVALID_VALUE' }) // TODO: provide invalid value`}
101
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'); // TODO: add valid token
102
+
103
+ expect(response.status).toBe(400);
104
+ });
105
+ });
106
+ `;
107
+ return header + body.trimStart();
108
+ }
109
+ function renderParameterNotValidated(gap, ctx) {
110
+ var _a, _b, _c, _d, _e, _f;
111
+ const paramName = (_b = (_a = gap.parameter) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'param';
112
+ const endpointPath = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : '/api/endpoint';
113
+ const method = (_f = (_e = gap.endpoint) === null || _e === void 0 ? void 0 : _e.method) !== null && _f !== void 0 ? _f : 'GET';
114
+ const methodLower = method.toLowerCase();
115
+ const header = renderFileHeader(gap.id, gap.type);
116
+ const body = `
117
+ import request from 'supertest'; // TODO: adjust import path
118
+ import app from '../../../src/app'; // TODO: adjust import path
119
+
120
+ describe('${method} ${endpointPath} — validation: ${paramName}', () => {
121
+ it('should reject empty ${paramName}', async () => {
122
+ const response = await request(app)
123
+ .${methodLower}('${endpointPath}')
124
+ .send({ ${paramName}: '' }) // TODO: adjust payload location
125
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
126
+
127
+ expect(response.status).toBe(400);
128
+ expect(response.body).toMatchObject({ error: expect.any(String) }); // TODO: match actual error shape
129
+ });
130
+
131
+ it('should reject ${paramName} exceeding max length', async () => {
132
+ const response = await request(app)
133
+ .${methodLower}('${endpointPath}')
134
+ .send({ ${paramName}: 'x'.repeat(1000) }) // TODO: adjust max length
135
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
136
+
137
+ expect(response.status).toBe(400);
138
+ });
139
+ });
140
+ `;
141
+ return header + body.trimStart();
142
+ }
143
+ function renderBusinessMissing(gap, ctx) {
144
+ var _a, _b, _c, _d;
145
+ const ruleId = (_b = (_a = gap.businessRule) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 'BR-XXX';
146
+ const description = (_d = (_c = gap.businessRule) === null || _c === void 0 ? void 0 : _c.description) !== null && _d !== void 0 ? _d : 'Business rule';
147
+ const header = renderFileHeader(gap.id, gap.type);
148
+ const body = `
149
+ import request from 'supertest'; // TODO: adjust import path
150
+ import app from '../../../src/app'; // TODO: adjust import path
151
+
152
+ // Business Rule: ${ruleId} — ${description}
153
+ describe('Business Rule ${ruleId}: ${description}', () => {
154
+ it('should enforce rule when conditions are met', async () => {
155
+ // TODO: set up test data that triggers the business rule
156
+ const response = await request(app)
157
+ .post('/api/TODO_ENDPOINT') // TODO: replace with actual endpoint
158
+ .send({
159
+ // TODO: add request body that triggers the rule
160
+ })
161
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
162
+
163
+ expect(response.status).toBe(422); // TODO: adjust expected status
164
+ expect(response.body).toMatchObject({
165
+ error: expect.any(String), // TODO: match actual error shape
166
+ });
167
+ });
168
+
169
+ it('should allow the operation when the rule is satisfied', async () => {
170
+ const response = await request(app)
171
+ .post('/api/TODO_ENDPOINT') // TODO: replace with actual endpoint
172
+ .send({
173
+ // TODO: add request body that satisfies the rule
174
+ })
175
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
176
+
177
+ expect(response.status).toBe(200); // TODO: adjust expected status
178
+ });
179
+ });
180
+ `;
181
+ return header + body.trimStart();
182
+ }
183
+ function renderIntegrationMissingStep(gap, ctx) {
184
+ var _a, _b, _c, _d, _e, _f;
185
+ const flowId = (_b = (_a = gap.integrationFlow) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 'flow-001';
186
+ const flowName = (_d = (_c = gap.integrationFlow) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : 'Integration Flow';
187
+ const missingStep = (_f = (_e = gap.integrationFlow) === null || _e === void 0 ? void 0 : _e.missingStep) !== null && _f !== void 0 ? _f : 'step';
188
+ const header = renderFileHeader(gap.id, gap.type);
189
+ const body = `
190
+ import request from 'supertest'; // TODO: adjust import path
191
+ import app from '../../../src/app'; // TODO: adjust import path
192
+
193
+ // Integration Flow: ${flowId} — ${flowName}
194
+ describe('Integration Flow ${flowId}: ${flowName}', () => {
195
+ it('should complete the full flow including: ${missingStep}', async () => {
196
+ // Step 1: TODO — describe step 1
197
+ const step1 = await request(app)
198
+ .post('/api/TODO_STEP_1') // TODO: replace with actual endpoint
199
+ .send({}) // TODO: add step 1 body
200
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
201
+ expect(step1.status).toBe(200);
202
+
203
+ // Step 2: ${missingStep} (this was the missing step)
204
+ const step2 = await request(app)
205
+ .post('/api/TODO_STEP_2') // TODO: replace with actual endpoint
206
+ .send({}) // TODO: add step 2 body
207
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
208
+ expect(step2.status).toBe(200);
209
+ expect(step2.body).toMatchObject({}); // TODO: verify step 2 result
210
+ });
211
+ });
212
+ `;
213
+ return header + body.trimStart();
214
+ }
215
+ function renderErrorMissingStatus(gap, ctx) {
216
+ var _a, _b, _c, _d;
217
+ const endpointPath = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/api/endpoint';
218
+ const method = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.method) !== null && _d !== void 0 ? _d : 'GET';
219
+ const methodLower = method.toLowerCase();
220
+ const header = renderFileHeader(gap.id, gap.type);
221
+ const body = `
222
+ import request from 'supertest'; // TODO: adjust import path
223
+ import app from '../../../src/app'; // TODO: adjust import path
224
+
225
+ describe('${method} ${endpointPath} — error handling', () => {
226
+ it('should return 400 for invalid request body', async () => {
227
+ const response = await request(app)
228
+ .${methodLower}('${endpointPath}')
229
+ .send({ invalidField: 'bad-value' }) // TODO: craft invalid payload
230
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
231
+
232
+ expect(response.status).toBe(400);
233
+ expect(response.body).toMatchObject({ error: expect.any(String) }); // TODO: match error shape
234
+ });
235
+
236
+ it('should return 401 when unauthenticated', async () => {
237
+ const response = await request(app).${methodLower}('${endpointPath}');
238
+ expect(response.status).toBe(401);
239
+ });
240
+
241
+ it('should return 404 for non-existent resource', async () => {
242
+ const response = await request(app)
243
+ .${methodLower}('${endpointPath.replace('{id}', 'nonexistent-99999')}')
244
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
245
+ expect(response.status).toBe(404);
246
+ });
247
+
248
+ it('should return 500 when the service fails', async () => {
249
+ // TODO: mock the service to throw an error
250
+ // Example with jest.spyOn:
251
+ // jest.spyOn(someService, 'someMethod').mockRejectedValueOnce(new Error('Service error'));
252
+ const response = await request(app)
253
+ .${methodLower}('${endpointPath}')
254
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
255
+ // TODO: trigger 500 condition
256
+ expect(response.status).toBeGreaterThanOrEqual(500);
257
+ });
258
+ });
259
+ `;
260
+ return header + body.trimStart();
261
+ }
262
+ function renderSecurityMissingAuth(gap, ctx) {
263
+ var _a, _b, _c, _d;
264
+ const endpointPath = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/api/endpoint';
265
+ const method = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.method) !== null && _d !== void 0 ? _d : 'GET';
266
+ const methodLower = method.toLowerCase();
267
+ const header = renderFileHeader(gap.id, gap.type);
268
+ const body = `
269
+ import request from 'supertest'; // TODO: adjust import path
270
+ import app from '../../../src/app'; // TODO: adjust import path
271
+
272
+ describe('${method} ${endpointPath} — authentication', () => {
273
+ it('should return 401 when no token is provided', async () => {
274
+ const response = await request(app).${methodLower}('${endpointPath}');
275
+ expect(response.status).toBe(401);
276
+ });
277
+
278
+ it('should return 401 when an invalid token is provided', async () => {
279
+ const response = await request(app)
280
+ .${methodLower}('${endpointPath}')
281
+ .set('Authorization', 'Bearer invalid-token-here');
282
+ expect(response.status).toBe(401);
283
+ });
284
+
285
+ it('should return 403 when the user lacks required permissions', async () => {
286
+ const response = await request(app)
287
+ .${methodLower}('${endpointPath}')
288
+ .set('Authorization', 'Bearer TODO_LOW_PRIVILEGE_TOKEN'); // TODO: provide a low-privilege token
289
+ expect(response.status).toBe(403);
290
+ });
291
+
292
+ it('should succeed when a valid authorized token is provided', async () => {
293
+ const response = await request(app)
294
+ .${methodLower}('${endpointPath}')
295
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'); // TODO: provide valid token
296
+ expect(response.status).toBe(200);
297
+ });
298
+ });
299
+ `;
300
+ return header + body.trimStart();
301
+ }
302
+ function renderSecurityInjection(gap, ctx) {
303
+ var _a, _b, _c, _d;
304
+ const endpointPath = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/api/endpoint';
305
+ const method = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.method) !== null && _d !== void 0 ? _d : 'POST';
306
+ const methodLower = method.toLowerCase();
307
+ const injectionCases = exports.OWASP_INJECTION_STRINGS.slice(0, 4)
308
+ .map((s) => ` { input: ${JSON.stringify(s)}, label: 'injection: ${s.slice(0, 20)}...' },`)
309
+ .join('\n');
310
+ const header = renderFileHeader(gap.id, gap.type);
311
+ const body = `
312
+ import request from 'supertest'; // TODO: adjust import path
313
+ import app from '../../../src/app'; // TODO: adjust import path
314
+
315
+ describe('${method} ${endpointPath} — injection security', () => {
316
+ const injectionPayloads = [
317
+ ${injectionCases}
318
+ ];
319
+
320
+ it.each(injectionPayloads)('should reject $label', async ({ input }) => {
321
+ const response = await request(app)
322
+ .${methodLower}('${endpointPath}')
323
+ .send({ field: input }) // TODO: replace 'field' with actual vulnerable field name
324
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
325
+
326
+ expect(response.status).toBe(400); // TODO: adjust — may be 422 or 200 with sanitised output
327
+ expect(response.body).not.toMatchObject({ data: input }); // TODO: verify injection is blocked
328
+ });
329
+ });
330
+ `;
331
+ return header + body.trimStart();
332
+ }
333
+ function renderPerformanceMissingSla(gap, ctx) {
334
+ var _a, _b, _c, _d;
335
+ const endpointPath = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/api/endpoint';
336
+ const method = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.method) !== null && _d !== void 0 ? _d : 'GET';
337
+ const methodLower = method.toLowerCase();
338
+ const header = renderFileHeader(gap.id, gap.type);
339
+ const body = `
340
+ import request from 'supertest'; // TODO: adjust import path
341
+ import app from '../../../src/app'; // TODO: adjust import path
342
+
343
+ const SLA_RESPONSE_MS = 500; // TODO: adjust SLA threshold
344
+
345
+ describe('${method} ${endpointPath} — performance SLA', () => {
346
+ it('should respond within SLA threshold', async () => {
347
+ const start = Date.now();
348
+ const response = await request(app)
349
+ .${methodLower}('${endpointPath}')
350
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
351
+ const elapsed = Date.now() - start;
352
+
353
+ expect(response.status).toBe(200);
354
+ expect(elapsed).toBeLessThan(SLA_RESPONSE_MS);
355
+ });
356
+
357
+ it('should handle concurrent requests within SLA', async () => {
358
+ const concurrency = 5; // TODO: adjust concurrency level
359
+ const start = Date.now();
360
+ const responses = await Promise.all(
361
+ Array.from({ length: concurrency }, () =>
362
+ request(app)
363
+ .${methodLower}('${endpointPath}')
364
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN'),
365
+ ),
366
+ );
367
+ const elapsed = Date.now() - start;
368
+
369
+ for (const response of responses) {
370
+ expect(response.status).toBe(200);
371
+ }
372
+ expect(elapsed / concurrency).toBeLessThan(SLA_RESPONSE_MS);
373
+ });
374
+ });
375
+ `;
376
+ return header + body.trimStart();
377
+ }
378
+ function renderResilienceMissingRetry(gap, ctx) {
379
+ var _a, _b, _c, _d;
380
+ const endpointPath = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/api/endpoint';
381
+ const method = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.method) !== null && _d !== void 0 ? _d : 'GET';
382
+ const methodLower = method.toLowerCase();
383
+ const header = renderFileHeader(gap.id, gap.type);
384
+ const body = `
385
+ import request from 'supertest'; // TODO: adjust import path
386
+ import app from '../../../src/app'; // TODO: adjust import path
387
+
388
+ describe('${method} ${endpointPath} — resilience / retry', () => {
389
+ it('should return 503 or retry-after header when service is unavailable', async () => {
390
+ // TODO: mock downstream dependency to simulate failure
391
+ // jest.spyOn(downstreamService, 'call').mockRejectedValueOnce(new Error('Downstream unavailable'));
392
+ const response = await request(app)
393
+ .${methodLower}('${endpointPath}')
394
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
395
+
396
+ // Either the service retries successfully (200) or signals unavailability (503)
397
+ expect([200, 503]).toContain(response.status);
398
+ if (response.status === 503) {
399
+ expect(response.headers).toHaveProperty('retry-after'); // TODO: check actual header name
400
+ }
401
+ });
402
+
403
+ it('should handle circuit-breaker open state gracefully', async () => {
404
+ // TODO: trigger circuit breaker open state
405
+ const response = await request(app)
406
+ .${methodLower}('${endpointPath}')
407
+ .set('Authorization', 'Bearer TODO_VALID_TOKEN');
408
+
409
+ expect(response.status).toBe(503); // TODO: adjust expected status
410
+ expect(response.body).toMatchObject({ error: expect.any(String) });
411
+ });
412
+ });
413
+ `;
414
+ return header + body.trimStart();
415
+ }
416
+ // ─── Python / pytest templates ────────────────────────────────────────────────
417
+ function renderPythonEndpoint(gap, ctx) {
418
+ var _a, _b, _c, _d;
419
+ const method = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.method) !== null && _b !== void 0 ? _b : 'GET';
420
+ const endpointPath = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : '/api/endpoint';
421
+ const methodLower = method.toLowerCase();
422
+ const header = renderFileHeader(gap.id, gap.type).replace(/\/\//g, '#');
423
+ const body = `
424
+ import pytest
425
+ import requests # TODO: adjust import path
426
+
427
+ BASE_URL = "${ctx.baseUrl}" # TODO: adjust base URL
428
+ AUTH_TOKEN = "TODO_VALID_TOKEN" # TODO: add valid token
429
+
430
+
431
+ class Test${method.charAt(0) + method.slice(1).toLowerCase()}${endpointPath.replace(/[^a-zA-Z0-9]/g, '_')}:
432
+ def test_returns_200_with_valid_request(self):
433
+ response = requests.${methodLower}(
434
+ f"{BASE_URL}${endpointPath}",
435
+ headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
436
+ )
437
+ assert response.status_code == 200
438
+ assert response.json() is not None # TODO: replace with specific assertion
439
+
440
+ def test_returns_401_when_unauthenticated(self):
441
+ response = requests.${methodLower}(f"{BASE_URL}${endpointPath}")
442
+ assert response.status_code == 401
443
+ `;
444
+ return header + body.trimStart();
445
+ }
446
+ // ─── Java / JUnit5 templates ──────────────────────────────────────────────────
447
+ function renderJavaEndpoint(gap, ctx) {
448
+ var _a, _b, _c, _d;
449
+ const method = (_b = (_a = gap.endpoint) === null || _a === void 0 ? void 0 : _a.method) !== null && _b !== void 0 ? _b : 'GET';
450
+ const endpointPath = (_d = (_c = gap.endpoint) === null || _c === void 0 ? void 0 : _c.path) !== null && _d !== void 0 ? _d : '/api/endpoint';
451
+ const className = `Test${gap.id.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('')}`;
452
+ const header = renderFileHeader(gap.id, gap.type).replace(/\/\//g, '//');
453
+ const body = `
454
+ package generated;
455
+
456
+ import org.junit.jupiter.api.Test;
457
+ import org.springframework.boot.test.context.SpringBootTest;
458
+ import org.springframework.beans.factory.annotation.Autowired;
459
+ import org.springframework.test.web.servlet.MockMvc;
460
+ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
461
+ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
462
+
463
+ // TODO: Add @SpringBootTest or @WebMvcTest and adjust imports
464
+ @SpringBootTest
465
+ class ${className} {
466
+
467
+ @Autowired
468
+ private MockMvc mockMvc; // TODO: inject MockMvc
469
+
470
+ @Test
471
+ void shouldReturn200WithValidRequest() throws Exception {
472
+ mockMvc.perform(${method.toLowerCase()}("${endpointPath}")
473
+ .header("Authorization", "Bearer TODO_VALID_TOKEN")) // TODO: add valid token
474
+ .andExpect(status().isOk())
475
+ .andExpect(jsonPath("$").isNotEmpty()); // TODO: replace with specific assertion
476
+ }
477
+
478
+ @Test
479
+ void shouldReturn401WhenUnauthenticated() throws Exception {
480
+ mockMvc.perform(${method.toLowerCase()}("${endpointPath}"))
481
+ .andExpect(status().isUnauthorized());
482
+ }
483
+ }
484
+ `;
485
+ return header + body.trimStart();
486
+ }
487
+ // ─── Public API ───────────────────────────────────────────────────────────────
488
+ /** Render test file content for a given gap and context */
489
+ function renderTemplate(gap, ctx) {
490
+ const { language } = ctx;
491
+ if (language === 'python') {
492
+ return renderPythonEndpoint(gap, ctx);
493
+ }
494
+ if (language === 'java' || language === 'kotlin') {
495
+ return renderJavaEndpoint(gap, ctx);
496
+ }
497
+ // TypeScript / JavaScript (Jest, Cypress, Playwright)
498
+ switch (gap.type) {
499
+ case 'endpoint:missing':
500
+ return renderEndpointMissing(gap, ctx);
501
+ case 'parameter:missing':
502
+ return renderParameterMissing(gap, ctx);
503
+ case 'parameter:not-validated':
504
+ return renderParameterNotValidated(gap, ctx);
505
+ case 'business:missing':
506
+ return renderBusinessMissing(gap, ctx);
507
+ case 'integration:missing-step':
508
+ return renderIntegrationMissingStep(gap, ctx);
509
+ case 'error:missing-status':
510
+ return renderErrorMissingStatus(gap, ctx);
511
+ case 'security:missing-auth':
512
+ return renderSecurityMissingAuth(gap, ctx);
513
+ case 'security:injection':
514
+ return renderSecurityInjection(gap, ctx);
515
+ case 'performance:missing-sla':
516
+ return renderPerformanceMissingSla(gap, ctx);
517
+ case 'resilience:missing-retry':
518
+ return renderResilienceMissingRetry(gap, ctx);
519
+ default:
520
+ return renderEndpointMissing(gap, ctx);
521
+ }
522
+ }
523
+ /** Returns the template name used for a given gap type */
524
+ function getTemplateName(gap, ctx) {
525
+ return `${ctx.language}/${ctx.framework}/${gap.type}`;
526
+ }
@@ -0,0 +1,73 @@
1
+ export type GapType = 'endpoint:missing' | 'parameter:missing' | 'parameter:not-validated' | 'business:missing' | 'integration:missing-step' | 'error:missing-status' | 'security:missing-auth' | 'security:injection' | 'performance:missing-sla' | 'resilience:missing-retry';
2
+ export type Language = 'typescript' | 'javascript' | 'python' | 'java' | 'kotlin';
3
+ export type Framework = 'jest' | 'pytest' | 'junit5' | 'cypress' | 'playwright';
4
+ export interface DetectedGap {
5
+ id: string;
6
+ type: GapType;
7
+ endpoint?: {
8
+ method: string;
9
+ path: string;
10
+ };
11
+ parameter?: {
12
+ name: string;
13
+ in: 'query' | 'path' | 'header' | 'body';
14
+ required: boolean;
15
+ };
16
+ businessRule?: {
17
+ id: string;
18
+ description: string;
19
+ };
20
+ integrationFlow?: {
21
+ id: string;
22
+ name: string;
23
+ missingStep: string;
24
+ };
25
+ riskScore: number;
26
+ sourceFile?: string;
27
+ existingSimilarTests: string[];
28
+ }
29
+ export interface GenerationContext {
30
+ projectRoot: string;
31
+ language: Language;
32
+ framework: Framework;
33
+ testFramework: 'jest' | 'pytest' | 'junit5';
34
+ baseUrl: string;
35
+ authHeader?: string;
36
+ gaps: DetectedGap[];
37
+ }
38
+ export interface GeneratedFile {
39
+ outputPath: string;
40
+ content: string;
41
+ gapId: string;
42
+ templateUsed: string;
43
+ qualityScore?: number;
44
+ }
45
+ export interface TestQualityReport {
46
+ file: string;
47
+ overallScore: number;
48
+ dimensions: {
49
+ hasStatusAssertion: boolean;
50
+ hasBodyAssertion: boolean;
51
+ hasErrorPath: boolean;
52
+ hasNoGenericMatchers: boolean;
53
+ hasDescriptiveName: boolean;
54
+ };
55
+ violations: string[];
56
+ }
57
+ export interface AiReadyFlow {
58
+ id: string;
59
+ gapId: string;
60
+ gapType: GapType;
61
+ suggestedOutputPath: string;
62
+ copilotPrompt: string;
63
+ existingSimilarTests: string[];
64
+ estimatedComplexity: 'low' | 'medium' | 'high';
65
+ riskScore: number;
66
+ }
67
+ export interface AiReadyFlowsManifest {
68
+ generatedAt: string;
69
+ totalFlows: number;
70
+ byType: Record<GapType, number>;
71
+ flows: AiReadyFlow[];
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/generation/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GACf,kBAAkB,GAClB,mBAAmB,GACnB,yBAAyB,GACzB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,GACtB,uBAAuB,GACvB,oBAAoB,GACpB,yBAAyB,GACzB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAClF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAEhF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1F,YAAY,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,eAAe,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,WAAW,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE;QACV,kBAAkB,EAAE,OAAO,CAAC;QAC5B,gBAAgB,EAAE,OAAO,CAAC;QAC1B,YAAY,EAAE,OAAO,CAAC;QACtB,oBAAoB,EAAE,OAAO,CAAC;QAC9B,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,mBAAmB,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });