Files
AIHostingTycoon/packages/game-engine/src/systems/researchBonuses.test.ts
T
josh a8746246f8
Balance Check / balance-simulation (push) Successful in 7m0s
Balance Check / multi-run-balance (push) Failing after 20m5s
CI / build-and-push (push) Successful in 1m18s
Add Vitest test suite with 184 tests covering all game engine systems
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-26 09:41:56 -04:00

189 lines
7.5 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { getResearchBonuses, resetResearchBonusCache } from './researchBonuses';
import { TECH_TREE } from '../data/techTree';
beforeEach(() => {
resetResearchBonusCache();
});
describe('getResearchBonuses', () => {
it('returns all-zero bonuses for empty completedResearch', () => {
const bonuses = getResearchBonuses([]);
expect(bonuses.energyCostReduction).toBe(0);
expect(bonuses.pipelineSpeedBonus).toBe(0);
expect(bonuses.trainingSpeedBonus).toBe(0);
expect(bonuses.inferenceEfficiencyBonus).toBe(0);
expect(bonuses.tokensPerFlopBonus).toBe(0);
expect(bonuses.dataQualityBonus).toBe(0);
expect(bonuses.sdkCoverageBonus).toBe(0);
expect(bonuses.globalCapabilityBonus).toBe(0);
expect(bonuses.reasoningBonus).toBe(0);
expect(bonuses.codingBonus).toBe(0);
expect(bonuses.creativeBonus).toBe(0);
expect(bonuses.multimodalBonus).toBe(0);
expect(bonuses.agentsBonus).toBe(0);
expect(bonuses.reputationBonus).toBe(0);
expect(bonuses.safetyBonus).toBe(0);
expect(bonuses.autoScalingBonus).toBe(0);
});
it('ignores unknown research IDs without crashing', () => {
const bonuses = getResearchBonuses(['nonexistent-research', 'also-fake']);
expect(bonuses.energyCostReduction).toBe(0);
expect(bonuses.globalCapabilityBonus).toBe(0);
});
it('accumulates energy cost reduction from advanced-cooling', () => {
// advanced-cooling has: { type: 'cost_reduction', target: 'energy', value: 0.25 }
const bonuses = getResearchBonuses(['advanced-cooling']);
expect(bonuses.energyCostReduction).toBe(0.25);
});
it('accumulates inference efficiency from quantization', () => {
// quantization has: { type: 'efficiency_boost', target: 'inference', value: 0.15 }
const bonuses = getResearchBonuses(['quantization']);
expect(bonuses.inferenceEfficiencyBonus).toBe(0.15);
});
it('accumulates global capability bonus from transformer-v2', () => {
// transformer-v2 has: { type: 'capability_boost', target: 'all', value: 10 }
const bonuses = getResearchBonuses(['transformer-v2']);
expect(bonuses.globalCapabilityBonus).toBe(10);
});
it('accumulates safety and reputation from alignment-research', () => {
// alignment-research has:
// { type: 'safety_boost', target: 'models', value: 10 }
// { type: 'capability_boost', target: 'reputation', value: 5 }
const bonuses = getResearchBonuses(['alignment-research']);
expect(bonuses.safetyBonus).toBe(10);
expect(bonuses.reputationBonus).toBe(5);
});
it('accumulates reasoning bonus from reasoning-enhancement', () => {
// reasoning-enhancement has: { type: 'capability_boost', target: 'reasoning', value: 15 }
const bonuses = getResearchBonuses(['reasoning-enhancement']);
expect(bonuses.reasoningBonus).toBe(15);
});
it('accumulates coding bonus from code-generation', () => {
// code-generation has: { type: 'capability_boost', target: 'coding', value: 15 }
const bonuses = getResearchBonuses(['code-generation']);
expect(bonuses.codingBonus).toBe(15);
});
it('accumulates pipeline speed from rapid-deployment', () => {
// rapid-deployment has: { type: 'efficiency_boost', target: 'pipeline_speed', value: 0.2 }
const bonuses = getResearchBonuses(['rapid-deployment']);
expect(bonuses.pipelineSpeedBonus).toBe(0.2);
});
it('accumulates data quality from data-pipeline', () => {
// data-pipeline has: { type: 'efficiency_boost', target: 'data_quality', value: 0.2 }
const bonuses = getResearchBonuses(['data-pipeline']);
expect(bonuses.dataQualityBonus).toBe(0.2);
});
it('accumulates auto-scaling from auto-scaling node', () => {
// auto-scaling has: { type: 'efficiency_boost', target: 'auto_scaling', value: 0.2 }
const bonuses = getResearchBonuses(['auto-scaling']);
expect(bonuses.autoScalingBonus).toBe(0.2);
});
it('additively accumulates bonuses from multiple research nodes', () => {
// distillation: { type: 'capability_boost', target: 'all', value: 5 }
// transformer-v2: { type: 'capability_boost', target: 'all', value: 10 }
const bonuses = getResearchBonuses(['transformer-v2', 'distillation']);
expect(bonuses.globalCapabilityBonus).toBe(15);
});
it('accumulates safety additively across multiple safety nodes', () => {
// alignment-research: safety 10, reputation 5
// interpretability: safety 10, reputation 5
// constitutional-ai: safety 15, reputation 10
const bonuses = getResearchBonuses([
'alignment-research',
'interpretability',
'constitutional-ai',
]);
expect(bonuses.safetyBonus).toBe(35);
expect(bonuses.reputationBonus).toBe(20);
});
it('accumulates multiple different bonus types from a diverse set', () => {
const bonuses = getResearchBonuses([
'advanced-cooling', // energyCostReduction: 0.25
'quantization', // inferenceEfficiencyBonus: 0.15
'transformer-v2', // globalCapabilityBonus: 10
'reasoning-enhancement', // reasoningBonus: 15
'alignment-research', // safetyBonus: 10, reputationBonus: 5
]);
expect(bonuses.energyCostReduction).toBe(0.25);
expect(bonuses.inferenceEfficiencyBonus).toBe(0.15);
expect(bonuses.globalCapabilityBonus).toBe(10);
expect(bonuses.reasoningBonus).toBe(15);
expect(bonuses.safetyBonus).toBe(10);
expect(bonuses.reputationBonus).toBe(5);
// Other fields remain zero
expect(bonuses.codingBonus).toBe(0);
expect(bonuses.creativeBonus).toBe(0);
});
});
describe('cache behavior', () => {
it('returns cached result when called with same-length array', () => {
const first = getResearchBonuses(['advanced-cooling']);
// Call with a different ID but same length -- cache triggers on length match
const second = getResearchBonuses(['quantization']);
// Because the cache keys on array length, second call returns the cached first result
expect(second).toBe(first);
expect(second.energyCostReduction).toBe(0.25);
});
it('recomputes when array length changes', () => {
const first = getResearchBonuses(['advanced-cooling']);
expect(first.energyCostReduction).toBe(0.25);
const second = getResearchBonuses(['advanced-cooling', 'quantization']);
expect(second.energyCostReduction).toBe(0.25);
expect(second.inferenceEfficiencyBonus).toBe(0.15);
// Different reference since length changed
expect(second).not.toBe(first);
});
it('resetResearchBonusCache forces recompute on next call', () => {
const first = getResearchBonuses(['advanced-cooling']);
expect(first.energyCostReduction).toBe(0.25);
resetResearchBonusCache();
// Same length, but cache was cleared so it recomputes with new input
const second = getResearchBonuses(['quantization']);
expect(second.energyCostReduction).toBe(0);
expect(second.inferenceEfficiencyBonus).toBe(0.15);
});
});
describe('TECH_TREE consistency', () => {
it('all tested node IDs exist in TECH_TREE', () => {
const testedIds = [
'advanced-cooling',
'quantization',
'transformer-v2',
'reasoning-enhancement',
'code-generation',
'alignment-research',
'interpretability',
'constitutional-ai',
'distillation',
'rapid-deployment',
'data-pipeline',
'auto-scaling',
];
const treeIds = new Set(TECH_TREE.map(n => n.id));
for (const id of testedIds) {
expect(treeIds.has(id), `Expected TECH_TREE to contain '${id}'`).toBe(true);
}
});
});