287 lines
11 KiB
JavaScript
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();
|