From c548d0ac051ec0bf1887d59c00b89441ec49b7e3 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Mar 2026 00:04:30 +0000 Subject: [PATCH] test: add desire lifecycle integration test Co-Authored-By: Claude Opus 4.6 --- .../systems/__tests__/desireLifecycle.test.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 server/src/systems/__tests__/desireLifecycle.test.ts diff --git a/server/src/systems/__tests__/desireLifecycle.test.ts b/server/src/systems/__tests__/desireLifecycle.test.ts new file mode 100644 index 0000000..2122d54 --- /dev/null +++ b/server/src/systems/__tests__/desireLifecycle.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from 'vitest'; +import { World } from '../../ecs/World.js'; +import { desireFulfillmentSystem } from '../desireFulfillmentSystem.js'; +import { industrySystem } from '../industrySystem.js'; +import { GameMap } from '../../map/GameMap.js'; +import { ItemRegistry } from '../../industry/itemRegistry.js'; +import { RecipeRegistry } from '../../industry/recipeRegistry.js'; +import type { Desire, Stats, NPCBrain, Needs } from '@dflike/shared'; + +function setupIntegrationWorld() { + const world = new World(); + const map = new GameMap(20, 20); + const itemRegistry = ItemRegistry.createDefault(); + const recipeRegistry = RecipeRegistry.createDefault(); + world.setSingleton('itemRegistry', itemRegistry); + world.setSingleton('recipeRegistry', recipeRegistry); + return { world, map }; +} + +describe('desire lifecycle integration', () => { + it('desire drives crafting priority then gets fulfilled', () => { + const { world, map } = setupIntegrationWorld(); + + // Create NPC with materials for both axe and hammer, but desires hammer + const entity = world.createEntity(); + world.addComponent(entity, 'position', { x: 5, y: 5 }); + world.addComponent(entity, 'name', 'TestNPC'); + const inv = new Map([['log', 4], ['stone', 2]]); + world.addComponent(entity, 'inventory', inv); + world.addComponent(entity, 'stats', { + strength: 10, dexterity: 10, constitution: 10, intelligence: 10, perception: 10, + sociability: 10, courage: 10, curiosity: 10, empathy: 10, temperament: 10, + }); + world.addComponent(entity, 'statModifiers', { modifiers: [] }); + world.addComponent(entity, 'needs', { hunger: 80, energy: 80, productivity: 20 }); + world.addComponent(entity, 'npcBrain', { currentGoal: 'wander', goalQueue: [] }); + world.addComponent(entity, 'movement', { state: 'idle', target: null, path: [], direction: 0, moveProgress: 0 }); + + const hammerDesire: Desire = { + id: 'test_hammer', description: 'wants a hammer', category: 'material', + fulfillment: { type: 'own_item', itemId: 'hammer', quantity: 1 }, + priority: 0.9, source: 'spawn', createdAtTick: 0, + }; + world.addComponent(entity, 'desires', [hammerDesire]); + + // Industry should pick hammer recipe due to desire + industrySystem(world, map); + const craftingState = world.getComponent(entity, 'craftingState'); + expect(craftingState).toBeTruthy(); + expect(craftingState.recipeId).toBe('craft_hammer'); + + // Simulate crafting completion: add hammer to inventory + inv.set('hammer', 1); + + // Fulfillment check should remove the desire + desireFulfillmentSystem(world, 100); + const desires = world.getComponent(entity, 'desires')!; + expect(desires).toHaveLength(0); + }); +});