@jrpool/kilotest 46.1.2 → 47.0.1
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/.claude/settings.local.json +13 -1
- package/.devin/config.local.json +5 -1
- package/.github/workflows/ci.yml +0 -3
- package/.github/workflows/periodic-smoke-tests.yml +30 -0
- package/.github/workflows/publish-mcp.yml +4 -4
- package/{alerts.test.js → alerts.test.ts} +17 -29
- package/{alerts.js → alerts.ts} +10 -13
- package/api/{getReport.test.js → getReport.test.ts} +9 -10
- package/api/{getReport.js → getReport.ts} +23 -24
- package/api/{listDiagnoses.test.js → listDiagnoses.test.ts} +26 -27
- package/api/{listDiagnoses.js → listDiagnoses.ts} +46 -70
- package/api/{listIssues.test.js → listIssues.test.ts} +21 -14
- package/api/{listIssues.js → listIssues.ts} +75 -74
- package/api/{listReports.test.js → listReports.test.ts} +17 -16
- package/api/listReports.ts +87 -0
- package/api/{listViolators.test.js → listViolators.test.ts} +19 -20
- package/api/{listViolators.js → listViolators.ts} +45 -70
- package/api/requestFeature.test.ts +54 -0
- package/api/{requestFeature.js → requestFeature.ts} +18 -15
- package/api/{requestRetest.test.js → requestRetest.test.ts} +33 -28
- package/api/{requestRetest.js → requestRetest.ts} +32 -25
- package/api/{requestTest.test.js → requestTest.test.ts} +35 -30
- package/api/{requestTest.js → requestTest.ts} +35 -28
- package/api/{routes.test.js → routes.test.ts} +5 -5
- package/api/{routes.js → routes.ts} +6 -6
- package/api/{schemas.js → schemas.ts} +69 -50
- package/api/{util.test.js → util.test.ts} +31 -11
- package/api/{util.js → util.ts} +61 -36
- package/api/version.ts +8 -0
- package/{apiIntegration.test.js → apiIntegration.test.ts} +12 -13
- package/balances.ts +87 -0
- package/{checkinfo.js → checkinfo.ts} +24 -20
- package/docs/AI-TOOLS.md +4 -4
- package/docs/BACKLOG.md +356 -0
- package/docs/SERVICE.md +144 -17
- package/docs/decisions/002-coverage-tool.md +40 -0
- package/docs/decisions/003-adr-threshold.md +34 -0
- package/docs/decisions/004-docs-directory.md +23 -0
- package/docs/decisions/005-observability.md +28 -0
- package/docs/decisions/006-git-workflow.md +28 -0
- package/docs/decisions/007-internal-testing.md +34 -0
- package/docs/decisions/008-dependencies.md +28 -0
- package/docs/decisions/009-qai-integration.md +40 -0
- package/docs/decisions/QAI-HISTORICAL-CONTEXT.md +42 -0
- package/eslint.config.mjs +27 -0
- package/index.html +3 -3
- package/{index.test.js → index.test.ts} +380 -143
- package/{index.js → index.ts} +382 -283
- package/{indexBalances.test.js → indexBalances.test.ts} +21 -17
- package/{indexServe.test.js → indexServe.test.ts} +9 -9
- package/linkValidation.test.ts +190 -0
- package/{mcp.test.js → mcp.test.ts} +53 -26
- package/{mcp.js → mcp.ts} +29 -20
- package/memory/MEMORY.md +7 -0
- package/memory/user_maintainer_context.md +24 -0
- package/openapi.yaml +115 -68
- package/package.json +35 -15
- package/pm2.config.cjs +29 -0
- package/{routes.test.js → routes.test.ts} +8 -7
- package/{routing.test.js → routing.test.ts} +11 -12
- package/scripts/{generate-openapi.js → generate-openapi.ts} +13 -13
- package/serve.ts +15 -0
- package/smokeTest.ts +234 -0
- package/test/dbFixture.ts +21 -0
- package/test/fixtures/{buildFixtures.js → buildFixtures.ts} +14 -13
- package/test/{setup.cjs → setup.ts} +2 -2
- package/tsconfig.json +16 -0
- package/{util.test.js → util.test.ts} +350 -257
- package/util.ts +921 -0
- package/web/ai0BalanceForm/{ai0BalanceForm.test.js → ai0BalanceForm.test.ts} +15 -15
- package/web/ai0BalanceForm/{index.js → index.ts} +10 -18
- package/web/enqueue/index.ts +56 -0
- package/web/enqueueForm/index.html +9 -9
- package/web/enqueueForm/index.ts +43 -0
- package/web/expungeReportsForm/{expungeReportsForm.test.js → expungeReportsForm.test.ts} +18 -20
- package/web/expungeReportsForm/index.ts +18 -0
- package/web/hideReportForm/{hideReportForm.test.js → hideReportForm.test.ts} +14 -14
- package/web/hideReportForm/{index.js → index.ts} +17 -21
- package/web/listDiagnoses/{index.js → index.ts} +48 -58
- package/web/listDiagnoses/{listDiagnoses.test.js → listDiagnoses.test.ts} +39 -34
- package/web/listIssues/{index.js → index.ts} +67 -92
- package/web/listIssues/{listIssues.test.js → listIssues.test.ts} +45 -51
- package/web/listReports/index.html +3 -3
- package/web/listReports/{index.js → index.ts} +48 -53
- package/web/listReports/{listReports.test.js → listReports.test.ts} +41 -43
- package/web/listRules/{index.js → index.ts} +18 -23
- package/web/listRules/{listRules.test.js → listRules.test.ts} +26 -19
- package/web/listTopIssues/{index.js → index.ts} +36 -47
- package/web/listTopIssues/{listTopIssues.test.js → listTopIssues.test.ts} +26 -17
- package/web/listViolators/{index.js → index.ts} +51 -71
- package/web/listViolators/{listViolators.test.js → listViolators.test.ts} +43 -36
- package/web/manage/index.ts +22 -0
- package/web/pruneReportsForm/index.ts +18 -0
- package/web/pruneReportsForm/{pruneReportsForm.test.js → pruneReportsForm.test.ts} +11 -11
- package/web/reannotate/index.ts +70 -0
- package/web/reannotate/{reannotate.test.js → reannotate.test.ts} +19 -22
- package/web/reannotateForm/{index.js → index.ts} +41 -50
- package/web/reannotateForm/{reannotateForm.test.js → reannotateForm.test.ts} +13 -16
- package/web/renewWCAG/{index.js → index.ts} +9 -9
- package/web/renewWCAG/{renewWCAG.test.js → renewWCAG.test.ts} +17 -20
- package/web/renewWCAGForm/index.ts +22 -0
- package/web/{expungeReportsForm/index.js → reportDeletion.ts} +56 -38
- package/web/requestRetest/index.html +7 -7
- package/web/requestRetest/index.ts +31 -0
- package/web/requestRetest/requestRetest.test.ts +116 -0
- package/web/requestRetestForm/{index.js → index.ts} +10 -16
- package/web/requestRetestForm/{requestRetestForm.test.js → requestRetestForm.test.ts} +11 -12
- package/web/requestTest/index.html +7 -7
- package/web/requestTest/{index.js → index.ts} +5 -5
- package/web/requestTest/{requestTest.test.js → requestTest.test.ts} +21 -21
- package/web/requestTestForm/index.html +4 -4
- package/web/requestTestForm/index.ts +21 -0
- package/web/requestTestForm/{requestTestForm.test.js → requestTestForm.test.ts} +9 -9
- package/web/rewindReportsForm/index.ts +18 -0
- package/web/rewindReportsForm/{rewindReportsForm.test.js → rewindReportsForm.test.ts} +11 -11
- package/web/tutorialAI/index.html +184 -0
- package/web/tutorialAI/index.ts +80 -0
- package/web/tutorialAI/tutorialAI.test.ts +192 -0
- package/web/{tutorial → tutorialWeb}/index.html +9 -8
- package/web/tutorialWeb/index.ts +78 -0
- package/web/tutorialWeb/tutorialWeb.test.ts +195 -0
- package/web/unhideReportForm/{index.js → index.ts} +17 -21
- package/web/unhideReportForm/{unhideReportForm.test.js → unhideReportForm.test.ts} +16 -16
- package/aceconfig.js +0 -28
- package/api/listReports.js +0 -89
- package/api/requestFeature.test.js +0 -58
- package/api/version.js +0 -8
- package/indexFallback.test.js +0 -33
- package/indexWorkers.test.js +0 -79
- package/jsconfig.json +0 -8
- package/pm2.config.js +0 -18
- package/smokeTest.js +0 -128
- package/util.js +0 -744
- package/web/enqueue/index.js +0 -67
- package/web/enqueueForm/index.js +0 -49
- package/web/manage/index.js +0 -22
- package/web/pruneReportsForm/index.js +0 -123
- package/web/reannotate/index.js +0 -58
- package/web/renewWCAGForm/index.js +0 -22
- package/web/requestRetest/index.js +0 -31
- package/web/requestRetest/requestRetest.test.js +0 -119
- package/web/requestTestForm/index.js +0 -21
- package/web/rewindReportsForm/index.js +0 -121
- package/web/tutorial/index.js +0 -63
- package/web/tutorial/tutorial.test.js +0 -97
- /package/test/fixtures/db/jobs/{recs.json → testRequests.json} +0 -0
- /package/web/{tutorial → tutorialWeb}/images/newsletter-form.png +0 -0
package/smokeTest.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/*
|
|
2
|
+
smokeTest.ts
|
|
3
|
+
Sends requests to all valid GET and POST paths on the deployed server and verifies that Caddy forwards them to Kilotest (i.e., the response is not a bare Caddy 404), then sends a real MCP initialize request to verify end-to-end MCP delivery through the proxy.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import https from 'node:https';
|
|
9
|
+
import {routes} from './index.ts';
|
|
10
|
+
import {mcpPath} from './mcp.ts';
|
|
11
|
+
|
|
12
|
+
// CONSTANTS
|
|
13
|
+
|
|
14
|
+
// Concrete paths matching each wildcard pattern in the routes table, for smoke testing.
|
|
15
|
+
const concretePaths: Record<string, Record<string, string>> = {
|
|
16
|
+
GET: {
|
|
17
|
+
'*.html*': '/listReports.html',
|
|
18
|
+
'/': '/',
|
|
19
|
+
'/api-docs': '/api-docs',
|
|
20
|
+
'/api/*': '/api/listReports',
|
|
21
|
+
'/capability.md': '/capability.md',
|
|
22
|
+
'/favicon.*': '/favicon.ico',
|
|
23
|
+
'/fullReport.json/*': '/fullReport.json/260101T0000/mix',
|
|
24
|
+
'/index.html': '/index.html',
|
|
25
|
+
'/llms-full.txt': '/llms-full.txt',
|
|
26
|
+
'/llms.txt': '/llms.txt',
|
|
27
|
+
'/mcp': '/mcp',
|
|
28
|
+
'/openapi.json': '/openapi.json',
|
|
29
|
+
'/openapi.yaml': '/openapi.yaml',
|
|
30
|
+
'/qai': '/qai',
|
|
31
|
+
'/qai/comments': '/qai/comments',
|
|
32
|
+
'/robots.txt': '/robots.txt',
|
|
33
|
+
'/sitemap.xml': '/sitemap.xml',
|
|
34
|
+
'/style.css': '/style.css',
|
|
35
|
+
'/swagger.json': '/swagger.json',
|
|
36
|
+
'/swagger.yaml': '/swagger.yaml',
|
|
37
|
+
'/tutorialAI/images/*': '/tutorialAI/images/example.png',
|
|
38
|
+
'/tutorialWeb/images/*': '/tutorialWeb/images/newsletter-form.png'
|
|
39
|
+
},
|
|
40
|
+
POST: {
|
|
41
|
+
'/api/*': '/api/requestFeature',
|
|
42
|
+
'/mcp': '/mcp',
|
|
43
|
+
'/reannotate.html': '/reannotate.html',
|
|
44
|
+
'/requestAction.html': '/requestAction.html',
|
|
45
|
+
'/renewWCAG.html': '/renewWCAG.html',
|
|
46
|
+
'/requestRetest.html/*': '/requestRetest.html/260101T0001/ct',
|
|
47
|
+
'/requestTest.html': '/requestTest.html',
|
|
48
|
+
'/tutorialAIComment.html': '/tutorialAIComment.html',
|
|
49
|
+
'/tutorialWebComment.html': '/tutorialWebComment.html',
|
|
50
|
+
'/worker/job': '/worker/job',
|
|
51
|
+
'/worker/report': '/worker/report'
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
// Minimal POST bodies for paths that require them.
|
|
55
|
+
const postBodies: Record<string, object> = {
|
|
56
|
+
'/api/requestFeature': {feature: 'smoke test'},
|
|
57
|
+
'/mcp': {},
|
|
58
|
+
'/reannotate.html': {authCode: 'invalid'},
|
|
59
|
+
'/requestAction.html': {
|
|
60
|
+
target: 'https://smoketest.example.com\tSmoke Test Page', authCode: 'invalid'
|
|
61
|
+
},
|
|
62
|
+
'/renewWCAG.html': {authCode: 'invalid'},
|
|
63
|
+
'/requestRetest.html/260101T0001/ct': {why: 'smoke test'},
|
|
64
|
+
'/requestTest.html': {description: 'Smoke Test Page', url: 'https://smoketest.example.com', why: 'smoke test'},
|
|
65
|
+
'/tutorialAIComment.html': {content: 'smoke test'},
|
|
66
|
+
'/tutorialWebComment.html': {content: 'smoke test'},
|
|
67
|
+
'/worker/job': {},
|
|
68
|
+
'/worker/report': {}
|
|
69
|
+
};
|
|
70
|
+
// The JSON-RPC initialize request sent by the positive MCP probe.
|
|
71
|
+
const mcpInitialize = {
|
|
72
|
+
jsonrpc: '2.0',
|
|
73
|
+
method: 'initialize',
|
|
74
|
+
id: 'smoke',
|
|
75
|
+
params: {
|
|
76
|
+
protocolVersion: '2025-06-18',
|
|
77
|
+
capabilities: {},
|
|
78
|
+
clientInfo: {name: 'kilotest-smoke', version: '1.0'}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// FUNCTIONS
|
|
83
|
+
|
|
84
|
+
// Sends an HTTPS request and returns the status code and body length.
|
|
85
|
+
const sendRequest = (method: string, requestPath: string) =>
|
|
86
|
+
new Promise<{statusCode: number | undefined, bodyLength: number}>((resolve, reject) => {
|
|
87
|
+
const body = method === 'POST' ? JSON.stringify(postBodies[requestPath] || {}) : null;
|
|
88
|
+
const headers: Record<string, string | number> = {'x-kilotest-smoke': '1'};
|
|
89
|
+
if (body) {
|
|
90
|
+
headers['content-type'] = 'application/json; charset=utf-8';
|
|
91
|
+
headers['content-length'] = Buffer.byteLength(body);
|
|
92
|
+
}
|
|
93
|
+
const options = {
|
|
94
|
+
method,
|
|
95
|
+
host: process.env.SMOKE_HOST || 'kilotest.com',
|
|
96
|
+
path: requestPath,
|
|
97
|
+
headers
|
|
98
|
+
};
|
|
99
|
+
const req = https.request(options, response => {
|
|
100
|
+
const chunks: Buffer[] = [];
|
|
101
|
+
response.on('data', chunk => chunks.push(chunk));
|
|
102
|
+
response.on('end', () => {
|
|
103
|
+
const responseBody = chunks.join('');
|
|
104
|
+
resolve({statusCode: response.statusCode, bodyLength: responseBody.length});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
req.on('error', reject);
|
|
108
|
+
req.end(body || '');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Returns whether a response is a bare Caddy 404 (the failure this test detects).
|
|
112
|
+
const isCaddy404 = (result: {statusCode: number | undefined, bodyLength: number}) =>
|
|
113
|
+
result.statusCode === 404 && result.bodyLength === 0;
|
|
114
|
+
|
|
115
|
+
// Sends a request to the MCP endpoint without the smoke header and returns the status code and body.
|
|
116
|
+
const sendMCPRequest = (method: string, accept: string, body: object | null = null) =>
|
|
117
|
+
new Promise<{statusCode: number | undefined, body: string}>((resolve, reject) => {
|
|
118
|
+
const bodyData = body ? JSON.stringify(body) : null;
|
|
119
|
+
const headers: Record<string, string | number> = {accept};
|
|
120
|
+
if (bodyData) {
|
|
121
|
+
headers['content-type'] = 'application/json';
|
|
122
|
+
headers['content-length'] = Buffer.byteLength(bodyData);
|
|
123
|
+
}
|
|
124
|
+
const req = https.request({
|
|
125
|
+
method,
|
|
126
|
+
host: process.env.SMOKE_HOST || 'kilotest.com',
|
|
127
|
+
path: mcpPath,
|
|
128
|
+
headers
|
|
129
|
+
}, response => {
|
|
130
|
+
const chunks: Buffer[] = [];
|
|
131
|
+
response.on('data', chunk => chunks.push(chunk));
|
|
132
|
+
response.on('end', () => {
|
|
133
|
+
resolve({statusCode: response.statusCode, body: chunks.join('')});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
req.on('error', reject);
|
|
137
|
+
req.end(bodyData || '');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Returns whether an MCP probe response is a valid initialize result from Kilotest.
|
|
141
|
+
const isValidMCPResponse = (result: {statusCode: number | undefined, body: string}) => {
|
|
142
|
+
if (result.statusCode !== 200) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const dataLine = result.body.split('\n').find(line => line.startsWith('data: '));
|
|
147
|
+
const message = JSON.parse(dataLine ? dataLine.slice(6) : result.body);
|
|
148
|
+
return message?.result?.serverInfo?.name === 'Kilotest';
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Returns whether a response is a JSON-RPC protocol error, the expected rejection of an invalid MCP request.
|
|
156
|
+
const isMCPErrorResponse = (result: {statusCode: number | undefined, body: string}) => {
|
|
157
|
+
if (result.statusCode === undefined || result.statusCode < 400 || result.statusCode >= 500) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const message = JSON.parse(result.body);
|
|
162
|
+
return message?.jsonrpc === '2.0' && typeof message?.error?.code === 'number';
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// EXECUTION
|
|
170
|
+
|
|
171
|
+
(async () => {
|
|
172
|
+
let failures = 0;
|
|
173
|
+
for (const method of ['GET', 'POST']) {
|
|
174
|
+
console.log(`\n=== ${method} paths ===`);
|
|
175
|
+
for (const pattern of routes[method as 'GET' | 'POST']) {
|
|
176
|
+
const requestPath = concretePaths[method]![pattern];
|
|
177
|
+
if (!requestPath) {
|
|
178
|
+
console.log(`FAIL: no concrete path defined for pattern ${pattern}`);
|
|
179
|
+
failures++;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const result = await sendRequest(method, requestPath);
|
|
184
|
+
if (isCaddy404(result)) {
|
|
185
|
+
console.log(`FAIL: ${method} ${requestPath} -> Caddy 404 (path not forwarded)`);
|
|
186
|
+
failures++;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.log(`PASS: ${method} ${requestPath} -> ${result.statusCode} (body ${result.bodyLength} bytes)`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
194
|
+
console.log(`FAIL: ${method} ${requestPath} -> error: ${message}`);
|
|
195
|
+
failures++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Send a real MCP initialize request to verify routing, header forwarding, and SSE delivery.
|
|
200
|
+
console.log('\n=== MCP probes ===');
|
|
201
|
+
try {
|
|
202
|
+
const probe = await sendMCPRequest('POST', 'application/json, text/event-stream', mcpInitialize);
|
|
203
|
+
if (isValidMCPResponse(probe)) {
|
|
204
|
+
console.log(`PASS: POST ${mcpPath} initialize -> ${probe.statusCode} (valid MCP response)`);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.log(`FAIL: POST ${mcpPath} initialize -> ${probe.statusCode} (body: ${probe.body.slice(0, 120)})`);
|
|
208
|
+
failures++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
213
|
+
console.log(`FAIL: POST ${mcpPath} initialize -> error: ${message}`);
|
|
214
|
+
failures++;
|
|
215
|
+
}
|
|
216
|
+
// Send a browser-style GET to verify that invalid MCP requests get JSON-RPC errors end-to-end.
|
|
217
|
+
try {
|
|
218
|
+
const probe = await sendMCPRequest('GET', 'text/html');
|
|
219
|
+
if (isMCPErrorResponse(probe)) {
|
|
220
|
+
console.log(`PASS: GET ${mcpPath} browser -> ${probe.statusCode} (JSON-RPC error)`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
console.log(`FAIL: GET ${mcpPath} browser -> ${probe.statusCode} (body: ${probe.body.slice(0, 120)})`);
|
|
224
|
+
failures++;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
229
|
+
console.log(`FAIL: GET ${mcpPath} browser -> error: ${message}`);
|
|
230
|
+
failures++;
|
|
231
|
+
}
|
|
232
|
+
console.log(`\n=== ${failures === 0 ? 'All checks passed' : `${failures} failure(s)`} ===`);
|
|
233
|
+
process.exit(failures === 0 ? 0 : 1);
|
|
234
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
test/dbFixture.ts
|
|
3
|
+
Provides a disposable copy of the fixture database, so that tests never modify the tracked fixtures in test/fixtures/db.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
|
|
12
|
+
// CONSTANTS
|
|
13
|
+
|
|
14
|
+
// Path of this process' temporary copy of the fixture database.
|
|
15
|
+
export const fixtureDBDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kilotest-db-'));
|
|
16
|
+
|
|
17
|
+
// Copy the tracked fixture database to the temporary directory.
|
|
18
|
+
fs.cpSync(path.join(import.meta.dirname, 'fixtures', 'db'), fixtureDBDir, {recursive: true});
|
|
19
|
+
|
|
20
|
+
// Remove the copy when the process exits.
|
|
21
|
+
process.on('exit', () => fs.rmSync(fixtureDBDir, {recursive: true, force: true}));
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/*
|
|
2
|
-
buildFixtures.
|
|
2
|
+
buildFixtures.ts
|
|
3
3
|
Builds the fixture corpus for Kilotest tests.
|
|
4
4
|
|
|
5
5
|
Each fixture is a minimal Testaro report in the current format, crafted so that the expected API response can be hand-computed and hard-coded into tests. The fixtures collectively cover the `outcome` property values (`failed`, `cantTell`, and missing/undefined), superseded reports, empty reports, and prevented rule engines.
|
|
6
6
|
|
|
7
|
-
Run with: node test/fixtures/buildFixtures.
|
|
7
|
+
Run with: node test/fixtures/buildFixtures.ts [targetDir]
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
// IMPORTS
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
import fs from 'node:fs/promises';
|
|
13
|
+
import path from 'node:path';
|
|
14
14
|
|
|
15
15
|
// CONSTANTS
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ const issueFocusIndication = 'focusIndicationBad';
|
|
|
22
22
|
// weight 1 (lowest)
|
|
23
23
|
const issueAllCaps = 'allCaps';
|
|
24
24
|
|
|
25
|
-
// Rule engine IDs that exist in util.
|
|
25
|
+
// Rule engine IDs that exist in util.ts ruleEngines.
|
|
26
26
|
const engineAxe = 'axe';
|
|
27
27
|
const engineAlfa = 'alfa';
|
|
28
28
|
const engineIbm = 'ibm';
|
|
@@ -30,7 +30,7 @@ const engineIbm = 'ibm';
|
|
|
30
30
|
// FUNCTIONS
|
|
31
31
|
|
|
32
32
|
// Returns a minimal catalog item.
|
|
33
|
-
const catalogItem = (tagName, text, pathID = '/html', boxID = '0:0:100:50') => ({
|
|
33
|
+
const catalogItem = (tagName: string, text: string, pathID = '/html', boxID = '0:0:100:50') => ({
|
|
34
34
|
tagName,
|
|
35
35
|
id: '',
|
|
36
36
|
startTag: `<${tagName.toLowerCase()}>`,
|
|
@@ -44,7 +44,8 @@ const catalogItem = (tagName, text, pathID = '/html', boxID = '0:0:100:50') => (
|
|
|
44
44
|
|
|
45
45
|
// Returns a minimal standard instance.
|
|
46
46
|
const instance = (
|
|
47
|
-
ruleID, what, outcome, issueID, catalogIndex
|
|
47
|
+
ruleID: string, what: string, outcome: string, issueID: string, catalogIndex: number,
|
|
48
|
+
ordinalSeverity = 2, count = 1
|
|
48
49
|
) => ({
|
|
49
50
|
ruleID,
|
|
50
51
|
what,
|
|
@@ -57,7 +58,7 @@ const instance = (
|
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
// Returns a minimal test act.
|
|
60
|
-
const testAct = (which, instances) => ({
|
|
61
|
+
const testAct = (which: string, instances: any[]) => ({
|
|
61
62
|
type: 'test',
|
|
62
63
|
which,
|
|
63
64
|
startTime: '26-01-01T00:00',
|
|
@@ -74,7 +75,7 @@ const testAct = (which, instances) => ({
|
|
|
74
75
|
});
|
|
75
76
|
|
|
76
77
|
// Returns a minimal valid report.
|
|
77
|
-
const report = (id, what, url, acts, catalog, endTime = '26-01-01T00:10') => ({
|
|
78
|
+
const report = (id: string, what: string, url: string, acts: any[], catalog: any, endTime = '26-01-01T00:10') => ({
|
|
78
79
|
id,
|
|
79
80
|
what,
|
|
80
81
|
strict: false,
|
|
@@ -99,14 +100,14 @@ const report = (id, what, url, acts, catalog, endTime = '26-01-01T00:10') => ({
|
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
// Writes a JSON file with a trailing newline.
|
|
102
|
-
const writeJSON = async (filePath, object) => {
|
|
103
|
+
const writeJSON = async (filePath: string, object: any) => {
|
|
103
104
|
await fs.writeFile(filePath, `${JSON.stringify(object, null, 2)}\n`);
|
|
104
105
|
};
|
|
105
106
|
|
|
106
107
|
// MAIN
|
|
107
108
|
|
|
108
109
|
const main = async () => {
|
|
109
|
-
const targetDir = process.argv[2] || path.join(
|
|
110
|
+
const targetDir = process.argv[2] || path.join(import.meta.dirname, 'db');
|
|
110
111
|
const reportsDir = path.join(targetDir, 'reports');
|
|
111
112
|
await fs.mkdir(reportsDir, {recursive: true});
|
|
112
113
|
await fs.mkdir(path.join(targetDir, 'jobs', 'queue'), {recursive: true});
|
|
@@ -316,8 +317,8 @@ const main = async () => {
|
|
|
316
317
|
delete brdReport.jobData.preventions;
|
|
317
318
|
await writeJSON(path.join(reportsDir, '260101T0009-brd.json'), brdReport);
|
|
318
319
|
|
|
319
|
-
// Write an empty
|
|
320
|
-
await writeJSON(path.join(targetDir, 'jobs', '
|
|
320
|
+
// Write an empty testRequests.json so getTestRequests does not try to create one.
|
|
321
|
+
await writeJSON(path.join(targetDir, 'jobs', 'testRequests.json'), {});
|
|
321
322
|
|
|
322
323
|
console.log(`Fixtures built in ${targetDir}`);
|
|
323
324
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
test/setup.
|
|
2
|
+
test/setup.ts
|
|
3
3
|
Clears alert configuration env vars before any modules are required,
|
|
4
4
|
so that tests calling sendAlert indirectly never trigger real email alerts.
|
|
5
5
|
This file is loaded via --require before any test file runs.
|
|
6
6
|
|
|
7
7
|
The env vars are set to empty strings rather than deleted, because
|
|
8
8
|
dotenv does not override existing env vars. This prevents the .env
|
|
9
|
-
file loaded by index.
|
|
9
|
+
file loaded by index.ts from re-arming the alert configuration.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
for (const key of ['MANAGER_EMAIL', 'ALERT_API_HOST', 'ALERT_API_PATH', 'ALERT_API_KEY', 'ALERT_FROM']) {
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"allowImportingTsExtensions": true,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"checkJs": false,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"exclude": ["node_modules", "coverage"]
|
|
16
|
+
}
|