import { beforeEach, describe, expect, it, vi } from 'vitest' import type { AssistantRuntimeContext } from '@/lib/assistant-platform' const saveModelTemplateConfigurationMock = vi.hoisted(() => vi.fn(async () => ({ modelKey: 'openai-compatible:oa-1::veo3.1' })), ) vi.mock('@/lib/user-api/model-template/save', () => ({ saveModelTemplateConfiguration: saveModelTemplateConfigurationMock, })) import { apiConfigTemplateSkill } from '@/lib/assistant-platform/skills/api-config-template' function buildRuntimeContext(): AssistantRuntimeContext { return { userId: 'user-1', assistantId: 'api-config-template', context: { providerId: 'openai-compatible:oa-1', }, analysisModelKey: 'openrouter::gpt-5-mini', resolvedModel: { providerId: 'openrouter', providerKey: 'openrouter', modelId: 'gpt-5-mini', }, } } describe('assistant-platform api-config-template skill', () => { beforeEach(() => { vi.clearAllMocks() }) it('returns invalid when template fails schema validation', async () => { const tools = apiConfigTemplateSkill.tools?.(buildRuntimeContext()) expect(tools).toBeTruthy() const saveTool = tools?.saveModelTemplate expect(saveTool).toBeTruthy() if (!saveTool?.execute) { throw new Error('saveModelTemplate.execute is required for test') } const result = await saveTool.execute({ modelId: 'veo3.1', name: 'Veo 3.1', type: 'video', compatMediaTemplate: { version: 1, mediaType: 'video', mode: 'async', create: { method: 'POST', path: '/v2/videos/generations', }, response: { taskIdPath: '$.task_id', }, }, }, {} as never) expect(result.status).toBe('invalid') expect(result.code).toBe('MODEL_TEMPLATE_INVALID') expect(saveModelTemplateConfigurationMock).not.toHaveBeenCalled() }) it('saves template when payload is valid', async () => { const tools = apiConfigTemplateSkill.tools?.(buildRuntimeContext()) expect(tools).toBeTruthy() const saveTool = tools?.saveModelTemplate expect(saveTool).toBeTruthy() if (!saveTool?.execute) { throw new Error('saveModelTemplate.execute is required for test') } const result = await saveTool.execute({ modelId: 'veo3.1', name: 'Veo 3.1', type: 'video', compatMediaTemplate: { version: 1, mediaType: 'video', mode: 'async', create: { method: 'POST', path: '/v2/videos/generations', contentType: 'application/json', bodyTemplate: { model: '{{model}}', prompt: '{{prompt}}', }, }, status: { method: 'GET', path: '/v2/videos/generations/{{task_id}}', }, response: { taskIdPath: '$.task_id', statusPath: '$.status', outputUrlPath: '$.video_url', }, polling: { intervalMs: 3000, timeoutMs: 180000, doneStates: ['done'], failStates: ['failed'], }, }, }, {} as never) expect(result.status).toBe('saved') expect(result.savedModelKey).toBe('openai-compatible:oa-1::veo3.1') expect(saveModelTemplateConfigurationMock).toHaveBeenCalledWith({ userId: 'user-1', providerId: 'openai-compatible:oa-1', modelId: 'veo3.1', name: 'Veo 3.1', type: 'video', template: expect.objectContaining({ mediaType: 'video', }), source: 'ai', }) }) it('saves multiple templates when batch payload is valid', async () => { const tools = apiConfigTemplateSkill.tools?.(buildRuntimeContext()) expect(tools).toBeTruthy() const batchTool = tools?.saveModelTemplates expect(batchTool).toBeTruthy() if (!batchTool?.execute) { throw new Error('saveModelTemplates.execute is required for test') } const result = await batchTool.execute({ models: [ { modelId: 'veo3-fast', name: 'Veo 3 Fast', type: 'video', compatMediaTemplate: { version: 1, mediaType: 'video', mode: 'async', create: { method: 'POST', path: '/video/create', contentType: 'application/json', bodyTemplate: { model: '{{model}}', prompt: '{{prompt}}', images: ['{{image}}'], }, }, status: { method: 'GET', path: '/video/query?id={{task_id}}', }, response: { taskIdPath: '$.id', statusPath: '$.status', outputUrlPath: '$.video_url', }, polling: { intervalMs: 5000, timeoutMs: 600000, doneStates: ['completed'], failStates: ['failed'], }, }, }, { modelId: 'veo3.1-fast', name: 'Veo 3.1 Fast', type: 'video', compatMediaTemplate: { version: 1, mediaType: 'video', mode: 'async', create: { method: 'POST', path: '/video/create', contentType: 'application/json', bodyTemplate: { model: '{{model}}', prompt: '{{prompt}}', images: ['{{image}}'], }, }, status: { method: 'GET', path: '/video/query?id={{task_id}}', }, response: { taskIdPath: '$.id', statusPath: '$.status', outputUrlPath: '$.video_url', }, polling: { intervalMs: 5000, timeoutMs: 600000, doneStates: ['completed'], failStates: ['failed'], }, }, }, ], }, {} as never) expect(result.status).toBe('saved') expect(result.savedModelKeys).toHaveLength(2) expect(saveModelTemplateConfigurationMock).toHaveBeenCalledTimes(2) expect(saveModelTemplateConfigurationMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ modelId: 'veo3-fast', name: 'Veo 3 Fast', providerId: 'openai-compatible:oa-1', userId: 'user-1', type: 'video', source: 'ai', })) expect(saveModelTemplateConfigurationMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ modelId: 'veo3.1-fast', name: 'Veo 3.1 Fast', providerId: 'openai-compatible:oa-1', userId: 'user-1', type: 'video', source: 'ai', })) }) })