@jrpool/kilotest 46.1.0 → 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.
Files changed (147) hide show
  1. package/.claude/settings.local.json +13 -1
  2. package/.devin/config.local.json +8 -1
  3. package/.github/workflows/ci.yml +0 -3
  4. package/.github/workflows/periodic-smoke-tests.yml +30 -0
  5. package/.github/workflows/publish-mcp.yml +4 -4
  6. package/{alerts.test.js → alerts.test.ts} +17 -29
  7. package/{alerts.js → alerts.ts} +10 -13
  8. package/api/{getReport.test.js → getReport.test.ts} +9 -10
  9. package/api/{getReport.js → getReport.ts} +23 -24
  10. package/api/{listDiagnoses.test.js → listDiagnoses.test.ts} +26 -27
  11. package/api/{listDiagnoses.js → listDiagnoses.ts} +46 -70
  12. package/api/{listIssues.test.js → listIssues.test.ts} +21 -14
  13. package/api/{listIssues.js → listIssues.ts} +75 -74
  14. package/api/{listReports.test.js → listReports.test.ts} +17 -16
  15. package/api/listReports.ts +87 -0
  16. package/api/{listViolators.test.js → listViolators.test.ts} +19 -20
  17. package/api/{listViolators.js → listViolators.ts} +45 -70
  18. package/api/requestFeature.test.ts +54 -0
  19. package/api/{requestFeature.js → requestFeature.ts} +18 -15
  20. package/api/{requestRetest.test.js → requestRetest.test.ts} +33 -28
  21. package/api/{requestRetest.js → requestRetest.ts} +32 -25
  22. package/api/{requestTest.test.js → requestTest.test.ts} +35 -30
  23. package/api/{requestTest.js → requestTest.ts} +35 -28
  24. package/api/{routes.test.js → routes.test.ts} +5 -5
  25. package/api/{routes.js → routes.ts} +6 -6
  26. package/api/{schemas.js → schemas.ts} +69 -50
  27. package/api/{util.test.js → util.test.ts} +31 -11
  28. package/api/{util.js → util.ts} +75 -46
  29. package/api/version.ts +8 -0
  30. package/{apiIntegration.test.js → apiIntegration.test.ts} +13 -14
  31. package/balances.ts +87 -0
  32. package/{checkinfo.js → checkinfo.ts} +24 -20
  33. package/docs/AI-TOOLS.md +4 -4
  34. package/docs/BACKLOG.md +356 -0
  35. package/docs/SERVICE.md +144 -17
  36. package/docs/decisions/002-coverage-tool.md +40 -0
  37. package/docs/decisions/003-adr-threshold.md +34 -0
  38. package/docs/decisions/004-docs-directory.md +23 -0
  39. package/docs/decisions/005-observability.md +28 -0
  40. package/docs/decisions/006-git-workflow.md +28 -0
  41. package/docs/decisions/007-internal-testing.md +34 -0
  42. package/docs/decisions/008-dependencies.md +28 -0
  43. package/docs/decisions/009-qai-integration.md +40 -0
  44. package/docs/decisions/QAI-HISTORICAL-CONTEXT.md +42 -0
  45. package/eslint.config.mjs +27 -0
  46. package/index.html +3 -3
  47. package/{index.test.js → index.test.ts} +462 -137
  48. package/{index.js → index.ts} +388 -283
  49. package/{indexBalances.test.js → indexBalances.test.ts} +21 -17
  50. package/{indexServe.test.js → indexServe.test.ts} +9 -9
  51. package/linkValidation.test.ts +190 -0
  52. package/{mcp.test.js → mcp.test.ts} +53 -26
  53. package/{mcp.js → mcp.ts} +29 -20
  54. package/memory/MEMORY.md +7 -0
  55. package/memory/user_maintainer_context.md +24 -0
  56. package/openapi.yaml +115 -68
  57. package/package.json +35 -15
  58. package/pm2.config.cjs +29 -0
  59. package/{routes.test.js → routes.test.ts} +8 -7
  60. package/{routing.test.js → routing.test.ts} +11 -12
  61. package/scripts/{generate-openapi.js → generate-openapi.ts} +13 -13
  62. package/serve.ts +15 -0
  63. package/smokeTest.ts +234 -0
  64. package/test/dbFixture.ts +21 -0
  65. package/test/fixtures/{buildFixtures.js → buildFixtures.ts} +14 -13
  66. package/test/{setup.cjs → setup.ts} +2 -2
  67. package/tsconfig.json +16 -0
  68. package/{util.test.js → util.test.ts} +350 -257
  69. package/util.ts +921 -0
  70. package/web/ai0BalanceForm/{ai0BalanceForm.test.js → ai0BalanceForm.test.ts} +15 -15
  71. package/web/ai0BalanceForm/{index.js → index.ts} +10 -18
  72. package/web/enqueue/index.ts +56 -0
  73. package/web/enqueueForm/index.html +9 -9
  74. package/web/enqueueForm/index.ts +43 -0
  75. package/web/expungeReportsForm/{expungeReportsForm.test.js → expungeReportsForm.test.ts} +18 -20
  76. package/web/expungeReportsForm/index.ts +18 -0
  77. package/web/hideReportForm/{hideReportForm.test.js → hideReportForm.test.ts} +14 -14
  78. package/web/hideReportForm/{index.js → index.ts} +17 -21
  79. package/web/listDiagnoses/{index.js → index.ts} +48 -58
  80. package/web/listDiagnoses/{listDiagnoses.test.js → listDiagnoses.test.ts} +39 -34
  81. package/web/listIssues/{index.js → index.ts} +67 -92
  82. package/web/listIssues/{listIssues.test.js → listIssues.test.ts} +45 -51
  83. package/web/listReports/index.html +3 -3
  84. package/web/listReports/{index.js → index.ts} +48 -53
  85. package/web/listReports/{listReports.test.js → listReports.test.ts} +41 -43
  86. package/web/listRules/{index.js → index.ts} +18 -23
  87. package/web/listRules/{listRules.test.js → listRules.test.ts} +26 -19
  88. package/web/listTopIssues/{index.js → index.ts} +36 -47
  89. package/web/listTopIssues/{listTopIssues.test.js → listTopIssues.test.ts} +26 -17
  90. package/web/listViolators/{index.js → index.ts} +51 -71
  91. package/web/listViolators/{listViolators.test.js → listViolators.test.ts} +43 -36
  92. package/web/manage/index.ts +22 -0
  93. package/web/pruneReportsForm/index.ts +18 -0
  94. package/web/pruneReportsForm/{pruneReportsForm.test.js → pruneReportsForm.test.ts} +11 -11
  95. package/web/reannotate/index.ts +70 -0
  96. package/web/reannotate/{reannotate.test.js → reannotate.test.ts} +19 -22
  97. package/web/reannotateForm/{index.js → index.ts} +41 -50
  98. package/web/reannotateForm/{reannotateForm.test.js → reannotateForm.test.ts} +13 -16
  99. package/web/renewWCAG/{index.js → index.ts} +9 -9
  100. package/web/renewWCAG/{renewWCAG.test.js → renewWCAG.test.ts} +17 -20
  101. package/web/renewWCAGForm/index.ts +22 -0
  102. package/web/{expungeReportsForm/index.js → reportDeletion.ts} +56 -38
  103. package/web/requestRetest/index.html +7 -7
  104. package/web/requestRetest/index.ts +31 -0
  105. package/web/requestRetest/requestRetest.test.ts +116 -0
  106. package/web/requestRetestForm/{index.js → index.ts} +10 -16
  107. package/web/requestRetestForm/{requestRetestForm.test.js → requestRetestForm.test.ts} +11 -12
  108. package/web/requestTest/index.html +7 -7
  109. package/web/requestTest/{index.js → index.ts} +5 -5
  110. package/web/requestTest/{requestTest.test.js → requestTest.test.ts} +21 -21
  111. package/web/requestTestForm/index.html +4 -4
  112. package/web/requestTestForm/index.ts +21 -0
  113. package/web/requestTestForm/{requestTestForm.test.js → requestTestForm.test.ts} +9 -9
  114. package/web/rewindReportsForm/index.ts +18 -0
  115. package/web/rewindReportsForm/{rewindReportsForm.test.js → rewindReportsForm.test.ts} +11 -11
  116. package/web/tutorialAI/index.html +184 -0
  117. package/web/tutorialAI/index.ts +80 -0
  118. package/web/tutorialAI/tutorialAI.test.ts +192 -0
  119. package/web/{tutorial → tutorialWeb}/index.html +9 -8
  120. package/web/tutorialWeb/index.ts +78 -0
  121. package/web/tutorialWeb/tutorialWeb.test.ts +195 -0
  122. package/web/unhideReportForm/{index.js → index.ts} +17 -21
  123. package/web/unhideReportForm/{unhideReportForm.test.js → unhideReportForm.test.ts} +16 -16
  124. package/aceconfig.js +0 -28
  125. package/api/listReports.js +0 -87
  126. package/api/requestFeature.test.js +0 -58
  127. package/api/version.js +0 -8
  128. package/indexFallback.test.js +0 -33
  129. package/indexWorkers.test.js +0 -79
  130. package/jsconfig.json +0 -8
  131. package/pm2.config.js +0 -18
  132. package/smokeTest.js +0 -126
  133. package/util.js +0 -733
  134. package/web/enqueue/index.js +0 -67
  135. package/web/enqueueForm/index.js +0 -49
  136. package/web/manage/index.js +0 -22
  137. package/web/pruneReportsForm/index.js +0 -123
  138. package/web/reannotate/index.js +0 -58
  139. package/web/renewWCAGForm/index.js +0 -22
  140. package/web/requestRetest/index.js +0 -31
  141. package/web/requestRetest/requestRetest.test.js +0 -119
  142. package/web/requestTestForm/index.js +0 -21
  143. package/web/rewindReportsForm/index.js +0 -121
  144. package/web/tutorial/index.js +0 -63
  145. package/web/tutorial/tutorial.test.js +0 -97
  146. /package/test/fixtures/db/jobs/{recs.json → testRequests.json} +0 -0
  147. /package/web/{tutorial → tutorialWeb}/images/newsletter-form.png +0 -0
