153 lines
5.6 KiB
TypeScript
153 lines
5.6 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { generateKeyPairSync } from 'node:crypto';
|
|
import { HttpNodeRegistrationClaimSource } from './node-registration-client.js';
|
|
import {
|
|
parseNodeRegistrationEndpointRegistry,
|
|
resolveNodeRegistrationEndpoint,
|
|
} from './node-registration.js';
|
|
|
|
const NOW = 1_786_291_200_000;
|
|
const COMMIT = 'a'.repeat(40);
|
|
const { publicKey } = generateKeyPairSync('ed25519');
|
|
|
|
function registeredEndpoint() {
|
|
const registry = parseNodeRegistrationEndpointRegistry({
|
|
endpoints: [{
|
|
algorithm: 'Ed25519',
|
|
domainIds: ['DOM-FIFTH-0001'],
|
|
endpointId: 'GH-NODE-REG-001',
|
|
nodeTypes: ['local-terminal'],
|
|
publicKeyPem: publicKey.export({ format: 'pem', type: 'spki' }).toString(),
|
|
signerId: 'GH-NODE-REG-SIGNER-001',
|
|
status: 'ACTIVE',
|
|
url: 'https://guanghulab.com/api/ai/v1/node-registrations/claims',
|
|
}],
|
|
registryId: 'GH-AIOS-NODE-REGISTRATION-ENDPOINTS-001',
|
|
schema: 'gh-aios.node-registration-endpoints/v1',
|
|
state: 'CURRENT',
|
|
version: '1.0.0',
|
|
}, {
|
|
repositoryId: 'REPO-012',
|
|
sourceCommit: COMMIT,
|
|
sourceUrl: `https://guanghulab.com/code/bingshuo/guanghu-ice-heart/raw/commit/${COMMIT}/routing/node-registration-endpoints.json`,
|
|
});
|
|
const endpoint = resolveNodeRegistrationEndpoint(registry, {
|
|
domainId: 'DOM-FIFTH-0001',
|
|
nodeType: 'local-terminal',
|
|
});
|
|
assert.ok(endpoint);
|
|
return endpoint;
|
|
}
|
|
|
|
function lookup(endpoint = registeredEndpoint()) {
|
|
return {
|
|
accountId: 'bingshuo',
|
|
domainId: 'DOM-FIFTH-0001',
|
|
endpoint,
|
|
nodeId: 'LOCAL-001',
|
|
nodeType: 'local-terminal' as const,
|
|
};
|
|
}
|
|
|
|
function responseBody(status: 'ISSUED' | 'NOT_FOUND' = 'ISSUED', overrides: Record<string, unknown> = {}) {
|
|
return {
|
|
claim: status === 'ISSUED' ? { signed: 'claim' } : null,
|
|
requestId: 'NODE-REQ-001',
|
|
schema: 'gh-aios.node-registration-claim-response/v1',
|
|
status,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function jsonResponse(body: unknown, init: ResponseInit = {}) {
|
|
return new Response(JSON.stringify(body), {
|
|
status: 200,
|
|
...init,
|
|
headers: { 'Content-Type': 'application/json', ...init.headers },
|
|
});
|
|
}
|
|
|
|
test('posts an exact credentialless claim lookup only to the registered endpoint', async () => {
|
|
let observedUrl = '';
|
|
let observedInit: RequestInit | undefined;
|
|
const source = new HttpNodeRegistrationClaimSource(async (url, init) => {
|
|
observedUrl = url;
|
|
observedInit = init;
|
|
return jsonResponse(responseBody());
|
|
}, () => NOW, () => 'NODE-REQ-001');
|
|
|
|
const claim = await source.read(lookup());
|
|
|
|
assert.deepEqual(claim, { signed: 'claim' });
|
|
assert.equal(observedUrl, 'https://guanghulab.com/api/ai/v1/node-registrations/claims');
|
|
assert.equal(observedInit?.method, 'POST');
|
|
assert.equal(observedInit?.credentials, 'omit');
|
|
assert.equal(observedInit?.redirect, 'manual');
|
|
assert.equal(observedInit?.cache, 'no-store');
|
|
assert.equal(observedInit?.referrerPolicy, 'no-referrer');
|
|
assert.deepEqual(JSON.parse(String(observedInit?.body)), {
|
|
accountId: 'bingshuo',
|
|
domainId: 'DOM-FIFTH-0001',
|
|
endpointId: 'GH-NODE-REG-001',
|
|
issuedAt: NOW,
|
|
nodeId: 'LOCAL-001',
|
|
nodeType: 'local-terminal',
|
|
requestId: 'NODE-REQ-001',
|
|
schema: 'gh-aios.node-registration-claim-request/v1',
|
|
});
|
|
const headers = observedInit?.headers as Record<string, string>;
|
|
assert.equal(headers.Authorization, undefined);
|
|
assert.equal(headers.Cookie, undefined);
|
|
});
|
|
|
|
test('returns null only for an exact NOT_FOUND response', async () => {
|
|
const source = new HttpNodeRegistrationClaimSource(
|
|
async () => jsonResponse(responseBody('NOT_FOUND')),
|
|
() => NOW,
|
|
() => 'NODE-REQ-001',
|
|
);
|
|
assert.equal(await source.read(lookup()), null);
|
|
});
|
|
|
|
test('rejects caller-created endpoints before any network request', async () => {
|
|
let called = false;
|
|
const source = new HttpNodeRegistrationClaimSource(async () => {
|
|
called = true;
|
|
return jsonResponse(responseBody());
|
|
}, () => NOW, () => 'NODE-REQ-001');
|
|
const forged = { ...registeredEndpoint(), url: 'https://guanghulab.com/api/ai/v1/other' };
|
|
|
|
await assert.rejects(source.read(lookup(forged)), /transport_invalid/);
|
|
assert.equal(called, false);
|
|
});
|
|
|
|
test('rejects redirects, non-json responses, oversized bodies and mismatched envelopes', async () => {
|
|
const cases: Array<() => Response> = [
|
|
() => new Response(null, { status: 302, headers: { Location: 'https://guanghulab.com/api/ai/v1/other' } }),
|
|
() => new Response('{}', { status: 200, headers: { 'Content-Type': 'text/plain' } }),
|
|
() => new Response('x', { status: 200, headers: { 'Content-Type': 'application/json', 'Content-Length': '65537' } }),
|
|
() => new Response(`"${'x'.repeat(65_536)}"`, { status: 200, headers: { 'Content-Type': 'application/json' } }),
|
|
() => jsonResponse(responseBody('ISSUED', { requestId: 'NODE-REQ-OTHER' })),
|
|
() => jsonResponse({ ...responseBody(), extra: true }),
|
|
() => jsonResponse(responseBody('NOT_FOUND', { claim: {} })),
|
|
];
|
|
for (const makeResponse of cases) {
|
|
const source = new HttpNodeRegistrationClaimSource(
|
|
async () => makeResponse(),
|
|
() => NOW,
|
|
() => 'NODE-REQ-001',
|
|
);
|
|
await assert.rejects(source.read(lookup()), /transport_invalid/);
|
|
}
|
|
});
|
|
|
|
test('reduces network errors to one non-sensitive transport failure', async () => {
|
|
const source = new HttpNodeRegistrationClaimSource(async () => {
|
|
throw new Error('secret endpoint detail');
|
|
}, () => NOW, () => 'NODE-REQ-001');
|
|
await assert.rejects(source.read(lookup()), error => {
|
|
assert.equal((error as Error).message, 'node_registration_claim_transport_invalid');
|
|
return true;
|
|
});
|
|
});
|