Files
rebirthlife/test/careerEngine.test.js

222 lines
7.2 KiB
JavaScript

import {
setCareerConfig,
getCareerConfig,
checkUnlocks,
calcDailyIncome,
calcDailyExp,
checkLevelUp,
STAT_MAP,
} from '../src/engine/careerEngine.js';
const careersConfig = {
careers: [
{
id: 'student',
name: '学童',
type: 'scholar',
unlockConditions: { type: 'age', op: '>=', value: 6 },
dailyIncome: 1,
expCurve: { base: 10, factor: 1.15 },
},
{
id: 'book_boy',
name: '书童',
type: 'scholar',
unlockConditions: { type: 'careerLevel', op: '>=', careerId: 'student', value: 100 },
dailyIncome: 2,
expCurve: { base: 50, factor: 1.2 },
},
{
id: 'scholar',
name: '书生',
type: 'scholar',
unlockConditions: {
type: 'and',
conditions: [
{ type: 'careerLevel', op: '>=', careerId: 'student', value: 250 },
{ type: 'age', op: '>=', value: 14 },
],
},
dailyIncome: -3,
expCurve: { base: 100, factor: 1.25 },
},
{
id: 'soldier',
name: '士兵',
type: 'military',
unlockConditions: {
type: 'and',
conditions: [
{ type: 'age', op: '>=', value: 14 },
{ type: 'stat', op: '>=', stat: 'body', value: 10 },
],
},
dailyIncome: 2,
expCurve: { base: 20, factor: 1.18 },
},
{
id: 'taoist_priest',
name: '道士',
type: 'immortal',
unlockConditions: {
type: 'and',
conditions: [
{ type: 'age', op: '>=', value: 10 },
{ type: 'hasItem', itemId: 'taoist_license' },
],
},
dailyIncome: 1,
expCurve: { base: 30, factor: 1.22 },
},
],
};
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 testCheckUnlocks() {
// Test age-based unlock
let state = { age: 6, stats: {}, careers: {} };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.student !== undefined, 'student should unlock at age 6');
assertEqual(state.careers.student.level, 0, 'student initial level');
assertEqual(state.careers.student.exp, 0, 'student initial exp');
// Test career level unlock
state = { age: 20, stats: {}, careers: { student: { level: 100, exp: 0 } } };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.book_boy !== undefined, 'book_boy should unlock when student reaches 100');
// Test combined unlock (student 250 + age 14)
state = { age: 14, stats: { body: 20 }, careers: { student: { level: 250, exp: 0 } } };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.scholar !== undefined, 'scholar should unlock at student 250 and age 14');
// Test not unlocking when conditions not met
state = { age: 5, stats: {}, careers: {} };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.student === undefined, 'student should not unlock at age 5');
// Test soldier unlock (age 14 + body >= 10)
state = { age: 14, stats: { body: 10 }, careers: {} };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.soldier !== undefined, 'soldier should unlock at age 14 with body 10');
// Test taoist_priest unlock (age 10 + hasItem)
state = { age: 10, stats: {}, shopItems: { taoist_license: 1 }, careers: {} };
checkUnlocks(careersConfig, state);
assertTrue(state.careers.taoist_priest !== undefined, 'taoist_priest should unlock at age 10 with taoist_license');
}
function testCalcDailyIncome() {
// Test basic income
let state = { stats: { business: 0 }, careers: { student: { level: 0, exp: 0 } } };
let income = calcDailyIncome(careersConfig, state);
assertEqual(income, 1, 'basic income with student only');
// Test multiple careers
state = { stats: { business: 0 }, careers: { student: { level: 0, exp: 0 }, soldier: { level: 0, exp: 0 } } };
income = calcDailyIncome(careersConfig, state);
assertEqual(income, 3, 'income with student + soldier');
// Test business bonus
state = { stats: { business: 50 }, careers: { student: { level: 0, exp: 0 } } };
income = calcDailyIncome(careersConfig, state);
assertEqual(income, 1.5, 'income with 50 business bonus');
// Test business cap at 100
state = { stats: { business: 150 }, careers: { student: { level: 0, exp: 0 } } };
income = calcDailyIncome(careersConfig, state);
assertEqual(income, 2, 'income with business capped at 100');
}
function testCalcDailyExp() {
// Test basic exp
let state = { stats: { wisdom: 0 }, metaExp: {} };
let exp = calcDailyExp(careersConfig, state, 'student');
assertEqual(exp, 10, 'basic student exp with 0 wisdom');
// Test stat bonus
state = { stats: { wisdom: 50 }, metaExp: {} };
exp = calcDailyExp(careersConfig, state, 'student');
assertEqual(exp, 15, 'student exp with 50 wisdom bonus');
// Test meta exp bonus
state = { stats: { wisdom: 0 }, metaExp: { student: 100 } };
exp = calcDailyExp(careersConfig, state, 'student');
const expectedMetaBonus = 1 + Math.log(2);
assertEqual(exp, 10 * expectedMetaBonus, 'student exp with meta bonus');
// Test stat cap at 100
state = { stats: { wisdom: 150 }, metaExp: {} };
exp = calcDailyExp(careersConfig, state, 'student');
assertEqual(exp, 20, 'student exp with wisdom capped at 100');
// Test military type uses body stat
state = { stats: { body: 50 }, metaExp: {} };
exp = calcDailyExp(careersConfig, state, 'soldier');
assertEqual(exp, 30, 'soldier exp with 50 body bonus');
}
function testCheckLevelUp() {
// Test single level up
let state = { careers: { student: { level: 0, exp: 12 } } };
checkLevelUp(careersConfig, state);
assertEqual(state.careers.student.level, 1, 'student should level up once');
assertEqual(state.careers.student.exp, 2, 'remaining exp after level up');
// Test multiple level ups
state = { careers: { student: { level: 0, exp: 30 } } };
checkLevelUp(careersConfig, state);
assertEqual(state.careers.student.level, 2, 'student should level up twice');
// need0=10, need1=11.5, total=21.5, remaining=8.5
assertTrue(Math.abs(state.careers.student.exp - 8.5) < 0.0001, 'remaining exp after 2 level ups');
// Test no level up when exp insufficient
state = { careers: { student: { level: 0, exp: 5 } } };
checkLevelUp(careersConfig, state);
assertEqual(state.careers.student.level, 0, 'student should not level up with 5 exp');
assertEqual(state.careers.student.exp, 5, 'exp unchanged');
}
function runTests() {
let passed = 0;
let failed = 0;
const tests = [
{ name: 'testCheckUnlocks', fn: testCheckUnlocks },
{ name: 'testCalcDailyIncome', fn: testCalcDailyIncome },
{ name: 'testCalcDailyExp', fn: testCalcDailyExp },
{ name: 'testCheckLevelUp', fn: testCheckLevelUp },
];
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 careerEngine tests PASS');
} else {
process.exit(1);
}
}
runTests();