返回 AiToEarn
aideo.mcp.spec.ts
根目录 / project / aitoearn-backend / apps / aitoearn-ai / src / core / agent / mcp / volcengine / aideo.mcp.spec.ts
1 import type { AiAvailabilityService } from '../../../ai-availability'
2 import { Logger } from '@nestjs/common'
3 import { UserType } from '@yikart/common'
4 import { vi } from 'vitest'
5 import { AideoService } from '../../../ai/aideo'
6 import { AideoTaskStatus } from '../../../ai/libs/volcengine'
7 import { AideoMcp, AideoToolName } from './aideo.mcp'
8
9 describe('aideoMcp', () => {
10 let aideoMcp: AideoMcp
11 let mockLogger: Logger
12 let mockAideoService: vi.Mocked<AideoService>
13 let mockAiAvailability: vi.Mocked<Pick<AiAvailabilityService, 'execute'>>
14
15 const userId = 'test-user-id'
16 const userType = UserType.User
17
18 beforeEach(() => {
19 mockLogger = {
20 debug: vi.fn(),
21 error: vi.fn(),
22 fatal: vi.fn(),
23 } as unknown as Logger
24
25 mockAideoService = {
26 submitAideoTask: vi.fn(),
27 getAideoTask: vi.fn(),
28 } as unknown as vi.Mocked<AideoService>
29
30 mockAiAvailability = {
31 execute: vi.fn().mockImplementation((_ctx: unknown, fn: () => unknown) => (fn as () => Promise<unknown>)()),
32 } as unknown as vi.Mocked<Pick<AiAvailabilityService, 'execute'>>
33
34 aideoMcp = new AideoMcp(mockAideoService, mockAiAvailability as unknown as AiAvailabilityService)
35 // Override the logger for testing
36 Object.defineProperty(aideoMcp, 'logger', { value: mockLogger })
37 })
38
39 describe('createSubmitAideoTaskTool', () => {
40 it('should have correct tool name', () => {
41 const tool = aideoMcp.createSubmitAideoTaskTool(userId, userType)
42 expect(tool.name).toBe(AideoToolName.SubmitAideoTask)
43 })
44
45 it('should call aideoService.submitAideoTask with correct params', async () => {
46 mockAideoService.submitAideoTask.mockResolvedValue({
47 taskId: 'aideo-task-123',
48 })
49
50 const tool = aideoMcp.createSubmitAideoTaskTool(userId, userType)
51 await tool.handler({
52 prompt: 'Translate this video to English',
53 multiInputs: ['https://example.com/video.mp4'],
54 }, {})
55
56 expect(mockAideoService.submitAideoTask).toHaveBeenCalledWith({
57 userId,
58 userType,
59 prompt: 'Translate this video to English',
60 multiInputs: ['https://example.com/video.mp4'],
61 })
62 })
63
64 it('should return success result with task id', async () => {
65 mockAideoService.submitAideoTask.mockResolvedValue({
66 taskId: 'aideo-task-123',
67 })
68
69 const tool = aideoMcp.createSubmitAideoTaskTool(userId, userType)
70 const result = await tool.handler({
71 prompt: 'Translate this video to English',
72 multiInputs: ['https://example.com/video.mp4'],
73 }, {})
74
75 expect(result.isError).toBeUndefined()
76 const textContent = result.content[0] as { type: 'text', text: string }
77 expect(textContent.text).toContain('aideo-task-123')
78 expect(textContent.text).toContain('submitted successfully')
79 })
80
81 it('should handle multiple inputs', async () => {
82 mockAideoService.submitAideoTask.mockResolvedValue({
83 taskId: 'aideo-task-456',
84 })
85
86 const tool = aideoMcp.createSubmitAideoTaskTool(userId, userType)
87 await tool.handler({
88 prompt: 'Merge these videos',
89 multiInputs: [
90 'https://example.com/video1.mp4',
91 'https://example.com/video2.mp4',
92 'https://example.com/video3.mp4',
93 ],
94 }, {})
95
96 expect(mockAideoService.submitAideoTask).toHaveBeenCalledWith({
97 userId,
98 userType,
99 prompt: 'Merge these videos',
100 multiInputs: [
101 'https://example.com/video1.mp4',
102 'https://example.com/video2.mp4',
103 'https://example.com/video3.mp4',
104 ],
105 })
106 })
107 })
108
109 describe('createGetAideoTaskStatusTool', () => {
110 it('should have correct tool name', () => {
111 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
112 expect(tool.name).toBe(AideoToolName.GetAideoTaskStatus)
113 })
114
115 it('should return error when task fails', async () => {
116 mockAideoService.getAideoTask.mockResolvedValue({
117 taskId: 'aideo-task-123',
118 model: 'aideo',
119 status: AideoTaskStatus.Failed,
120 errorMessage: 'Video processing failed',
121 apiResponses: [],
122 createdAt: new Date(),
123 updatedAt: new Date(),
124 } as never)
125
126 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
127 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
128
129 expect(result.isError).toBe(true)
130 const textContent = result.content[0] as { type: 'text', text: string }
131 expect(textContent.text).toContain('failed')
132 expect(textContent.text).toContain('Video processing failed')
133 })
134
135 it('should return translation output URL when completed', async () => {
136 mockAideoService.getAideoTask.mockResolvedValue({
137 taskId: 'aideo-task-123',
138 model: 'aideo',
139 status: AideoTaskStatus.Completed,
140 createdAt: new Date(),
141 updatedAt: new Date(),
142 apiResponses: [
143 {
144 AITranslation: {
145 ProjectInfo: {
146 OutputVideo: {
147 Url: 'https://example.com/translated.mp4',
148 Vid: 'vid-123',
149 },
150 },
151 },
152 },
153 ],
154 } as never)
155
156 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
157 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
158
159 expect(result.isError).toBeUndefined()
160 const textContent = result.content[0] as { type: 'text', text: string }
161 expect(textContent.text).toContain('completed successfully')
162 expect(textContent.text).toContain('https://example.com/translated.mp4')
163 expect(textContent.text).toContain('vid-123')
164 })
165
166 it('should return erase output URL when completed', async () => {
167 mockAideoService.getAideoTask.mockResolvedValue({
168 taskId: 'aideo-task-123',
169 model: 'aideo',
170 status: AideoTaskStatus.Completed,
171 createdAt: new Date(),
172 updatedAt: new Date(),
173 apiResponses: [
174 {
175 Erase: {
176 Output: {
177 Task: {
178 Erase: {
179 File: {
180 url: 'https://example.com/erased.mp4',
181 },
182 },
183 },
184 },
185 },
186 },
187 ],
188 } as never)
189
190 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
191 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
192
193 expect(result.isError).toBeUndefined()
194 const textContent = result.content[0] as { type: 'text', text: string }
195 expect(textContent.text).toContain('completed successfully')
196 expect(textContent.text).toContain('https://example.com/erased.mp4')
197 })
198
199 it('should return highlight clips when completed', async () => {
200 mockAideoService.getAideoTask.mockResolvedValue({
201 taskId: 'aideo-task-123',
202 model: 'aideo',
203 status: AideoTaskStatus.Completed,
204 createdAt: new Date(),
205 updatedAt: new Date(),
206 apiResponses: [
207 {
208 Highlight: {
209 Edits: [
210 { url: 'https://example.com/clip1.mp4' },
211 { url: 'https://example.com/clip2.mp4' },
212 { url: 'https://example.com/clip3.mp4' },
213 ],
214 },
215 },
216 ],
217 } as never)
218
219 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
220 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
221
222 expect(result.isError).toBeUndefined()
223 const textContent = result.content[0] as { type: 'text', text: string }
224 expect(textContent.text).toContain('completed successfully')
225 expect(textContent.text).toContain('Highlight Clips')
226 expect(textContent.text).toContain('https://example.com/clip1.mp4')
227 expect(textContent.text).toContain('https://example.com/clip2.mp4')
228 expect(textContent.text).toContain('https://example.com/clip3.mp4')
229 })
230
231 it('should return VCreative output URL when completed', async () => {
232 mockAideoService.getAideoTask.mockResolvedValue({
233 taskId: 'aideo-task-123',
234 model: 'aideo',
235 status: AideoTaskStatus.Completed,
236 createdAt: new Date(),
237 updatedAt: new Date(),
238 apiResponses: [
239 {
240 VCreative: {
241 OutputJson: { Result: { url: 'https://example.com/creative.mp4' } },
242 },
243 },
244 ],
245 } as never)
246
247 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
248 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
249
250 expect(result.isError).toBeUndefined()
251 const textContent = result.content[0] as { type: 'text', text: string }
252 expect(textContent.text).toContain('completed successfully')
253 expect(textContent.text).toContain('https://example.com/creative.mp4')
254 })
255
256 it('should return Vision analysis results when completed', async () => {
257 mockAideoService.getAideoTask.mockResolvedValue({
258 taskId: 'aideo-task-123',
259 model: 'aideo',
260 status: AideoTaskStatus.Completed,
261 createdAt: new Date(),
262 updatedAt: new Date(),
263 apiResponses: [
264 {
265 Vision: {
266 Content: 'This video shows a cat playing with a ball. Keywords: cat, play, ball.',
267 },
268 },
269 ],
270 } as never)
271
272 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
273 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
274
275 expect(result.isError).toBeUndefined()
276 const textContent = result.content[0] as { type: 'text', text: string }
277 expect(textContent.text).toContain('completed successfully')
278 expect(textContent.text).toContain('Vision Analysis Results')
279 expect(textContent.text).toContain('cat playing with a ball')
280 })
281
282 it('should return processing status when task is still running', async () => {
283 mockAideoService.getAideoTask.mockResolvedValue({
284 taskId: 'aideo-task-123',
285 model: 'aideo',
286 status: AideoTaskStatus.Processing,
287 createdAt: new Date(),
288 updatedAt: new Date(),
289 apiResponses: [],
290 } as never)
291
292 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
293 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
294
295 expect(result.isError).toBeUndefined()
296 const textContent = result.content[0] as { type: 'text', text: string }
297 expect(textContent.text).toContain(AideoTaskStatus.Processing)
298 expect(textContent.text).toContain('continue polling')
299 })
300
301 it('should handle unknown error message', async () => {
302 mockAideoService.getAideoTask.mockResolvedValue({
303 taskId: 'aideo-task-123',
304 model: 'aideo',
305 status: AideoTaskStatus.Failed,
306 createdAt: new Date(),
307 updatedAt: new Date(),
308 apiResponses: [],
309 } as never)
310
311 const tool = aideoMcp.createGetAideoTaskStatusTool(userId, userType)
312 const result = await tool.handler({ taskId: 'aideo-task-123' }, {})
313
314 expect(result.isError).toBe(true)
315 const textContent = result.content[0] as { type: 'text', text: string }
316 expect(textContent.text).toContain('Unknown error')
317 })
318 })
319
320 describe('createServer', () => {
321 it('should create server with correct name', () => {
322 const server = aideoMcp.createServer(userId, userType)
323 expect(server.name).toBe('aideo')
324 })
325
326 it('should include expected tools', () => {
327 const server = aideoMcp.createServer(userId, userType) as { tools?: Array<{ name: string }> }
328 const toolNames = server.tools?.map(t => t.name)
329
330 expect(toolNames).toContain(AideoToolName.SubmitAideoTask)
331 expect(toolNames).toContain(AideoToolName.GetAideoTaskStatus)
332 })
333
334 it('should have version 1.0.0', () => {
335 const server = aideoMcp.createServer(userId, userType) as { version?: string }
336 expect(server.version).toBe('1.0.0')
337 })
338 })
339 })
340
340 lines TYPESCRIPT