161 lines
4.7 KiB
TypeScript
161 lines
4.7 KiB
TypeScript
jest.mock('@/qqbot/plugins/bangDream/tsugu/models/main-data-store', () => ({
|
|
__esModule: true,
|
|
default: {},
|
|
}));
|
|
|
|
import {
|
|
calculateCnEventEstimateStartAt,
|
|
getBangDreamOccupiedDays,
|
|
type BangDreamEventSchedule,
|
|
} from '@/qqbot/plugins/bangDream/tsugu/models/cn-event-estimate-policy';
|
|
import {
|
|
getCutoffDateByServerTimezone,
|
|
getCutoffDayIndex,
|
|
getCutoffEventStatus,
|
|
getCutoffPredictionWindow,
|
|
getCutoffTierList,
|
|
isCutoffDailyCheckpoint,
|
|
isCutoffTierSupported,
|
|
selectRecentCutoffEventIds,
|
|
} from '@/qqbot/plugins/bangDream/tsugu/models/cutoff-policy';
|
|
import {
|
|
getBangDreamServerUtcOffset,
|
|
normalizeBangDreamTimestamp,
|
|
} from '@/qqbot/plugins/bangDream/tsugu/models/server-policy';
|
|
import {
|
|
BangDreamEventStatus,
|
|
BangDreamServerId as Server,
|
|
} from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-protocol';
|
|
|
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
|
|
describe('BangDream server policy', () => {
|
|
it('normalizes timestamps and applies server UTC offsets', () => {
|
|
expect(normalizeBangDreamTimestamp(1_700_000_000)).toBe(1_700_000_000_000);
|
|
expect(normalizeBangDreamTimestamp(1_700_000_000_000)).toBe(
|
|
1_700_000_000_000,
|
|
);
|
|
expect(getBangDreamServerUtcOffset(Server.cn)).toBe(8);
|
|
expect(getBangDreamServerUtcOffset(Server.jp)).toBe(9);
|
|
expect(getBangDreamServerUtcOffset(Server.en)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('BangDream CN event estimate policy', () => {
|
|
it('keeps occupied day calculation inclusive', () => {
|
|
expect(
|
|
getBangDreamOccupiedDays(Date.UTC(2026, 0, 1), Date.UTC(2026, 0, 1)),
|
|
).toBe(1);
|
|
expect(
|
|
getBangDreamOccupiedDays(Date.UTC(2026, 0, 1), Date.UTC(2026, 0, 3)),
|
|
).toBe(3);
|
|
});
|
|
|
|
it('calculates the next missing CN event start from pure schedules', () => {
|
|
const schedules: Record<number, BangDreamEventSchedule> = {
|
|
100: {
|
|
endAt: [Date.UTC(2026, 0, 5), null, null, Date.UTC(2026, 0, 5), null],
|
|
startAt: [Date.UTC(2026, 0, 1), null, null, Date.UTC(2026, 0, 1), null],
|
|
},
|
|
101: {
|
|
endAt: [Date.UTC(2026, 0, 10), null, null, null, null],
|
|
startAt: [Date.UTC(2026, 0, 6), null, null, null, null],
|
|
},
|
|
};
|
|
|
|
const result = calculateCnEventEstimateStartAt({
|
|
currentEvent: { eventId: 100, ...schedules[100] },
|
|
eventId: 101,
|
|
getSchedule: (eventId) => schedules[eventId] || null,
|
|
options: {
|
|
defaultNoBangDays: 1,
|
|
estimateStartEventId: 100,
|
|
},
|
|
presentJpEventId: 101,
|
|
});
|
|
|
|
expect(result).toBe(Date.UTC(2026, 0, 6));
|
|
});
|
|
});
|
|
|
|
describe('BangDream cutoff policy', () => {
|
|
it('checks tier support by server', () => {
|
|
expect(getCutoffTierList(Server.cn)).toContain(1500);
|
|
expect(isCutoffTierSupported(Server.cn, 1500)).toBe(true);
|
|
expect(isCutoffTierSupported(Server.jp, 1500)).toBe(false);
|
|
});
|
|
|
|
it('resolves event status and prediction windows', () => {
|
|
const startAt = Date.UTC(2026, 0, 1);
|
|
const endAt = Date.UTC(2026, 0, 5);
|
|
|
|
expect(getCutoffEventStatus(startAt, endAt, startAt - 1)).toBe(
|
|
BangDreamEventStatus.notStart,
|
|
);
|
|
expect(getCutoffEventStatus(startAt, endAt, startAt + 1)).toBe(
|
|
BangDreamEventStatus.inProgress,
|
|
);
|
|
expect(getCutoffEventStatus(startAt, endAt, endAt + 1)).toBe(
|
|
BangDreamEventStatus.ended,
|
|
);
|
|
expect(getCutoffPredictionWindow(startAt, endAt)).toEqual({
|
|
endTs: Math.floor(endAt / 1000),
|
|
startTs: Math.floor(startAt / 1000),
|
|
});
|
|
});
|
|
|
|
it('calculates daily checkpoints and event day index', () => {
|
|
const checkpoint = getCutoffDateByServerTimezone(
|
|
Date.UTC(2026, 0, 1, 18, 45),
|
|
Server.jp,
|
|
);
|
|
expect(isCutoffDailyCheckpoint(Server.jp, checkpoint)).toBe(true);
|
|
expect(isCutoffDailyCheckpoint(Server.en, checkpoint)).toBe(false);
|
|
|
|
const eventStartAt = Date.UTC(2026, 0, 1, 12);
|
|
expect(getCutoffDayIndex(Server.jp, eventStartAt, eventStartAt + 1)).toBe(
|
|
0,
|
|
);
|
|
expect(
|
|
getCutoffDayIndex(Server.jp, eventStartAt, eventStartAt + DAY_MS),
|
|
).toBe(1);
|
|
});
|
|
|
|
it('selects recent comparable events by type and server start time', () => {
|
|
const candidates = [
|
|
eventCandidate(1, 'story', [Date.UTC(2026, 0, 1)]),
|
|
eventCandidate(2, 'festival', [Date.UTC(2026, 0, 5)]),
|
|
eventCandidate(3, 'story', [Date.UTC(2026, 0, 10)]),
|
|
eventCandidate(4, 'story', [Date.UTC(2026, 0, 15)]),
|
|
];
|
|
|
|
expect(
|
|
selectRecentCutoffEventIds({
|
|
candidates,
|
|
count: 2,
|
|
event: candidates[3],
|
|
sameType: true,
|
|
server: Server.jp,
|
|
}),
|
|
).toEqual([3, 4]);
|
|
});
|
|
});
|
|
|
|
function eventCandidate(
|
|
eventId: number,
|
|
eventType: string,
|
|
jpStartAt: number[],
|
|
) {
|
|
return {
|
|
eventId,
|
|
eventType,
|
|
startAt: [
|
|
jpStartAt[0],
|
|
null,
|
|
null,
|
|
jpStartAt[Server.cn] ?? jpStartAt[0],
|
|
null,
|
|
],
|
|
};
|
|
}
|