Skip to main content
Glama
edge-cases.test.ts7.02 kB
import { describe, it, expect } from 'vitest'; import { SpatialEngine } from '../../src/engine/spatial/engine'; describe('SpatialEngine - Edge Cases', () => { const engine = new SpatialEngine(); describe('Input Validation', () => { it('should reject NaN coordinates in getDistance', () => { expect(() => { engine.getDistance({ x: NaN, y: 0 }, { x: 1, y: 1 }); }).toThrow('Invalid p1: coordinates must be finite numbers'); }); it('should reject Infinity coordinates in getDistance', () => { expect(() => { engine.getDistance({ x: 0, y: 0 }, { x: Infinity, y: 1 }); }).toThrow('Invalid p2: coordinates must be finite numbers'); }); it('should reject negative radius in getCircleTiles', () => { expect(() => { engine.getCircleTiles({ x: 0, y: 0 }, -5); }).toThrow('Invalid radius: must be a non-negative finite number'); }); it('should reject NaN radius in getCircleTiles', () => { expect(() => { engine.getCircleTiles({ x: 0, y: 0 }, NaN); }).toThrow('Invalid radius: must be a non-negative finite number'); }); it('should reject zero-length direction vector in getConeTiles', () => { expect(() => { engine.getConeTiles({ x: 0, y: 0 }, { x: 0, y: 0 }, 10, 90); }).toThrow('Invalid direction vector: length cannot be zero'); }); it('should reject invalid angle in getConeTiles', () => { expect(() => { engine.getConeTiles({ x: 0, y: 0 }, { x: 1, y: 0 }, 10, 0); }).toThrow('Invalid angle: must be between 0 and 360 degrees'); expect(() => { engine.getConeTiles({ x: 0, y: 0 }, { x: 1, y: 0 }, 10, 361); }).toThrow('Invalid angle: must be between 0 and 360 degrees'); }); it('should reject invalid points in hasLineOfSight', () => { expect(() => { engine.hasLineOfSight({ x: NaN, y: 0 }, { x: 1, y: 1 }, new Set()); }).toThrow('Invalid start'); }); it('should reject invalid points in findPath', () => { expect(() => { engine.findPath({ x: 0, y: 0 }, { x: Infinity, y: 1 }, new Set()); }).toThrow('Invalid end'); }); }); describe('Edge Cases - Pathfinding', () => { it('should return single point when start equals end', () => { const start = { x: 5, y: 5 }; const end = { x: 5, y: 5 }; const obstacles = new Set<string>(); const path = engine.findPath(start, end, obstacles); expect(path).toBeDefined(); expect(path).toHaveLength(1); expect(path![0]).toEqual(start); }); it('should respect maxIterations limit', () => { const start = { x: 0, y: 0 }; const end = { x: 100, y: 100 }; const obstacles = new Set<string>(); // Very low iteration limit should fail on large map const path = engine.findPath(start, end, obstacles, { maxIterations: 10 }); // Might return null if iterations exceeded // (depends on path found before limit) expect(path).toBeDefined(); // Small diagonal should work }); it('should handle very large distances', () => { const start = { x: 0, y: 0 }; const end = { x: 1000, y: 1000 }; const obstacles = new Set<string>(); const path = engine.findPath(start, end, obstacles, { maxIterations: 100000 }); expect(path).toBeDefined(); if (path) { expect(path[0]).toEqual(start); expect(path[path.length - 1]).toEqual(end); } }); }); describe('Edge Cases - Circle AoE', () => { it('should return empty array for effectively zero radius', () => { const tiles = engine.getCircleTiles({ x: 0, y: 0 }, 0); expect(tiles).toHaveLength(1); expect(tiles[0]).toEqual({ x: 0, y: 0 }); }); it('should handle fractional radius correctly', () => { const tiles = engine.getCircleTiles({ x: 0, y: 0 }, 0.5); // Only center tile should be included (distance 0 < 0.5) expect(tiles).toHaveLength(1); }); it('should handle large radius', () => { const tiles = engine.getCircleTiles({ x: 0, y: 0 }, 100); // Should be approximately π * r² tiles const expectedCount = Math.PI * 100 * 100; expect(tiles.length).toBeGreaterThan(expectedCount * 0.9); expect(tiles.length).toBeLessThan(expectedCount * 1.1); }); }); describe('Edge Cases - Cone AoE', () => { it('should handle 360-degree cone', () => { const tiles = engine.getConeTiles({ x: 0, y: 0 }, { x: 1, y: 0 }, 2, 360); // 360° cone with radius 2 should be similar to circle const circleTiles = engine.getCircleTiles({ x: 0, y: 0 }, 2); // Should have similar tile counts (within margin) expect(Math.abs(tiles.length - circleTiles.length)).toBeLessThan(3); }); it('should handle very narrow cone (1 degree)', () => { const tiles = engine.getConeTiles({ x: 0, y: 0 }, { x: 1, y: 0 }, 10, 1); // Very narrow cone should have few tiles expect(tiles.length).toBeLessThan(15); }); }); describe('Edge Cases - Line of Sight', () => { it('should handle zero-length LOS (start equals end)', () => { const point = { x: 5, y: 5 }; const obstacles = new Set<string>(); expect(engine.hasLineOfSight(point, point, obstacles)).toBe(true); }); it('should handle very long LOS', () => { const start = { x: 0, y: 0 }; const end = { x: 1000, y: 1000 }; const obstacles = new Set<string>(); expect(engine.hasLineOfSight(start, end, obstacles)).toBe(true); }); }); describe('Determinism', () => { it('should produce identical paths for same inputs', () => { const start = { x: 0, y: 0 }; const end = { x: 10, y: 10 }; const obstacles = new Set(['5,5', '5,6', '6,5']); const path1 = engine.findPath(start, end, obstacles); const path2 = engine.findPath(start, end, obstacles); expect(path1).toEqual(path2); }); it('should produce identical circle tiles for same inputs', () => { const center = { x: 10, y: 10 }; const radius = 5; const tiles1 = engine.getCircleTiles(center, radius); const tiles2 = engine.getCircleTiles(center, radius); expect(tiles1).toEqual(tiles2); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/rpg-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server