import { describe, expect, it, vi } from 'vitest'; import { AspectRatioDerivativeService } from '../src/media/aspect-ratio-derivative.service.js'; const workspaceId = '30cf54b7-06a3-4e5f-a5fc-d74165115094'; const sourceAssetId = '664d0d73-3d39-4a98-ac0a-5c0e521d8998'; const derivativeId = '68db226f-3fcb-4579-ab9e-4932b1d84067'; const productId = '733d0d73-3d39-4a98-ac0a-5c0e521d8998'; describe('一键 3:4 派生任务', () => { it('在同一个租户事务写入派生记录、操作进度和 Outbox', async () => { const statements: string[] = []; const query = vi.fn(async (sql: string) => { statements.push(sql); if (sql.includes('SELECT id FROM atlas.media_assets')) return { rows: [{ id: sourceAssetId }] }; if (sql.includes('RETURNING id')) return { rows: [{ id: derivativeId }] }; return { rows: [] }; }); const run = vi.fn(async (_input: unknown, work: (tx: { query: typeof query }) => Promise) => work({ query })); const service = new AspectRatioDerivativeService( {} as ConstructorParameters[0], { run } as unknown as ConstructorParameters[1], {} as ConstructorParameters[2], ); const result = await service.create( workspaceId, 'user-id', sourceAssetId, 'idempotency-key', { preset: '3:4', fit: 'contain' }, ); expect(result).toMatchObject({ status: 202, body: { data: { id: derivativeId, status: 'queued', source_asset_id: sourceAssetId } }, }); expect(run).toHaveBeenCalledOnce(); expect(statements.some((sql) => sql.includes('INSERT INTO atlas.media_aspect_ratio_derivatives'))).toBe(true); expect(statements.some((sql) => sql.includes('INSERT INTO atlas.operations'))).toBe(true); expect(statements.some((sql) => sql.includes('INSERT INTO atlas.outbox_events'))).toBe(true); }); it('缺少或跨工作空间的源素材不会创建任务', async () => { const query = vi.fn(async () => ({ rows: [] })); const run = vi.fn(async (_input: unknown, work: (tx: { query: typeof query }) => Promise) => work({ query })); const service = new AspectRatioDerivativeService( {} as ConstructorParameters[0], { run } as unknown as ConstructorParameters[1], {} as ConstructorParameters[2], ); await expect(service.create( workspaceId, 'user-id', sourceAssetId, 'idempotency-key', { preset: '3:4', fit: 'contain' }, )).rejects.toMatchObject({ status: 404 }); expect(query).toHaveBeenCalledOnce(); }); it('将当前工作空间的商品关联到新派生任务', async () => { const query = vi.fn(async (sql: string, values?: unknown[]) => { if (sql.includes('SELECT id FROM atlas.media_assets')) return { rows: [{ id: sourceAssetId }] }; if (sql.includes('SELECT id FROM atlas.products')) return { rows: [{ id: productId }] }; if (sql.includes('INSERT INTO atlas.media_aspect_ratio_derivatives')) { expect(values).toEqual([productId, sourceAssetId, 'user-id']); return { rows: [{ id: derivativeId }] }; } return { rows: [] }; }); const run = vi.fn(async (_input: unknown, work: (tx: { query: typeof query }) => Promise) => work({ query })); const service = new AspectRatioDerivativeService( {} as ConstructorParameters[0], { run } as unknown as ConstructorParameters[1], {} as ConstructorParameters[2], ); await service.create(workspaceId, 'user-id', sourceAssetId, 'key', { preset: '3:4', fit: 'contain', product_id: productId }); expect(query.mock.calls.some(([sql]) => String(sql).includes('INSERT INTO atlas.media_aspect_ratio_derivatives'))).toBe(true); }); });