blob: 3f88804253cf4655f1d888e4d7d712b57833aac8 [file]
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import {
permissionCanonicalOutcome,
runtimePermissionOutcome,
} from '../server/interaction-projection.js';
describe('Runtime Host Interaction projection', () => {
test('round-trips auto-review rationale through the canonical permission outcome', () => {
const runtimeAnswer = {
decision: 'allow',
rememberForTurn: true,
reviewer: 'auto_review',
rationale: 'The requested write is limited to the approved workspace path.',
riskLevel: 'medium',
} as const;
const canonical = permissionCanonicalOutcome(runtimeAnswer, 42);
assert.deepEqual(canonical, {
kind: 'permission_answer',
...runtimeAnswer,
committedAt: 42,
});
assert.deepEqual(runtimePermissionOutcome(canonical), {
kind: 'permission_answer',
answer: runtimeAnswer,
});
});
test('does not inject rationale into a user permission answer', () => {
const canonical = permissionCanonicalOutcome(
{
decision: 'deny',
reviewer: 'user',
},
43,
);
assert.deepEqual(canonical, {
kind: 'permission_answer',
decision: 'deny',
rememberForTurn: false,
reviewer: 'user',
committedAt: 43,
});
assert.deepEqual(runtimePermissionOutcome(canonical), {
kind: 'permission_answer',
answer: {
decision: 'deny',
rememberForTurn: false,
reviewer: 'user',
},
});
assert.equal('rationale' in canonical, false);
});
test('rejects rationale from a non-auto-review runtime answer', () => {
assert.throws(
() =>
permissionCanonicalOutcome(
{
decision: 'allow',
reviewer: 'user',
rationale: 'User answers must not acquire an auto-review rationale.',
},
44,
),
/Only auto-review permission outcomes can include rationale/,
);
});
});