Files
inkreach-official-website/apps/api/src/origin-goods/origin-goods.service.spec.ts
T
yeuimu 674d38a9a7 fix(api,admin): stop test fixture junk leaking to shared dev DB; badge text x个配置
清理标签筛选里的「Upd {ts}-v2」空分组与原产品库树里的
「Tree Cat {ts}」垃圾分类——两者均为集成测试夹具残留:
- tag-groups spec「改名」用例把分组改成 -v2 后缀,afterAll 按原名
  删除永远漏掉它(累积 31 个空分组);修复:改名前把 v2 名一并登记
- origin-goods spec getTree 用例的 finally 先删 good,被
  goodOriginGood 外键限制挡住且错误被 .catch 吞掉,分类/商品/国家/
  链接全部残留;修复:先删引用行再删 good
- 已清库:31 个 Upd 空分组、Tree Cat 分类 + Tree Good 商品 +
  Tree Country 国家 + 2 条 tree-a/b 链接;复跑两 spec 复核零残留

左树一族一行徽标文案按用户反馈去掉「N 条链接」,仅显示「M 个配置」
(memberCount 字段保留在 DTO,悬浮弹层行为不变)

验证:tag-groups + origin-goods spec 13/13 绿且零残留;admin
vue-tsc 干净 + vitest 22/22
2026-08-30 03:15:18 +08:00

165 lines
6.1 KiB
TypeScript

import { Test } from '@nestjs/testing';
import { OriginGoodsService } from './origin-goods.service';
import { FamilyRecomputeService } from '../product-families/family-recompute.service';
import { ProductFamiliesService } from '../product-families/product-families.service';
import { OrganizeService } from '../product-families/organize.service';
import { PrismaService } from '../prisma/prisma.service';
describe('OriginGoodsService', () => {
let service: OriginGoodsService;
let prisma: PrismaService;
const stamp = Date.now();
const createdSds: string[] = [];
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [OriginGoodsService, FamilyRecomputeService, ProductFamiliesService, OrganizeService, PrismaService],
}).compile();
service = moduleRef.get(OriginGoodsService);
prisma = moduleRef.get(PrismaService);
await prisma.onModuleInit();
// Seed 25 rows with sequential goodNames so we can paginate/filter.
const rows = Array.from({ length: 25 }).map((_, i) => ({
sdsGoodId: `sds-${stamp}-${i}`,
goodName: `Origin Good ${stamp} ${i.toString().padStart(2, '0')}`,
sdsCategoryId: `cat-${stamp}-${i % 3}`,
}));
await prisma.originGood.createMany({ data: rows });
createdSds.push(...rows.map((r) => r.sdsGoodId));
});
afterAll(async () => {
if (createdSds.length) {
await prisma.originGood.deleteMany({
where: { sdsGoodId: { in: createdSds } },
});
}
await prisma.onModuleDestroy();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('findOne returns og with detail/variants payload', async () => {
const og = await prisma.originGood.findFirstOrThrow({
where: { sdsGoodId: `sds-${stamp}-3` },
});
const result = await service.findOne(og.id);
expect(result.id).toBe(og.id.toString());
expect(result.sdsGoodId).toBe(`sds-${stamp}-3`);
expect(result.variantCount).toBe(0);
expect(result.hasDetail).toBe(false);
expect(result.detail).toBeNull();
expect(result.variants).toEqual([]);
await expect(service.findOne(og.id + 10_000_000n)).rejects.toThrow();
});
it('returns paginated results', async () => {
const page1 = await service.findAll({
page: 1,
pageSize: 10,
keyword: `Origin Good ${stamp}`,
});
expect(page1.total).toBe(25);
expect(page1.items.length).toBe(10);
expect(page1.page).toBe(1);
expect(page1.pageSize).toBe(10);
const page3 = await service.findAll({
page: 3,
pageSize: 10,
keyword: `Origin Good ${stamp}`,
});
expect(page3.items.length).toBe(5);
});
it('searches by keyword (case insensitive)', async () => {
const result = await service.findAll({
page: 1,
pageSize: 5,
keyword: `origin good ${stamp} 05`,
});
expect(result.items.length).toBe(1);
expect(result.items[0].goodName).toContain('05');
});
it('returns empty page when no matches', async () => {
const result = await service.findAll({
page: 1,
pageSize: 5,
keyword: 'definitely-does-not-exist',
});
expect(result.total).toBe(0);
expect(result.items.length).toBe(0);
});
describe('getTree merged references', () => {
/* eslint-disable @typescript-eslint/no-explicit-any */
function findOgNode(
treeResponse: { tree: any[] },
ogId: string,
): { configuredCount: number; configuredCountries: string[] } {
let found: { configuredCount: number; configuredCountries: string[] } | null = null;
const walk = (nodes: any[]) => {
for (const n of nodes) {
const hit = (n.originGoods ?? []).find((o: any) => o.id === ogId);
if (hit) {
found = hit;
return;
}
if (n.children?.length) walk(n.children);
}
};
walk(treeResponse.tree);
if (!found) throw new Error(`og node ${ogId} not found in tree`);
return found;
}
it('counts secondary references as configured', async () => {
const sdsCat = `tree-cat-${stamp}`;
await prisma.originGood.createMany({
data: [
{ sdsGoodId: `tree-a-${stamp}`, goodName: `Tree A ${stamp}`, sdsCategoryId: sdsCat },
{ sdsGoodId: `tree-b-${stamp}`, goodName: `Tree B ${stamp}`, sdsCategoryId: sdsCat },
],
});
createdSds.push(`tree-a-${stamp}`, `tree-b-${stamp}`);
const originA = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-a-${stamp}` } });
const originB = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-b-${stamp}` } });
const cat = await prisma.category.create({
data: { categoryName: `Tree Cat ${stamp}`, sdsCategoryId: sdsCat },
});
const country = await prisma.country.create({
data: { countryName: `Tree Country ${stamp}` },
});
const good = await prisma.good.create({
data: {
goodName: `Tree Good ${stamp}`,
originGoodId: originA.id,
countryId: country.id,
categoryId: cat.id,
},
});
await prisma.goodOriginGood.create({
data: { goodId: good.id, originGoodId: originB.id },
});
try {
const tree = await service.getTree();
const nodeB = findOgNode(tree, originB.id.toString());
expect(nodeB.configuredCount).toBeGreaterThanOrEqual(1);
expect(nodeB.configuredCountries).toContain(`Tree Country ${stamp}`);
} finally {
// 先删引用行,否则 good.delete 被外键限制挡住(错误被吞后分类/商品/国家全残留)
await prisma.goodOriginGood.deleteMany({ where: { goodId: good.id } }).catch(() => undefined);
await prisma.good.delete({ where: { id: good.id } }).catch(() => undefined);
await prisma.country.delete({ where: { id: country.id } }).catch(() => undefined);
await prisma.category.delete({ where: { id: cat.id } }).catch(() => undefined);
}
});
});
});