Files
heros_web/scripts/verify-sortie-roster-history.mjs

125 lines
4.7 KiB
JavaScript

import { createServer } from 'vite';
const server = await createServer({
logLevel: 'error',
server: { middlewareMode: true, hmr: false },
appType: 'custom'
});
try {
const {
defaultLongWaitBattleThreshold,
defaultRecentRecruitBattleWindow,
deriveSortieRosterHistory,
deriveSortieRosterHistoryByUnitId
} = await server.ssrLoadModule('/src/game/data/sortieRosterHistory.ts');
assert(defaultRecentRecruitBattleWindow === 1, 'Recent-recruit window must retain its one-battle default.');
assert(defaultLongWaitBattleThreshold === 3, 'Long-wait threshold must retain its three-battle default.');
const empty = deriveSortieRosterHistory(['liu-bei'], {});
assert(empty.length === 1, `Empty history must still return the requested roster unit: ${JSON.stringify(empty)}`);
assert(
empty[0].sortieCount === 0 &&
empty[0].firstSortiePending === true &&
empty[0].battlesSinceLastSortie === null &&
empty[0].recentRecruit === false &&
empty[0].badge.kind === 'first-sortie' &&
empty[0].badge.label === '미출전',
`Empty history must produce a stable first-sortie state: ${JSON.stringify(empty[0])}`
);
const firstSortieHistory = {
opening: battle('opening', 1, ['guan-yu', 'guan-yu'])
};
const frozenFirstSortieHistory = JSON.stringify(firstSortieHistory);
const firstSortie = deriveSortieRosterHistory(['guan-yu'], firstSortieHistory)[0];
assert(
firstSortie.sortieCount === 1 &&
firstSortie.firstSortiePending === false &&
firstSortie.battlesSinceLastSortie === 0 &&
firstSortie.lastSortieBattleId === 'opening' &&
firstSortie.badge.kind === 'active' &&
firstSortie.badge.label === '출전 1회',
`A first completed sortie must become the active state exactly once: ${JSON.stringify(firstSortie)}`
);
assert(
JSON.stringify(firstSortieHistory) === frozenFirstSortieHistory,
'History derivation must not mutate campaign battle-history-like input.'
);
const longWait = deriveSortieRosterHistory(['zhang-fei'], {
fourth: battle('fourth', 4),
first: battle('first', 1, ['zhang-fei']),
third: battle('third', 3),
second: battle('second', 2)
})[0];
assert(
longWait.sortieCount === 1 &&
longWait.battlesSinceLastSortie === 3 &&
longWait.lastSortieBattleId === 'first' &&
longWait.badge.kind === 'long-wait' &&
longWait.badge.label === '대기 3전',
`Three later battles must produce a long-wait badge even when record insertion is unordered: ${JSON.stringify(longWait)}`
);
const recentRecruit = deriveSortieRosterHistory(['jian-yong'], {
recruitment: battle('recruitment', 1, [], ['jian-yong'])
})[0];
assert(
recentRecruit.sortieCount === 0 &&
recentRecruit.firstSortiePending === true &&
recentRecruit.recentRecruit === true &&
recentRecruit.battlesSinceRecruit === 0 &&
recentRecruit.recruitBattleId === 'recruitment' &&
recentRecruit.badge.kind === 'recent-recruit' &&
recentRecruit.badge.label === 'NEW' &&
recentRecruit.summary === '최근 합류 · 첫 출전 대기',
`A latest-battle recruit must expose the concise NEW state: ${JSON.stringify(recentRecruit)}`
);
const experiencedRecentRecruit = deriveSortieRosterHistory(['guan-yu'], {
recruitment: battle('recruitment', 1, ['guan-yu'], ['guan-yu'])
})[0];
assert(
experiencedRecentRecruit.sortieCount === 1 &&
experiencedRecentRecruit.firstSortiePending === false &&
experiencedRecentRecruit.recentRecruit === true &&
experiencedRecentRecruit.badge.kind === 'recent-recruit' &&
experiencedRecentRecruit.badge.label === 'NEW',
`A recruit who fought before formally joining must retain the NEW roster badge: ${JSON.stringify(experiencedRecentRecruit)}`
);
const byUnitId = deriveSortieRosterHistoryByUnitId(
['jian-yong', 'liu-bei', 'jian-yong', ' '],
[battle('recruitment', 1, ['liu-bei'], ['jian-yong'])]
);
assert(
byUnitId.size === 2 &&
byUnitId.get('jian-yong')?.recentRecruit === true &&
byUnitId.get('liu-bei')?.sortieCount === 1,
`Map derivation must preserve unique roster entries and their states: ${JSON.stringify([...byUnitId])}`
);
console.log('Verified sortie roster history: empty, first sortie, long wait, recent recruit, ordering, and immutability.');
} finally {
await server.close();
}
function battle(battleId, day, selectedUnitIds = [], recruitUnitIds = []) {
return {
battleId,
completedAt: new Date(Date.UTC(2026, 0, day)).toISOString(),
sortiePerformance: { selectedUnitIds },
campaignRewards: {
recruits: recruitUnitIds.map((unitId) => ({ unitId }))
}
};
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}