文件
OmniCard/backend/test/modules/card/cases.test.ts
wikiboss eec259a3a7 feat: 完成全系统30个功能点开发与E2E全链路验收 (8轮调度32个子代理闭环)
- 模块一: 出海案例作品集、博客动态发布、人名发音语音条、RTL阿语多语言切换
- 模块二: AI顾问人设配置、行业术语库润色、RAG切片沙盒调试、流式会话与意向留资卡片
- 模块三: 全球访客雷达大屏、多维加权意向算分、外部CRM对接推送、团队公海池流转
- 模块四: 移动端NFC安全挂失、NFC动态路由规则配置、动态防伪验签、展会制卡Zip批量导出
- 模块五: 组织架构与名片分配、移动端个人中心设置、离职交接与历史线索迁移、安全审计日志
- 模块六: Stripe国际信用卡订阅Webhook、独立二级域名与SSL检测、坐席配额预警、多币种发票
- 模块七: 纸质名片OCR解析入库、国际商务日程RFC 5545 ICS日历、微官网离线SWR预加载、展会实时排行榜大屏
- 模块八: 全球出海SEO OpenGraph元标签注入与社交爬虫探测、30点端到端全链路业务流集成测试套件
2026-09-20 23:47:47 +08:00

206 行
7.8 KiB
TypeScript

