@jrpool/kilotest 47.0.1 → 48.0.0
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/api/requestRetest.test.ts +66 -7
- package/api/requestRetest.ts +55 -39
- package/api/requestTest.test.ts +63 -7
- package/api/requestTest.ts +36 -26
- package/api/util.test.ts +6 -5
- package/api/util.ts +2 -24
- package/docs/BACKLOG.md +12 -8
- package/docs/SERVICE.md +32 -6
- package/index.test.ts +20 -12
- package/index.ts +4 -2
- package/package.json +1 -1
- package/util.test.ts +195 -127
- package/util.ts +292 -196
- package/web/listReports/index.ts +33 -12
- package/web/listReports/listReports.test.ts +1 -1
- package/web/reannotate/index.ts +2 -1
- package/web/reportDeletion.ts +2 -2
- package/web/requestRetest/index.html +3 -3
- package/web/requestRetest/index.ts +47 -16
- package/web/requestRetest/requestRetest.test.ts +38 -24
- package/web/requestTest/index.html +4 -4
- package/web/requestTest/index.ts +37 -9
- package/web/requestTest/requestTest.test.ts +21 -6
- package/web/unhideReportForm/index.ts +2 -2
package/index.test.ts
CHANGED
|
@@ -1036,16 +1036,20 @@ test('GET /style.css with a read error returns an error page', async () => {
|
|
|
1036
1036
|
}
|
|
1037
1037
|
});
|
|
1038
1038
|
|
|
1039
|
-
|
|
1039
|
+
// requestTest.html and requestRetest.html are POST-only routes (see the
|
|
1040
|
+
// `routes.POST` list above). They also match the generic '*.html*' GET pattern,
|
|
1041
|
+
// but the generic `pageName.endsWith('.html')` branch of the GET dispatcher
|
|
1042
|
+
// excludes any path that is POST-only, since such a path's answer handler
|
|
1043
|
+
// expects POST's argument list and performs no GET-appropriate rendering.
|
|
1044
|
+
|
|
1045
|
+
test('GET /requestTest.html without arguments is rejected as an invalid GET request', async () => {
|
|
1040
1046
|
const res = await request('GET', '/requestTest.html');
|
|
1041
1047
|
assert.equal(res.statusCode, 400);
|
|
1042
|
-
assert.ok(res.headers['content-type'].includes('text/html'));
|
|
1043
1048
|
});
|
|
1044
1049
|
|
|
1045
|
-
test('GET /requestRetest.html/260202T0000/new
|
|
1050
|
+
test('GET /requestRetest.html/260202T0000/new is rejected as an invalid GET request', async () => {
|
|
1046
1051
|
const res = await request('GET', '/requestRetest.html/260202T0000/new');
|
|
1047
1052
|
assert.equal(res.statusCode, 400);
|
|
1048
|
-
assert.ok(res.headers['content-type'].includes('text/html'));
|
|
1049
1053
|
});
|
|
1050
1054
|
|
|
1051
1055
|
// TESTS: GET HTML page routing
|
|
@@ -1186,34 +1190,38 @@ test('POST /mcp with a valid initialize request returns server info via SSE', as
|
|
|
1186
1190
|
|
|
1187
1191
|
test('POST /requestTest.html with valid format but duplicate URL returns an answer error', {timeout: 500}, async () => {
|
|
1188
1192
|
await fs.writeFile(testRequestsPath, '{}\n');
|
|
1189
|
-
// First request to create the request.
|
|
1193
|
+
// First request to create the request. processTestRequest treats a request as a
|
|
1194
|
+
// duplicate only when description, URL, AND reason all match an existing pending
|
|
1195
|
+
// request, so the reason must be identical between the two requests here.
|
|
1190
1196
|
await formRequest('POST', '/requestTest.html', {
|
|
1191
1197
|
description: `Dup Test Page ${uniqueStamp}`,
|
|
1192
1198
|
url: `https://example.com/dup-${uniqueStamp}`,
|
|
1193
1199
|
why: 'Because accessibility matters'
|
|
1194
1200
|
});
|
|
1195
|
-
// Second request
|
|
1201
|
+
// Second, identical request should get a duplicate error.
|
|
1196
1202
|
const res = await formRequest('POST', '/requestTest.html', {
|
|
1197
1203
|
description: `Dup Test Page ${uniqueStamp}`,
|
|
1198
1204
|
url: `https://example.com/dup-${uniqueStamp}`,
|
|
1199
|
-
why: 'Because accessibility matters
|
|
1205
|
+
why: 'Because accessibility matters'
|
|
1200
1206
|
});
|
|
1201
1207
|
assert.equal(res.statusCode, 400);
|
|
1202
|
-
assert.ok(res.body.includes('
|
|
1208
|
+
assert.ok(res.body.includes('Test request duplicates an already submitted request'));
|
|
1203
1209
|
});
|
|
1204
1210
|
|
|
1205
1211
|
test('POST /requestRetest.html with valid format but duplicate retest returns an answer error', {timeout: 500}, async () => {
|
|
1206
1212
|
await fs.writeFile(testRequestsPath, '{}\n');
|
|
1207
|
-
// First retest to create the recommendation.
|
|
1213
|
+
// First retest to create the recommendation. As above, the reason must match
|
|
1214
|
+
// exactly between the two requests for processTestRequest to treat the second as
|
|
1215
|
+
// a duplicate rather than a second, independent (and thus also 'ok') request.
|
|
1208
1216
|
await formRequest('POST', '/requestRetest.html/260202T0000/new', {
|
|
1209
1217
|
why: 'Because the report is obsolete and needs refreshing'
|
|
1210
1218
|
});
|
|
1211
|
-
// Second retest
|
|
1219
|
+
// Second, identical retest of the same report should get a duplicate error.
|
|
1212
1220
|
const res = await formRequest('POST', '/requestRetest.html/260202T0000/new', {
|
|
1213
|
-
why: 'Because the report is obsolete and needs refreshing
|
|
1221
|
+
why: 'Because the report is obsolete and needs refreshing'
|
|
1214
1222
|
});
|
|
1215
1223
|
assert.equal(res.statusCode, 400);
|
|
1216
|
-
assert.ok(res.body.includes('
|
|
1224
|
+
assert.ok(res.body.includes('Test request duplicates an already submitted request'));
|
|
1217
1225
|
});
|
|
1218
1226
|
|
|
1219
1227
|
test('POST /requestAction.html with valid auth code and approval of a duplicate returns an error', {timeout: 500}, async () => {
|
package/index.ts
CHANGED
|
@@ -533,8 +533,10 @@ const handleRequest = async (request: IncomingMessage, response: ServerResponse)
|
|
|
533
533
|
// Otherwise, if it is for an HTML page other than the home page:
|
|
534
534
|
else if (pageName.endsWith('.html')) {
|
|
535
535
|
const topic = pageName.slice(0, -5);
|
|
536
|
-
// If the page can be generated
|
|
537
|
-
|
|
536
|
+
// If the page can be generated and is not POST-only (a POST-only path also
|
|
537
|
+
// matches the '*.html*' GET pattern, but its handler expects POST's argument
|
|
538
|
+
// list and performs no GET-appropriate rendering):
|
|
539
|
+
if (answer[topic] && !isPathAllowed('POST', pathname)) {
|
|
538
540
|
setHeaders('text/html', pathname, 'ultra');
|
|
539
541
|
// Get the answer data.
|
|
540
542
|
const answerData = await answer[topic](pathTail, search);
|
package/package.json
CHANGED
package/util.test.ts
CHANGED
|
@@ -52,8 +52,7 @@ import {
|
|
|
52
52
|
processTestRequest,
|
|
53
53
|
testRequestsLock,
|
|
54
54
|
testRequestsPath,
|
|
55
|
-
reportsPath
|
|
56
|
-
addTestRequest
|
|
55
|
+
reportsPath
|
|
57
56
|
} from './util.ts';
|
|
58
57
|
|
|
59
58
|
// TESTS
|
|
@@ -366,9 +365,8 @@ test('objectSort sorts objects numerically descending', () => {
|
|
|
366
365
|
|
|
367
366
|
// TESTS FOR processTestRequest AND isReportAvailable
|
|
368
367
|
|
|
369
|
-
// These tests use the fixture database directory
|
|
368
|
+
// These tests use the fixture database directory.
|
|
370
369
|
import {fixtureDBDir as fixtureDbDir} from './test/dbFixture.ts';
|
|
371
|
-
const requestTestDir = path.join(import.meta.dirname, 'web', 'requestTest');
|
|
372
370
|
const savedDbDir = process.env.DB_DIR;
|
|
373
371
|
|
|
374
372
|
before(() => {
|
|
@@ -428,44 +426,15 @@ test('getPageDataStrings uses provided pageData instead of reading the report',
|
|
|
428
426
|
assert.ok(strings.testInfo.includes('1 day ago'));
|
|
429
427
|
});
|
|
430
428
|
|
|
431
|
-
test('processTestRequest
|
|
432
|
-
const result: any = await processTestRequest('invalid', requestTestDir, 'Page', 'https://example.com', 'because');
|
|
433
|
-
assert.equal(result.status, 'error');
|
|
434
|
-
assert.equal(result.message, 'Invalid request');
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test('processTestRequest returns an error for an invalid URL', async () => {
|
|
438
|
-
const result: any = await processTestRequest('test', requestTestDir, 'Page', 'not-a-url', 'because');
|
|
439
|
-
assert.equal(result.status, 'error');
|
|
440
|
-
assert.equal(result.message, 'Invalid request');
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
test('processTestRequest returns an error for a short reason', async () => {
|
|
444
|
-
const result: any = await processTestRequest('test', requestTestDir, 'Page', 'https://example.com', 'abc');
|
|
445
|
-
assert.equal(result.status, 'error');
|
|
446
|
-
assert.equal(result.message, 'Invalid request');
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
test('processTestRequest returns an error for a missing description', async () => {
|
|
450
|
-
const result: any = await processTestRequest('test', requestTestDir, '', 'https://example.com', 'because');
|
|
451
|
-
assert.equal(result.status, 'error');
|
|
452
|
-
assert.equal(result.message, 'Invalid request');
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
test('processTestRequest returns an error for a mismatched directory name', async () => {
|
|
456
|
-
const result: any = await processTestRequest('test', '/tmp/wrongDir', 'Page', 'https://example.com', 'because');
|
|
457
|
-
assert.equal(result.status, 'error');
|
|
458
|
-
assert.equal(result.message, 'Invalid request');
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
test('processTestRequest succeeds and populates the template for a valid request', {timeout: 500}, async () => {
|
|
429
|
+
test('processTestRequest succeeds for a valid new-test request', {timeout: 500}, async () => {
|
|
463
430
|
// Reset testRequests.json to empty before the test.
|
|
464
431
|
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
465
|
-
const result
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
assert.
|
|
432
|
+
const {result} = await processTestRequest(
|
|
433
|
+
'because accessibility', {description: 'Example Page', url: 'https://example.com'}
|
|
434
|
+
);
|
|
435
|
+
assert.equal(result, 'ok');
|
|
436
|
+
const testRequests = JSON.parse(await fs.readFile(testRequestsPath(), 'utf8'));
|
|
437
|
+
assert.equal(testRequests['https://example.com'][0].description, 'Example Page');
|
|
469
438
|
// Clean up testRequests.json.
|
|
470
439
|
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
471
440
|
});
|
|
@@ -473,15 +442,23 @@ test('processTestRequest succeeds and populates the template for a valid request
|
|
|
473
442
|
test('processTestRequest returns a duplicate error for a repeated request', {timeout: 500}, async () => {
|
|
474
443
|
// Reset testRequests.json to empty, then make a successful request.
|
|
475
444
|
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
assert.equal(result
|
|
445
|
+
const target = {description: 'Example Page', url: 'https://example.com'};
|
|
446
|
+
await processTestRequest('because accessibility', target);
|
|
447
|
+
// Repeat the same request (same description, URL, and reason).
|
|
448
|
+
const {result} = await processTestRequest('because accessibility', target);
|
|
449
|
+
assert.equal(result, 'duplicate');
|
|
481
450
|
// Clean up testRequests.json.
|
|
482
451
|
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
483
452
|
});
|
|
484
453
|
|
|
454
|
+
test('processTestRequest returns a retest error when a report already exists for the page', async () => {
|
|
455
|
+
// The fixture database already has a report for this description and URL.
|
|
456
|
+
const {result} = await processTestRequest(
|
|
457
|
+
'because accessibility', {description: 'Mixed Outcomes Page', url: 'https://example.com/mixed'}
|
|
458
|
+
);
|
|
459
|
+
assert.equal(result, 'retest');
|
|
460
|
+
});
|
|
461
|
+
|
|
485
462
|
test('annotateReportObject annotates a report object in place without reading or writing a file', async () => {
|
|
486
463
|
// A report object that was never written to disk (e.g. one just received from a worker,
|
|
487
464
|
// as in index.ts's worker/report handler, which annotates before the first write).
|
|
@@ -558,6 +535,92 @@ test('isReportAvailable returns false for an unknown page and URL', async () =>
|
|
|
558
535
|
assert.equal(result, false);
|
|
559
536
|
});
|
|
560
537
|
|
|
538
|
+
// TESTS FOR processTestRequest's claimed/queued-job detection
|
|
539
|
+
|
|
540
|
+
test('processTestRequest returns "description" for a page matching a claimed job by description', async () => {
|
|
541
|
+
const claimedPath = path.join(jobsPath(), 'claimed', 'clm.json');
|
|
542
|
+
await fs.writeFile(claimedPath, getJSON({
|
|
543
|
+
target: {what: 'Claimed Page', url: 'https://example.com/claimed-job'}
|
|
544
|
+
}));
|
|
545
|
+
try {
|
|
546
|
+
const {result} = await processTestRequest(
|
|
547
|
+
'because', {description: 'Claimed Page', url: 'https://example.com/some-other-url'}
|
|
548
|
+
);
|
|
549
|
+
assert.equal(result, 'description');
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
await fs.unlink(claimedPath);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test('processTestRequest returns "url" for a page matching a claimed job by URL', async () => {
|
|
557
|
+
const claimedPath = path.join(jobsPath(), 'claimed', 'clm.json');
|
|
558
|
+
await fs.writeFile(claimedPath, getJSON({
|
|
559
|
+
target: {what: 'Some Claimed Page', url: 'https://example.com/claimed-url'}
|
|
560
|
+
}));
|
|
561
|
+
try {
|
|
562
|
+
const {result} = await processTestRequest(
|
|
563
|
+
'because', {description: 'A Different Page', url: 'https://example.com/claimed-url'}
|
|
564
|
+
);
|
|
565
|
+
assert.equal(result, 'url');
|
|
566
|
+
}
|
|
567
|
+
finally {
|
|
568
|
+
await fs.unlink(claimedPath);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('processTestRequest returns "description" for a page matching a queued job by description', async () => {
|
|
573
|
+
const queuedPath = path.join(jobsPath(), 'queue', 'que.json');
|
|
574
|
+
await fs.writeFile(queuedPath, getJSON({
|
|
575
|
+
target: {what: 'Queued Page', url: 'https://example.com/queued-job'}
|
|
576
|
+
}));
|
|
577
|
+
try {
|
|
578
|
+
const {result} = await processTestRequest(
|
|
579
|
+
'because', {description: 'Queued Page', url: 'https://example.com/some-other-url'}
|
|
580
|
+
);
|
|
581
|
+
assert.equal(result, 'description');
|
|
582
|
+
}
|
|
583
|
+
finally {
|
|
584
|
+
await fs.unlink(queuedPath);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
test('processTestRequest returns "url" for a page matching a queued job by URL', async () => {
|
|
589
|
+
const queuedPath = path.join(jobsPath(), 'queue', 'que.json');
|
|
590
|
+
await fs.writeFile(queuedPath, getJSON({
|
|
591
|
+
target: {what: 'Some Queued Page', url: 'https://example.com/queued-url'}
|
|
592
|
+
}));
|
|
593
|
+
try {
|
|
594
|
+
const {result} = await processTestRequest(
|
|
595
|
+
'because', {description: 'A Different Page', url: 'https://example.com/queued-url'}
|
|
596
|
+
);
|
|
597
|
+
assert.equal(result, 'url');
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
await fs.unlink(queuedPath);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test('processTestRequest returns "nonreport" when the cited report does not exist', async () => {
|
|
605
|
+
// Cite a timeStamp/jobID that does not correspond to any available report.
|
|
606
|
+
const {result} = await processTestRequest('because', {timeStamp: '999999T9999', jobID: 'xxx'});
|
|
607
|
+
assert.equal(result, 'nonreport');
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
test('processTestRequest throws when a job file in a category directory is defective', async () => {
|
|
611
|
+
const claimedPath = path.join(jobsPath(), 'claimed', 'bad.json');
|
|
612
|
+
await fs.writeFile(claimedPath, 'not valid json');
|
|
613
|
+
try {
|
|
614
|
+
await assert.rejects(
|
|
615
|
+
processTestRequest('because', {description: 'Any Page', url: 'https://example.com/any'}),
|
|
616
|
+
/Failed to process test request/
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
finally {
|
|
620
|
+
await fs.unlink(claimedPath);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
561
624
|
// TESTS FOR REMAINING BRANCH COVERAGE
|
|
562
625
|
|
|
563
626
|
test('getJobNames creates missing job directories and returns empty arrays', async () => {
|
|
@@ -605,6 +668,93 @@ test('getJobNames throws when a job directory is a file, not a directory', async
|
|
|
605
668
|
}
|
|
606
669
|
});
|
|
607
670
|
|
|
671
|
+
test('getJobsData returns an empty array, instead of throwing ENOENT, when its category directory does not yet exist', async () => {
|
|
672
|
+
// Regression test: on a fresh deployment, jobs/claimed and jobs/queue may never
|
|
673
|
+
// have been created yet (no job has ever been claimed or queued there). getJobsData
|
|
674
|
+
// must tolerate that the same way getJobNames does, rather than reading the
|
|
675
|
+
// category directory directly and throwing ENOENT.
|
|
676
|
+
const os = await import('node:os');
|
|
677
|
+
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kilotest-jobsdata-'));
|
|
678
|
+
const savedDbDir = process.env.DB_DIR;
|
|
679
|
+
process.env.DB_DIR = path.join(tmpRoot, 'db');
|
|
680
|
+
try {
|
|
681
|
+
const {getJobsData} = await import('./util.ts');
|
|
682
|
+
const result = await getJobsData('claimed');
|
|
683
|
+
assert.deepEqual(result, []);
|
|
684
|
+
}
|
|
685
|
+
finally {
|
|
686
|
+
process.env.DB_DIR = savedDbDir;
|
|
687
|
+
await fs.rm(tmpRoot, {recursive: true}).catch(() => {});
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test('getJobsData skips a job file that is removed between the directory listing and its read', async (t) => {
|
|
692
|
+
// Regression test: on the deployed server, a worker can complete or reclaim a
|
|
693
|
+
// claimed job (moving or deleting its file) between getJobsData listing the
|
|
694
|
+
// claimed directory and reading a specific file it found there. That race must
|
|
695
|
+
// result in the vanished job simply being skipped, not an uncaught ENOENT.
|
|
696
|
+
const claimedPath = path.join(jobsPath(), 'claimed', 'raced.json');
|
|
697
|
+
await fs.writeFile(claimedPath, getJSON({
|
|
698
|
+
target: {what: 'Raced Page', url: 'https://example.com/raced'}
|
|
699
|
+
}));
|
|
700
|
+
const originalReadFile = fs.readFile;
|
|
701
|
+
t.mock.method(fs, 'readFile', async (filePath: any, ...rest: any[]) => {
|
|
702
|
+
// Simulate the file having been removed by a worker just before this read.
|
|
703
|
+
if (String(filePath) === claimedPath) {
|
|
704
|
+
await fs.unlink(claimedPath);
|
|
705
|
+
const error: any = new Error('ENOENT: no such file or directory');
|
|
706
|
+
error.code = 'ENOENT';
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
return (originalReadFile as any)(filePath, ...rest);
|
|
710
|
+
});
|
|
711
|
+
try {
|
|
712
|
+
const {getJobsData} = await import('./util.ts');
|
|
713
|
+
const result = await getJobsData('claimed');
|
|
714
|
+
assert.ok(!result.some(job => job.description === 'Raced Page'));
|
|
715
|
+
}
|
|
716
|
+
finally {
|
|
717
|
+
t.mock.reset();
|
|
718
|
+
await fs.unlink(claimedPath).catch(() => {});
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
test('getJobsData throws when a job file cannot be read for a reason other than its removal', async () => {
|
|
723
|
+
// A directory where a job file is expected causes fs.readFile to fail with
|
|
724
|
+
// EISDIR, not ENOENT, so getJobsData must still treat it as defective (unlike
|
|
725
|
+
// the ENOENT case above, which is skipped as a normal race with a worker).
|
|
726
|
+
const claimedDirAsFile = path.join(jobsPath(), 'claimed', 'notAFile.json');
|
|
727
|
+
await fs.mkdir(claimedDirAsFile);
|
|
728
|
+
try {
|
|
729
|
+
const {getJobsData} = await import('./util.ts');
|
|
730
|
+
await assert.rejects(getJobsData('claimed'), /defective/);
|
|
731
|
+
}
|
|
732
|
+
finally {
|
|
733
|
+
await fs.rm(claimedDirAsFile, {recursive: true}).catch(() => {});
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
test('processTestRequest succeeds on a fresh deployment with no job directories yet', async () => {
|
|
738
|
+
// Regression test for the same ENOENT scenario, exercised through the actual
|
|
739
|
+
// public interface: a brand-new DB_DIR with no jobs/claimed or jobs/queue
|
|
740
|
+
// directories must not prevent a new-test request from being approved.
|
|
741
|
+
const os = await import('node:os');
|
|
742
|
+
const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'kilotest-freshdeploy-'));
|
|
743
|
+
const savedDbDir = process.env.DB_DIR;
|
|
744
|
+
process.env.DB_DIR = path.join(tmpRoot, 'db');
|
|
745
|
+
try {
|
|
746
|
+
const {processTestRequest: freshProcessTestRequest} = await import('./util.ts');
|
|
747
|
+
const {result} = await freshProcessTestRequest(
|
|
748
|
+
'because accessibility', {description: 'Fresh Deploy Page', url: 'https://example.com/fresh'}
|
|
749
|
+
);
|
|
750
|
+
assert.equal(result, 'ok');
|
|
751
|
+
}
|
|
752
|
+
finally {
|
|
753
|
+
process.env.DB_DIR = savedDbDir;
|
|
754
|
+
await fs.rm(tmpRoot, {recursive: true}).catch(() => {});
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
608
758
|
test('getObject throws for a file that is not valid JSON', async () => {
|
|
609
759
|
const tmpFile = path.join((await import('node:os')).tmpdir(), 'kilotest-test-invalid.json');
|
|
610
760
|
(await import('node:fs')).writeFileSync(tmpFile, 'not json');
|
|
@@ -683,69 +833,6 @@ test('getPOSTData resolves with parsed query for form-urlencoded requests', asyn
|
|
|
683
833
|
assert.equal(result.why, 'Because');
|
|
684
834
|
});
|
|
685
835
|
|
|
686
|
-
test('getRequestability returns "claimed" for a URL in a claimed job', async () => {
|
|
687
|
-
const tmpDir = (await import('node:os')).tmpdir() + '/kilotest-requestability-test';
|
|
688
|
-
const fsSync = await import('node:fs');
|
|
689
|
-
fsSync.mkdirSync(tmpDir + '/jobs/claimed', {recursive: true});
|
|
690
|
-
fsSync.mkdirSync(tmpDir + '/jobs/queue', {recursive: true});
|
|
691
|
-
fsSync.mkdirSync(tmpDir + '/jobs/failed', {recursive: true});
|
|
692
|
-
fsSync.writeFileSync(tmpDir + '/jobs/claimed/job1.json', JSON.stringify({
|
|
693
|
-
target: {url: 'https://example.com/test', what: 'Test Page'}
|
|
694
|
-
}));
|
|
695
|
-
const savedDbDir = process.env.DB_DIR;
|
|
696
|
-
process.env.DB_DIR = tmpDir;
|
|
697
|
-
try {
|
|
698
|
-
const {getRequestability} = await import('./util.ts');
|
|
699
|
-
const result = await getRequestability('https://example.com/test');
|
|
700
|
-
assert.equal(result, 'claimed');
|
|
701
|
-
}
|
|
702
|
-
finally {
|
|
703
|
-
process.env.DB_DIR = savedDbDir;
|
|
704
|
-
fsSync.rmSync(tmpDir, {recursive: true});
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
test('getRequestability returns "queued" for a URL in a queued job', async () => {
|
|
709
|
-
const tmpDir = (await import('node:os')).tmpdir() + '/kilotest-requestability-test';
|
|
710
|
-
const fsSync = await import('node:fs');
|
|
711
|
-
fsSync.mkdirSync(tmpDir + '/jobs/claimed', {recursive: true});
|
|
712
|
-
fsSync.mkdirSync(tmpDir + '/jobs/queue', {recursive: true});
|
|
713
|
-
fsSync.mkdirSync(tmpDir + '/jobs/failed', {recursive: true});
|
|
714
|
-
fsSync.writeFileSync(tmpDir + '/jobs/queue/job1.json', JSON.stringify({
|
|
715
|
-
target: {url: 'https://example.com/test', what: 'Test Page'}
|
|
716
|
-
}));
|
|
717
|
-
const savedDbDir = process.env.DB_DIR;
|
|
718
|
-
process.env.DB_DIR = tmpDir;
|
|
719
|
-
try {
|
|
720
|
-
const {getRequestability} = await import('./util.ts');
|
|
721
|
-
const result = await getRequestability('https://example.com/test');
|
|
722
|
-
assert.equal(result, 'queued');
|
|
723
|
-
}
|
|
724
|
-
finally {
|
|
725
|
-
process.env.DB_DIR = savedDbDir;
|
|
726
|
-
fsSync.rmSync(tmpDir, {recursive: true});
|
|
727
|
-
}
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
test('getRequestability returns empty string for a URL with no matching jobs', async () => {
|
|
731
|
-
const tmpDir = (await import('node:os')).tmpdir() + '/kilotest-requestability-test';
|
|
732
|
-
const fsSync = await import('node:fs');
|
|
733
|
-
fsSync.mkdirSync(tmpDir + '/jobs/claimed', {recursive: true});
|
|
734
|
-
fsSync.mkdirSync(tmpDir + '/jobs/queue', {recursive: true});
|
|
735
|
-
fsSync.mkdirSync(tmpDir + '/jobs/failed', {recursive: true});
|
|
736
|
-
const savedDbDir = process.env.DB_DIR;
|
|
737
|
-
process.env.DB_DIR = tmpDir;
|
|
738
|
-
try {
|
|
739
|
-
const {getRequestability} = await import('./util.ts');
|
|
740
|
-
const result = await getRequestability('https://example.com/no-match');
|
|
741
|
-
assert.equal(result, '');
|
|
742
|
-
}
|
|
743
|
-
finally {
|
|
744
|
-
process.env.DB_DIR = savedDbDir;
|
|
745
|
-
fsSync.rmSync(tmpDir, {recursive: true});
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
|
|
749
836
|
test('isURL returns false for a malformed URL', () => {
|
|
750
837
|
assert.equal(isURL('https://[invalid'), false);
|
|
751
838
|
});
|
|
@@ -988,25 +1075,6 @@ test('testRequestsLock is a function (the lock returned by createLock)', () => {
|
|
|
988
1075
|
assert.equal(typeof testRequestsLock, 'function');
|
|
989
1076
|
});
|
|
990
1077
|
|
|
991
|
-
test('addTestRequest adds a request and returns success', async () => {
|
|
992
|
-
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
993
|
-
const result = await addTestRequest('Test Page', 'https://example.com/test', 'because');
|
|
994
|
-
assert.equal(result.error, undefined);
|
|
995
|
-
const testRequests = JSON.parse(await fs.readFile(testRequestsPath(), 'utf8'));
|
|
996
|
-
assert.ok(testRequests['https://example.com/test']);
|
|
997
|
-
assert.equal(testRequests['https://example.com/test'].length, 1);
|
|
998
|
-
assert.equal(testRequests['https://example.com/test'][0].description, 'Test Page');
|
|
999
|
-
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
test('addTestRequest returns a duplicate error for a repeated request', async () => {
|
|
1003
|
-
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
1004
|
-
await addTestRequest('Test Page', 'https://example.com/test', 'because');
|
|
1005
|
-
const result = await addTestRequest('Test Page', 'https://example.com/test', 'another reason');
|
|
1006
|
-
assert.equal(result.error, 'duplicate');
|
|
1007
|
-
await fs.writeFile(testRequestsPath(), '{}\n');
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
1078
|
test('getReportPath returns the path of a report file', () => {
|
|
1011
1079
|
const result = getReportPath('260101T0000', 'mix');
|
|
1012
1080
|
assert.ok(result.endsWith('260101T0000-mix.json'));
|