import { describe, expect, it, vi } from 'vitest'; import { ProductEditorService } from '../src/product-editor/product-editor.service.js'; const productId = '733d0d73-3d39-4a98-ac0a-5c0e521d8998'; const variantId = '50ea00b7-6675-4cc7-b7a2-d45f05c07100'; const assetId = '664d0d73-3d39-4a98-ac0a-5c0e521d8998'; function mediaTransaction(processed: boolean, library?: 'upload' | 'import', owner = productId) { const query = vi.fn(async (sql: string, values?: unknown[]) => { if (sql.includes('FROM atlas.product_variant_platform_listings')) return { rows: [{ id: 'listing-id', product_variant_id: variantId }] }; if (sql.includes('FROM atlas.media_assets')) return { rows: [{ id: assetId, content_type: 'image/jpeg', byte_size: '1200', width_px: 1200, height_px: 1600 }] }; if (sql.includes('FROM atlas.media_upload_intents') || sql.includes('FROM atlas.product_image_imports')) { expect(values).toEqual([productId, [assetId]]); expect(sql).toContain('product_id = $1'); const matches = library === 'upload' ? sql.includes('media_upload_intents') : library === 'import' && sql.includes('product_image_imports'); return { rows: matches && owner === productId ? [{ asset_id: assetId }] : [] }; } if (sql.includes('FROM atlas.media_aspect_ratio_derivatives')) { expect(values).toEqual([productId, [assetId]]); return { rows: processed ? [{ asset_id: assetId }] : [] }; } return { rows: [] }; }); return { query }; } describe('SKU 图片处理结果的保存资格', () => { const input = { product: { id: productId, collection_item_id: 'collection-id', source_snapshot_revision: 1 }, profile: { id: 'profile-id' }, variants: [{ variantId, assetIds: [assetId] }], }; const service = new ProductEditorService( {} as ConstructorParameters[0], {} as ConstructorParameters[1], { pricingAllowDraftRates: false } as ConstructorParameters[2], ) as unknown as { persistListingMedia: (tx: ReturnType, value: typeof input) => Promise; }; it.each(['upload', 'import'] as const)('%s 图片仅允许所属商品保存', async (library) => { await service.persistListingMedia(mediaTransaction(false, library), input); await expect(service.persistListingMedia(mediaTransaction(false, library, '其他商品'), input)).rejects.toMatchObject({ status: 422, response: { error: { code: 'LISTING_MEDIA_NOT_SELECTABLE' } }, }); }); it('当前商品的成功处理结果可加入 SKU 并保存', async () => { const tx = mediaTransaction(true); await service.persistListingMedia(tx, input); expect(tx.query.mock.calls.some(([sql]) => String(sql).includes('INSERT INTO atlas.product_variant_listing_media'))).toBe(true); }); it('其他商品或未成功的处理结果仍不能加入 SKU', async () => { const tx = mediaTransaction(false); await expect(service.persistListingMedia(tx, input)).rejects.toMatchObject({ status: 422, response: { error: { code: 'LISTING_MEDIA_NOT_SELECTABLE' } }, }); }); });