165 lines
5.4 KiB
TypeScript
165 lines
5.4 KiB
TypeScript
import { RankingMergerService } from '../../../src/domain/services/ranking-merger.service';
|
|
import { LeaderboardRanking } from '../../../src/domain/aggregates/leaderboard-ranking/leaderboard-ranking.aggregate';
|
|
import { LeaderboardType, LeaderboardPeriod, UserSnapshot } from '../../../src/domain/value-objects';
|
|
|
|
describe('RankingMergerService', () => {
|
|
let service: RankingMergerService;
|
|
let mockPeriod: LeaderboardPeriod;
|
|
|
|
beforeEach(() => {
|
|
service = new RankingMergerService();
|
|
mockPeriod = LeaderboardPeriod.currentDaily();
|
|
});
|
|
|
|
const createRealRanking = (userId: bigint, rankPosition: number) => {
|
|
return LeaderboardRanking.createRealRanking({
|
|
leaderboardType: LeaderboardType.DAILY,
|
|
period: mockPeriod,
|
|
userId,
|
|
rankPosition,
|
|
displayPosition: rankPosition,
|
|
previousRank: null,
|
|
totalTeamPlanting: 100,
|
|
maxDirectTeamPlanting: 50,
|
|
userSnapshot: UserSnapshot.create({
|
|
userId,
|
|
nickname: `用户${userId}`,
|
|
}),
|
|
});
|
|
};
|
|
|
|
const createVirtualRanking = (virtualAccountId: bigint, displayPosition: number) => {
|
|
return LeaderboardRanking.createVirtualRanking({
|
|
leaderboardType: LeaderboardType.DAILY,
|
|
period: mockPeriod,
|
|
virtualAccountId,
|
|
displayPosition,
|
|
generatedScore: 500,
|
|
displayName: `虚拟用户${virtualAccountId}`,
|
|
avatar: null,
|
|
});
|
|
};
|
|
|
|
describe('mergeRankings', () => {
|
|
it('没有虚拟排名时应该保持原始排名', () => {
|
|
const realRankings = [
|
|
createRealRanking(1n, 1),
|
|
createRealRanking(2n, 2),
|
|
createRealRanking(3n, 3),
|
|
];
|
|
|
|
const merged = service.mergeRankings([], realRankings, 30);
|
|
|
|
expect(merged.length).toBe(3);
|
|
expect(merged[0].displayPosition.value).toBe(1);
|
|
expect(merged[1].displayPosition.value).toBe(2);
|
|
expect(merged[2].displayPosition.value).toBe(3);
|
|
});
|
|
|
|
it('有虚拟排名时应该正确调整真实用户排名', () => {
|
|
const virtualRankings = [
|
|
createVirtualRanking(100n, 1),
|
|
createVirtualRanking(101n, 2),
|
|
];
|
|
|
|
const realRankings = [
|
|
createRealRanking(1n, 1),
|
|
createRealRanking(2n, 2),
|
|
];
|
|
|
|
const merged = service.mergeRankings(virtualRankings, realRankings, 30);
|
|
|
|
expect(merged.length).toBe(4);
|
|
expect(merged[0].isVirtual).toBe(true);
|
|
expect(merged[0].displayPosition.value).toBe(1);
|
|
expect(merged[1].isVirtual).toBe(true);
|
|
expect(merged[1].displayPosition.value).toBe(2);
|
|
expect(merged[2].isVirtual).toBe(false);
|
|
expect(merged[2].displayPosition.value).toBe(3); // 原来第1名变成第3名
|
|
expect(merged[3].isVirtual).toBe(false);
|
|
expect(merged[3].displayPosition.value).toBe(4); // 原来第2名变成第4名
|
|
});
|
|
|
|
it('应该遵守显示数量限制', () => {
|
|
const virtualRankings = [
|
|
createVirtualRanking(100n, 1),
|
|
createVirtualRanking(101n, 2),
|
|
];
|
|
|
|
const realRankings = [
|
|
createRealRanking(1n, 1),
|
|
createRealRanking(2n, 2),
|
|
createRealRanking(3n, 3),
|
|
];
|
|
|
|
const merged = service.mergeRankings(virtualRankings, realRankings, 3);
|
|
|
|
expect(merged.length).toBe(3);
|
|
expect(merged[0].isVirtual).toBe(true);
|
|
expect(merged[1].isVirtual).toBe(true);
|
|
expect(merged[2].isVirtual).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getRealRankingsOnly', () => {
|
|
it('应该只返回真实用户排名', () => {
|
|
const virtualRanking = createVirtualRanking(100n, 1);
|
|
const realRanking = createRealRanking(1n, 2);
|
|
|
|
const rankings = [virtualRanking, realRanking];
|
|
const realOnly = service.getRealRankingsOnly(rankings, 10);
|
|
|
|
expect(realOnly.length).toBe(1);
|
|
expect(realOnly[0].isVirtual).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getVirtualRankingsOnly', () => {
|
|
it('应该只返回虚拟排名', () => {
|
|
const virtualRanking = createVirtualRanking(100n, 1);
|
|
const realRanking = createRealRanking(1n, 2);
|
|
|
|
const rankings = [virtualRanking, realRanking];
|
|
const virtualOnly = service.getVirtualRankingsOnly(rankings);
|
|
|
|
expect(virtualOnly.length).toBe(1);
|
|
expect(virtualOnly[0].isVirtual).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('calculateRealRankPosition', () => {
|
|
it('应该正确计算真实排名位置', () => {
|
|
const virtualRanking = createVirtualRanking(100n, 1);
|
|
const realRanking1 = createRealRanking(1n, 2);
|
|
const realRanking2 = createRealRanking(2n, 3);
|
|
|
|
const rankings = [virtualRanking, realRanking1, realRanking2];
|
|
|
|
expect(service.calculateRealRankPosition(rankings, 1n)).toBe(1);
|
|
expect(service.calculateRealRankPosition(rankings, 2n)).toBe(2);
|
|
});
|
|
|
|
it('用户不在排名中应该返回null', () => {
|
|
const rankings = [createRealRanking(1n, 1)];
|
|
|
|
expect(service.calculateRealRankPosition(rankings, 999n)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('validateRankingContinuity', () => {
|
|
it('连续排名应该验证通过', () => {
|
|
const rankings = [
|
|
createRealRanking(1n, 1),
|
|
createRealRanking(2n, 2),
|
|
createRealRanking(3n, 3),
|
|
];
|
|
|
|
expect(service.validateRankingContinuity(rankings)).toBe(true);
|
|
});
|
|
|
|
it('空数组应该验证通过', () => {
|
|
expect(service.validateRankingContinuity([])).toBe(true);
|
|
});
|
|
});
|
|
});
|