/** * Strict-publish: manifest-claimed versions for known capabilities must * match the framework's runtime registry. Mismatches refuse the publish * and surface as failed checks in `module check`. */ import { describe, expect, test } from 'bun:test'; import { CAPABILITY_CONTRACT_VERSIONS } from '@celilo/capabilities'; import { validateCapabilityVersions } from './capability-versions'; describe('validateCapabilityVersions', () => { test('no errors when no capabilities are declared', () => { expect(validateCapabilityVersions({ id: 'x', version: '1.0.0' })).toEqual([]); }); test('no errors when provides matches the runtime registry exactly', () => { // Read from the registry, not pinned to a literal (see the comment on the // "differs from runtime" test below — the registry moves, the test is // about matching it). const runtime = CAPABILITY_CONTRACT_VERSIONS.public_web; expect( validateCapabilityVersions({ id: 'caddy', version: '3.0.0', provides: { capabilities: [{ name: 'public_web', version: runtime }] }, }), ).toEqual([]); }); test('error when provides[X].version differs from runtime', () => { const errors = validateCapabilityVersions({ id: 'caddy', version: '1.0.0', provides: { capabilities: [{ name: 'public_web', version: '1.0.0' }] }, }); expect(errors).toHaveLength(1); expect(errors[0]).toContain('provides[public_web]'); expect(errors[0]).toContain('1.0.0'); // Read from the registry, not pinned to today's number. The assertion is // "the message names the RUNTIME version so the author knows what to move // to"; a literal here turns a behaviour test into a version tracker that // goes red on every legitimate bump, which is what happened at 3.1.0 → 3.2.0. expect(errors[0]).toContain(CAPABILITY_CONTRACT_VERSIONS.public_web); }); test('error when provides[X].version is newer than runtime', () => { // One minor above the registry is newer without being a different major, // which is what this case tests. Computed from the registry so the test // stays a behaviour test as the registry moves. const [maj, min, pat] = CAPABILITY_CONTRACT_VERSIONS.public_web .split('.') .map((n) => Number.parseInt(n, 10)); const tooNew = `${maj}.${(min ?? 0) + 1}.${pat ?? 0}`; const errors = validateCapabilityVersions({ id: 'caddy', version: '4.0.0', provides: { capabilities: [{ name: 'public_web', version: tooNew }] }, }); expect(errors).toHaveLength(1); expect(errors[0]).toContain('provides[public_web]'); }); test('skips capabilities not in the framework registry (third-party)', () => { expect( validateCapabilityVersions({ id: 'odd', version: '1.0.0', provides: { capabilities: [{ name: 'custom_metric', version: '99.0.0' }] }, }), ).toEqual([]); }); test('error when requires[X].version is a higher major than runtime', () => { const errors = validateCapabilityVersions({ id: 'lunacycle', version: '1.0.0', requires: { capabilities: [{ name: 'idp', version: '2.0.0' }] }, }); expect(errors).toHaveLength(1); expect(errors[0]).toContain('requires[idp]'); }); test('no error when requires[X].version is at most the runtime version', () => { expect( validateCapabilityVersions({ id: 'caddy', version: '1.0.0', requires: { capabilities: [{ name: 'dns_registrar', version: '4.0.0' }] }, }), ).toEqual([]); }); test('reports multiple errors at once', () => { const errors = validateCapabilityVersions({ id: 'broken', version: '1.0.0', provides: { capabilities: [ { name: 'public_web', version: '99.0.0' }, { name: 'idp', version: '99.0.0' }, ], }, }); expect(errors).toHaveLength(2); }); });