Move the Hub channel table, organization tags and device capability definitions into the workspace packages so the control plane and the console validate the same contract, and refresh the frozen upstream evidence for the new paths.
123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { upstream58e2204Operations } from '../src/upstream-58e2204.ts';
|
|
|
|
const PHASE_0_2_COMMIT = '9dffadbec271227da7ffb192f31dc78c6ee3c2be';
|
|
const BASELINE_PATH = 'packages/operation-registry/src/upstream-58e2204.ts';
|
|
const repoRoot = fileURLToPath(new URL('../../../', import.meta.url));
|
|
const safetyFields = [
|
|
'operationId',
|
|
'riskLevel',
|
|
'confirmationPolicy',
|
|
'capability',
|
|
'executionPolicy',
|
|
] as const;
|
|
type SafetyRow = Record<(typeof safetyFields)[number], string>;
|
|
|
|
function git(...args: string[]) {
|
|
return execFileSync('git', args, {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
}).trim();
|
|
}
|
|
|
|
/** Parse only the JSON array literal assigned to the frozen raw operations constant; never evaluate TypeScript. */
|
|
export function parseFrozenSafetyBaseline(source: string): SafetyRow[] {
|
|
const marker = 'const rawUpstream58e2204Operations = ';
|
|
const start = source.indexOf('[', source.indexOf(marker) + marker.length);
|
|
assert.ok(source.includes(marker) && start >= 0, 'frozen operation array marker missing');
|
|
let quoted = false,
|
|
escaped = false,
|
|
depth = 0,
|
|
end = -1;
|
|
for (let i = start; i < source.length; i++) {
|
|
const ch = source[i];
|
|
if (quoted) {
|
|
if (escaped) escaped = false;
|
|
else if (ch === '\\') escaped = true;
|
|
else if (ch === '"') quoted = false;
|
|
continue;
|
|
}
|
|
if (ch === '"') {
|
|
quoted = true;
|
|
continue;
|
|
}
|
|
if (ch === '[') depth++;
|
|
else if (ch === ']' && --depth === 0) {
|
|
end = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
assert.ok(end > start, 'unterminated frozen operation array');
|
|
const parsed = JSON.parse(source.slice(start, end));
|
|
assert.ok(Array.isArray(parsed), 'frozen operation baseline is not an array');
|
|
return parsed.map(
|
|
(row: any, index: number) =>
|
|
Object.fromEntries(
|
|
safetyFields.map((field) => {
|
|
assert.equal(typeof row?.[field], 'string', `baseline row ${index} missing ${field}`);
|
|
return [field, row[field]];
|
|
}),
|
|
) as SafetyRow,
|
|
);
|
|
}
|
|
|
|
export function assertExactSafetyBaseline(
|
|
actual: readonly SafetyRow[],
|
|
expected: readonly SafetyRow[],
|
|
) {
|
|
assert.equal(expected.length, 117, 'independent baseline must contain exactly 117 operations');
|
|
assert.equal(
|
|
new Set(expected.map((x) => x.operationId)).size,
|
|
117,
|
|
'independent baseline operationIds must be unique',
|
|
);
|
|
assert.equal(actual.length, 117, 'current Registry must contain exactly 117 operations');
|
|
assert.equal(
|
|
new Set(actual.map((x) => x.operationId)).size,
|
|
117,
|
|
'current Registry operationIds must be unique',
|
|
);
|
|
const sort = (rows: readonly SafetyRow[]) =>
|
|
[...rows].sort((a, b) => a.operationId.localeCompare(b.operationId));
|
|
assert.deepEqual(
|
|
sort(actual),
|
|
sort(expected),
|
|
'current Registry safety fields differ from frozen Phase 0.2 Git object; update requires explicit safety review',
|
|
);
|
|
}
|
|
|
|
const project = (rows: readonly any[]): SafetyRow[] =>
|
|
rows.map(
|
|
(row) => Object.fromEntries(safetyFields.map((field) => [field, row[field]])) as SafetyRow,
|
|
);
|
|
|
|
test('independent Phase 0.2 Git object freezes all 117 Registry safety decisions', () => {
|
|
assert.equal(git('rev-parse', '9dffadb'), PHASE_0_2_COMMIT);
|
|
assert.equal(git('cat-file', '-t', PHASE_0_2_COMMIT), 'commit');
|
|
const baseline = parseFrozenSafetyBaseline(git('show', `${PHASE_0_2_COMMIT}:${BASELINE_PATH}`));
|
|
assertExactSafetyBaseline(project(upstream58e2204Operations), baseline);
|
|
});
|
|
|
|
test('independent safety comparison rejects risk, confirmation, capability and execution-policy mutations', () => {
|
|
const baseline = parseFrozenSafetyBaseline(git('show', `${PHASE_0_2_COMMIT}:${BASELINE_PATH}`));
|
|
const mutations: [string, string, string][] = [
|
|
['postData', 'riskLevel', 'R0'],
|
|
['postSmsSend', 'confirmationPolicy', 'none'],
|
|
['postSmsSend', 'capability', 'query'],
|
|
['postSmsSend', 'executionPolicy', 'dedicatedFlow'],
|
|
];
|
|
for (const [operationId, field, value] of mutations) {
|
|
const current = structuredClone(project(upstream58e2204Operations));
|
|
(current.find((x) => x.operationId === operationId)! as any)[field] = value;
|
|
assert.throws(
|
|
() => assertExactSafetyBaseline(current, baseline),
|
|
/safety fields differ/,
|
|
`${operationId}:${field}`,
|
|
);
|
|
}
|
|
});
|