import { describe, test, expect, beforeEach } from 'vitest' import { createMockKv, createMockLoader, MockEmbeddingService } from './setup.js' import { WorkerPool } from '../src/backend/worker-pool.js' import { AuthModule } from '../src/auth.js' import { KvStore } from '../src/kv.js' import { handleRequest } from '../src/router.js' describe('Unified Invoke API', () => { let mockKv: KVNamespace let mockLoader: ReturnType let mockEmbed: MockEmbeddingService let pool: WorkerPool let auth: AuthModule let kv: KvStore beforeEach(async () => { mockKv = createMockKv() mockLoader = createMockLoader({ invokeResponse: (workerId, request) => { // Mock external capability responses return new Response(JSON.stringify({ result: 'external success' }), { status: 200, headers: { 'Content-Type': 'application/json' } }) } }) mockEmbed = new MockEmbeddingService() pool = new WorkerPool(mockKv, mockLoader.loader, mockEmbed as any) kv = new KvStore(mockKv) auth = new AuthModule(kv) // Set unified deploy token await auth.setToken('valid-token') }) const makeRequest = (method: string, path: string, options?: { body?: unknown token?: string headers?: Record }): Request => { const url = `https://sigil.shazhou.workers.dev${path}` const headers: Record = { 'Content-Type': 'application/json', ...options?.headers, } if (options?.token) { headers['Authorization'] = `Bearer ${options.token}` } const init: RequestInit = { method, headers, } if (options?.body !== undefined) { init.body = JSON.stringify(options.body) } return new Request(url, init) } // Test builtin capabilities describe('Builtin Capabilities', () => { test('sigil.discover - query capabilities', async () => { // Deploy a test capability first await pool.deploy({ name: 'hello', code: "export default { fetch() { return new Response('hello') } }", type: 'normal' }) const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.discover', params: { q: 'hello', limit: 5 } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result.items).toBeInstanceOf(Array) expect(result.total).toBeGreaterThan(0) }) test('sigil.deploy - deploy capability', async () => { const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.deploy', params: { name: 'test-cap', execute: 'return "hello world"', type: 'normal' } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(201) const result = await response.json() expect(result.capability).toBe('test-cap') }) test('sigil.remove - remove capability', async () => { // Deploy a capability first await pool.deploy({ name: 'test-cap', code: "export default { fetch() { return new Response('test') } }", type: 'normal' }) const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.remove', params: { capability: 'test-cap' } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result.removed).toBe('test-cap') }) test('sigil.inspect - inspect capability', async () => { // Deploy a capability first await pool.deploy({ name: 'hello', code: "export default { fetch() { return new Response('hello') } }", type: 'normal' }) const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.inspect', params: { capability: 'hello' } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result.capability).toBe('hello') expect(result.type).toBe('normal') }) test('sigil.list - list all capabilities', async () => { // Deploy some test capabilities await pool.deploy({ name: 'cap1', code: "export default { fetch() { return new Response('cap1') } }", type: 'normal' }) await pool.deploy({ name: 'cap2', code: "export default { fetch() { return new Response('cap2') } }", type: 'persistent' }) const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.list', params: { limit: 10 } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result.items).toBeInstanceOf(Array) expect(result.items.length).toBeGreaterThanOrEqual(2) }) test('sigil.status - get system status', async () => { const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.status', params: {} } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result).toHaveProperty('backend') expect(result).toHaveProperty('total_slots') }) test('unknown builtin capability should return 404', async () => { const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'sigil.unknown', params: {} } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(404) const result = await response.json() expect(result.error).toContain('Builtin capability not found') }) }) // Test external capabilities describe('External Capabilities', () => { test('should invoke external capability', async () => { // Deploy a capability first await pool.deploy({ name: 'external-cap', code: "export default { fetch() { return new Response(JSON.stringify({result: 'external success'}), {headers: {'Content-Type': 'application/json'}}) } }", type: 'normal' }) const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'external-cap', params: { input: 'test data' } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(200) const result = await response.json() expect(result.result).toBe('external success') }) test('external capability not found should return error', async () => { const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { capability: 'missing-cap', params: {} } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBeGreaterThanOrEqual(400) }) }) // Test error cases describe('Error Cases', () => { test('missing capability field should return 400', async () => { const request = makeRequest('POST', '/_api/invoke', { token: 'valid-token', body: { params: { test: 'data' } } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(400) const result = await response.json() expect(result.error).toBe('capability field is required') }) test('missing auth should return 401', async () => { const request = makeRequest('POST', '/_api/invoke', { body: { capability: 'sigil.status', params: {} } }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(401) }) test('invalid JSON should return 400', async () => { const request = new Request('https://sigil.shazhou.workers.dev/_api/invoke', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer valid-token' }, body: 'invalid json' }) const response = await handleRequest(request, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(response.status).toBe(400) const result = await response.json() expect(result.error).toBe('Invalid JSON in request body') }) }) // Test compatibility with existing routes describe('Backward Compatibility', () => { test('existing routes should still work', async () => { // Test old health route const healthRequest = makeRequest('GET', '/_health') const healthResponse = await handleRequest(healthRequest, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(healthResponse.status).toBe(200) // Test old query route const queryRequest = makeRequest('GET', '/_api/query?q=test', { token: 'valid-token' }) const queryResponse = await handleRequest(queryRequest, { SIGIL_KV: mockKv, backend: pool, auth, kv }) expect(queryResponse.status).toBe(200) }) }) })