225 lines
6.9 KiB
TypeScript
225 lines
6.9 KiB
TypeScript
import { Test } from '@nestjs/testing';
|
|
import {
|
|
BadRequestException,
|
|
NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { GoodsService } from './goods.service';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { SyncService } from '../sync/sync.service';
|
|
|
|
describe('GoodsService', () => {
|
|
let service: GoodsService;
|
|
let prisma: PrismaService;
|
|
const stamp = Date.now();
|
|
|
|
// Fixtures
|
|
let countryId: bigint;
|
|
let country2Id: bigint;
|
|
let categoryId: bigint;
|
|
let childCategoryId: bigint;
|
|
let tagId: bigint;
|
|
let positionId: bigint;
|
|
let originGoodIds: bigint[] = [];
|
|
|
|
beforeAll(async () => {
|
|
const moduleRef = await Test.createTestingModule({
|
|
providers: [
|
|
GoodsService,
|
|
PrismaService,
|
|
{
|
|
provide: SyncService,
|
|
useValue: { queueProductDetailSync: jest.fn() },
|
|
},
|
|
],
|
|
}).compile();
|
|
service = moduleRef.get(GoodsService);
|
|
prisma = moduleRef.get(PrismaService);
|
|
await prisma.onModuleInit();
|
|
|
|
const country = await prisma.country.create({
|
|
data: { countryName: `Goods Country ${stamp}` },
|
|
});
|
|
countryId = country.id;
|
|
const country2 = await prisma.country.create({
|
|
data: { countryName: `Goods Country 2 ${stamp}` },
|
|
});
|
|
country2Id = country2.id;
|
|
|
|
const cat = await prisma.category.create({
|
|
data: { categoryName: `Goods Cat ${stamp}` },
|
|
});
|
|
categoryId = cat.id;
|
|
const child = await prisma.category.create({
|
|
data: { categoryName: `Goods Child ${stamp}`, parentCategoryId: cat.id },
|
|
});
|
|
childCategoryId = child.id;
|
|
|
|
const tag = await prisma.tag.create({
|
|
data: { tagName: `Goods Tag ${stamp}`, tagColor: '#00FF00' },
|
|
});
|
|
tagId = tag.id;
|
|
|
|
const pos = await prisma.position.create({
|
|
data: { indexVal: 1, countryId, categoryId },
|
|
});
|
|
positionId = pos.id;
|
|
|
|
const originGoods = await Promise.all(
|
|
Array.from({ length: 5 }).map((_, i) =>
|
|
prisma.originGood.create({
|
|
data: {
|
|
sdsGoodId: `sds-goods-${stamp}-${i}`,
|
|
goodName: `Goods Origin ${stamp} ${i}`,
|
|
},
|
|
}),
|
|
),
|
|
);
|
|
originGoodIds = originGoods.map((og) => og.id);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Wipe all goods first so origin_goods/category can be removed.
|
|
await prisma.good.deleteMany({
|
|
where: { goodName: { contains: `Goods Test ${stamp}` } },
|
|
});
|
|
await prisma.good.deleteMany({
|
|
where: { goodName: { contains: `Origin ${stamp}` } },
|
|
});
|
|
await prisma.originGood.deleteMany({
|
|
where: { id: { in: originGoodIds } },
|
|
});
|
|
await prisma.position.delete({ where: { id: positionId } });
|
|
await prisma.tag.delete({ where: { id: tagId } });
|
|
await prisma.category.delete({ where: { id: childCategoryId } });
|
|
await prisma.category.delete({ where: { id: categoryId } });
|
|
await prisma.country.delete({ where: { id: countryId } });
|
|
await prisma.country.delete({ where: { id: country2Id } });
|
|
await prisma.onModuleDestroy();
|
|
});
|
|
|
|
it('should be defined', () => {
|
|
expect(service).toBeDefined();
|
|
});
|
|
|
|
it('creates and reads back a good', async () => {
|
|
const created = await service.create({
|
|
goodName: `Goods Test ${stamp} basic`,
|
|
originGoodId: Number(originGoodIds[0]),
|
|
countryId: Number(countryId),
|
|
categoryId: Number(categoryId),
|
|
tagIds: [Number(tagId)],
|
|
positionId: Number(positionId),
|
|
goodPriority: 3,
|
|
});
|
|
expect(created.id).toBeTruthy();
|
|
expect(created.country?.countryName).toBeTruthy();
|
|
expect(created.tags.some((t) => t.tagColor === '#00FF00')).toBe(true);
|
|
|
|
const fetched = await service.findOne(BigInt(created.id));
|
|
expect(fetched.goodName).toBe(`Goods Test ${stamp} basic`);
|
|
});
|
|
|
|
it('filters by countryId, tagId, positionId and keyword', async () => {
|
|
const result = await service.findAll({
|
|
page: 1,
|
|
pageSize: 20,
|
|
countryId: Number(countryId),
|
|
tagId: Number(tagId),
|
|
keyword: `Goods Test ${stamp}`,
|
|
});
|
|
expect(result.items.length).toBeGreaterThan(0);
|
|
expect(result.items.every((g) => g.countryId === countryId.toString())).toBe(true);
|
|
expect(
|
|
result.items.every((g) => g.tags.some((t) => t.id === tagId.toString())),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('categoryId filter includes descendants recursively', async () => {
|
|
const inChild = await service.create({
|
|
goodName: `Goods Test ${stamp} child`,
|
|
originGoodId: Number(originGoodIds[1]),
|
|
countryId: Number(countryId),
|
|
categoryId: Number(childCategoryId),
|
|
});
|
|
const result = await service.findAll({
|
|
page: 1,
|
|
pageSize: 20,
|
|
categoryId: Number(categoryId),
|
|
keyword: `Goods Test ${stamp}`,
|
|
});
|
|
const ids = result.items.map((g) => g.id);
|
|
expect(ids).toContain(inChild.id);
|
|
});
|
|
|
|
it('batch update priority is atomic', async () => {
|
|
const created = await service.create({
|
|
goodName: `Goods Test ${stamp} prio`,
|
|
originGoodId: Number(originGoodIds[2]),
|
|
countryId: Number(countryId),
|
|
categoryId: Number(categoryId),
|
|
});
|
|
const result = await service.batchUpdatePriority({
|
|
items: [{ id: Number(created.id), priority: 42 }],
|
|
});
|
|
expect(result.count).toBe(1);
|
|
const after = await service.findOne(BigInt(created.id));
|
|
expect(after.goodPriority).toBe(42);
|
|
});
|
|
|
|
it('batch create creates all rows or none', async () => {
|
|
// Count of goods whose originGoodId is one of the two fixture ids,
|
|
// so we are independent of goodName (which batch derives from origin).
|
|
const before = await service.findAll({
|
|
page: 1,
|
|
pageSize: 100,
|
|
keyword: `Goods Origin ${stamp}`,
|
|
});
|
|
const created = await service.batchCreate({
|
|
countryId: Number(countryId),
|
|
categoryId: Number(categoryId),
|
|
defaultPriority: 1,
|
|
items: [
|
|
{ originGoodId: Number(originGoodIds[3]) },
|
|
{ originGoodId: Number(originGoodIds[4]) },
|
|
],
|
|
});
|
|
expect(created.length).toBe(2);
|
|
const after = await service.findAll({
|
|
page: 1,
|
|
pageSize: 100,
|
|
keyword: `Goods Origin ${stamp}`,
|
|
});
|
|
expect(after.total).toBe(before.total + 2);
|
|
});
|
|
|
|
it('batch create rolls back on failure', async () => {
|
|
const before = await service.findAll({
|
|
page: 1,
|
|
pageSize: 100,
|
|
keyword: `Goods Origin ${stamp}`,
|
|
});
|
|
await expect(
|
|
service.batchCreate({
|
|
countryId: Number(countryId),
|
|
categoryId: Number(categoryId),
|
|
items: [
|
|
{ originGoodId: Number(originGoodIds[0]) },
|
|
{ originGoodId: 99999999 }, // missing -> failure
|
|
],
|
|
}),
|
|
).rejects.toBeInstanceOf(BadRequestException);
|
|
const after = await service.findAll({
|
|
page: 1,
|
|
pageSize: 100,
|
|
keyword: `Goods Origin ${stamp}`,
|
|
});
|
|
expect(after.total).toBe(before.total);
|
|
});
|
|
|
|
it('throws NotFoundException for unknown id', async () => {
|
|
await expect(service.findOne(BigInt(99999999))).rejects.toBeInstanceOf(
|
|
NotFoundException,
|
|
);
|
|
});
|
|
});
|