import { checkDeath, processDeath } from '../src/engine/deathEngine.js'; function assertEqual(actual, expected, message) { if (actual !== expected) { throw new Error(`${message}: expected ${expected}, got ${actual}`); } } function assertTrue(value, message) { if (!value) { throw new Error(`${message}: expected true, got ${value}`); } } function assertFalse(value, message) { if (value) { throw new Error(`${message}: expected false, got ${value}`); } } function createState(overrides = {}) { return { age: 0, alive: true, stats: { body: 10 }, worldFlags: {}, careers: {}, metaExp: {}, ...overrides, }; } const careersConfig = { careers: [ { id: 'student', expCurve: { base: 10, factor: 1.15 }, }, { id: 'soldier', expCurve: { base: 20, factor: 1.18 }, }, ], }; function testCheckDeathOldAge() { const state = createState({ age: 100 }); const result = checkDeath(state); assertTrue(result, 'should die at age 100'); assertFalse(state.alive, 'alive should be false'); assertEqual(state.deathReason, '寿终正寝', 'death reason should be 寿终正寝'); } function testCheckDeathBodyZero() { const state = createState({ age: 30, stats: { body: 0 } }); const result = checkDeath(state); assertTrue(result, 'should die when body <= 0'); assertFalse(state.alive, 'alive should be false'); assertEqual(state.deathReason, '体弱身亡', 'death reason should be 体弱身亡'); } function testCheckDeathNotDead() { const state = createState({ age: 30, stats: { body: 5 } }); const result = checkDeath(state); assertFalse(result, 'should not die when young and body > 0'); assertTrue(state.alive, 'should remain alive'); } function testCheckDeathMilitaryInvasion() { // Mock Math.random to always return 0.01 (below 0.05 threshold) const originalRandom = Math.random; Math.random = () => 0.01; try { const state = createState({ age: 45, stats: { body: 5 } }); const result = checkDeath(state); assertTrue(result, 'should die from military invasion'); assertFalse(state.alive, 'alive should be false'); assertEqual(state.deathReason, '死于蛮族入侵', 'death reason should be 死于蛮族入侵'); } finally { Math.random = originalRandom; } } function testCheckDeathMilitaryInvasionResisted() { // Mock Math.random to always return 0.01 (below 0.05 threshold) const originalRandom = Math.random; Math.random = () => 0.01; try { const state = createState({ age: 45, stats: { body: 5 }, worldFlags: { invasion_military_resisted: true } }); const result = checkDeath(state); // Since military is resisted, should fall through to next condition (age >= 60 random check) // But age is 45, so not dead assertFalse(result, 'should not die when military invasion resisted'); assertTrue(state.alive, 'should remain alive when resisted'); } finally { Math.random = originalRandom; } } function testCheckDeathSpiritualInvasion() { const originalRandom = Math.random; let callCount = 0; Math.random = () => { callCount++; return callCount === 1 ? 0.1 : 0.01; }; try { const state = createState({ age: 50, stats: { body: 5 } }); const result = checkDeath(state); assertTrue(result, 'should die from spiritual invasion'); assertFalse(state.alive, 'alive should be false'); assertEqual(state.deathReason, '被天魔吞噬', 'death reason should be 被天魔吞噬'); } finally { Math.random = originalRandom; } } function testCheckDeathPoliticalInvasion() { const originalRandom = Math.random; let callCount = 0; Math.random = () => { callCount++; return callCount <= 2 ? 0.1 : 0.01; }; try { const state = createState({ age: 55, stats: { body: 5 } }); const result = checkDeath(state); assertTrue(result, 'should die from political invasion'); assertFalse(state.alive, 'alive should be false'); assertEqual(state.deathReason, '死于战乱', 'death reason should be 死于战乱'); } finally { Math.random = originalRandom; } } function testProcessDeath() { const state = createState({ careers: { student: { level: 2, exp: 5 }, soldier: { level: 1, exp: 10 }, }, metaExp: {}, }); const result = processDeath(state, careersConfig); // student: totalExp = 5 + floor(10 * 1.15^0) + floor(10 * 1.15^1) = 5 + 10 + 11 = 26 // gain = floor(26 * 0.1) = 2 assertEqual(result.metaGains.student, 2, 'student meta gain should be 2'); assertEqual(state.metaExp.student, 2, 'student metaExp should be 2'); // soldier: totalExp = 10 + floor(20 * 1.18^0) = 10 + 20 = 30 // gain = floor(30 * 0.1) = 3 assertEqual(result.metaGains.soldier, 3, 'soldier meta gain should be 3'); assertEqual(state.metaExp.soldier, 3, 'soldier metaExp should be 3'); } function testProcessDeathAccumulates() { const state = createState({ careers: { student: { level: 0, exp: 10 }, }, metaExp: { student: 5 }, }); const result = processDeath(state, careersConfig); // student: totalExp = 10, gain = floor(10 * 0.1) = 1 assertEqual(result.metaGains.student, 1, 'student meta gain should be 1'); assertEqual(state.metaExp.student, 6, 'student metaExp should accumulate from 5 to 6'); } function testProcessDeathNoCareerConfig() { const state = createState({ careers: { unknown: { level: 1, exp: 10 }, }, metaExp: {}, }); const result = processDeath(state, careersConfig); assertEqual(Object.keys(result.metaGains).length, 0, 'no gains for unknown career'); } function runTests() { let passed = 0; let failed = 0; const tests = [ { name: 'testCheckDeathOldAge', fn: testCheckDeathOldAge }, { name: 'testCheckDeathBodyZero', fn: testCheckDeathBodyZero }, { name: 'testCheckDeathNotDead', fn: testCheckDeathNotDead }, { name: 'testCheckDeathMilitaryInvasion', fn: testCheckDeathMilitaryInvasion }, { name: 'testCheckDeathMilitaryInvasionResisted', fn: testCheckDeathMilitaryInvasionResisted }, { name: 'testCheckDeathSpiritualInvasion', fn: testCheckDeathSpiritualInvasion }, { name: 'testCheckDeathPoliticalInvasion', fn: testCheckDeathPoliticalInvasion }, { name: 'testProcessDeath', fn: testProcessDeath }, { name: 'testProcessDeathAccumulates', fn: testProcessDeathAccumulates }, { name: 'testProcessDeathNoCareerConfig', fn: testProcessDeathNoCareerConfig }, ]; for (const test of tests) { try { test.fn(); console.log(` PASS: ${test.name}`); passed++; } catch (err) { console.log(` FAIL: ${test.name} - ${err.message}`); failed++; } } console.log(`\n${passed} passed, ${failed} failed`); if (failed === 0) { console.log('All deathEngine tests PASS'); } else { process.exit(1); } } runTests();