feat(operations): add audited runtime catalog
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { OperationRegistry, operationMetadata58e2204, requireOperation } from '../src/index.ts';
|
||||
import { operationAcceptanceOverrides58e2204 } from '../src/acceptance-58e2204.ts';
|
||||
import { upstream58e2204Operations } from '../src/upstream-58e2204.ts';
|
||||
|
||||
test('explicit reviewed metadata covers every operation and preserves domain acronyms', () => {
|
||||
assert.equal(Object.keys(operationMetadata58e2204).length, 117);
|
||||
assert.deepEqual(
|
||||
Object.keys(operationMetadata58e2204).toSorted(),
|
||||
upstream58e2204Operations.map(({ operationId }) => operationId).toSorted(),
|
||||
);
|
||||
for (const [operationId, metadata] of Object.entries(operationMetadata58e2204)) {
|
||||
assert.match(metadata.title, /\S/);
|
||||
assert.equal(metadata.parameterSchemaId, `simadmin.58e2204.${operationId}.parameters.v1`);
|
||||
}
|
||||
const titles = Object.values(operationMetadata58e2204)
|
||||
.map(({ title }) => title)
|
||||
.join(' ');
|
||||
for (const acronym of ['SMS', 'APN', 'DDNS', 'WLAN', 'IMS', 'OTA', 'LPAC', 'eUICC', 'ICCID']) {
|
||||
assert.match(titles, new RegExp(`\\b${acronym}\\b`), acronym);
|
||||
}
|
||||
});
|
||||
|
||||
test('constructor rejects missing/extra/blank/malformed metadata and freezes metadata fields', () => {
|
||||
const rows = upstream58e2204Operations;
|
||||
const missing = { ...operationMetadata58e2204 } as Record<string, unknown>;
|
||||
delete missing.getDevice;
|
||||
assert.throws(
|
||||
() => new OperationRegistry(rows, operationAcceptanceOverrides58e2204, missing as never),
|
||||
/missing operation metadata: getDevice/,
|
||||
);
|
||||
const extra = {
|
||||
...operationMetadata58e2204,
|
||||
inventedOperation: {
|
||||
title: 'Invented',
|
||||
parameterSchemaId: 'simadmin.58e2204.inventedOperation.parameters.v1',
|
||||
},
|
||||
};
|
||||
assert.throws(
|
||||
() => new OperationRegistry(rows, operationAcceptanceOverrides58e2204, extra),
|
||||
/extra operation metadata: inventedOperation/,
|
||||
);
|
||||
const blank = {
|
||||
...operationMetadata58e2204,
|
||||
getDevice: { ...operationMetadata58e2204.getDevice, title: ' ' },
|
||||
};
|
||||
assert.throws(
|
||||
() => new OperationRegistry(rows, operationAcceptanceOverrides58e2204, blank),
|
||||
/blank operation title: getDevice/,
|
||||
);
|
||||
const malformed = {
|
||||
...operationMetadata58e2204,
|
||||
getDevice: { ...operationMetadata58e2204.getDevice, parameterSchemaId: '../schema' },
|
||||
};
|
||||
assert.throws(
|
||||
() => new OperationRegistry(rows, operationAcceptanceOverrides58e2204, malformed),
|
||||
/invalid parameter schema id: getDevice/,
|
||||
);
|
||||
const descriptor = requireOperation('getDevice');
|
||||
assert.equal(descriptor.title, 'Get Device');
|
||||
assert.equal(descriptor.parameterSchemaId, 'simadmin.58e2204.getDevice.parameters.v1');
|
||||
assert.ok(Object.isFrozen(descriptor));
|
||||
});
|
||||
@@ -10,7 +10,25 @@ import {
|
||||
} from '../src/index.ts';
|
||||
import { upstream58e2204Operations } from '../src/upstream-58e2204.ts';
|
||||
import { operationAcceptanceOverrides58e2204 } from '../src/acceptance-58e2204.ts';
|
||||
import { operationMetadata58e2204 } from '../src/catalog-metadata-58e2204.ts';
|
||||
|
||||
test('generated API catalog is an exact safe projection of the runtime registry', async () => {
|
||||
const { default: generated } = await import(
|
||||
'../../../apps/api/src/application/operations/operation-catalog-58e2204.json',
|
||||
{ with: { type: 'json' } }
|
||||
);
|
||||
const expected = listOperations().map(
|
||||
({ operationId, title, riskLevel, capability, batchable, parameterSchemaId }) => ({
|
||||
operationId,
|
||||
title,
|
||||
riskLevel,
|
||||
capability,
|
||||
batchable,
|
||||
parameterSchemaId,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(generated, expected);
|
||||
});
|
||||
test('runtime registry projects exactly 117 descriptors in stable operationId order', () => {
|
||||
const listed = listOperations();
|
||||
assert.equal(listed.length, 117);
|
||||
@@ -34,13 +52,18 @@ test('runtime registry projects exactly 117 descriptors in stable operationId or
|
||||
test('runtime construction rejects duplicate rows and missing acceptance overrides', () => {
|
||||
const rows = upstream58e2204Operations as readonly (typeof upstream58e2204Operations)[number][];
|
||||
assert.throws(
|
||||
() => new OperationRegistry([...rows, rows[0]], operationAcceptanceOverrides58e2204),
|
||||
() =>
|
||||
new OperationRegistry(
|
||||
[...rows, rows[0]],
|
||||
operationAcceptanceOverrides58e2204,
|
||||
operationMetadata58e2204,
|
||||
),
|
||||
/duplicate operationId: getDevice/,
|
||||
);
|
||||
const missing = { ...operationAcceptanceOverrides58e2204 };
|
||||
delete (missing as Record<string, unknown>).getDevice;
|
||||
assert.throws(
|
||||
() => new OperationRegistry(rows, missing),
|
||||
() => new OperationRegistry(rows, missing, operationMetadata58e2204),
|
||||
/missing acceptance override: getDevice/,
|
||||
);
|
||||
});
|
||||
@@ -86,6 +109,18 @@ test('dedicated auth flows are catalogued but cannot cross executable boundary',
|
||||
id,
|
||||
);
|
||||
}
|
||||
const malformed = new OperationRegistry(
|
||||
upstream58e2204Operations.map((row) =>
|
||||
row.operationId === 'getDevice' ? { ...row, executionPolicy: 'futurePolicy' } : row,
|
||||
),
|
||||
operationAcceptanceOverrides58e2204,
|
||||
operationMetadata58e2204,
|
||||
);
|
||||
assert.throws(
|
||||
() => malformed.requireExecutableOperation('getDevice'),
|
||||
(error: unknown) =>
|
||||
error instanceof RuntimeRegistryError && error.code === 'DEDICATED_FLOW_REQUIRED',
|
||||
);
|
||||
assert.strictEqual(requireExecutableOperation('getDevice'), requireOperation('getDevice'));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user