Files
rebirthlife/test/shopEngine.test.js

287 lines
11 KiB
JavaScript

import {
setShopConfig,
getShopConfig,
findEntry,
canBuyItem,
buyItem,
getArtifactUpgradeCost,
resetShopItems,
applyArtifactEffects,
} from '../src/engine/shopEngine.js';
// Load shop config
import shopConfig from '../src/config/shop.json' assert { type: 'json' };
function createState(overrides = {}) {
return {
money: 0,
age: 0,
stats: {},
careers: {},
worldFlags: {},
shopItems: {},
activeBuffs: {},
artifacts: {},
...overrides,
};
}
function assertEqual(actual, expected, message) {
if (actual !== expected) {
console.error(`FAIL: ${message}`);
console.error(` Expected: ${expected}`);
console.error(` Actual: ${actual}`);
throw new Error(`Assertion failed: ${message}`);
}
}
function assertTrue(value, message) {
if (!value) {
console.error(`FAIL: ${message}`);
throw new Error(`Assertion failed: ${message}`);
}
}
function assertDeepEqual(actual, expected, message) {
const actualStr = JSON.stringify(actual);
const expectedStr = JSON.stringify(expected);
if (actualStr !== expectedStr) {
console.error(`FAIL: ${message}`);
console.error(` Expected: ${expectedStr}`);
console.error(` Actual: ${actualStr}`);
throw new Error(`Assertion failed: ${message}`);
}
}
function testCanBuyItem() {
console.log('Running testCanBuyItem...');
// Test 1: Basic item - enough money, no unlock conditions
let state = createState({ money: 100 });
assertTrue(canBuyItem(shopConfig, state, 'power_pill'), 'Should be able to buy power_pill with enough money');
// Test 2: Basic item - not enough money
state = createState({ money: 10 });
assertEqual(canBuyItem(shopConfig, state, 'power_pill'), false, 'Should not afford power_pill with insufficient money');
// Test 3: Item with unlock conditions - age not met
state = createState({ money: 1000, age: 5, identity: 'taoist_orphan' });
assertEqual(canBuyItem(shopConfig, state, 'taoist_license'), false, 'Should not buy taoist_license if age < 10');
// Test 4: Item with unlock conditions - age met, identity matches
state = createState({ money: 1000, age: 15, identity: 'taoist_orphan' });
assertTrue(canBuyItem(shopConfig, state, 'taoist_license'), 'Should buy taoist_license with age >= 10 and correct identity');
// Test 5: Item with unlock conditions - age met, worldFlag matches
state = createState({ money: 1000, age: 15, worldFlags: { met_taoist_priest: true } });
assertTrue(canBuyItem(shopConfig, state, 'taoist_license'), 'Should buy taoist_license with age >= 10 and worldFlag');
// Test 6: Item with unlock conditions - age met, neither condition matches
state = createState({ money: 1000, age: 15, identity: 'commoner' });
assertEqual(canBuyItem(shopConfig, state, 'taoist_license'), false, 'Should not buy taoist_license without matching identity or flag');
// Test 7: Buff - enough money
state = createState({ money: 300 });
assertTrue(canBuyItem(shopConfig, state, 'clever_brush'), 'Should be able to buy clever_brush with enough money');
// Test 8: Artifact - unlock condition not met
state = createState({ money: 2000 });
assertEqual(canBuyItem(shopConfig, state, 'immortal_sword'), false, 'Should not buy immortal_sword without career level');
// Test 9: Artifact - unlock condition met
state = createState({ money: 2000, careers: { taoist_priest: { level: 50, exp: 0 } } });
assertTrue(canBuyItem(shopConfig, state, 'immortal_sword'), 'Should buy immortal_sword with sufficient career level');
// Test 10: Artifact - max level reached
state = createState({ money: 100000, careers: { taoist_priest: { level: 50, exp: 0 } }, artifacts: { immortal_sword: 10 } });
assertEqual(canBuyItem(shopConfig, state, 'immortal_sword'), false, 'Should not buy artifact at max level');
console.log(' testCanBuyItem PASSED');
}
function testBuyItem() {
console.log('Running testBuyItem...');
// Test 1: Buy basic item
let state = createState({ money: 100 });
const result1 = buyItem(shopConfig, state, 'power_pill');
assertTrue(result1, 'buyItem should return true for successful purchase');
assertEqual(state.money, 50, 'Should deduct cost from money');
assertEqual(state.shopItems.power_pill, 1, 'Should record item purchase');
assertEqual(state.stats.body, 5, 'Should apply effect (addStat body +5)');
// Test 2: Buy item that fails canBuyItem
state = createState({ money: 10 });
const result2 = buyItem(shopConfig, state, 'power_pill');
assertEqual(result2, false, 'buyItem should return false when cannot afford');
assertEqual(state.money, 10, 'Should not deduct money when purchase fails');
// Test 3: Buy buff
state = createState({ money: 300 });
const result3 = buyItem(shopConfig, state, 'clever_brush');
assertTrue(result3, 'buyItem should return true for buff purchase');
assertEqual(state.money, 100, 'Should deduct buff cost');
assertDeepEqual(state.activeBuffs.clever_brush, { expiresDay: -1 }, 'Should record buff with expiresDay: -1');
assertEqual(state.stats.wisdom, 10, 'Should apply buff effect (addStat wisdom +10)');
// Test 4: Buy artifact
state = createState({ money: 2000, careers: { taoist_priest: { level: 50, exp: 0 } } });
const result4 = buyItem(shopConfig, state, 'immortal_sword');
assertTrue(result4, 'buyItem should return true for artifact purchase');
assertEqual(state.money, 1000, 'Should deduct artifact cost (baseCost=1000)');
assertEqual(state.artifacts.immortal_sword, 1, 'Should record artifact level');
assertEqual(state.stats.body, 10, 'Should apply artifact effect (addStat body +10)');
// Test 5: Buy item with multiple effects
state = createState({ money: 1000, age: 15, identity: 'taoist_orphan' });
const result5 = buyItem(shopConfig, state, 'taoist_license');
assertTrue(result5, 'buyItem should return true for taoist_license');
assertEqual(state.money, 500, 'Should deduct taoist_license cost');
assertEqual(state.shopItems.taoist_license, 1, 'Should record taoist_license purchase');
assertTrue(state.careers.taoist_priest !== undefined, 'Should unlock career');
assertEqual(state.worldFlags.has_taoist_license, true, 'Should set world flag');
console.log(' testBuyItem PASSED');
}
function testGetArtifactUpgradeCost() {
console.log('Running testGetArtifactUpgradeCost...');
// Level 0: baseCost * 1.5^0 = 1000 * 1 = 1000
const cost0 = getArtifactUpgradeCost(shopConfig, 'immortal_sword', 0);
assertEqual(cost0, 1000, 'Level 0 cost should be baseCost');
// Level 1: baseCost * 1.5^1 = 1000 * 1.5 = 1500
const cost1 = getArtifactUpgradeCost(shopConfig, 'immortal_sword', 1);
assertEqual(cost1, 1500, 'Level 1 cost should be baseCost * 1.5');
// Level 2: baseCost * 1.5^2 = 1000 * 2.25 = 2250
const cost2 = getArtifactUpgradeCost(shopConfig, 'immortal_sword', 2);
assertEqual(cost2, 2250, 'Level 2 cost should be baseCost * 1.5^2');
// Level 3: baseCost * 1.5^3 = 1000 * 3.375 = 3375
const cost3 = getArtifactUpgradeCost(shopConfig, 'immortal_sword', 3);
assertEqual(cost3, 3375, 'Level 3 cost should be baseCost * 1.5^3');
// Verify Math.floor is applied
const cost5 = getArtifactUpgradeCost(shopConfig, 'immortal_sword', 5);
const expected5 = Math.floor(1000 * Math.pow(1.5, 5));
assertEqual(cost5, expected5, 'Level 5 cost should use Math.floor');
console.log(' testGetArtifactUpgradeCost PASSED');
}
function testResetShopItems() {
console.log('Running testResetShopItems...');
let state = createState({
shopItems: { power_pill: 3, taoist_license: 1 },
activeBuffs: { clever_brush: { expiresDay: -1 } },
artifacts: { immortal_sword: 5 },
});
resetShopItems(state);
assertDeepEqual(state.shopItems, {}, 'shopItems should be reset to empty object');
assertDeepEqual(state.activeBuffs, {}, 'activeBuffs should be reset to empty object');
assertEqual(state.artifacts.immortal_sword, 5, 'artifacts should be preserved');
console.log(' testResetShopItems PASSED');
}
function testApplyArtifactEffects() {
console.log('Running testApplyArtifactEffects...');
// Test 1: Single artifact, level 1
let state = createState({ artifacts: { immortal_sword: 1 } });
applyArtifactEffects(shopConfig, state);
assertEqual(state.stats.body, 10, 'Level 1 artifact should add 10 body');
// Test 2: Single artifact, level 3
state = createState({ artifacts: { immortal_sword: 3 } });
applyArtifactEffects(shopConfig, state);
assertEqual(state.stats.body, 30, 'Level 3 artifact should add 30 body (10 * 3)');
// Test 3: Multiple artifacts (simulate by adding another artifact config)
const customConfig = {
artifacts: [
{
id: 'immortal_sword',
effects: [{ type: 'addStat', stat: 'body', value: 10 }],
},
{
id: 'wisdom_scroll',
effects: [{ type: 'addStat', stat: 'wisdom', value: 5 }],
},
],
};
state = createState({ artifacts: { immortal_sword: 2, wisdom_scroll: 4 } });
applyArtifactEffects(customConfig, state);
assertEqual(state.stats.body, 20, 'Multiple artifacts: immortal_sword level 2 should add 20 body');
assertEqual(state.stats.wisdom, 20, 'Multiple artifacts: wisdom_scroll level 4 should add 20 wisdom (5 * 4)');
// Test 4: Artifact level 0 should not apply
state = createState({ artifacts: { immortal_sword: 0 } });
applyArtifactEffects(shopConfig, state);
assertEqual(state.stats.body, undefined, 'Level 0 artifact should not apply effects');
console.log(' testApplyArtifactEffects PASSED');
}
function testSetAndGetShopConfig() {
console.log('Running testSetAndGetShopConfig...');
const customConfig = { items: [], buffs: [], artifacts: [] };
setShopConfig(customConfig);
assertEqual(getShopConfig(), customConfig, 'getShopConfig should return set config');
// Reset to shopConfig for other tests
setShopConfig(shopConfig);
console.log(' testSetAndGetShopConfig PASSED');
}
function testFindEntry() {
console.log('Running testFindEntry...');
const item = findEntry(shopConfig, 'power_pill');
assertTrue(item !== null, 'findEntry should find existing item');
assertEqual(item.id, 'power_pill', 'findEntry should return correct item id');
assertEqual(item.section, 'items', 'findEntry should return correct section for item');
const buff = findEntry(shopConfig, 'clever_brush');
assertTrue(buff !== null, 'findEntry should find existing buff');
assertEqual(buff.section, 'buffs', 'findEntry should return correct section for buff');
const artifact = findEntry(shopConfig, 'immortal_sword');
assertTrue(artifact !== null, 'findEntry should find existing artifact');
assertEqual(artifact.section, 'artifacts', 'findEntry should return correct section for artifact');
const notFound = findEntry(shopConfig, 'nonexistent');
assertEqual(notFound, null, 'findEntry should return null for nonexistent id');
console.log(' testFindEntry PASSED');
}
function runAllTests() {
console.log('=== shopEngine Tests ===\n');
try {
testSetAndGetShopConfig();
testFindEntry();
testCanBuyItem();
testBuyItem();
testGetArtifactUpgradeCost();
testResetShopItems();
testApplyArtifactEffects();
console.log('\nAll shopEngine tests PASS');
process.exit(0);
} catch (error) {
console.error(`\nTests FAILED: ${error.message}`);
process.exit(1);
}
}
runAllTests();