- Implemented 10 sub-systems (Economy, Pathfinding, Combat, Selection, Network, Map, Entity/Building/ControlPoint state machines, Orchestrator) - Refactored Custom_Entity.js → Unit.js with 5 components (health, owner, inventory, movement, combat) - Added Jest test suite with 100+ tests (EconomySystem 100%, EntityStateMachine 100%, PathfindingSystem 99%, Unit.js 72%) - All webpack builds pass (0 errors) - BMAD-auto team-respawn flow: 10 parallel sub-agents implemented systems Architecture: Phaser 3 + XState + socket.io + EasyStar Mode: team-respawn Model: custom/ollama-cloud-pro
152 lines
4.6 KiB
JavaScript
152 lines
4.6 KiB
JavaScript
/**
|
|
* EconomySystem Unit Tests
|
|
*/
|
|
import EconomySystem from '../src/systems/EconomySystem';
|
|
|
|
// Mock Phaser Scene
|
|
const createMockScene = () => ({
|
|
events: {
|
|
emit: jest.fn(),
|
|
on: jest.fn()
|
|
}
|
|
});
|
|
|
|
describe('EconomySystem', () => {
|
|
let scene;
|
|
let economy;
|
|
|
|
beforeEach(() => {
|
|
scene = createMockScene();
|
|
economy = new EconomySystem(scene);
|
|
});
|
|
|
|
describe('initPlayer', () => {
|
|
it('should initialize player with default resources', () => {
|
|
economy.initPlayer('player1');
|
|
const resources = economy.getResources('player1');
|
|
|
|
expect(resources.fuel).toBe(100);
|
|
expect(resources.ammo).toBe(100);
|
|
expect(resources.capturePoints).toBe(0);
|
|
});
|
|
|
|
it('should initialize player with custom resources', () => {
|
|
economy.initPlayer('player1', { fuel: 200, ammo: 50, capturePoints: 10 });
|
|
const resources = economy.getResources('player1');
|
|
|
|
expect(resources.fuel).toBe(200);
|
|
expect(resources.ammo).toBe(50);
|
|
expect(resources.capturePoints).toBe(10);
|
|
});
|
|
});
|
|
|
|
describe('canAfford', () => {
|
|
beforeEach(() => {
|
|
economy.initPlayer('player1', { fuel: 100, ammo: 50 });
|
|
});
|
|
|
|
it('should return true when player has enough resources', () => {
|
|
expect(economy.canAfford('player1', { fuel: 50, ammo: 25 })).toBe(true);
|
|
});
|
|
|
|
it('should return false when player lacks fuel', () => {
|
|
expect(economy.canAfford('player1', { fuel: 150, ammo: 25 })).toBe(false);
|
|
});
|
|
|
|
it('should return false when player lacks ammo', () => {
|
|
expect(economy.canAfford('player1', { fuel: 50, ammo: 100 })).toBe(false);
|
|
});
|
|
|
|
it('should return false for non-existent player', () => {
|
|
expect(economy.canAfford('player2', { fuel: 10 })).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('deduct', () => {
|
|
beforeEach(() => {
|
|
economy.initPlayer('player1', { fuel: 100, ammo: 50 });
|
|
});
|
|
|
|
it('should deduct resources and return true', () => {
|
|
const result = economy.deduct('player1', { fuel: 30, ammo: 20 });
|
|
const resources = economy.getResources('player1');
|
|
|
|
expect(result).toBe(true);
|
|
expect(resources.fuel).toBe(70);
|
|
expect(resources.ammo).toBe(30);
|
|
});
|
|
|
|
it('should not deduct and return false when insufficient resources', () => {
|
|
const result = economy.deduct('player1', { fuel: 150, ammo: 20 });
|
|
const resources = economy.getResources('player1');
|
|
|
|
expect(result).toBe(false);
|
|
expect(resources.fuel).toBe(100); // Unchanged
|
|
expect(resources.ammo).toBe(50); // Unchanged
|
|
});
|
|
|
|
it('should emit economy:purchaseFailed on insufficient resources', () => {
|
|
economy.deduct('player1', { fuel: 150, ammo: 20 });
|
|
|
|
expect(scene.events.emit).toHaveBeenCalledWith(
|
|
'economy:purchaseFailed',
|
|
expect.objectContaining({ playerId: 'player1', reason: expect.any(String) })
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('addIncome', () => {
|
|
it('should add income to player resources', () => {
|
|
economy.initPlayer('player1', { fuel: 100, ammo: 50 });
|
|
economy.addIncome('player1', { fuel: 25, ammo: 10, capturePoints: 5 });
|
|
|
|
const resources = economy.getResources('player1');
|
|
expect(resources.fuel).toBe(125);
|
|
expect(resources.ammo).toBe(60);
|
|
expect(resources.capturePoints).toBe(5);
|
|
});
|
|
|
|
it('should auto-initialize player if not exists', () => {
|
|
economy.addIncome('player2', { fuel: 50 });
|
|
const resources = economy.getResources('player2');
|
|
|
|
expect(resources.fuel).toBe(50);
|
|
});
|
|
|
|
it('should emit economy:incomeReceived and economy:updated', () => {
|
|
economy.initPlayer('player1');
|
|
economy.addIncome('player1', { fuel: 10 });
|
|
|
|
expect(scene.events.emit).toHaveBeenCalledWith(
|
|
'economy:incomeReceived',
|
|
expect.objectContaining({ playerId: 'player1' })
|
|
);
|
|
expect(scene.events.emit).toHaveBeenCalledWith(
|
|
'economy:updated',
|
|
expect.objectContaining({ playerId: 'player1' })
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it('should call addIncome every 1000ms', () => {
|
|
const addIncomeSpy = jest.spyOn(economy, 'addIncome');
|
|
|
|
// First call at 1000ms
|
|
economy.update(1000, 1000);
|
|
expect(addIncomeSpy).toHaveBeenCalled();
|
|
|
|
// Second call at 2000ms
|
|
economy.update(2000, 1000);
|
|
expect(addIncomeSpy).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('should not call addIncome before 1000ms', () => {
|
|
const addIncomeSpy = jest.spyOn(economy, 'addIncome');
|
|
economy.update(500, 500);
|
|
|
|
expect(addIncomeSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|