112 lines
6.3 KiB
TypeScript
112 lines
6.3 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { events } from '../data/events.ts';
|
|
import { activeEventAt, compareEventRankings, eventBonusBreakdown, eventChallengeSongScoreBonusPercent, eventOptimizationCardEligible, eventPointIndex } from './event-optimization.ts';
|
|
|
|
const currentEvent = events[0];
|
|
|
|
test('솔로 이벤트는 챕터 경계에서 대상 멤버와 과제곡을 함께 전환한다', () => {
|
|
assert.equal(activeEventAt(events, Date.parse('2026-09-19T10:59:59.999Z')), null);
|
|
const expected = [
|
|
[19, 'chr-00004', 'm0349'], [21, 'chr-04013', 'm0351'],
|
|
[23, 'chr-00035', 'm0350'], [25, 'chr-03005', 'm0352']
|
|
] as const;
|
|
for (const [day, characterId, songId] of expected) {
|
|
const start = Date.parse(`2026-09-${day}T11:00:00.000Z`);
|
|
const chapter = activeEventAt(events, start)!;
|
|
assert.deepEqual(chapter.bonusCharacterIds, [characterId]);
|
|
assert.deepEqual(Object.keys(chapter.challengeSongBonusCardIds), [songId]);
|
|
assert.equal(activeEventAt(events, start + 48 * 60 * 60 * 1000 - 1)?.id, chapter.id);
|
|
assert.notEqual(activeEventAt(events, start - 1)?.id, chapter.id);
|
|
}
|
|
assert.equal(activeEventAt(events, Date.parse('2026-09-27T11:00:00.000Z')), null);
|
|
});
|
|
|
|
test('신규 카드 공통 보너스와 현재 챕터 멤버 보너스를 따로 적용한다', () => {
|
|
const aki = activeEventAt(events, Date.parse('2026-09-19T11:00:00Z'))!;
|
|
const shiori = activeEventAt(events, Date.parse('2026-09-21T11:00:00Z'))!;
|
|
const cards = [
|
|
{ id: aki.featuredCardIds[0], characterId: 'chr-00004', rarity: 5 },
|
|
{ id: 'shiori-regular', characterId: 'chr-04013', rarity: 3 }
|
|
];
|
|
assert.deepEqual(eventBonusBreakdown(aki, cards, () => 5), {
|
|
formationPercent: 50, featuredCardPercent: 50, awakeningPercent: 33, totalPercent: 133
|
|
});
|
|
assert.equal(eventBonusBreakdown(shiori, [cards[0]], () => 0).totalPercent, 50);
|
|
assert.equal(eventBonusBreakdown(shiori, [cards[1]], () => 0).totalPercent, 50);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(aki, 'm0349', [cards[0].id]), 10);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(shiori, 'm0349', [cards[0].id]), 0);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(aki, 'm0349', [aki.featuredCardIds[1]]), 0);
|
|
});
|
|
|
|
test('9월 25일 20시부터 아냐와 64.가 마지막 챕터 보너스를 받는다', () => {
|
|
const before = activeEventAt(events, Date.parse('2026-09-25T10:59:59.999Z'))!;
|
|
const anya = activeEventAt(events, Date.parse('2026-09-25T11:00:00.000Z'))!;
|
|
const featuredAnya = { id: 'card-03005-5-uniq-0084-00', characterId: 'chr-03005', rarity: 5 };
|
|
|
|
assert.equal(before.id, 'marathon_event-relay-002-chapter-03');
|
|
assert.equal(anya.id, 'marathon_event-relay-002-chapter-04');
|
|
assert.deepEqual(anya.bonusCharacterIds, ['chr-03005']);
|
|
assert.deepEqual(anya.challengeSongBonusCardIds, { m0352: [featuredAnya.id] });
|
|
assert.equal(eventBonusBreakdown(anya, [featuredAnya], () => 0).totalPercent, 100);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(anya, 'm0352', [featuredAnya.id]), 10);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(before, 'm0352', [featuredAnya.id]), 0);
|
|
});
|
|
|
|
test('이벤트 기간 안에서만 진행 중 이벤트를 반환한다', () => {
|
|
assert.equal(activeEventAt(events, Date.parse('2026-08-29T02:59:59.999Z')), null);
|
|
assert.equal(activeEventAt(events, Date.parse('2026-08-31T00:00:00.000Z'))?.id, currentEvent.id);
|
|
assert.equal(activeEventAt(events, Date.parse('2026-09-06T11:00:00.000Z')), null);
|
|
});
|
|
|
|
test('이벤트 최적화는 모든 4·5성과 보너스 멤버의 저등급 카드를 후보로 사용한다', () => {
|
|
const bonusCharacters = new Set(['bonus']);
|
|
|
|
assert.equal(eventOptimizationCardEligible({ id: 'five', characterId: 'other', rarity: 5 }, bonusCharacters), true);
|
|
assert.equal(eventOptimizationCardEligible({ id: 'four', characterId: 'other', rarity: 4 }, bonusCharacters), true);
|
|
assert.equal(eventOptimizationCardEligible({ id: 'bonus-three', characterId: 'bonus', rarity: 3 }, bonusCharacters), true);
|
|
assert.equal(eventOptimizationCardEligible({ id: 'regular-three', characterId: 'other', rarity: 3 }, bonusCharacters), false);
|
|
});
|
|
|
|
test('편성·신규 카드·개화 보너스를 카드별로 합산한다', () => {
|
|
const cards = [
|
|
{ id: currentEvent.featuredCardIds[0], characterId: currentEvent.bonusCharacterIds[0], rarity: 5 },
|
|
{ id: 'regular-event-member', characterId: currentEvent.bonusCharacterIds[1], rarity: 4 },
|
|
{ id: 'other-member', characterId: 'chr-other', rarity: 3 }
|
|
];
|
|
const awakening = new Map([[cards[0].id, 5], [cards[1].id, 2], [cards[2].id, 1]]);
|
|
const bonus = eventBonusBreakdown(currentEvent, cards, (card) => awakening.get(card.id) ?? 0);
|
|
|
|
assert.deepEqual(bonus, {
|
|
formationPercent: 60,
|
|
featuredCardPercent: 30,
|
|
awakeningPercent: 38,
|
|
totalPercent: 128
|
|
});
|
|
});
|
|
|
|
test('과제곡에 대응하는 신규 카드가 있을 때만 스코어 보너스를 적용한다', () => {
|
|
assert.equal(eventChallengeSongScoreBonusPercent(currentEvent, 'm0338', [currentEvent.featuredCardIds[0]]), 10);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(currentEvent, 'm0338', [currentEvent.featuredCardIds[1]]), 0);
|
|
assert.equal(eventChallengeSongScoreBonusPercent(currentEvent, 'm0342', [currentEvent.featuredCardIds[3]]), 10);
|
|
});
|
|
|
|
test('예상 점수에 이벤트 획득 배율을 적용해 비교 지수를 만든다', () => {
|
|
assert.equal(eventPointIndex(1_000_000, 128), 2_280_000);
|
|
});
|
|
|
|
test('이벤트 순위는 최고점보다 평균 점수를 우선한다', () => {
|
|
const highMaximum = { result: { expectedScore: 100, theoreticalMaxScore: 300 } };
|
|
const highAverage = { result: { expectedScore: 120, theoreticalMaxScore: 200 } };
|
|
assert.deepEqual([highMaximum, highAverage].sort(compareEventRankings), [highAverage, highMaximum]);
|
|
});
|
|
|
|
test('평균 이벤트 Pt에는 획득 보너스를 반영하고 동률은 평균 점수로 정렬한다', () => {
|
|
const bonus = { result: { expectedScore: 100 }, eventBonusPercent: 100 };
|
|
const score = { result: { expectedScore: 150 }, eventBonusPercent: 0 };
|
|
assert.ok(compareEventRankings(bonus, score) < 0);
|
|
assert.ok(compareEventRankings(bonus, { result: { expectedScore: 200 } }) > 0);
|
|
const sameAverage = { ...bonus, result: { expectedScore: 100, theoreticalMaxScore: 999 } };
|
|
assert.equal(compareEventRankings(bonus, sameAverage), 0);
|
|
});
|