549 行
19 KiB
TypeScript
549 行
19 KiB
TypeScript
import { BaseController } from '@cool-midway/core';
|
|
import { BaseSysUserEntity } from '../../../src/modules/base/entity/sys/user';
|
|
import { CardInfoEntity } from '../../../src/modules/card/entity/info';
|
|
import { CrmLeadEntity } from '../../../src/modules/crm/entity/lead';
|
|
import { CrmTrackEntity } from '../../../src/modules/crm/entity/track';
|
|
import {
|
|
AdminTeamMembersController,
|
|
adminTeamMembersCurdOption,
|
|
} from '../../../src/modules/team/controller/admin/members';
|
|
import {
|
|
TeamLiveRankService,
|
|
TEAM_LIVE_RANK_EVENT,
|
|
} from '../../../src/modules/team/service/live-rank';
|
|
import type {
|
|
TeamMemberRankItem,
|
|
TeamRankBroadcastPayload,
|
|
} from '../../../src/comm/types/omni';
|
|
|
|
describe('Point 28: 展会现场团队获客实时排行榜大屏广播 (Live Rank & Broadcast)', () => {
|
|
let service: TeamLiveRankService;
|
|
let users: BaseSysUserEntity[];
|
|
let cards: CardInfoEntity[];
|
|
let leads: CrmLeadEntity[];
|
|
let tracks: CrmTrackEntity[];
|
|
|
|
beforeEach(() => {
|
|
users = [];
|
|
cards = [];
|
|
leads = [];
|
|
tracks = [];
|
|
|
|
// Mock Repositories
|
|
const mockUserRepo = {
|
|
find: jest.fn(async (options?: { where?: Record<string, unknown> }) => {
|
|
if (options?.where?.tenantId !== undefined) {
|
|
return users.filter(
|
|
u => Number(u.tenantId) === Number(options.where?.tenantId)
|
|
);
|
|
}
|
|
return [...users];
|
|
}),
|
|
};
|
|
|
|
const mockCardRepo = {
|
|
find: jest.fn(async (options?: { where?: Record<string, unknown> }) => {
|
|
if (options?.where?.tenantId !== undefined) {
|
|
return cards.filter(
|
|
c => Number(c.tenantId) === Number(options.where?.tenantId)
|
|
);
|
|
}
|
|
return [...cards];
|
|
}),
|
|
};
|
|
|
|
const mockLeadRepo = {
|
|
find: jest.fn(async (options?: { where?: Record<string, unknown> }) => {
|
|
if (options?.where?.tenantId !== undefined) {
|
|
return leads.filter(
|
|
l => Number(l.tenantId) === Number(options.where?.tenantId)
|
|
);
|
|
}
|
|
return [...leads];
|
|
}),
|
|
};
|
|
|
|
const mockTrackRepo = {
|
|
find: jest.fn(async (options?: { where?: Record<string, unknown> }) => {
|
|
if (options?.where?.tenantId !== undefined) {
|
|
return tracks.filter(
|
|
t => Number(t.tenantId) === Number(options.where?.tenantId)
|
|
);
|
|
}
|
|
return [...tracks];
|
|
}),
|
|
};
|
|
|
|
service = new TeamLiveRankService();
|
|
service.baseSysUserEntity = mockUserRepo as unknown as typeof service.baseSysUserEntity;
|
|
service.cardInfoEntity = mockCardRepo as unknown as typeof service.cardInfoEntity;
|
|
service.crmLeadEntity = mockLeadRepo as unknown as typeof service.crmLeadEntity;
|
|
service.crmTrackEntity = mockTrackRepo as unknown as typeof service.crmTrackEntity;
|
|
});
|
|
|
|
// ==========================================
|
|
// 1. 排行榜加权计分与降序排序测试
|
|
// ==========================================
|
|
describe('1. 排行榜加权计分与降序排序 (Weighted Score & Ranking)', () => {
|
|
it('1.1 正确按每线索 10 分、每触碰 1 分计算总分并降序排列', async () => {
|
|
// 成员 1: Alex (User 101, Card 1)
|
|
const u1 = new BaseSysUserEntity();
|
|
u1.id = 101;
|
|
u1.name = 'Alex Rivera';
|
|
u1.tenantId = 1;
|
|
u1.departmentName = '国际业务一队';
|
|
|
|
const c1 = new CardInfoEntity();
|
|
c1.id = 1;
|
|
c1.userId = 101;
|
|
c1.tenantId = 1;
|
|
|
|
// 成员 2: Sarah (User 102, Card 2)
|
|
const u2 = new BaseSysUserEntity();
|
|
u2.id = 102;
|
|
u2.name = 'Sarah Wang';
|
|
u2.tenantId = 1;
|
|
u2.departmentName = '国际业务二队';
|
|
|
|
const c2 = new CardInfoEntity();
|
|
c2.id = 2;
|
|
c2.userId = 102;
|
|
c2.tenantId = 1;
|
|
|
|
// 成员 3: Tariq (User 103, Card 3)
|
|
const u3 = new BaseSysUserEntity();
|
|
u3.id = 103;
|
|
u3.name = 'Tariq Mansour';
|
|
u3.tenantId = 1;
|
|
u3.departmentName = '中东中枢';
|
|
|
|
const c3 = new CardInfoEntity();
|
|
c3.id = 3;
|
|
c3.userId = 103;
|
|
c3.tenantId = 1;
|
|
|
|
users.push(u1, u2, u3);
|
|
cards.push(c1, c2, c3);
|
|
|
|
// Alex: 2 leads (20分), 5 touches (5分) => 25分
|
|
leads.push(
|
|
Object.assign(new CrmLeadEntity(), { id: 1, cardId: 1, userId: 101, tenantId: 1 }),
|
|
Object.assign(new CrmLeadEntity(), { id: 2, cardId: 1, userId: 101, tenantId: 1 })
|
|
);
|
|
for (let i = 0; i < 5; i++) {
|
|
tracks.push(
|
|
Object.assign(new CrmTrackEntity(), { id: 10 + i, cardId: 1, tenantId: 1 })
|
|
);
|
|
}
|
|
|
|
// Sarah: 5 leads (50分), 12 touches (12分) => 62分
|
|
for (let i = 0; i < 5; i++) {
|
|
leads.push(
|
|
Object.assign(new CrmLeadEntity(), { id: 20 + i, cardId: 2, userId: 102, tenantId: 1 })
|
|
);
|
|
}
|
|
for (let i = 0; i < 12; i++) {
|
|
tracks.push(
|
|
Object.assign(new CrmTrackEntity(), { id: 30 + i, cardId: 2, tenantId: 1 })
|
|
);
|
|
}
|
|
|
|
// Tariq: 0 leads (0分), 8 touches (8分) => 8分
|
|
for (let i = 0; i < 8; i++) {
|
|
tracks.push(
|
|
Object.assign(new CrmTrackEntity(), { id: 50 + i, cardId: 3, tenantId: 1 })
|
|
);
|
|
}
|
|
|
|
const ranks = await service.getTeamLiveRank('1');
|
|
|
|
expect(ranks).toHaveLength(3);
|
|
|
|
// 第 1 名: Sarah (62分)
|
|
expect(ranks[0].memberId).toBe(102);
|
|
expect(ranks[0].memberName).toBe('Sarah Wang');
|
|
expect(ranks[0].leads).toBe(5);
|
|
expect(ranks[0].touches).toBe(12);
|
|
expect(ranks[0].score).toBe(62);
|
|
expect(ranks[0].rank).toBe(1);
|
|
|
|
// 第 2 名: Alex (25分)
|
|
expect(ranks[1].memberId).toBe(101);
|
|
expect(ranks[1].memberName).toBe('Alex Rivera');
|
|
expect(ranks[1].leads).toBe(2);
|
|
expect(ranks[1].touches).toBe(5);
|
|
expect(ranks[1].score).toBe(25);
|
|
expect(ranks[1].rank).toBe(2);
|
|
|
|
// 第 3 名: Tariq (8分)
|
|
expect(ranks[2].memberId).toBe(103);
|
|
expect(ranks[2].memberName).toBe('Tariq Mansour');
|
|
expect(ranks[2].leads).toBe(0);
|
|
expect(ranks[2].touches).toBe(8);
|
|
expect(ranks[2].score).toBe(8);
|
|
expect(ranks[2].rank).toBe(3);
|
|
});
|
|
|
|
it('1.2 成员关联多张名片时,正确合并统计多名片的线索与触碰', async () => {
|
|
const u = new BaseSysUserEntity();
|
|
u.id = 201;
|
|
u.name = 'Multi Card Member';
|
|
u.tenantId = 1;
|
|
users.push(u);
|
|
|
|
const cA = new CardInfoEntity();
|
|
cA.id = 11;
|
|
cA.userId = 201;
|
|
cA.tenantId = 1;
|
|
|
|
const cB = new CardInfoEntity();
|
|
cB.id = 12;
|
|
cB.userId = 201;
|
|
cB.tenantId = 1;
|
|
cards.push(cA, cB);
|
|
|
|
leads.push(
|
|
Object.assign(new CrmLeadEntity(), { id: 101, cardId: 11, userId: 201, tenantId: 1 }),
|
|
Object.assign(new CrmLeadEntity(), { id: 102, cardId: 12, userId: 201, tenantId: 1 })
|
|
);
|
|
tracks.push(
|
|
Object.assign(new CrmTrackEntity(), { id: 201, cardId: 11, tenantId: 1 }),
|
|
Object.assign(new CrmTrackEntity(), { id: 202, cardId: 12, tenantId: 1 }),
|
|
Object.assign(new CrmTrackEntity(), { id: 203, cardId: 12, tenantId: 1 })
|
|
);
|
|
|
|
const ranks = await service.getTeamLiveRank('1');
|
|
expect(ranks).toHaveLength(1);
|
|
expect(ranks[0].leads).toBe(2);
|
|
expect(ranks[0].touches).toBe(3);
|
|
expect(ranks[0].score).toBe(2 * 10 + 3 * 1); // 23分
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 2. 同分并列规则测试
|
|
// ==========================================
|
|
describe('2. 同分并列规则测试 (Tie-Break & Shared Rank Rules)', () => {
|
|
it('2.1 当两位成员总分相同时,应获得相同 rank 并标记 isTied', async () => {
|
|
const u1 = Object.assign(new BaseSysUserEntity(), { id: 301, name: 'Alice', tenantId: 2 });
|
|
const u2 = Object.assign(new BaseSysUserEntity(), { id: 302, name: 'Bob', tenantId: 2 });
|
|
const u3 = Object.assign(new BaseSysUserEntity(), { id: 303, name: 'Charlie', tenantId: 2 });
|
|
users.push(u1, u2, u3);
|
|
|
|
const c1 = Object.assign(new CardInfoEntity(), { id: 31, userId: 301, tenantId: 2 });
|
|
const c2 = Object.assign(new CardInfoEntity(), { id: 32, userId: 302, tenantId: 2 });
|
|
const c3 = Object.assign(new CardInfoEntity(), { id: 33, userId: 303, tenantId: 2 });
|
|
cards.push(c1, c2, c3);
|
|
|
|
// Alice: 3 leads (30), 10 touches (10) => 40 分
|
|
for (let i = 0; i < 3; i++) {
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 300 + i, cardId: 31, userId: 301, tenantId: 2 }));
|
|
}
|
|
for (let i = 0; i < 10; i++) {
|
|
tracks.push(Object.assign(new CrmTrackEntity(), { id: 400 + i, cardId: 31, tenantId: 2 }));
|
|
}
|
|
|
|
// Bob: 4 leads (40), 0 touches (0) => 40 分 (与 Alice 同分!)
|
|
for (let i = 0; i < 4; i++) {
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 310 + i, cardId: 32, userId: 302, tenantId: 2 }));
|
|
}
|
|
|
|
// Charlie: 1 lead (10), 5 touches (5) => 15 分
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 320, cardId: 33, userId: 303, tenantId: 2 }));
|
|
for (let i = 0; i < 5; i++) {
|
|
tracks.push(Object.assign(new CrmTrackEntity(), { id: 420 + i, cardId: 33, tenantId: 2 }));
|
|
}
|
|
|
|
const ranks = await service.getTeamLiveRank('2');
|
|
|
|
expect(ranks).toHaveLength(3);
|
|
// 前两名总分都是 40 分
|
|
expect(ranks[0].score).toBe(40);
|
|
expect(ranks[1].score).toBe(40);
|
|
|
|
// 并列第 1 名
|
|
expect(ranks[0].rank).toBe(1);
|
|
expect(ranks[1].rank).toBe(1);
|
|
expect(ranks[0].isTied).toBe(true);
|
|
expect(ranks[1].isTied).toBe(true);
|
|
|
|
// 第 3 位 Charlie 的排名递推为第 3 名 (标准竞赛并列: 1, 1, 3)
|
|
expect(ranks[2].score).toBe(15);
|
|
expect(ranks[2].rank).toBe(3);
|
|
expect(ranks[2].isTied).toBe(false);
|
|
});
|
|
|
|
it('2.2 中间位置同分并列:第 2 名并列时第 4 名为 rank 4', async () => {
|
|
const u1 = Object.assign(new BaseSysUserEntity(), { id: 401, name: 'Leader', tenantId: 3 });
|
|
const u2 = Object.assign(new BaseSysUserEntity(), { id: 402, name: 'Runner1', tenantId: 3 });
|
|
const u3 = Object.assign(new BaseSysUserEntity(), { id: 403, name: 'Runner2', tenantId: 3 });
|
|
const u4 = Object.assign(new BaseSysUserEntity(), { id: 404, name: 'Fourth', tenantId: 3 });
|
|
users.push(u1, u2, u3, u4);
|
|
|
|
cards.push(
|
|
Object.assign(new CardInfoEntity(), { id: 41, userId: 401, tenantId: 3 }),
|
|
Object.assign(new CardInfoEntity(), { id: 42, userId: 402, tenantId: 3 }),
|
|
Object.assign(new CardInfoEntity(), { id: 43, userId: 403, tenantId: 3 }),
|
|
Object.assign(new CardInfoEntity(), { id: 44, userId: 404, tenantId: 3 })
|
|
);
|
|
|
|
// Leader: 10 leads = 100分
|
|
for (let i = 0; i < 10; i++) {
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 500 + i, cardId: 41, userId: 401, tenantId: 3 }));
|
|
}
|
|
// Runner1: 5 leads = 50分
|
|
for (let i = 0; i < 5; i++) {
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 520 + i, cardId: 42, userId: 402, tenantId: 3 }));
|
|
}
|
|
// Runner2: 50 touches = 50分
|
|
for (let i = 0; i < 50; i++) {
|
|
tracks.push(Object.assign(new CrmTrackEntity(), { id: 600 + i, cardId: 43, tenantId: 3 }));
|
|
}
|
|
// Fourth: 1 lead = 10分
|
|
leads.push(Object.assign(new CrmLeadEntity(), { id: 550, cardId: 44, userId: 404, tenantId: 3 }));
|
|
|
|
const ranks = await service.getTeamLiveRank('3');
|
|
expect(ranks[0].rank).toBe(1);
|
|
expect(ranks[0].score).toBe(100);
|
|
|
|
expect(ranks[1].rank).toBe(2);
|
|
expect(ranks[1].score).toBe(50);
|
|
expect(ranks[1].isTied).toBe(true);
|
|
|
|
expect(ranks[2].rank).toBe(2);
|
|
expect(ranks[2].score).toBe(50);
|
|
expect(ranks[2].isTied).toBe(true);
|
|
|
|
expect(ranks[3].rank).toBe(4);
|
|
expect(ranks[3].score).toBe(10);
|
|
expect(ranks[3].isTied).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 3. 展会事件 (eventId) 筛选测试
|
|
// ==========================================
|
|
describe('3. 展会现场事件筛选 (Event Filtering)', () => {
|
|
it('3.1 传入 eventId 时,仅聚合该展会事件的线索与触碰量', async () => {
|
|
const u1 = Object.assign(new BaseSysUserEntity(), { id: 501, name: 'Exhibitor', tenantId: 4 });
|
|
users.push(u1);
|
|
|
|
const c1 = Object.assign(new CardInfoEntity(), { id: 51, userId: 501, tenantId: 4 });
|
|
cards.push(c1);
|
|
|
|
// CES-2026 线索 (2条) 与 触碰 (3条)
|
|
const leadCes1 = Object.assign(new CrmLeadEntity(), {
|
|
id: 701,
|
|
cardId: 51,
|
|
userId: 501,
|
|
tenantId: 4,
|
|
notes: 'Met at #CES-2026 Booth 808',
|
|
});
|
|
(leadCes1 as unknown as { eventId?: string }).eventId = 'CES-2026';
|
|
|
|
const leadCes2 = Object.assign(new CrmLeadEntity(), {
|
|
id: 702,
|
|
cardId: 51,
|
|
userId: 501,
|
|
tenantId: 4,
|
|
notes: '#CES-2026 Client',
|
|
});
|
|
(leadCes2 as unknown as { eventId?: string }).eventId = 'CES-2026';
|
|
|
|
// MWC-2026 线索 (1条,不同展会)
|
|
const leadMwc = Object.assign(new CrmLeadEntity(), {
|
|
id: 703,
|
|
cardId: 51,
|
|
userId: 501,
|
|
tenantId: 4,
|
|
notes: '#MWC-2026 Lead',
|
|
});
|
|
(leadMwc as unknown as { eventId?: string }).eventId = 'MWC-2026';
|
|
|
|
leads.push(leadCes1, leadCes2, leadMwc);
|
|
|
|
// 触碰: 3条 CES-2026, 2条 MWC-2026
|
|
for (let i = 0; i < 3; i++) {
|
|
const t = Object.assign(new CrmTrackEntity(), { id: 800 + i, cardId: 51, tenantId: 4 });
|
|
(t as unknown as { eventId?: string }).eventId = 'CES-2026';
|
|
tracks.push(t);
|
|
}
|
|
for (let i = 0; i < 2; i++) {
|
|
const t = Object.assign(new CrmTrackEntity(), { id: 810 + i, cardId: 51, tenantId: 4 });
|
|
(t as unknown as { eventId?: string }).eventId = 'MWC-2026';
|
|
tracks.push(t);
|
|
}
|
|
|
|
// 按 CES-2026 查询
|
|
const ranksCes = await service.getTeamLiveRank('4', 'CES-2026');
|
|
expect(ranksCes).toHaveLength(1);
|
|
expect(ranksCes[0].leads).toBe(2);
|
|
expect(ranksCes[0].touches).toBe(3);
|
|
expect(ranksCes[0].score).toBe(2 * 10 + 3 * 1); // 23分
|
|
|
|
// 按 MWC-2026 查询
|
|
const ranksMwc = await service.getTeamLiveRank('4', 'MWC-2026');
|
|
expect(ranksMwc).toHaveLength(1);
|
|
expect(ranksMwc[0].leads).toBe(1);
|
|
expect(ranksMwc[0].touches).toBe(2);
|
|
expect(ranksMwc[0].score).toBe(1 * 10 + 2 * 1); // 12分
|
|
|
|
// 不传 eventId 时全部统计
|
|
const ranksAll = await service.getTeamLiveRank('4');
|
|
expect(ranksAll[0].leads).toBe(3);
|
|
expect(ranksAll[0].touches).toBe(5);
|
|
expect(ranksAll[0].score).toBe(35);
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 4. 空数据容错测试
|
|
// ==========================================
|
|
describe('4. 空数据与边界容错测试 (Empty Data Tolerance)', () => {
|
|
it('4.1 租户无任何成员时安全返回空数组,不报错', async () => {
|
|
const ranks = await service.getTeamLiveRank('9999');
|
|
expect(Array.isArray(ranks)).toBe(true);
|
|
expect(ranks).toHaveLength(0);
|
|
});
|
|
|
|
it('4.2 成员没有任何名片、线索与触碰时,全零并列第 1 名', async () => {
|
|
const u = Object.assign(new BaseSysUserEntity(), { id: 901, name: 'Newbie', tenantId: 5 });
|
|
users.push(u);
|
|
|
|
const ranks = await service.getTeamLiveRank('5');
|
|
expect(ranks).toHaveLength(1);
|
|
expect(ranks[0].memberId).toBe(901);
|
|
expect(ranks[0].leads).toBe(0);
|
|
expect(ranks[0].touches).toBe(0);
|
|
expect(ranks[0].score).toBe(0);
|
|
expect(ranks[0].rank).toBe(1);
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 5. WebSocket 广播事件载荷格式测试
|
|
// ==========================================
|
|
describe('5. WebSocket 广播事件载荷生成 (formatRankBroadcastPayload)', () => {
|
|
it('5.1 正确生成包含 event、timestamp、ranks、data 与 meta 的结构化广播载荷', () => {
|
|
const mockRanks: TeamMemberRankItem[] = [
|
|
{
|
|
memberId: 101,
|
|
memberName: 'Sarah',
|
|
departmentName: 'BD',
|
|
cardId: 1,
|
|
leads: 6,
|
|
touches: 10,
|
|
score: 70,
|
|
rank: 1,
|
|
isTied: false,
|
|
},
|
|
{
|
|
memberId: 102,
|
|
memberName: 'Alex',
|
|
departmentName: 'BD',
|
|
cardId: 2,
|
|
leads: 3,
|
|
touches: 10,
|
|
score: 40,
|
|
rank: 2,
|
|
isTied: false,
|
|
},
|
|
];
|
|
|
|
const before = Date.now();
|
|
const payload: TeamRankBroadcastPayload = service.formatRankBroadcastPayload(
|
|
mockRanks,
|
|
{ tenantId: '10', eventId: 'GITEX_2026' }
|
|
);
|
|
const after = Date.now();
|
|
|
|
expect(payload.event).toBe(TEAM_LIVE_RANK_EVENT);
|
|
expect(payload.event).toBe('team.live_rank');
|
|
expect(payload.timestamp).toBeGreaterThanOrEqual(before);
|
|
expect(payload.timestamp).toBeLessThanOrEqual(after);
|
|
expect(payload.total).toBe(2);
|
|
expect(payload.ranks).toEqual(mockRanks);
|
|
expect(payload.data).toEqual(mockRanks);
|
|
expect(payload.meta?.tenantId).toBe('10');
|
|
expect(payload.meta?.eventId).toBe('GITEX_2026');
|
|
expect(payload.meta?.topScore).toBe(70);
|
|
expect(payload.meta?.totalLeads).toBe(9);
|
|
expect(payload.meta?.totalTouches).toBe(20);
|
|
});
|
|
|
|
it('5.2 空数组 ranks 能够安全生成结构化载荷,各项指标为 0', () => {
|
|
const payload = service.formatRankBroadcastPayload([]);
|
|
expect(payload.event).toBe('team.live_rank');
|
|
expect(payload.total).toBe(0);
|
|
expect(payload.ranks).toEqual([]);
|
|
expect(payload.meta?.topScore).toBe(0);
|
|
expect(payload.meta?.totalLeads).toBe(0);
|
|
expect(payload.meta?.totalTouches).toBe(0);
|
|
});
|
|
});
|
|
|
|
// ==========================================
|
|
// 6. 控制器方法与 CRUD 配置测试
|
|
// ==========================================
|
|
describe('6. 控制器接口暴露与调用测试 (AdminTeamMembersController /live-rank)', () => {
|
|
let controller: AdminTeamMembersController;
|
|
let mockLiveRankService: TeamLiveRankService;
|
|
|
|
beforeEach(() => {
|
|
mockLiveRankService = new TeamLiveRankService();
|
|
mockLiveRankService.getTeamLiveRank = jest.fn(async () => [
|
|
{
|
|
memberId: 1,
|
|
memberName: 'Champion',
|
|
leads: 10,
|
|
touches: 5,
|
|
score: 105,
|
|
rank: 1,
|
|
},
|
|
]);
|
|
|
|
controller = new AdminTeamMembersController();
|
|
controller.teamLiveRankService = mockLiveRankService;
|
|
controller.ok = (data: unknown) => ({
|
|
code: 1000,
|
|
message: 'success',
|
|
data,
|
|
});
|
|
});
|
|
|
|
it('6.1 adminTeamMembersCurdOption 包含 liveRank 接口说明', () => {
|
|
const methods = (adminTeamMembersCurdOption.serviceApis || []).map(
|
|
(api: { method: string }) => api.method
|
|
);
|
|
expect(methods).toContain('liveRank');
|
|
});
|
|
|
|
it('6.2 postLiveRank 接口正常响应 code 1000 并返回排行榜', async () => {
|
|
const res = (await controller.postLiveRank({
|
|
tenantId: '8',
|
|
eventId: 'EXPO-2026',
|
|
})) as unknown as { code: number; message: string; data: TeamMemberRankItem[] };
|
|
expect(res.code).toBe(1000);
|
|
expect(mockLiveRankService.getTeamLiveRank).toHaveBeenCalledWith(
|
|
'8',
|
|
'EXPO-2026'
|
|
);
|
|
const data = res.data;
|
|
expect(data).toHaveLength(1);
|
|
expect(data[0].score).toBe(105);
|
|
});
|
|
|
|
it('6.3 postLiveRank 在未传参时默认兜底 tenantId 为 1', async () => {
|
|
const res = (await controller.postLiveRank()) as unknown as {
|
|
code: number;
|
|
message: string;
|
|
};
|
|
expect(res.code).toBe(1000);
|
|
expect(mockLiveRankService.getTeamLiveRank).toHaveBeenCalledWith(
|
|
'1',
|
|
undefined
|
|
);
|
|
});
|
|
});
|
|
});
|