import { describe, expect, it } from 'vitest'; import { operationCatalogRegistry } from './operation-catalog-data.js'; import { OperationCatalogService } from './operation-catalog-service.js'; const service = new OperationCatalogService(operationCatalogRegistry); describe('OperationCatalogService', () => { it('returns a frozen contract-safe default page with six fields only', () => { const page = service.list({}); expect(page.page).toEqual({ page: 1, pageSize: 25, total: 117 }); expect(page.items).toHaveLength(25); expect(Object.keys(page.items[0]!)).toEqual([ 'operationId', 'title', 'risk', 'capability', 'batchable', 'parameterSchemaId', ]); expect(page.items.map(({ operationId }) => operationId)).toEqual( [...page.items.map(({ operationId }) => operationId)].sort(), ); expect(Object.isFrozen(page)).toBe(true); expect(Object.isFrozen(page.items)).toBe(true); expect(page.items.every(Object.isFrozen)).toBe(true); }); it('filters all supported fields and searches case-insensitively', () => { const filtered = service.list({ risk: 'R0', capability: 'query', batchable: false, search: 'sms', }); expect(filtered.items.length).toBeGreaterThan(0); expect( filtered.items.every( (item) => item.risk === 'R0' && item.capability === 'query' && !item.batchable && /sms/i.test(`${item.operationId} ${item.title}`), ), ).toBe(true); }); it('keeps operationId ascending as the final tie breaker when descending', () => { for (const sort of ['operationId', 'risk', 'capability'] as const) { const page = service.list({ pageSize: 100, sort, direction: 'desc' }); for (let index = 1; index < page.items.length; index += 1) { const previous = page.items[index - 1]!; const current = page.items[index]!; if (previous[sort] === current[sort]) expect(previous.operationId < current.operationId).toBe(true); } } }); it('returns an empty beyond-last page without changing total', () => { const page = service.list({ page: 999 }); expect(page.items).toEqual([]); expect(page.page.total).toBe(117); }); });