Ctrl K
ComunidadGuías…

contract.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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}`);
    }
  });
});