/** * Task 103: per-tool shape tests for the three new value-pillar tools * (loop-auto-responder, loop-property-sold, and the split property-intent * tools). Asserts that each tool source references the swagger path family * it claims to cover, and that index.ts registers it with the correct name. */ import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_SRC = resolve(__dirname, '..'); const SWAGGER = JSON.parse( readFileSync(resolve(__dirname, 'loop-swagger.snapshot.json'), 'utf-8'), ) as { paths: Record> }; const indexSrc = readFileSync(resolve(PLUGIN_SRC, 'index.ts'), 'utf-8'); const readSrc = (rel: string) => readFileSync(resolve(PLUGIN_SRC, rel), 'utf-8'); interface ToolSpec { tool: string; source: string; // `sourceFragment` is the literal substring the source file must contain — // typically the stable prefix before a `${}` template variable. sourceFragment: string; swaggerPaths: { method: 'get' | 'post' | 'put'; path: string }[]; } const NEW_TOOLS: ToolSpec[] = [ { tool: 'loop-auto-responder', source: 'tools/auto-responder.ts', sourceFragment: '/marketing/enquiries/auto-responder/', swaggerPaths: [ { method: 'get', path: '/marketing/enquiries/auto-responder/{id}/{key}' }, { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/answers/{key}' }, { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/details/{key}' }, { method: 'put', path: '/marketing/enquiries/auto-responder/{id}/refer/{key}' }, ], }, { tool: 'loop-property-sold', source: 'tools/property-sold.ts', sourceFragment: '/property/residential/sold/', swaggerPaths: [ { method: 'get', path: '/property/residential/sold/{channel}' }, ], }, { tool: 'loop-property-viewing', source: 'tools/property-intent.ts', sourceFragment: '/property/residential/', swaggerPaths: [ { method: 'post', path: '/property/residential/sales/{id}/viewing' }, { method: 'post', path: '/property/residential/lettings/{id}/viewing' }, ], }, { tool: 'loop-property-callback', source: 'tools/property-intent.ts', sourceFragment: '/property/residential/', swaggerPaths: [ { method: 'post', path: '/property/residential/sales/{id}/call-back' }, { method: 'post', path: '/property/residential/lettings/{id}/call-back' }, ], }, { tool: 'loop-property-information', source: 'tools/property-intent.ts', sourceFragment: '/property/residential/', swaggerPaths: [ { method: 'post', path: '/property/residential/sales/{id}/information' }, { method: 'post', path: '/property/residential/lettings/{id}/information' }, ], }, ]; describe('Task 103 new tools', () => { it.each(NEW_TOOLS)('$tool is registered in index.ts', ({ tool }) => { const re = new RegExp(`server\\.tool\\(\\s*"${tool}"`); expect(re.test(indexSrc), `tool ${tool} missing from index.ts`).toBe(true); }); it.each(NEW_TOOLS)('$tool source file references the swagger path family', ({ source, sourceFragment }) => { const src = readSrc(source); expect(src.includes(sourceFragment), `${source} missing literal "${sourceFragment}"`).toBe(true); }); it.each(NEW_TOOLS)('$tool swagger paths exist in the snapshot', ({ swaggerPaths }) => { for (const { method, path } of swaggerPaths) { const op = SWAGGER.paths[path]; expect(op, `path ${path} missing from swagger snapshot`).toBeDefined(); expect(op[method], `${method.toUpperCase()} ${path} missing from swagger snapshot`).toBeDefined(); } }); });