contract.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, it, expect } from 'vitest';
import * as studio from './studio.mjs';
import { STUDIO_ACTIONS } from './studio-api.mjs';
import {
CONTRACT_ROUTE_COUNT,
CONTRACT_MD_ROUTE_COUNT,
STUDIO_API_ROUTE_COUNT,
CONTRACT_ROUTES,
STUDIO_COMMAND_ALIASES,
aliasArgs,
formatConformanceReport,
routeForAction,
validateContractConformance,
} from './contract.mjs';
describe('CONTRACT routes', () => {
it('defines 68 CONTRACT.md routes plus control-panel supplements', () => {
expect(CONTRACT_MD_ROUTE_COUNT).toBe(68);
expect(STUDIO_API_ROUTE_COUNT).toBe(69);
expect(CONTRACT_ROUTE_COUNT).toBe(69);
expect(CONTRACT_ROUTES).toHaveLength(69);
const ids = CONTRACT_ROUTES.map((r) => r.id);
expect(ids).toEqual([...Array(69)].map((_, i) => i + 1));
});
it('every route has at least one Slate surface', () => {
for (const r of CONTRACT_ROUTES) {
expect(r.commands.length + r.actions.length, `#${r.id} ${r.path}`).toBeGreaterThan(0);
}
});
it('attachment-only routes have no !api action', () => {
const attachmentOnly = [25, 38, 39, 68];
for (const id of attachmentOnly) {
const r = CONTRACT_ROUTES.find((x) => x.id === id)!;
expect(r.actions, `#${id}`).toEqual([]);
expect(r.commands.length, `#${id}`).toBeGreaterThan(0);
}
});
});
describe('validateContractConformance', () => {
it('passes against current studio.mjs and STUDIO_ACTIONS', () => {
const result = validateContractConformance(studio, STUDIO_ACTIONS);
if (!result.ok) {
throw new Error(result.errors.join('\n'));
}
expect(result.ok).toBe(true);
});
it('maps every STUDIO_ACTION (except help) to a route', () => {
for (const key of Object.keys(STUDIO_ACTIONS)) {
if (key === 'help') continue;
expect(routeForAction(key), `action ${key}`).toBeTruthy();
}
});
it('includes render retry (control panel parity)', () => {
const retry = CONTRACT_ROUTES.find((r) => r.id === 69)!;
expect(retry.path).toBe('/api/storyboard/renders/:id/retry');
expect(retry.actions).toContain('render-retry');
expect(retry.commands).toContain('!retry');
});
});
describe('STUDIO_COMMAND_ALIASES', () => {
it('every alias targets a valid action', () => {
for (const [cmd, alias] of Object.entries(STUDIO_COMMAND_ALIASES)) {
expect(STUDIO_ACTIONS[alias.action], `!${cmd}`).toBeTruthy();
}
});
it('builds poll args', () => {
expect(aliasArgs(STUDIO_COMMAND_ALIASES.poll, 'job-1 score-music')).toBe('id:job-1 module:score-music');
expect(aliasArgs(STUDIO_COMMAND_ALIASES.regen, 'r1 s2')).toBe('id:r1 shotId:s2');
expect(aliasArgs(STUDIO_COMMAND_ALIASES.refine, 'tighter pacing')).toBe('message:tighter pacing');
});
});
describe('formatConformanceReport', () => {
it('includes all route ids', () => {
const text = formatConformanceReport({ compact: true });
for (let i = 1; i <= STUDIO_API_ROUTE_COUNT; i++) {
expect(text).toContain(`#${i}`);
}
});
});