feat: 完成 SKU 图片选择及素材导入后端
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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 bucket, object_key')) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
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<unknown>,
|
||||
) => {
|
||||
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<typeof MediaService>[0],
|
||||
{} as ConstructorParameters<typeof MediaService>[1],
|
||||
{
|
||||
OBJECT_STORAGE_ENDPOINT: 'http://127.0.0.1:9000',
|
||||
OBJECT_STORAGE_BUCKET: 'test',
|
||||
objectStorageForcePathStyle: true,
|
||||
objectStorageAccessKey: 'test',
|
||||
objectStorageSecretKey: 'test',
|
||||
} as ConstructorParameters<typeof MediaService>[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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user