142 lines
4.7 KiB
JavaScript
142 lines
4.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import test from 'node:test';
|
|
|
|
import { createGatewayPresenceRoute } from '../../dist/server/http/routes/gatewayPresenceRoute.js';
|
|
|
|
const subscriptionUrl = 'https://subscription.example/0123456789abcdef0123456789abcdef';
|
|
const nonce = '0123456789abcdef0123456789abcdef';
|
|
|
|
function response() {
|
|
return {
|
|
writeHead(status, headers) {
|
|
this.status = status;
|
|
this.headers = headers;
|
|
},
|
|
end(payload) {
|
|
this.payload = JSON.parse(payload);
|
|
},
|
|
};
|
|
}
|
|
|
|
function createHarness(overrides = {}) {
|
|
const events = [];
|
|
const route = createGatewayPresenceRoute({
|
|
appMode: overrides.appMode || 'gateway',
|
|
readState: () => {
|
|
events.push('state');
|
|
if (overrides.stateError) throw overrides.stateError;
|
|
return { subscriptionUrl: overrides.subscriptionUrl ?? subscriptionUrl };
|
|
},
|
|
getHwid: () => {
|
|
events.push('hwid');
|
|
if (overrides.hwidError) throw overrides.hwidError;
|
|
return overrides.gatewayId ?? 'gateway-1';
|
|
},
|
|
});
|
|
return { route, events };
|
|
}
|
|
|
|
test('Gateway presence route preserves query nonce, read order and raw response', async () => {
|
|
const harness = createHarness();
|
|
const res = response();
|
|
assert.equal(await harness.route.handle({
|
|
method: 'GET',
|
|
url: `/api/gateway-presence?source=client&nonce=${nonce}&nonce=ignored`,
|
|
}, res), true);
|
|
assert.deepEqual(harness.events, ['state', 'hwid']);
|
|
assert.equal(res.status, 200);
|
|
assert.equal(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
assert.deepEqual({
|
|
...res.payload,
|
|
proof: '[proof]',
|
|
}, {
|
|
success: true,
|
|
available: true,
|
|
product: 'harbor',
|
|
role: 'gateway',
|
|
protocolVersion: 1,
|
|
gatewayId: 'gateway-1',
|
|
transparentRouting: true,
|
|
proof: '[proof]',
|
|
});
|
|
assert.match(res.payload.proof, /^[a-f0-9]{64}$/);
|
|
});
|
|
|
|
test('Gateway presence route preserves unavailable client and missing identity responses', async () => {
|
|
for (const options of [
|
|
{ appMode: 'client' },
|
|
{ gatewayId: '' },
|
|
{ subscriptionUrl: 'https://subscription.example/public-feed' },
|
|
]) {
|
|
const harness = createHarness(options);
|
|
const res = response();
|
|
await harness.route.handle({
|
|
method: 'GET',
|
|
url: `/api/gateway-presence?nonce=${nonce}`,
|
|
}, res);
|
|
assert.deepEqual(res.payload, {
|
|
success: true,
|
|
available: false,
|
|
product: 'harbor',
|
|
role: options.appMode || 'gateway',
|
|
protocolVersion: 1,
|
|
});
|
|
}
|
|
});
|
|
|
|
test('Gateway presence route reads state and HWID before preserving nonce validation errors', async () => {
|
|
for (const value of [null, 'ABCDEF0123456789ABCDEF0123456789', 'short']) {
|
|
const harness = createHarness();
|
|
const url = value === null
|
|
? '/api/gateway-presence'
|
|
: `/api/gateway-presence?nonce=${value}`;
|
|
await assert.rejects(
|
|
harness.route.handle({ method: 'GET', url }, response()),
|
|
(error) => error.code === 'REQUEST_INVALID',
|
|
);
|
|
assert.deepEqual(harness.events, ['state', 'hwid']);
|
|
}
|
|
});
|
|
|
|
test('Gateway presence route guards before dependencies and propagates dependency errors', async () => {
|
|
for (const [method, url] of [
|
|
['POST', `/api/gateway-presence?nonce=${nonce}`],
|
|
['GET', `/api/gateway-presence/extra?nonce=${nonce}`],
|
|
['GET', `/api/other?nonce=${nonce}`],
|
|
]) {
|
|
const harness = createHarness();
|
|
assert.equal(await harness.route.handle({ method, url }, response()), false);
|
|
assert.deepEqual(harness.events, []);
|
|
}
|
|
|
|
const stateError = new Error('state failed');
|
|
const brokenState = createHarness({ stateError });
|
|
await assert.rejects(
|
|
brokenState.route.handle({ method: 'GET', url: `/api/gateway-presence?nonce=${nonce}` }, response()),
|
|
(error) => error === stateError,
|
|
);
|
|
assert.deepEqual(brokenState.events, ['state']);
|
|
|
|
const hwidError = new Error('hwid failed');
|
|
const brokenHwid = createHarness({ hwidError });
|
|
await assert.rejects(
|
|
brokenHwid.route.handle({ method: 'GET', url: `/api/gateway-presence?nonce=${nonce}` }, response()),
|
|
(error) => error === hwidError,
|
|
);
|
|
assert.deepEqual(brokenHwid.events, ['state', 'hwid']);
|
|
});
|
|
|
|
test('Gateway presence route is the sole HTTP owner', () => {
|
|
const index = readFileSync(new URL('../../src/server/index.ts', import.meta.url), 'utf8');
|
|
const route = readFileSync(
|
|
new URL('../../src/server/http/routes/gatewayPresenceRoute.ts', import.meta.url),
|
|
'utf8',
|
|
);
|
|
assert.match(index, /createGatewayPresenceRoute\(\{/);
|
|
assert.match(index, /gatewayPresenceRoute\.handle\(req, res\)/);
|
|
assert.doesNotMatch(index, /\/api\/gateway-presence|buildGatewayPresence|searchParams\.get\(['"]nonce/);
|
|
assert.match(route, /buildGatewayPresence\(\{/);
|
|
assert.match(route, /searchParams\.get\(['"]nonce['"]\)/);
|
|
});
|