@@ -1,17 +1,17 @@
1
1
  /*
2
- unhideReportForm.test.js
3
- Unit tests for web/unhideReportForm/index.js.
2
+ unhideReportForm.test.ts
3
+ Unit tests for web/unhideReportForm/index.ts.
4
4
  */
5
5
 
6
6
  // IMPORTS
7
7
 
8
- const {test, before, after, beforeEach} = require('node:test');
9
- const assert = require('node:assert/strict');
10
- const path = require('node:path');
11
- const fs = require('fs/promises');
12
- const {parse} = require('node-html-parser');
13
- const {answer} = require('./index');
14
- const {reportsPath, hiddenReportsPath} = require('../../util');
8
+ import {test, before, after, beforeEach} from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import path from 'node:path';
11
+ import fs from 'fs/promises';
12
+ import {parse} from 'node-html-parser';
13
+ import {answer} from './index.ts';
14
+ import {reportsPath, hiddenReportsPath} from '../../util.ts';
15
15
 
16
16
  // CONSTANTS
17
17
 
@@ -21,7 +21,7 @@ const hiddenFile = '260101T0007-hid.json';
21
21
 
22
22
  const savedAuthCode = process.env.AUTH_CODE;
23
23
  const savedDBDir = process.env.DB_DIR;
24
- const fixtureDBDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
24
+ import {fixtureDBDir} from '../../test/dbFixture.ts';
25
25
 
