文件
OmniCard/backend/test/modules/card/batch-locale.test.ts

192 行
6.6 KiB
TypeScript

import {
CardBatchLocaleService,
BatchLocaleMemberCard,
validateLocaleFields,
} from '../../../src/modules/card/service/batch-locale';
import { CardLocaleEntity } from '../../../src/modules/card/entity/locale';
describe('Point 43: 出海团队多语言名片批量生成向导与多语种字段镜像校验', () => {
let service: CardBatchLocaleService;
let mockRepository: any;
let savedRecords: CardLocaleEntity[];
beforeEach(() => {
savedRecords = [];
mockRepository = {
findOneBy: jest.fn(async ({ cardId, locale }: any) => {
return savedRecords.find(r => r.cardId === cardId && r.locale === locale) || null;
}),
find: jest.fn(async ({ where }: any) => {
if (where && where.cardId) {
return savedRecords.filter(r => r.cardId === where.cardId);
}
return savedRecords;
}),
save: jest.fn(async (entity: any) => {
const existingIdx = savedRecords.findIndex(
r => r.cardId === entity.cardId && r.locale === entity.locale
);
if (existingIdx >= 0) {
savedRecords[existingIdx] = { ...savedRecords[existingIdx], ...entity };
return savedRecords[existingIdx];
}
const created = { id: savedRecords.length + 1, ...entity };
savedRecords.push(created);
return created;
}),
};
service = new CardBatchLocaleService();
service.cardLocaleEntity = mockRepository;
});
describe('1. validateLocaleFields (多语言字段规范与合法性校验)', () => {
it('正常字段应校验通过', () => {
const res = validateLocaleFields({
name: 'Sarah Chen',
title: 'VP of Global BD',
company: 'OmniCard AI',
slogan: 'Connect the world with intelligent cards',
});
expect(res.valid).toBe(true);
expect(res.errors).toHaveLength(0);
});
it('姓名为空或超长应精准拦截', () => {
const emptyRes = validateLocaleFields({ name: ' ' });
expect(emptyRes.valid).toBe(false);
expect(emptyRes.errors).toContain('姓名不能为空');
const longRes = validateLocaleFields({ name: 'A'.repeat(65) });
expect(longRes.valid).toBe(false);
expect(longRes.errors).toContain('姓名长度不能超过64字符');
});
it('职位、公司与标语超长时应拦截', () => {
const res = validateLocaleFields({
name: 'Test',
title: 'T'.repeat(129),
company: 'C'.repeat(129),
slogan: 'S'.repeat(256),
});
expect(res.valid).toBe(false);
expect(res.errors.length).toBeGreaterThanOrEqual(3);
});
});
describe('2. batchGenerateMemberLocales (团队成员多语言名片批量生成向导)', () => {
const mockMembers: BatchLocaleMemberCard[] = [
{
cardId: 101,
baseFields: {
name: '张三',
title: '出海销售总监',
company: '深圳数字智能出海科技',
slogan: '智能连接全球',
},
},
{
cardId: 102,
baseFields: {
name: '李四',
title: '中东区域代表',
company: '深圳数字智能出海科技',
slogan: '专注海湾六国',
},
},
];
it('应成功为团队成员批量生成指定目标语言分支 (en, ar)', async () => {
const result = await service.batchGenerateMemberLocales({
members: mockMembers,
targetLocales: ['en', 'ar'],
});
expect(result.totalTasks).toBe(4); // 2 members * 2 locales
expect(result.successTasks).toBe(4);
expect(result.failedTasks).toBe(0);
expect(result.completionRate).toBe(1.0);
expect(savedRecords.length).toBe(4);
// 验证保存的记录属性
const en101 = savedRecords.find(r => r.cardId === 101 && r.locale === 'en');
expect(en101).toBeDefined();
expect(en101?.name).toBe('张三');
const ar102 = savedRecords.find(r => r.cardId === 102 && r.locale === 'ar');
expect(ar102).toBeDefined();
expect(ar102?.name).toBe('李四');
});
it('支持传入语言自定义词典或转换映射处理器 (localeTransformer)', async () => {
const result = await service.batchGenerateMemberLocales({
members: [mockMembers[0]],
targetLocales: ['en'],
localeTransformer: (locale, base) => {
if (locale === 'en') {
return {
name: 'Alex Zhang',
title: 'Global Sales Director',
company: 'OmniCard Tech Co., Ltd.',
slogan: 'Connecting globally with AI cards',
};
}
return base;
},
});
expect(result.successTasks).toBe(1);
const enCard = savedRecords.find(r => r.cardId === 101 && r.locale === 'en');
expect(enCard?.name).toBe('Alex Zhang');
expect(enCard?.title).toBe('Global Sales Director');
});
it('遇到非法非法参数时应记录错误且不阻断其它任务', async () => {
const invalidMembers: BatchLocaleMemberCard[] = [
{
cardId: 999,
baseFields: { name: '' }, // 姓名为空导致失败
},
mockMembers[0],
];
const result = await service.batchGenerateMemberLocales({
members: invalidMembers,
targetLocales: ['en'],
});
expect(result.totalTasks).toBe(2);
expect(result.successTasks).toBe(1);
expect(result.failedTasks).toBe(1);
expect(result.errors.length).toBe(1);
expect(result.errors[0].cardId).toBe(999);
expect(result.completionRate).toBe(0.5);
});
});
describe('3. inspectTeamLocaleCoverage (出海团队语言覆盖率与就绪度诊断)', () => {
it('应精准统计团队名片各语言的覆盖率与缺失名单', async () => {
savedRecords.push(
{ id: 1, cardId: 101, locale: 'en', name: 'Alex' } as CardLocaleEntity,
{ id: 2, cardId: 101, locale: 'ar', name: 'Alex' } as CardLocaleEntity,
{ id: 3, cardId: 102, locale: 'en', name: 'John' } as CardLocaleEntity
);
const coverage = await service.inspectTeamLocaleCoverage(
[101, 102],
['en', 'ar', 'es']
);
expect(coverage.teamMemberCount).toBe(2);
expect(coverage.locales['en'].coverageRate).toBe(1.0); // 101, 102 都有 en
expect(coverage.locales['en'].missingCardIds).toHaveLength(0);
expect(coverage.locales['ar'].coverageRate).toBe(0.5); // 仅 101 有
expect(coverage.locales['ar'].missingCardIds).toEqual([102]);
expect(coverage.locales['es'].coverageRate).toBe(0.0); // 都没有
expect(coverage.locales['es'].missingCardIds).toEqual([101, 102]);
});
});
});