Spaces:
Runtime error
Runtime error
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |
| import { detectObjects } from '../detect-objects'; | |
| import { extractFramesAtIntervals } from '@/features/discovery/services/frame-extraction.service'; | |
| import { detectObjectsInFrame } from '@/features/discovery/services/ai-vision.service'; | |
| import { db } from '@/lib/db'; | |
| import { uploadThumbnail } from '@/features/discovery/services/storage.service'; | |
| // Mock dependencies | |
| vi.mock('@/features/discovery/services/storage.service', () => ({ | |
| uploadThumbnail: vi.fn().mockResolvedValue('https://supa-storage.com/thumb.jpg') | |
| })); | |
| vi.mock('sharp', () => { | |
| return { | |
| default: vi.fn(() => ({ | |
| metadata: vi.fn().mockResolvedValue({ width: 1920, height: 1080 }), | |
| extract: vi.fn().mockReturnThis(), | |
| resize: vi.fn().mockReturnThis(), | |
| toFormat: vi.fn().mockReturnThis(), | |
| toBuffer: vi.fn().mockResolvedValue(Buffer.from('thumb')), | |
| })) | |
| }; | |
| }); | |
| vi.mock('@/features/discovery/services/frame-extraction.service', () => ({ | |
| extractFramesAtIntervals: vi.fn(), | |
| VideoUnavailableError: class VideoUnavailableError extends Error { | |
| constructor(message: string) { | |
| super(message); | |
| this.name = 'VideoUnavailableError'; | |
| } | |
| } | |
| })); | |
| vi.mock('@/features/discovery/services/ai-vision.service', () => ({ | |
| detectObjectsInFrame: vi.fn(), | |
| VisionRateLimitError: class VisionRateLimitError extends Error { }, | |
| })); | |
| vi.mock('@/lib/db', () => ({ | |
| db: { | |
| insert: vi.fn(() => ({ | |
| values: vi.fn(() => ({ | |
| returning: vi.fn().mockResolvedValue([{ id: 'det-1' }, { id: 'det-2' }]), | |
| })), | |
| })), | |
| update: vi.fn(() => ({ | |
| set: vi.fn(() => ({ | |
| where: vi.fn().mockResolvedValue(undefined), | |
| })), | |
| })), | |
| query: { | |
| youtubeVideos: { | |
| findFirst: vi.fn().mockResolvedValue({ id: 'v123', duration: 33 }), | |
| }, | |
| }, | |
| }, | |
| })); | |
| vi.mock('@/lib/db/schema', () => ({ | |
| detectedObjects: {}, | |
| youtubeVideos: {}, | |
| })); | |
| // Mock Inngest client to expose handler | |
| vi.mock('../../client', () => ({ | |
| inngest: { | |
| createFunction: vi.fn((config, trigger, handler) => ({ config, trigger, handler, fn: handler })), | |
| send: vi.fn().mockResolvedValue(undefined), | |
| } | |
| })); | |
| vi.mock('@sentry/nextjs', () => ({ | |
| captureException: vi.fn(), | |
| })); | |
| describe('Detect Objects Inngest Function', () => { | |
| beforeEach(() => { | |
| vi.clearAllMocks(); | |
| }); | |
| it('should be defined', () => { | |
| expect(detectObjects).toBeDefined(); | |
| }); | |
| it('should execute analysis and save results', async () => { | |
| // Setup mocks | |
| vi.mocked(extractFramesAtIntervals).mockResolvedValue([ | |
| { timestamp: 0, frameBuffer: Buffer.from('f1') }, | |
| { timestamp: 10, frameBuffer: Buffer.from('f2') } | |
| ]); | |
| vi.mocked(detectObjectsInFrame).mockResolvedValue([ | |
| { name: 'Obj1', category: 'Tech', confidence: 0.9, boundingBox: { x: 10, y: 10, width: 100, height: 100 } } | |
| ]); | |
| const stepMock = { | |
| run: vi.fn(async (_name: string, fn: () => Promise<any>) => { | |
| return await fn(); | |
| }) | |
| }; | |
| const event = { | |
| data: { | |
| videoId: 'v123', | |
| videoUrl: 'http://video.com' | |
| } | |
| }; | |
| // Execute handler | |
| const result = await detectObjects.fn({ event, step: stepMock }); | |
| expect(result.success).toBe(true); | |
| expect(result.count).toBe(2); // 2 frames × 1 detection each | |
| expect(extractFramesAtIntervals).toHaveBeenCalledWith('http://video.com', 5); // Adaptive: 5s for 33s video | |
| expect(detectObjectsInFrame).toHaveBeenCalledTimes(2); | |
| expect(uploadThumbnail).toHaveBeenCalledTimes(2); | |
| expect(db.insert).toHaveBeenCalled(); | |
| }); | |
| }); | |