feat: add agent API and operational analytics
This commit is contained in:
1 parent
720ea0652a
commit
e8b730b592
65 files changed
+2650
-1214
No files matched your search
@@ -0,0 +1,52 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { API_OPERATIONS, capabilities, createOpenApiDocument } from '../src/web/api-catalog.js';
|
||||
|
||||
describe('agent API discovery', () => {
|
||||
it('publishes every operation under a unique method and path', () => {
|
||||
const keys = API_OPERATIONS.map((operation) => `${operation.method} ${operation.path}`);
|
||||
expect(new Set(keys).size).toBe(keys.length);
|
||||
expect(keys).toContain('GET /api/v1/overview');
|
||||
expect(keys).toContain('POST /api/v1/control/start');
|
||||
expect(keys).toContain('POST /api/v1/control/stop');
|
||||
expect(keys).toContain('POST /api/v1/jobs');
|
||||
expect(keys).toContain('GET /api/events/stream');
|
||||
});
|
||||
|
||||
it('cannot drift from the Express route modules', () => {
|
||||
const routesDirectory = path.resolve(process.cwd(), 'src/web/routes');
|
||||
const implemented = fs.readdirSync(routesDirectory)
|
||||
.filter((file) => file.endsWith('.ts'))
|
||||
.flatMap((file) => {
|
||||
const source = fs.readFileSync(path.join(routesDirectory, file), 'utf8');
|
||||
return [...source.matchAll(/router\.(get|post|put|delete|patch)\('([^']+)'/g)].map((match) => {
|
||||
const routePath = match[2] === '/healthz' ? match[2] : `/api${match[2]}`;
|
||||
return `${match[1].toUpperCase()} ${routePath.replace(/:([A-Za-z0-9_]+)/g, '{$1}')}`;
|
||||
});
|
||||
});
|
||||
const catalogued = API_OPERATIONS.map((item) => `${item.method} ${item.path}`);
|
||||
|
||||
expect([...new Set(catalogued)].sort()).toEqual([...new Set(implemented)].sort());
|
||||
});
|
||||
|
||||
it('generates OpenAPI with safety metadata and schemas for versioned operations', () => {
|
||||
const document = createOpenApiDocument() as { openapi: string; paths: Record<string, Record<string, Record<string, unknown>>> };
|
||||
expect(document.openapi).toBe('3.1.0');
|
||||
expect(document.paths['/healthz'].get.security).toEqual([]);
|
||||
expect(document.paths['/api/v1/overview'].get['x-access']).toBe('read');
|
||||
expect(document.paths['/api/v1/control/start'].post['x-access']).toBe('control');
|
||||
expect(document.paths['/api/v1/control/start'].post['x-mutates']).toBe(true);
|
||||
expect(document.paths['/api/v1/jobs'].post.requestBody).toBeDefined();
|
||||
expect((document.paths['/api/v1/jobs'].post.responses as Record<string, unknown>)['202']).toBeDefined();
|
||||
});
|
||||
|
||||
it('returns a compact capability catalogue for agents', () => {
|
||||
const result = capabilities();
|
||||
expect(result.authentication.agentScopes).toEqual(['read', 'control', 'admin']);
|
||||
expect(result.operations.find((item) => item.operationId === 'sendReply')).toMatchObject({
|
||||
access: 'admin',
|
||||
mutates: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user