26
26
  const getPaths = () => ({
27
27
  hidden: path.join(hiddenReportsPath(), hiddenFile),
@@ -64,7 +64,7 @@ after(async () => {
64
64
  // TESTS
65
65
 
66
66
  test('unhideReportForm displays a list of hidden reports when no submission', async () => {
67
- const result = await answer(null, '');
67
+ const result: any = await answer(null, '');
68
68
  assert.equal(result.status, 'ok');
69
69
  assert.ok(result.answerPage);
70
70
  const html = parse(result.answerPage);
@@ -73,21 +73,21 @@ test('unhideReportForm displays a list of hidden reports when no submission', as
73
73
  });
74
74
 
75
75
  test('unhideReportForm returns an error for an invalid auth code', async () => {
76
- const result = await answer(null, 'authCode=wrong&report=260101T0007-hid');
76
+ const result: any = await answer(null, 'authCode=wrong&report=260101T0007-hid');
77
77
  assert.equal(result.status, 'error');
78
78
  assert.equal(result.message, 'Invalid authorization code');
79
79
  });
80
80
 
81
81
  test('unhideReportForm unhides a report with valid auth code', async () => {
82
82
  const {report} = getPaths();
83
- const result = await answer(null, 'authCode=test-auth-code&report=260101T0007-hid');
83
+ const result: any = await answer(null, 'authCode=test-auth-code&report=260101T0007-hid');
84
84
  assert.equal(result.status, 'ok');
85
85
  // The report should now be in the reports directory.
86
86
  await fs.access(report);
87
87
  });
88
88
 
89
89
  test('unhideReportForm returns an error when unhiding a nonexistent report', async () => {
90
- const result = await answer(null, 'authCode=test-auth-code&report=999999T9999-nope');
90
+ const result: any = await answer(null, 'authCode=test-auth-code&report=999999T9999-nope');
91
91
  assert.equal(result.status, 'error');
92
92
  assert.ok(result.message.includes('Unhiding report'));
93
93
  });
@@ -102,7 +102,7 @@ test('unhideReportForm sorts hidden reports with the same page name by timeStamp
102
102
  const copyPath = path.join(hiddenReportsPath(), '260101T0010-hid2.json');
103
103
  await fs.writeFile(copyPath, JSON.stringify(copy));
104
104
  try {
105
- const result = await answer(null, '');
105
+ const result: any = await answer(null, '');
106
106
  assert.equal(result.status, 'ok');
107
107
  // Both reports should appear in the form.
108
108
  assert.ok(result.answerPage.includes('260101T0007-hid'));
@@ -123,7 +123,7 @@ test('unhideReportForm sorts hidden reports with different page names alphabetic
123
123
  const copyPath = path.join(hiddenReportsPath(), '260101T0011-hid3.json');
124
124
  await fs.writeFile(copyPath, JSON.stringify(copy));
125
125
  try {
126
- const result = await answer(null, '');
126
+ const result: any = await answer(null, '');
127
127
  assert.equal(result.status, 'ok');
128
128
  // Both reports should appear in the form.
129
129
  assert.ok(result.answerPage.includes('260101T0007-hid'));
package/aceconfig.js DELETED
@@ -1,28 +0,0 @@
1
- /*
2
- © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
4
-
5
- Licensed under the MIT License. See LICENSE file at the project root or
6
- https://opensource.org/license/mit/ for details.
7
-
8
- SPDX-License-Identifier: MIT
9
- */
10
-
11
- /*
12
- aceconfig
13
- Configuration for the ibm rule engine.
14
- */
15
-
16
- const os = require('os');
17
-
18
- const tmpDir = os.tmpdir();
19
-
20
- module.exports = {
21
- reportLevels: [
22
- 'violation',
23
- 'recommendation'
24
- ],
25
- cacheFolder: tmpDir,
26
- outputFolder: tmpDir,
27
- puppeteerArgs: ['--no-sandbox', '--disable-setuid-sandbox']
28
- };
@@ -1,87 +0,0 @@
1
- /*
2
- listReports.js
3
- Returns basics about all the available reports.
4
- */
5
-
6
- // IMPORTS
7
-
8
- const {getReportBasics, getResponseMetadata, getToolsFacts, thisHost} = require('./util');
9
- const {getReportExtracts} = require('../util');
10
-
11
- // FUNCTIONS
12
-
13
- // Returns the response body.
14
- exports.response = async () => {
15
- // Initialize the response content.
16
- const responseContent = {
17
- 'basics about all available reports': null,
18
- 'how to request that a page with no report be tested': null,
19
- 'how a web user can request that the page be tested': null
20
- };
21
- // Initialize an array of basics about the reports.
22
- const reportsBasics = [];
23
- // Get extracts of all available reports.
24
- const reportExtracts = await getReportExtracts();
25
- // For each report:
26
- for (const extract of reportExtracts) {
27
- const {error, jobID, timeStamp} = extract;
28
- // Get the basics about it (which may be only an error message).
29
- const reportBasics = await getReportBasics(timeStamp, jobID);
30
- // If this succeeded:
31
- if (!error) {
32
- // Add instructions for getting details to the basics.
33
- reportBasics['how to get details about the report'] = {
34
- method: 'GET',
35
- URL: `${thisHost}/api/listIssues/${timeStamp}/${jobID}`
36
- };
37
- reportBasics['web users can get details about the report at'] = `${thisHost}/listIssues.html/${timeStamp}/${jobID}`;
38
- // Add the basics to the array.
39
- reportsBasics.push(reportBasics);
40
- }
41
- }
42
- // Sort the array by page description and secondarily by completion recency.
43
- reportsBasics.sort((a, b) => {
44
- if (a['tested web page'].description !== b['tested web page'].description) {
45
- return a['tested web page']
46
- .description
47
- .localeCompare(b['tested web page'].description, 'en', {sensitivity: 'base'});
48
- }
49
- return a['completion date and time'].localeCompare(b['completion date and time']);
50
- });
51
- // Add the sorted basics about the reports to the response content.
52
- responseContent['basics about all available reports'] = reportsBasics;
53
- // Add instructions for requesting a test to the response content.
54
- responseContent['how to request that a page with no report be tested'] = {
55
- method: 'POST',
56
- URL: `${thisHost}/api/requestTest`,
57
- 'request body': {
58
- description: '10- to 100-character description of the page conforming to the naming convention used in this list of reports',
59
- URL: '12- to 300-character URL of the page, including the https:// scheme and any query',
60
- reason: '20- to 100-character reason why the page should be tested'
61
- },
62
- 'how to check whether the request has been fulfilled': 'use this listReports tool to determine whether a report about the page has become available (typical wait time: 1 hour to 1 day)'
63
- };
64
- // Add instructions for a web user to request a test to the response content.
65
- responseContent['how a web user can request that the page be tested'] = {
66
- URL: `${thisHost}/requestTestForm.html`
67
- };
68
- // Create a response body.
69
- const body = {
70
- 'tool collection': getToolsFacts(),
71
- 'tool name': 'listReports',
72
- 'this request': {
73
- description: 'Provide basics about all available reports.',
74
- method: 'GET',
75
- URL: `${thisHost}/api/listReports`,
76
- 'closest ancestor request': null
77
- },
78
- 'URLs of similar requests for web users': {
79
- 'this request': `${thisHost}/listReports.html`,
80
- 'closest ancestor request': null
81
- },
82
- 'response metadata': getResponseMetadata(),
83
- 'response content': responseContent
84
- };
85
- // Return it.
86
- return body;
87
- };
@@ -1,58 +0,0 @@
1
- /*
2
- requestFeature.test.js
3
- Tests for api/requestFeature.js with mocked side effects.
4
- */
5
-
6
- // IMPORTS
7
-
8
- const {test, before, beforeEach, after} = require('node:test');
9
- const assert = require('node:assert/strict');
10
- const alerts = require('../alerts');
11
-
12
- // SETUP AND TEARDOWN
13
-
14
- let sendAlertCalls = [];
15
-
16
- before(() => {
17
- // @ts-expect-error: Replacing the real function with a mock for testing.
18
- alerts.sendAlert = async (subject, body) => {
19
- sendAlertCalls.push({subject, body});
20
- };
21
- });
22
-
23
- // Reset the call log before each test, so tests do not depend on execution order.
24
- beforeEach(() => {
25
- sendAlertCalls = [];
26
- });
27
-
28
- // Require requestFeature after the mock is in place, so it captures the mocked sendAlert.
29
- const {response} = require('./requestFeature');
30
-
31
- after(() => {
32
- });
33
-
34
- // TESTS
35
-
36
- test('requestFeature rejects an empty feature request', async () => {
37
- const body = await response(['']);
38
- const details = body['response content']['details about your request'];
39
- assert.ok(details.error);
40
- assert.equal(sendAlertCalls.length, 0);
41
- });
42
-
43
- test('requestFeature accepts a non-empty feature request and notifies the manager', async () => {
44
- const body = await response(['Add a dark mode toggle']);
45
- const details = body['response content']['details about your request'];
46
- assert.equal(details.error, undefined);
47
- assert.ok(details['date and time received']);
48
- assert.equal(details.disposition, 'received and logged; manager notified');
49
- assert.equal(sendAlertCalls.length, 1);
50
- assert.equal(sendAlertCalls[0].subject, 'MCP feature request received');
51
- assert.equal(sendAlertCalls[0].body, 'Add a dark mode toggle');
52
- });
53
-
54
- test('requestFeature includes tool name and metadata', async () => {
55
- const body = await response(['Some feature']);
56
- assert.equal(body['tool name'], 'requestFeature');
57
- assert.ok(body['response metadata'].identifier);
58
- });
package/api/version.js DELETED
@@ -1,8 +0,0 @@
1
- /*
2
- version.js
3
- Single source of truth for the version of the Kilotest API and MCP tool contract, consumed by mcp.js and scripts/generate-openapi.js.
4
- */
5
-
6
- // CONSTANTS
7
-
8
- exports.version = '2.1.0';
@@ -1,33 +0,0 @@
1
- /*
2
- indexFallback.test.js
3
- Tests that index.js loads successfully when PROTOCOL and TESTARO_WORKERS
4
- are not set, exercising the || fallback branches at module load time.
5
- This must be a separate file because index.js reads these env vars
6
- at module load time, and each test file runs in its own process.
7
- */
8
-
9
- // ENVIRONMENT (must be set before requiring index.js)
10
-
11
- const path = require('node:path');
12
- const fixtureDBDir = path.join(__dirname, 'test', 'fixtures', 'db');
13
-
14
- process.env.DB_DIR = fixtureDBDir;
15
- process.env.AUTH_CODE = 'test-auth-code';
16
- // Set PROTOCOL and TESTARO_WORKERS to empty strings (falsy) so that
17
- // the || fallbacks in index.js are exercised. dotenv does not override
18
- // existing env vars, so .env will not replace these.
19
- process.env.PROTOCOL = '';
20
- process.env.TESTARO_WORKERS = '';
21
-
22
- // IMPORTS
23
-
24
- const {test} = require('node:test');
25
- const assert = require('node:assert/strict');
26
- const {requestHandler} = require('./index');
27
-
28
- // TESTS
29
-
30
- test('index.js loads with empty PROTOCOL and TESTARO_WORKERS using fallbacks', () => {
31
- // If index.js loaded successfully, requestHandler is a function.
32
- assert.equal(typeof requestHandler, 'function');
33
- });
@@ -1,79 +0,0 @@
1
- /*
2
- indexWorkers.test.js
3
- Tests for index.js when TESTARO_WORKERS is invalid JSON.
4
- This must be a separate file because index.js reads TESTARO_WORKERS
5
- at module load time.
6
- */
7
-
8
- // ENVIRONMENT (must be set before requiring index.js)
9
-
10
- const path = require('node:path');
11
- const fixtureDBDir = path.join(__dirname, 'test', 'fixtures', 'db');
12
-
13
- process.env.DB_DIR = fixtureDBDir;
14
- process.env.AUTH_CODE = 'test-auth-code';
15
- process.env.TESTARO_WORKERS = 'not valid json';
16
-
17
- // IMPORTS
18
-
19
- const {test} = require('node:test');
20
- const assert = require('node:assert/strict');
21
- const http = require('node:http');
22
- const {requestHandler} = require('./index');
23
-
24
- // CONSTANTS
25
-
26
- const port = 3993;
27
-
28
- // HELPERS
29
-
30
- const request = (method, requestPath, body = null, headers = {}) => new Promise((resolve, reject) => {
31
- const options = {method, host: 'localhost', port, path: requestPath, headers: {...headers}};
32
- let bodyData = '';
33
- if (body) {
34
- bodyData = JSON.stringify(body);
35
- options.headers['content-type'] = 'application/json';
36
- options.headers['content-length'] = Buffer.byteLength(bodyData);
37
- }
38
- const req = http.request(options, response => {
39
- const chunks = [];
40
- response.on('data', chunk => chunks.push(chunk));
41
- response.on('end', () => {
42
- resolve({statusCode: response.statusCode, headers: response.headers, body: Buffer.concat(chunks).toString()});
43
- });
44
- });
45
- req.on('error', reject);
46
- req.end(bodyData || '');
47
- });
48
-
49
- // TESTS
50
-
51
- test('index.js loads with invalid TESTARO_WORKERS and treats workers as empty', () => {
52
- // If index.js loaded successfully, requestHandler is a function.
53
- assert.equal(typeof requestHandler, 'function');
54
- });
55
-
56
- test('POST /worker/job with invalid TESTARO_WORKERS returns 401 for any credentials', async () => {
57
- const server = http.createServer(requestHandler);
58
- await new Promise(resolve => server.listen(port, () => resolve()));
59
- try {
60
- const auth = Buffer.from('worker1:secret1').toString('base64');
61
- const res = await request('POST', '/worker/job', {}, {
62
- authorization: `Basic ${auth}`
63
- });
64
- assert.equal(res.statusCode, 401);
65
- }
66
- finally {
67
- server.closeAllConnections?.();
68
- await new Promise(resolve => {
69
- const timer = setTimeout(() => {
70
- server.closeAllConnections?.();
71
- resolve();
72
- }, 1000);
73
- server.close(() => {
74
- clearTimeout(timer);
75
- resolve();
76
- });
77
- });
78
- }
79
- });
package/jsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "allowJs": true,
4
- "checkJs": true,
5
- "noEmit": true,
6
- "strict": false
7
- }
8
- }
package/pm2.config.js DELETED
@@ -1,18 +0,0 @@
1
- module.exports = {
2
- apps: [
3
- {
4
- name: 'kilotest',
5
- script: 'index.js',
6
- instances: 1,
7
- autorestart: true,
8
- watch: false,
9
- max_memory_restart: '500M',
10
- time: true,
11
- env: {
12
- NODE_ENV: 'production',
13
- BASE_PATH: '/',
14
- DEMO_SSE_DELAY_MS: '100'
15
- }
16
- }
17
- ]
18
- };
package/smokeTest.js DELETED
@@ -1,126 +0,0 @@
1
- /*
2
- smokeTest.js
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).
4
- */
5
-
6
- // IMPORTS
7
-
8
- const https = require('https');
9
- const {routes} = require('./index');
10
-
11
- // CONSTANTS
12
-
13
- const host = process.env.SMOKE_HOST || 'kilotest.com';
14
- // Concrete paths matching each wildcard pattern in the routes table, for smoke testing.
15
- const concretePaths = {
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
- '/robots.txt': '/robots.txt',
31
- '/sitemap.xml': '/sitemap.xml',
32
- '/style.css': '/style.css',
33
- '/swagger.json': '/swagger.json',
34
- '/swagger.yaml': '/swagger.yaml',
35
- '/tutorial/images/*': '/tutorial/images/diagram.png'
36
- },
37
- POST: {
38
- '/api/*': '/api/requestFeature',
39
- '/mcp': '/mcp',
40
- '/reannotate.html': '/reannotate.html',
41
- '/recAction.html': '/recAction.html',
42
- '/renewWCAG.html': '/renewWCAG.html',
43
- '/requestRetest.html/*': '/requestRetest.html/260101T0001/ct',
44
- '/requestTest.html': '/requestTest.html',
45
- '/tutorialComment.html': '/tutorialComment.html',
46
- '/worker/job': '/worker/job',
47
- '/worker/report': '/worker/report'
48
- }
49
- };
50
- // Minimal POST bodies for paths that require them.
51
- const postBodies = {
52
- '/api/requestFeature': {feature: 'smoke test'},
53
- '/mcp': {},
54
- '/reannotate.html': {authCode: 'invalid'},
55
- '/recAction.html': {
56
- target: 'https://smoketest.example.com\tSmoke Test Page', authCode: 'invalid'
57
- },
58
- '/renewWCAG.html': {authCode: 'invalid'},
59
- '/requestRetest.html/260101T0001/ct': {why: 'smoke test'},
60
- '/requestTest.html': {what: 'Smoke Test Page', url: 'https://smoketest.example.com', why: 'smoke test'},
61
- '/tutorialComment.html': {content: 'smoke test'},
62
- '/worker/job': {},
63
- '/worker/report': {}
64
- };
65
-
66
- // FUNCTIONS
67
-
68
- // Sends an HTTPS request and returns the status code and body length.
69
- const sendRequest = (method, requestPath) => new Promise((resolve, reject) => {
70
- const body = method === 'POST' ? JSON.stringify(postBodies[requestPath] || {}) : null;
71
- const options = {
72
- method,
73
- host,
74
- path: requestPath,
75
- headers: body ? {
76
- 'content-type': 'application/json; charset=utf-8',
77
- 'content-length': Buffer.byteLength(body)
78
- } : {}
79
- };
80
- const req = https.request(options, response => {
81
- const chunks = [];
82
- response.on('data', chunk => chunks.push(chunk));
83
- response.on('end', () => {
84
- const responseBody = chunks.join('');
85
- resolve({statusCode: response.statusCode, bodyLength: responseBody.length});
86
- });
87
- });
88
- req.on('error', reject);
89
- req.end(body || '');
90
- });
91
-
92
- // Returns whether a response is a bare Caddy 404 (the failure this test detects).
93
- const isCaddy404 = result => result.statusCode === 404 && result.bodyLength === 0;
94
-
95
- // EXECUTION
96
-
97
- (async () => {
98
- let failures = 0;
99
- for (const method of ['GET', 'POST']) {
100
- console.log(`\n=== ${method} paths ===`);
101
- for (const pattern of routes[method]) {
102
- const requestPath = concretePaths[method][pattern];
103
- if (!requestPath) {
104
- console.log(`FAIL: no concrete path defined for pattern ${pattern}`);
105
- failures++;
106
- continue;
107
- }
108
- try {
109
- const result = await sendRequest(method, requestPath);
110
- if (isCaddy404(result)) {
111
- console.log(`FAIL: ${method} ${requestPath} -> Caddy 404 (path not forwarded)`);
112
- failures++;
113
- }
114
- else {
115
- console.log(`PASS: ${method} ${requestPath} -> ${result.statusCode} (body ${result.bodyLength} bytes)`);
116
- }
117
- }
118
- catch (error) {
119
- console.log(`FAIL: ${method} ${requestPath} -> error: ${error.message}`);
120
- failures++;
121
- }
122
- }
123
- }
124
- console.log(`\n=== ${failures === 0 ? 'All paths forwarded' : `${failures} failure(s)`} ===`);
125
- process.exit(failures === 0 ? 0 : 1);
126
- })();