- Core engine: simulator, game mechanics, triggers (138 tests) - Fog-of-war per-player state tracking - Meeting flow: interrupt, discussion, voting, consolidation - Prompt assembler with strategy injection tiers - LLM client with fallbacks for models without JSON/system support - Prompt templates: action, discussion, voting, reflection - Full integration in main.py orchestrator - Verified working with free OpenRouter models (Gemma)
220 lines
7.7 KiB
Python
220 lines
7.7 KiB
Python
"""
|
|
Tests for the map/graph system.
|
|
"""
|
|
|
|
import unittest
|
|
import sys
|
|
import os
|
|
import tempfile
|
|
import json
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from src.map.graph import GameMap, Room, Edge, Task, Vent
|
|
|
|
|
|
class TestRoom(unittest.TestCase):
|
|
"""Tests for Room dataclass."""
|
|
|
|
def test_room_creation(self):
|
|
room = Room(id="test", name="Test Room")
|
|
self.assertEqual(room.id, "test")
|
|
self.assertEqual(room.name, "Test Room")
|
|
self.assertEqual(room.tasks, [])
|
|
self.assertIsNone(room.vent)
|
|
|
|
def test_room_with_tasks(self):
|
|
task = Task(id="task1", name="Do Thing", duration=5.0)
|
|
room = Room(id="test", name="Test Room", tasks=[task])
|
|
self.assertEqual(len(room.tasks), 1)
|
|
self.assertEqual(room.tasks[0].duration, 5.0)
|
|
|
|
def test_room_with_vent(self):
|
|
vent = Vent(id="vent1", connects_to=["vent2", "vent3"])
|
|
room = Room(id="test", name="Test Room", vent=vent)
|
|
self.assertIsNotNone(room.vent)
|
|
self.assertEqual(len(room.vent.connects_to), 2)
|
|
|
|
|
|
class TestEdge(unittest.TestCase):
|
|
"""Tests for Edge dataclass."""
|
|
|
|
def test_edge_creation(self):
|
|
edge = Edge(id="e1", room_a="a", room_b="b", distance=5.0)
|
|
self.assertEqual(edge.id, "e1")
|
|
self.assertEqual(edge.distance, 5.0)
|
|
|
|
def test_edge_other_room(self):
|
|
edge = Edge(id="e1", room_a="a", room_b="b", distance=5.0)
|
|
self.assertEqual(edge.other_room("a"), "b")
|
|
self.assertEqual(edge.other_room("b"), "a")
|
|
|
|
|
|
class TestGameMap(unittest.TestCase):
|
|
"""Tests for GameMap class."""
|
|
|
|
def setUp(self):
|
|
"""Create a simple test map."""
|
|
self.game_map = GameMap()
|
|
|
|
# Create rooms: A -- B -- C
|
|
# |
|
|
# D
|
|
self.game_map.add_room(Room(id="a", name="Room A"))
|
|
self.game_map.add_room(Room(id="b", name="Room B"))
|
|
self.game_map.add_room(Room(id="c", name="Room C"))
|
|
self.game_map.add_room(Room(id="d", name="Room D"))
|
|
|
|
self.game_map.add_edge(Edge(id="ab", room_a="a", room_b="b", distance=3.0))
|
|
self.game_map.add_edge(Edge(id="bc", room_a="b", room_b="c", distance=4.0))
|
|
self.game_map.add_edge(Edge(id="bd", room_a="b", room_b="d", distance=2.0))
|
|
|
|
def test_add_room(self):
|
|
self.assertEqual(len(self.game_map.rooms), 4)
|
|
self.assertIn("a", self.game_map.rooms)
|
|
|
|
def test_add_edge(self):
|
|
self.assertEqual(len(self.game_map.edges), 3)
|
|
self.assertIn("ab", self.game_map.edges)
|
|
|
|
def test_get_room(self):
|
|
room = self.game_map.get_room("a")
|
|
self.assertIsNotNone(room)
|
|
self.assertEqual(room.name, "Room A")
|
|
|
|
self.assertIsNone(self.game_map.get_room("nonexistent"))
|
|
|
|
def test_get_edge(self):
|
|
edge = self.game_map.get_edge("ab")
|
|
self.assertIsNotNone(edge)
|
|
self.assertEqual(edge.distance, 3.0)
|
|
|
|
def test_get_neighbors(self):
|
|
neighbors = self.game_map.get_neighbors("b")
|
|
self.assertEqual(len(neighbors), 3) # a, c, d
|
|
neighbor_rooms = [n[1] for n in neighbors]
|
|
self.assertIn("a", neighbor_rooms)
|
|
self.assertIn("c", neighbor_rooms)
|
|
self.assertIn("d", neighbor_rooms)
|
|
|
|
def test_find_edge(self):
|
|
edge = self.game_map.find_edge("a", "b")
|
|
self.assertIsNotNone(edge)
|
|
self.assertEqual(edge.id, "ab")
|
|
|
|
# Reverse direction
|
|
edge = self.game_map.find_edge("b", "a")
|
|
self.assertIsNotNone(edge)
|
|
|
|
# Non-adjacent
|
|
self.assertIsNone(self.game_map.find_edge("a", "c"))
|
|
|
|
def test_find_path_adjacent(self):
|
|
path = self.game_map.find_path("a", "b")
|
|
self.assertEqual(path, ["ab"])
|
|
|
|
def test_find_path_multi_hop(self):
|
|
path = self.game_map.find_path("a", "c")
|
|
self.assertEqual(path, ["ab", "bc"])
|
|
|
|
def test_find_path_same_room(self):
|
|
path = self.game_map.find_path("a", "a")
|
|
self.assertEqual(path, [])
|
|
|
|
def test_find_path_no_path(self):
|
|
# Add isolated room
|
|
self.game_map.add_room(Room(id="isolated", name="Isolated"))
|
|
path = self.game_map.find_path("a", "isolated")
|
|
self.assertIsNone(path)
|
|
|
|
def test_path_distance(self):
|
|
path = self.game_map.find_path("a", "c")
|
|
distance = self.game_map.path_distance(path)
|
|
self.assertEqual(distance, 7.0) # 3 + 4
|
|
|
|
def test_shortest_path(self):
|
|
# Add direct edge from a to d (should be longer)
|
|
self.game_map.add_edge(Edge(id="ad", room_a="a", room_b="d", distance=10.0))
|
|
|
|
# Shortest path should still go through b
|
|
path = self.game_map.find_path("a", "d")
|
|
distance = self.game_map.path_distance(path)
|
|
self.assertEqual(distance, 5.0) # 3 + 2 via b
|
|
|
|
|
|
class TestMapSerialization(unittest.TestCase):
|
|
"""Tests for map serialization."""
|
|
|
|
def test_to_dict(self):
|
|
game_map = GameMap()
|
|
game_map.add_room(Room(id="a", name="A"))
|
|
game_map.add_room(Room(id="b", name="B"))
|
|
game_map.add_edge(Edge(id="ab", room_a="a", room_b="b", distance=5.0))
|
|
|
|
data = game_map.to_dict()
|
|
self.assertEqual(len(data["rooms"]), 2)
|
|
self.assertEqual(len(data["edges"]), 1)
|
|
|
|
def test_save_and_load(self):
|
|
game_map = GameMap()
|
|
task = Task(id="t1", name="Task", duration=3.0)
|
|
vent = Vent(id="v1", connects_to=["v2"])
|
|
game_map.add_room(Room(id="a", name="A", tasks=[task], vent=vent))
|
|
game_map.add_room(Room(id="b", name="B"))
|
|
game_map.add_edge(Edge(id="ab", room_a="a", room_b="b", distance=5.0))
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
|
|
game_map.save(f.name)
|
|
|
|
loaded = GameMap.load(f.name)
|
|
self.assertEqual(len(loaded.rooms), 2)
|
|
self.assertEqual(len(loaded.edges), 1)
|
|
self.assertEqual(loaded.rooms["a"].tasks[0].duration, 3.0)
|
|
self.assertIsNotNone(loaded.rooms["a"].vent)
|
|
|
|
os.unlink(f.name)
|
|
|
|
|
|
class TestSkeldMap(unittest.TestCase):
|
|
"""Tests for the actual Skeld map."""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
map_path = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"data", "maps", "skeld.json"
|
|
)
|
|
cls.skeld = GameMap.load(map_path)
|
|
|
|
def test_skeld_has_all_rooms(self):
|
|
expected_rooms = [
|
|
"cafeteria", "weapons", "navigation", "o2", "admin",
|
|
"storage", "communications", "shields", "electrical",
|
|
"lower_engine", "security", "reactor", "upper_engine", "medbay"
|
|
]
|
|
for room_id in expected_rooms:
|
|
self.assertIn(room_id, self.skeld.rooms, f"Missing room: {room_id}")
|
|
|
|
def test_skeld_connectivity(self):
|
|
# Every room should be reachable from cafeteria
|
|
for room_id in self.skeld.rooms:
|
|
path = self.skeld.find_path("cafeteria", room_id)
|
|
self.assertIsNotNone(path, f"No path to {room_id}")
|
|
|
|
def test_skeld_has_vents(self):
|
|
vent_rooms = ["weapons", "navigation", "admin", "electrical",
|
|
"lower_engine", "security", "reactor", "upper_engine", "medbay", "shields"]
|
|
for room_id in vent_rooms:
|
|
room = self.skeld.get_room(room_id)
|
|
self.assertIsNotNone(room.vent, f"{room_id} should have a vent")
|
|
|
|
def test_vent_connectivity(self):
|
|
# Check medbay-security-electrical vent network
|
|
medbay = self.skeld.get_room("medbay")
|
|
self.assertIn("vent_security", medbay.vent.connects_to)
|
|
self.assertIn("vent_elec", medbay.vent.connects_to)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|