"""Tests for mob templates, registry, and spawn/despawn.""" import tomllib from pathlib import Path from unittest.mock import MagicMock import pytest from mudlib.entity import Mob from mudlib.mobs import ( MobTemplate, despawn_mob, get_nearby_mob, load_mob_template, load_mob_templates, mobs, spawn_mob, ) @pytest.fixture(autouse=True) def clear_mobs(): """Clear mobs list before and after each test.""" mobs.clear() yield mobs.clear() @pytest.fixture def goblin_toml(tmp_path): """Create a goblin TOML file.""" path = tmp_path / "goblin.toml" path.write_text( 'name = "goblin"\n' 'description = "a snarling goblin with a crude club"\n' "pl = 50.0\n" "stamina = 40.0\n" "max_stamina = 40.0\n" 'moves = ["punch left", "punch right", "sweep"]\n' ) return path @pytest.fixture def dummy_toml(tmp_path): """Create a training dummy TOML file.""" path = tmp_path / "training_dummy.toml" path.write_text( 'name = "training dummy"\n' 'description = "a battered wooden training dummy"\n' "pl = 200.0\n" "stamina = 100.0\n" "max_stamina = 100.0\n" "moves = []\n" ) return path class TestLoadTemplate: def test_load_single_template(self, goblin_toml): template = load_mob_template(goblin_toml) assert template.name == "goblin" assert template.description == "a snarling goblin with a crude club" assert template.pl == 50.0 assert template.stamina == 40.0 assert template.max_stamina == 40.0 assert template.moves == ["punch left", "punch right", "sweep"] def test_load_template_no_moves(self, dummy_toml): template = load_mob_template(dummy_toml) assert template.name == "training dummy" assert template.moves == [] def test_load_all_templates(self, goblin_toml, dummy_toml): templates = load_mob_templates(goblin_toml.parent) assert "goblin" in templates assert "training dummy" in templates assert len(templates) == 2 class TestSpawnDespawn: def test_spawn_creates_mob(self, goblin_toml): template = load_mob_template(goblin_toml) mob = spawn_mob(template, 10, 20) assert isinstance(mob, Mob) assert mob.name == "goblin" assert mob.x == 10 assert mob.y == 20 assert mob.pl == 50.0 assert mob.stamina == 40.0 assert mob.max_stamina == 40.0 assert mob.moves == ["punch left", "punch right", "sweep"] assert mob.alive is True assert mob in mobs def test_spawn_adds_to_registry(self, goblin_toml): template = load_mob_template(goblin_toml) spawn_mob(template, 0, 0) spawn_mob(template, 5, 5) assert len(mobs) == 2 def test_despawn_removes_from_list(self, goblin_toml): template = load_mob_template(goblin_toml) mob = spawn_mob(template, 0, 0) despawn_mob(mob) assert mob not in mobs assert mob.alive is False def test_despawn_sets_alive_false(self, goblin_toml): template = load_mob_template(goblin_toml) mob = spawn_mob(template, 0, 0) despawn_mob(mob) assert mob.alive is False class TestGetNearbyMob: @pytest.fixture def mock_world(self): w = MagicMock() w.width = 256 w.height = 256 return w def test_finds_by_name_within_range(self, goblin_toml, mock_world): template = load_mob_template(goblin_toml) mob = spawn_mob(template, 5, 5) found = get_nearby_mob("goblin", 3, 3, mock_world) assert found is mob def test_returns_none_when_out_of_range(self, goblin_toml, mock_world): template = load_mob_template(goblin_toml) spawn_mob(template, 100, 100) found = get_nearby_mob("goblin", 0, 0, mock_world) assert found is None def test_returns_none_for_wrong_name(self, goblin_toml, mock_world): template = load_mob_template(goblin_toml) spawn_mob(template, 5, 5) found = get_nearby_mob("dragon", 3, 3, mock_world) assert found is None def test_picks_closest_when_multiple(self, goblin_toml, mock_world): template = load_mob_template(goblin_toml) far_mob = spawn_mob(template, 8, 8) close_mob = spawn_mob(template, 1, 1) found = get_nearby_mob("goblin", 0, 0, mock_world) assert found is close_mob def test_skips_dead_mobs(self, goblin_toml, mock_world): template = load_mob_template(goblin_toml) mob = spawn_mob(template, 5, 5) mob.alive = False found = get_nearby_mob("goblin", 3, 3, mock_world) assert found is None def test_wrapping_distance(self, goblin_toml, mock_world): """Mob near world edge is close to player at opposite edge.""" template = load_mob_template(goblin_toml) mob = spawn_mob(template, 254, 254) found = get_nearby_mob("goblin", 2, 2, mock_world, range_=10) assert found is mob