Files
rogue/src/engine/__tests__/EntityManager.test.ts
Peter Stockings a2a1d0cc58 Add more tests
2026-01-06 10:01:59 +11:00

133 lines
4.9 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { EntityManager } from '../EntityManager';
import { type World, type Actor } from '../../core/types';
describe('EntityManager', () => {
let mockWorld: World;
let entityManager: EntityManager;
beforeEach(() => {
mockWorld = {
width: 10,
height: 10,
tiles: new Array(100).fill(0),
actors: new Map<number, Actor>(),
exit: { x: 9, y: 9 }
};
entityManager = new EntityManager(mockWorld);
});
it('should add an actor and update the grid', () => {
const actor: Actor = { id: 1, category: 'combatant', type: 'player', pos: { x: 2, y: 3 }, isPlayer: true } as any;
entityManager.addActor(actor);
expect(mockWorld.actors.has(1)).toBe(true);
expect(entityManager.getActorsAt(2, 3).map(a => a.id)).toContain(1);
expect(entityManager.isOccupied(2, 3)).toBe(true);
});
it('should remove an actor and update the grid', () => {
const actor: Actor = { id: 1, category: 'combatant', type: 'player', pos: { x: 2, y: 3 }, isPlayer: true } as any;
entityManager.addActor(actor);
entityManager.removeActor(1);
expect(mockWorld.actors.has(1)).toBe(false);
expect(entityManager.getActorsAt(2, 3).map(a => a.id)).not.toContain(1);
expect(entityManager.isOccupied(2, 3)).toBe(false);
});
it('should update the grid when an actor moves', () => {
const actor: Actor = { id: 1, category: 'combatant', type: 'player', pos: { x: 2, y: 3 }, isPlayer: true } as any;
entityManager.addActor(actor);
entityManager.moveActor(1, { x: 2, y: 3 }, { x: 4, y: 5 });
expect(actor.pos.x).toBe(4);
expect(actor.pos.y).toBe(5);
expect(entityManager.isOccupied(2, 3)).toBe(false);
expect(entityManager.isOccupied(4, 5)).toBe(true);
expect(entityManager.getActorsAt(4, 5).map(a => a.id)).toContain(1);
});
it('should correctly identify occupied tiles while ignoring specific types', () => {
const orb: Actor = { id: 1, category: 'collectible', type: 'exp_orb', pos: { x: 2, y: 2 } } as any;
const enemy: Actor = { id: 2, category: 'combatant', type: 'rat', pos: { x: 5, y: 5 } } as any;
entityManager.addActor(orb);
entityManager.addActor(enemy);
expect(entityManager.isOccupied(2, 2)).toBe(true);
expect(entityManager.isOccupied(2, 2, 'exp_orb')).toBe(false);
expect(entityManager.isOccupied(5, 5)).toBe(true);
expect(entityManager.isOccupied(5, 5, 'exp_orb')).toBe(true);
});
it('should generate the next available ID by scanning current actors', () => {
mockWorld.actors.set(10, { id: 10, pos: { x: 0, y: 0 } } as any);
mockWorld.actors.set(15, { id: 15, pos: { x: 1, y: 1 } } as any);
// Create new manager to trigger scan since current one has stale lastId
const manager = new EntityManager(mockWorld);
expect(manager.getNextId()).toBe(16);
});
it('should handle multiple actors at the same position', () => {
const actor1: Actor = { id: 1, pos: { x: 1, y: 1 } } as any;
const actor2: Actor = { id: 2, pos: { x: 1, y: 1 } } as any;
entityManager.addActor(actor1);
entityManager.addActor(actor2);
const atPos = entityManager.getActorsAt(1, 1);
expect(atPos.length).toBe(2);
expect(atPos.map(a => a.id)).toContain(1);
expect(atPos.map(a => a.id)).toContain(2);
entityManager.removeActor(1);
expect(entityManager.getActorsAt(1, 1).map(a => a.id)).toEqual([2]);
});
it('should handle removing non-existent actor gracefully', () => {
// Should not throw
entityManager.removeActor(999);
});
it('should handle moving non-existent actor gracefully', () => {
// Should not throw
entityManager.moveActor(999, { x: 0, y: 0 }, { x: 1, y: 1 });
});
it('should handle moving an actor that is not in the grid at expected position (inconsistent state)', () => {
const actor: Actor = { id: 1, pos: { x: 0, y: 0 } } as any;
// Add to actors map but NOT to grid (simulating desync)
mockWorld.actors.set(1, actor);
// Attempt move
entityManager.moveActor(1, { x: 0, y: 0 }, { x: 1, y: 1 });
expect(actor.pos.x).toBe(1);
expect(actor.pos.y).toBe(1);
// Should be added to new position in grid
expect(entityManager.getActorsAt(1, 1).map(a => a.id)).toContain(1);
});
it('should handle moving an actor that is in grid but ID not found in list (very rare edge case)', () => {
// Manually pollute grid with empty array for old pos
// This forces `ids` to exist but `indexOf` to return -1
const idx = 0; // 0,0
// @ts-ignore
entityManager.grid.set(idx, [999]); // occupied by someone else
const actor: Actor = { id: 1, pos: { x: 0, y:0 } } as any;
mockWorld.actors.set(1, actor);
entityManager.moveActor(1, { x: 0, y: 0 }, { x: 1, y: 1 });
expect(actor.pos).toEqual({ x: 1, y: 1 });
});
});