Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/awaitTest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@ test('awaitTest response has no web UI equivalents for this request or its ances
assert.equal(similarWeb['closest ancestor request'], null);
});

test('awaitTest reports both orderTest and orderRetest as possible ancestors', async () => {
test('awaitTest reports both orderNewTest and orderRetest as possible ancestors', async () => {
const body = await response(['260101T0000', 'mix']);
const ancestors = (body['this request'] as any)['closest ancestor request'];
assert.equal(ancestors.length, 2);
const names = ancestors.map((a: any) => a['tool name']).sort();
assert.deepEqual(names, ['orderRetest', 'orderTest']);
assert.deepEqual(names, ['orderNewTest', 'orderRetest']);
for (const ancestor of ancestors) {
assert.equal(ancestor.method, 'POST');
}
Expand Down
10 changes: 5 additions & 5 deletions api/awaitTest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
awaitTest.ts
Waits for a job ordered via orderTest or orderRetest to complete, and returns its outcome.
Waits for a job ordered via orderNewTest or orderRetest to complete, and returns its outcome.
*/

// IMPORTS
Expand Down Expand Up @@ -75,18 +75,18 @@ export const response = async (args: string[]) => {
'tool collection': getToolsFacts(),
'tool name': 'awaitTest',
'this request': {
description: 'Wait for a job ordered via the orderTest or orderRetest tool to complete. The timeStamp and jobID parameters are the report identifier that orderTest or orderRetest returned. This call blocks until the report is ready, the job fails, or a maximum wait time elapses.',
description: 'Wait for a job ordered via the orderNewTest or orderRetest tool to complete. The timeStamp and jobID parameters are the report identifier that orderNewTest or orderRetest returned. This call blocks until the report is ready, the job fails, or a maximum wait time elapses.',
method: 'POST',
URL: `${thisHost}/api/awaitTest/${timeStamp}/${jobID}`,
// awaitTest's own parameters do not say whether the job was ordered via orderTest
// awaitTest's own parameters do not say whether the job was ordered via orderNewTest
// or orderRetest, so both are reported as possible ancestors, rather than
// arbitrarily naming just one.
'closest ancestor request': [
{
'tool name': 'orderTest',
'tool name': 'orderNewTest',
description: 'Fulfill or reject my order to test a page about which no report is available yet.',
method: 'POST',
URL: `${thisHost}/api/orderTest`
URL: `${thisHost}/api/orderNewTest`
},
{
'tool name': 'orderRetest',
Expand Down
4 changes: 2 additions & 2 deletions api/listReports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const response = async () => {
'basics about all available reports': reportsBasics,
'how to request that a page with no report be tested': {
method: 'POST',
URL: `${thisHost}/api/requestTest`,
URL: `${thisHost}/api/requestNewTest`,
'request body': {
description: '10- to 100-character description of the page conforming to the naming convention used in this list of reports',
URL: '12- to 300-character URL of the page, including the https:// scheme and any query',
Expand All @@ -62,7 +62,7 @@ export const response = async () => {
'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)'
},
'how a web user can request that the page be tested': {
URL: `${thisHost}/requestTestForm.html`
URL: `${thisHost}/requestNewTestForm.html`
}
};
// Create a response body.
Expand Down
34 changes: 17 additions & 17 deletions api/orderTest.test.ts → api/orderNewTest.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
orderTest.test.ts
Tests for api/orderTest.ts using the fixture corpus.
orderNewTest.test.ts
Tests for api/orderNewTest.ts using the fixture corpus.
*/

// IMPORTS
Expand All @@ -11,7 +11,7 @@ import fs from 'node:fs/promises';
import path from 'node:path';
import {fixtureDBDir} from '../test/dbFixture.ts';

// Blank the alert configuration unconditionally, before ./orderTest.ts (whose orderJob
// Blank the alert configuration unconditionally, before ./orderNewTest.ts (whose orderJob
// calls no alert directly, but whose redirect-rejection branch does) is ever imported
// below, so this file sends no real alert emails even when run directly rather than via
// `npm test`.
Expand Down Expand Up @@ -44,7 +44,7 @@ beforeEach(async () => {
console.log = (...args) => logged.push(args.join(' '));
});

import {response} from './orderTest.ts';
import {response} from './orderNewTest.ts';

after(() => {
console.log = originalLog;
Expand All @@ -58,36 +58,36 @@ after(() => {

// TESTS

test('orderTest rejects an empty description', async () => {
test('orderNewTest rejects an empty description', async () => {
const body = await response(['', 'https://example.com/test', 'A reason that is long enough.']);
assert.ok((body['response content']['details about your order'] as any).error);
});

test('orderTest rejects a reason shorter than 20 characters', async () => {
test('orderNewTest rejects a reason shorter than 20 characters', async () => {
const body = await response(['Ordered Page', 'https://example.com/ordered', 'short']);
const details = body['response content']['details about your order'] as any;
assert.ok(details.error.includes('reason'));
});

test('orderTest rejects a reason longer than 100 characters', async () => {
test('orderNewTest rejects a reason longer than 100 characters', async () => {
const longReason = 'x'.repeat(101);
const body = await response(['Ordered Page', 'https://example.com/ordered', longReason]);
const details = body['response content']['details about your order'] as any;
assert.ok(details.error.includes('reason'));
});

test('orderTest rejects a URL shorter than 12 characters', async () => {
test('orderNewTest rejects a URL shorter than 12 characters', async () => {
const body = await response(['Test Page', 'short', 'A reason that is long enough.']);
assert.ok((body['response content']['details about your order'] as any).error);
});

test('orderTest rejects a syntactically invalid URL with the correct length', async () => {
test('orderNewTest rejects a syntactically invalid URL with the correct length', async () => {
const body = await response(['Test Page', 'not-a-valid-url', 'A reason that is long enough.']);
const details = body['response content']['details about your order'] as any;
assert.ok(details.error.includes('invalid URL'));
});

test('orderTest rejects a private-address URL unless internal targets are allowed', async () => {
test('orderNewTest rejects a private-address URL unless internal targets are allowed', async () => {
delete process.env.ALLOW_INTERNAL_TARGETS;
try {
const body = await response(['Internal Page', 'https://192.168.1.1/page', 'A reason that is long enough.']);
Expand All @@ -98,7 +98,7 @@ test('orderTest rejects a private-address URL unless internal targets are allowe
}
});

test('orderTest rejects a URL that redirects to a disallowed target, and alerts a manager', async (t) => {
test('orderNewTest rejects a URL that redirects to a disallowed target, and alerts a manager', async (t) => {
delete process.env.ALLOW_INTERNAL_TARGETS;
t.mock.method(globalThis, 'fetch', async () => ({url: 'https://10.0.0.5/page'}) as Response);
try {
Expand All @@ -114,7 +114,7 @@ test('orderTest rejects a URL that redirects to a disallowed target, and alerts
}
});

test('orderTest rejects an already-tested page', async () => {
test('orderNewTest rejects an already-tested page', async () => {
const body = await response(['Mixed Outcomes Page', 'https://example.com/mixed', 'A reason that is long enough.']);
const details = body['response content']['details about your order'] as any;
assert.equal(details.error, undefined);
Expand All @@ -126,7 +126,7 @@ test('orderTest rejects an already-tested page', async () => {
assert.equal(disposition['report identifier'], null);
});

test('orderTest rejects a page matching a claimed job by description', async () => {
test('orderNewTest rejects a page matching a claimed job by description', async () => {
const claimedPath = path.join(fixtureDBDir, 'jobs', 'claimed', 'clm.json');
await fs.writeFile(claimedPath, JSON.stringify({
target: {what: 'Claimed Test Page', url: 'https://example.com/claimed-job'}
Expand All @@ -146,7 +146,7 @@ test('orderTest rejects a page matching a claimed job by description', async ()
}
});

test('orderTest rejects a page matching a queued job by URL', async () => {
test('orderNewTest rejects a page matching a queued job by URL', async () => {
const queuedPath = path.join(fixtureDBDir, 'jobs', 'queue', 'que.json');
await fs.writeFile(queuedPath, JSON.stringify({
target: {what: 'Some Other Page', url: 'https://example.com/queued-url'}
Expand All @@ -166,7 +166,7 @@ test('orderTest rejects a page matching a queued job by URL', async () => {
}
});

test('orderTest rejects an order once the job queue is full', async () => {
test('orderNewTest rejects an order once the job queue is full', async () => {
process.env.JOB_QUEUE_MAX = '0';
const fillerPaths = await Promise.all(Array.from({length: 1}, async (_, i) => {
const fillerPath = path.join(fixtureDBDir, 'jobs', 'queue', `fil${i}.json`);
Expand All @@ -187,7 +187,7 @@ test('orderTest rejects an order once the job queue is full', async () => {
}
});

test('orderTest accepts a valid new page order and enqueues a job directly', async () => {
test('orderNewTest accepts a valid new page order and enqueues a job directly', async () => {
const body = await response(['Brand New Ordered Page', 'https://example.com/brandnewordered', 'A reason that is long enough.']);
const details = body['response content']['details about your order'] as any;
assert.equal(details.error, undefined);
Expand All @@ -205,7 +205,7 @@ test('orderTest accepts a valid new page order and enqueues a job directly', asy
assert.equal(job.sources.reason, 'A reason that is long enough.');
});

test('orderTest response omits a web UI URL for this request but includes the ancestor', async () => {
test('orderNewTest response omits a web UI URL for this request but includes the ancestor', async () => {
const body = await response(['Brand New Ordered Page 2', 'https://example.com/brandnewordered2', 'A reason that is long enough.']);
const similarWeb = body['URLs of similar requests for web users'] as any;
assert.equal(similarWeb['this request'], null);
Expand Down
12 changes: 6 additions & 6 deletions api/orderTest.ts → api/orderNewTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
orderTest.ts
orderNewTest.ts
Processes an order to test an untested page immediately, without manual approval, and returns an acknowledgement.
*/

Expand All @@ -8,13 +8,13 @@
import {z} from 'zod';
import {getRequestFailureReason, getResponseMetadata, getThisHost, getToolsFacts} from './util.ts';
import {checkLength, isAllowedRedirectTarget, isAllowedTarget, isURL, orderJob} from '../util.ts';
import {orderTestResponseSchema} from './schemas.ts';
import {orderNewTestResponseSchema} from './schemas.ts';
import {sendAlert} from '../alerts.ts';

// TYPES

// The response content defined by the response schema.
type ResponseContent = z.infer<typeof orderTestResponseSchema>['response content'];
type ResponseContent = z.infer<typeof orderNewTestResponseSchema>['response content'];

// FUNCTIONS

Expand Down Expand Up @@ -100,7 +100,7 @@ export const response = async (args: string[]) => {
requestDisposition = {
'what happens next': `Your order will not be processed, because ${getRequestFailureReason(
orderResult.result,
{result: 'retest', reason: 'a report about a page with the same description and URL is available.'}
{result: 'reportExists', reason: 'a report about a page with the same description and URL is available.'}
)}`,
'report identifier': null,
'how you can check for completion': 'Not applicable.',
Expand All @@ -116,11 +116,11 @@ export const response = async (args: string[]) => {
// Create a response body.
const body = {
'tool collection': getToolsFacts(),
'tool name': 'orderTest',
'tool name': 'orderNewTest',
'this request': {
description: 'Process my order to test a page about which no report is available yet, immediately and without manual approval. I have provided a description and the URL of the page and a reason why it should be tested.',
method: 'POST',
URL: `${thisHost}/api/orderTest`,
URL: `${thisHost}/api/orderNewTest`,
body: {
description,
URL: url,
Expand Down
Loading
Loading