import { createHash } from 'node:crypto'; import { describe, expect, it, vi } from 'vitest'; import { MediaService } from '../src/media/media.service.js'; vi.mock('../src/media/product-image-download.js', async (importOriginal) => { const original = await importOriginal< typeof import('../src/media/product-image-download.js') >(); return { ...original, downloadProductImage: vi.fn(async () => ({ bytes: Buffer.from('validated-image'), contentType: 'image/png', width: 800, height: 800, })), }; }); describe('网络商品图片导入', () => { it('复用同工作空间素材并返回正式 ID 与短时预览地址', async () => { const assetId = '7c910684-7070-4f36-8a7b-b10bd91acd79'; const bytes = Buffer.from('validated-image'); const checksum = createHash('sha256').update(bytes).digest('hex'); const query = vi.fn(async (sql: string, args?: unknown[]) => { if (sql.includes('WHERE checksum_sha256 = $1')) { expect(args).toEqual([checksum]); return { rows: [{ id: assetId }] }; } if (sql.includes('INSERT INTO atlas.product_image_imports')) { expect(args?.[0]).toBe('https://example.com/photo.png'); expect(args?.[1]).toBe(assetId); return { rows: [] }; } if (sql.includes('SELECT id, content_type')) { return { rows: [ { id: assetId, content_type: 'image/png', byte_size: '15', width_px: 800, height_px: 800, created_at: new Date('2026-09-24T00:00:00Z'), }, ], }; } if (sql.includes('SELECT storage_provider, bucket, object_key')) { return { rows: [ { storage_provider: 'local_minio', bucket: 'test', object_key: 'product-imports/existing.png', }, ], }; } throw new Error(`未知 SQL: ${sql}`); }); const run = vi.fn( async ( request: { path: string; key: string }, callback: (tx: { query: typeof query }) => Promise, ) => { expect(request.path).toBe('/api/media-assets/product-imports'); expect(request.key).toBe('idempotency-key'); return callback({ query }); }, ); const service = new MediaService( { run } as unknown as ConstructorParameters[0], {} as ConstructorParameters[1], { OBJECT_STORAGE_ENDPOINT: 'http://127.0.0.1:9000', OBJECT_STORAGE_REGION: 'us-east-1', OBJECT_STORAGE_BUCKET: 'test', objectStorageForcePathStyle: true, objectStorageAccessKey: 'test', objectStorageSecretKey: 'test', } as ConstructorParameters[2], ); const result = await service.importProductImage( 'workspace-id', 'user-id', 'idempotency-key', { url: 'https://example.com/photo.png' }, ); const body = result.body as { data: { id: string; source_url: string; preview_url: string }; }; expect(result.status).toBe(201); expect(body.data).toMatchObject({ id: assetId, source_url: 'https://example.com/photo.png', }); expect(body.data.preview_url).toContain('product-imports/existing.png'); expect(query).toHaveBeenCalledTimes(4); }); });