import * as fs from 'fs';
import * as path from 'path';
import { BaseController } from '@cool-midway/core';
import { CardCasesEntity } from '../../../src/modules/card/entity/cases';
import { CardCasesService } from '../../../src/modules/card/service/cases';
import {
AdminCardCasesController,
adminCardCasesCurdOption,
} from '../../../src/modules/card/controller/admin/cases';
describe('后台出海案例作品集 (Admin Card Cases CRUD)', () => {
const entityFile = path.join(
__dirname,
'../../../src/modules/card/entity/cases.ts'
);
const serviceFile = path.join(
__dirname,
'../../../src/modules/card/service/cases.ts'
);
const controllerFile = path.join(
__dirname,
'../../../src/modules/card/controller/admin/cases.ts'
);
describe('1. 契约文件与自动路由约定', () => {
it('文件路径应符合 /admin/card/cases 路由与模块约定', () => {
expect(fs.existsSync(entityFile)).toBe(true);
expect(fs.existsSync(serviceFile)).toBe(true);
expect(fs.existsSync(controllerFile)).toBe(true);
});
it('控制器应继承 BaseController 并挂载 CoolController CRUD', () => {
expect(AdminCardCasesController.prototype).toBeInstanceOf(BaseController);
expect(adminCardCasesCurdOption.entity).toBe(CardCasesEntity);
expect(adminCardCasesCurdOption.service).toBe(CardCasesService);
});
it('装饰器 api 必须包含 add/delete/update/info/list/page', () => {
expect(adminCardCasesCurdOption.api).toEqual([
'add',
'delete',
'update',
'info',
'list',
'page',
]);
});
it('pageQueryOp 应支持 a.cardId 精确筛选与 title/client/metrics 模糊搜索', () => {
const pageQueryOp = adminCardCasesCurdOption.pageQueryOp;
expect(pageQueryOp.fieldEq).toEqual(expect.arrayContaining(['a.cardId']));
expect(pageQueryOp.keyWordLikeFields).toEqual(
expect.arrayContaining(['a.title', 'a.client', 'a.metrics'])
);
expect(pageQueryOp.addOrderBy).toMatchObject({ sortOrder: 'asc' });
});
});
describe('2. 实体字段契约 (CardCasesEntity)', () => {
it('实体实例应支持出海案例必需契约字段', () => {
const item = new CardCasesEntity();
item.cardId = 1001;
item.title = '智能电动滑板车欧美 DTC 出海';
item.client = 'VoltRide Global';
item.duration = '2024.01 - 2024.06';
item.metrics = '+320% ROI, 45,000+ 留资量';
item.coverImage = 'https://cdn.example.com/cases/voltride.webp';
item.pdfUrl = 'https://cdn.example.com/cases/voltride-deck.pdf';
item.description = '品牌从 0 到 1 进军北美市场独立站及全渠道获客';
item.sortOrder = 1;
expect(item.cardId).toBe(1001);
expect(item.title).toBe('智能电动滑板车欧美 DTC 出海');
expect(item.client).toBe('VoltRide Global');
expect(item.duration).toBe('2024.01 - 2024.06');
expect(item.metrics).toBe('+320% ROI, 45,000+ 留资量');
expect(item.coverImage).toBe('https://cdn.example.com/cases/voltride.webp');
expect(item.pdfUrl).toBe('https://cdn.example.com/cases/voltride-deck.pdf');
expect(item.description).toBe('品牌从 0 到 1 进军北美市场独立站及全渠道获客');
expect(item.sortOrder).toBe(1);
});
});
describe('3. 服务层按 cardId 筛选与 CRUD 逻辑 (CardCasesService)', () => {
let service: CardCasesService;
let mockList: CardCasesEntity[] = [];
beforeEach(() => {
mockList = [
Object.assign(new CardCasesEntity(), {
id: 1,
cardId: 101,
title: '跨境智能硬件众筹出海',
client: 'TechNova',
duration: '3个月',
metrics: '+280% 众筹目标达成',
coverImage: 'https://cdn.example.com/case1.png',
pdfUrl: 'https://cdn.example.com/case1.pdf',
sortOrder: 2,
}),
Object.assign(new CardCasesEntity(), {
id: 2,
cardId: 101,
title: '东南亚 TikTok 跨境直播获客',
client: 'FashionGo',
duration: '6个月',
metrics: '千万级曝光, +150% GMV',
coverImage: 'https://cdn.example.com/case2.png',
pdfUrl: 'https://cdn.example.com/case2.pdf',
sortOrder: 1,
}),
Object.assign(new CardCasesEntity(), {
id: 3,
cardId: 102,
title: 'B2B 重工机械中东拓客',
client: 'HeavyMach',
duration: '12个月',
metrics: '$5M+ 意向大单',
coverImage: 'https://cdn.example.com/case3.png',
pdfUrl: 'https://cdn.example.com/case3.pdf',
sortOrder: 1,
}),
];
service = new CardCasesService();
service.cardCasesEntity = {
find: jest.fn().mockImplementation(async (options?: { where?: { cardId?: number }; order?: Record<string, string> }) => {
let res = [...mockList];
if (options?.where?.cardId !== undefined) {
res = res.filter(item => Number(item.cardId) === Number(options.where.cardId));
}
if (options?.order?.sortOrder === 'ASC') {
res.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
}
return res;
}),
findOneBy: jest.fn().mockImplementation(async (where: { id: number }) => {
return mockList.find(item => item.id === where.id) || null;
}),
save: jest.fn().mockImplementation(async (entity: Partial<CardCasesEntity>) => {
if (entity.id) {
const index = mockList.findIndex(item => item.id === entity.id);
if (index >= 0) {
mockList[index] = Object.assign(mockList[index], entity);
return mockList[index];
}
}
const created = Object.assign(new CardCasesEntity(), entity, {
id: mockList.length + 1,
});
mockList.push(created);
return created;
}),
delete: jest.fn().mockImplementation(async (id: number) => {
mockList = mockList.filter(item => item.id !== id);
return { affected: 1 };
}),
} as unknown as typeof service.cardCasesEntity;
});
it('getCasesByCardId 应根据名片 cardId 筛选并按 sortOrder 升序排列', async () => {
const cases = await service.getCasesByCardId(101);
expect(cases).toHaveLength(2);
expect(cases[0].title).toBe('东南亚 TikTok 跨境直播获客');
expect(cases[0].sortOrder).toBe(1);
expect(cases[1].title).toBe('跨境智能硬件众筹出海');
expect(cases[1].sortOrder).toBe(2);
});
it('getCasesByCardId 传入无效 cardId 时应返回空数组', async () => {
const cases = await service.getCasesByCardId(0);
expect(cases).toEqual([]);
});
it('创建案例并能查询出最新案例', async () => {
const created = await service.cardCasesEntity.save({
cardId: 101,
title: '拉美电商本土化品牌出海',
client: 'LatamDirect',
duration: '4个月',
metrics: '+90% 复购率',
coverImage: 'https://cdn.example.com/case4.png',
pdfUrl: 'https://cdn.example.com/case4.pdf',
sortOrder: 3,
});
expect(created.id).toBeDefined();
expect(created.title).toBe('拉美电商本土化品牌出海');
const found = await service.cardCasesEntity.findOneBy({ id: created.id });
expect(found).not.toBeNull();
expect(found?.client).toBe('LatamDirect');
});
it('更新案例信息应成功', async () => {
await service.cardCasesEntity.save({
id: 1,
metrics: '+350% 众筹目标达成',
});
const updated = await service.cardCasesEntity.findOneBy({ id: 1 });
expect(updated?.metrics).toBe('+350% 众筹目标达成');
});
});
});