/** * plan-calls.test — the safe-write projection: GETs exact, denied writes exact * (the gate answers before binding), allowed writes authz_only with an empty * body, parameterized routes skipped honestly. */ import { describe, it, expect } from 'vitest'; import { PlanTestSchema, type PlanTest } from '../../lib/plantest-schema.js'; import { assertCall, hasRouteParams, planApiCalls } from '../plan-calls.js'; function makePlan(): PlanTest { return PlanTestSchema.parse({ schema_version: '1.0.0', meta: { application: 'administration', path: 'administration' }, roles: ['admin', 'viewer', 'anonymous'], execution: { screenshots: { out_dir: 'shots' } }, routes: [], endpoints: [ { id: 'get:administration/users', method: 'GET', route: '/api/administration/users', controller: 'UsersController', permission: 'administration.users.read', expected_by_role: { admin: 200, viewer: 200, anonymous: 401 }, }, { id: 'post:administration/users', method: 'POST', route: '/api/administration/users', expected_by_role: { admin: 201, viewer: 403, anonymous: 401 }, }, { id: 'get:administration/users/{id}', method: 'GET', route: '/api/administration/users/{id:guid}', expected_by_role: { admin: 200, viewer: 200, anonymous: 401 }, }, ], }); } describe('hasRouteParams', () => { it('detects {param} and :param but not ports or plain segments', () => { expect(hasRouteParams('/api/users/{id:guid}')).toBe(true); expect(hasRouteParams('/api/users/:id')).toBe(true); expect(hasRouteParams('/api/users')).toBe(false); }); }); describe('planApiCalls', () => { it('classifies modes per (verb × expectation) and skips parameterized routes', () => { const calls = planApiCalls(makePlan(), { includeWriteProbes: true }); expect(calls).toHaveLength(9); const byKey = (id: string, role: string) => calls.find((c) => c.endpointId === id && c.role === role)!; // GET → exact, executed, no body. expect(byKey('get:administration/users', 'admin')).toMatchObject({ mode: 'exact', execute: true, expected: 200 }); // anonymous → flagged, exact 401. expect(byKey('get:administration/users', 'anonymous')).toMatchObject({ anonymous: true, expected: 401 }); // Denied write → exact with an empty body (the gate answers first). expect(byKey('post:administration/users', 'viewer')).toMatchObject({ mode: 'exact', execute: true, body: '{}', expected: 403 }); // Allowed write → authz_only probe. expect(byKey('post:administration/users', 'admin')).toMatchObject({ mode: 'authz_only', execute: true, body: '{}' }); // Param route → honest skip. expect(byKey('get:administration/users/{id}', 'admin')).toMatchObject({ execute: false, reason: 'route_params' }); }); it('carries the plan endpoint permission/controller onto the planned call (absent when the plan has none)', () => { const calls = planApiCalls(makePlan(), { includeWriteProbes: true }); const withPerm = calls.find((c) => c.endpointId === 'get:administration/users' && c.role === 'admin')!; expect(withPerm.permission).toBe('administration.users.read'); expect(withPerm.controller).toBe('UsersController'); const withoutPerm = calls.find((c) => c.endpointId === 'post:administration/users' && c.role === 'admin')!; expect(withoutPerm.permission).toBeUndefined(); expect(withoutPerm.controller).toBeUndefined(); }); it('disabling write probes skips ONLY the allowed writes', () => { const calls = planApiCalls(makePlan(), { includeWriteProbes: false }); const adminPost = calls.find((c) => c.endpointId === 'post:administration/users' && c.role === 'admin')!; const viewerPost = calls.find((c) => c.endpointId === 'post:administration/users' && c.role === 'viewer')!; expect(adminPost).toMatchObject({ execute: false, reason: 'write_probes_disabled' }); expect(viewerPost.execute).toBe(true); // denial stays provable }); it('restricts to the requested roles (unknown ones dropped)', () => { const calls = planApiCalls(makePlan(), { roles: ['viewer', 'ghost'], includeWriteProbes: true }); expect([...new Set(calls.map((c) => c.role))]).toEqual(['viewer']); }); }); describe('assertCall', () => { const exact = { endpointId: 'e', method: 'GET' as const, route: '/x', role: 'admin', expected: 200, mode: 'exact' as const, execute: true, anonymous: false }; const authz = { ...exact, mode: 'authz_only' as const, expected: 201 }; it('exact mode requires equality', () => { expect(assertCall(exact, 200).ok).toBe(true); expect(assertCall(exact, 403).ok).toBe(false); }); it('authz_only fails ONLY on 401/403, flags real 2xx writes, notes validation-only passes', () => { expect(assertCall(authz, 403)).toEqual({ ok: false }); expect(assertCall(authz, 401)).toEqual({ ok: false }); expect(assertCall(authz, 400)).toEqual({ ok: true, note: 'authorized_validation_only' }); // A real 2xx means the empty-body write actually executed — never a silent pass. expect(assertCall(authz, 201)).toEqual({ ok: true, note: 'real_write_executed' }); expect(assertCall(authz, 200)).toEqual({ ok: true, note: 'real_write_executed' }); }); });