feat(api): sync hooks for parsing, auto-attach and family recompute
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { SyncService } from './sync.service';
|
||||
import { SdsClientService } from './sds-client.service';
|
||||
import { FamilyRecomputeService } from '../product-families/family-recompute.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
/**
|
||||
* 同步钩子集成测试:upsertOriginGood 的解析列写入、新链接自动挂族、
|
||||
* persistProductDetail 后的族重算入队。
|
||||
*/
|
||||
describe('SyncService family hooks', () => {
|
||||
let service: SyncService;
|
||||
let prisma: PrismaService;
|
||||
let recompute: FamilyRecomputeService;
|
||||
const stamp = Date.now();
|
||||
const createdOriginGoodIds: bigint[] = [];
|
||||
const createdFamilyIds: bigint[] = [];
|
||||
|
||||
const sdsProduct = (id: string, name: string) =>
|
||||
({ id, name, pic: 'https://example.com/pic.jpg' }) as any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
providers: [
|
||||
SyncService,
|
||||
{
|
||||
provide: SdsClientService,
|
||||
useValue: {},
|
||||
},
|
||||
FamilyRecomputeService,
|
||||
PrismaService,
|
||||
],
|
||||
}).compile();
|
||||
service = moduleRef.get(SyncService);
|
||||
prisma = moduleRef.get(PrismaService);
|
||||
recompute = moduleRef.get(FamilyRecomputeService);
|
||||
await prisma.onModuleInit();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.originGood.deleteMany({ where: { id: { in: createdOriginGoodIds } } });
|
||||
await prisma.productFamily.deleteMany({ where: { id: { in: createdFamilyIds } } });
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
|
||||
it('upsertOriginGood:写入四个解析列(create 与 update 全量覆盖)', async () => {
|
||||
const sdsId = `hook-${stamp}-parse`;
|
||||
const result1 = await (service as any).upsertOriginGood(
|
||||
sdsProduct(sdsId, '美国(包邮)240g涤纶休闲短裤-DG206-单面印花-美西洛杉矶一仓'),
|
||||
'cat-1',
|
||||
);
|
||||
expect(result1).toBe('inserted');
|
||||
const og = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: sdsId } });
|
||||
createdOriginGoodIds.push(og.id);
|
||||
expect(og.skuCode).toBe('DG206');
|
||||
expect(og.logisticsLabel).toBe('包邮');
|
||||
expect(og.craftLabel).toBe('单面印花');
|
||||
expect(og.warehouseLabel).toBe('美西洛杉矶一仓');
|
||||
|
||||
// 更新为不带仓库的名称 → 解析列全量覆盖(warehouseLabel 置空)
|
||||
await (service as any).upsertOriginGood(
|
||||
sdsProduct(sdsId, '美国(不包邮)240g涤纶休闲短裤-DG206-单面印花'),
|
||||
'cat-1',
|
||||
);
|
||||
const og2 = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: sdsId } });
|
||||
expect(og2.logisticsLabel).toBe('不包邮');
|
||||
expect(og2.warehouseLabel).toBeNull();
|
||||
});
|
||||
|
||||
it('新链接自动挂族:唯一命中族则挂载并触发重算', async () => {
|
||||
const seed = await prisma.originGood.create({
|
||||
data: {
|
||||
sdsGoodId: `hook-${stamp}-seed`,
|
||||
goodName: `自动挂${stamp}(包邮)卫衣-ZZA${stamp}-单面印花`,
|
||||
craftLabel: '单面印花',
|
||||
logisticsLabel: '包邮',
|
||||
},
|
||||
});
|
||||
createdOriginGoodIds.push(seed.id);
|
||||
await prisma.originGoodVariant.create({
|
||||
data: {
|
||||
originGoodId: seed.id,
|
||||
sdsVariantId: 'seed-v1',
|
||||
sku: 'SEED-S',
|
||||
sizeId: 'size_S',
|
||||
sizeName: 'S',
|
||||
colorId: 'color_blk',
|
||||
colorName: '黑色',
|
||||
price: new Prisma.Decimal(25),
|
||||
},
|
||||
});
|
||||
const family = await prisma.productFamily.create({
|
||||
data: {
|
||||
familyName: `自动挂族-${stamp}`,
|
||||
primaryOriginGoodId: seed.id,
|
||||
},
|
||||
});
|
||||
createdFamilyIds.push(family.id);
|
||||
await prisma.originGood.update({
|
||||
where: { id: seed.id },
|
||||
data: { familyId: family.id },
|
||||
});
|
||||
await recompute.recomputeFamily(family.id);
|
||||
const before = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
|
||||
expect((before.priceMatrix as any).rows).toHaveLength(1);
|
||||
|
||||
// 同键新链接(多一个仓库段)→ 自动挂进唯一族
|
||||
const newSdsId = `hook-${stamp}-new`;
|
||||
await (service as any).upsertOriginGood(
|
||||
sdsProduct(newSdsId, `自动挂${stamp}(包邮)卫衣-ZZA${stamp}-单面印花-某仓`),
|
||||
'cat-1',
|
||||
);
|
||||
const newOg = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: newSdsId } });
|
||||
createdOriginGoodIds.push(newOg.id);
|
||||
expect(newOg.familyId).toBe(family.id);
|
||||
|
||||
// 入队的重算已执行(等待异步完成)
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
});
|
||||
|
||||
it('多族命中时不确定归属 → 不挂载', async () => {
|
||||
const key = `自动挂${stamp}B`;
|
||||
const mk = async (suffix: string) => {
|
||||
const og = await prisma.originGood.create({
|
||||
data: {
|
||||
sdsGoodId: `hook-${stamp}-multi-${suffix}`,
|
||||
goodName: `${key}(包邮)卫衣-ZZB${stamp}-单面印花`,
|
||||
craftLabel: '单面印花',
|
||||
logisticsLabel: '包邮',
|
||||
},
|
||||
});
|
||||
createdOriginGoodIds.push(og.id);
|
||||
const family = await prisma.productFamily.create({
|
||||
data: { familyName: `多族${suffix}-${stamp}`, primaryOriginGoodId: og.id },
|
||||
});
|
||||
createdFamilyIds.push(family.id);
|
||||
await prisma.originGood.update({ where: { id: og.id }, data: { familyId: family.id } });
|
||||
return og;
|
||||
};
|
||||
await mk('x');
|
||||
await mk('y');
|
||||
|
||||
const sdsId = `hook-${stamp}-multi-new`;
|
||||
await (service as any).upsertOriginGood(
|
||||
sdsProduct(sdsId, `${key}(包邮)卫衣-ZZB${stamp}-单面印花-新仓`),
|
||||
'cat-1',
|
||||
);
|
||||
const og = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: sdsId } });
|
||||
createdOriginGoodIds.push(og.id);
|
||||
expect(og.familyId).toBeNull(); // 两个候选族 → 留给管理员
|
||||
});
|
||||
|
||||
it('maybeEnqueueFamilyRecompute:有族入队、无族跳过', async () => {
|
||||
const enqueueSpy = jest.spyOn(recompute, 'enqueue').mockImplementation(() => undefined);
|
||||
|
||||
const og = await prisma.originGood.create({
|
||||
data: { sdsGoodId: `hook-${stamp}-noattach`, goodName: `不成组名称-${stamp}` },
|
||||
});
|
||||
createdOriginGoodIds.push(og.id);
|
||||
await (service as any).maybeEnqueueFamilyRecompute(og.id);
|
||||
expect(enqueueSpy).not.toHaveBeenCalled();
|
||||
|
||||
const family = await prisma.productFamily.create({
|
||||
data: { familyName: `入队族-${stamp}` },
|
||||
});
|
||||
createdFamilyIds.push(family.id);
|
||||
await prisma.originGood.update({ where: { id: og.id }, data: { familyId: family.id } });
|
||||
await (service as any).maybeEnqueueFamilyRecompute(og.id);
|
||||
expect(enqueueSpy).toHaveBeenCalledWith(family.id);
|
||||
|
||||
enqueueSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user