"""Tests for furniture persistence in home zones.""" import logging import tomllib import pytest from mudlib.entity import Entity from mudlib.housing import ( create_home_zone, init_housing, load_home_zone, save_home_zone, ) from mudlib.portal import Portal from mudlib.thing import Thing from mudlib.things import ThingTemplate, spawn_thing, thing_templates from mudlib.zones import zone_registry @pytest.fixture(autouse=True) def _clean_registries(): """Clear registries between tests.""" saved_zones = dict(zone_registry) saved_templates = dict(thing_templates) zone_registry.clear() thing_templates.clear() yield zone_registry.clear() zone_registry.update(saved_zones) thing_templates.clear() thing_templates.update(saved_templates) def test_save_furniture_in_zone_toml(tmp_path): """save_home_zone() writes furniture to TOML.""" init_housing(tmp_path) # Create zone zone = create_home_zone("Alice") # Add a thing template table_template = ThingTemplate( name="table", description="A wooden table", portable=False, ) thing_templates["table"] = table_template # Spawn furniture in the zone spawn_thing(table_template, zone, x=3, y=4) # Save save_home_zone("Alice", zone) # Read the TOML file zone_file = tmp_path / "alice.toml" with open(zone_file, "rb") as f: data = tomllib.load(f) # Check furniture section assert "furniture" in data assert len(data["furniture"]) == 1 assert data["furniture"][0]["template"] == "table" assert data["furniture"][0]["x"] == 3 assert data["furniture"][0]["y"] == 4 def test_load_furniture_from_toml(tmp_path): """load_home_zone() spawns furniture from TOML.""" init_housing(tmp_path) # Create zone to get the file _ = create_home_zone("Bob") zone_file = tmp_path / "bob.toml" # Add furniture entries to the TOML with open(zone_file) as f: content = f.read() content += """ [[furniture]] template = "chair" x = 5 y = 6 """ with open(zone_file, "w") as f: f.write(content) # Add template chair_template = ThingTemplate( name="chair", description="A wooden chair", portable=True, ) thing_templates["chair"] = chair_template # Clear registry and load zone_registry.clear() loaded = load_home_zone("Bob") assert loaded is not None # Check that furniture was spawned chairs = [ obj for obj in loaded._contents if isinstance(obj, Thing) and obj.name == "chair" ] assert len(chairs) == 1 assert chairs[0].x == 5 assert chairs[0].y == 6 assert chairs[0].description == "A wooden chair" def test_furniture_round_trip(tmp_path): """Furniture survives save -> load cycle.""" init_housing(tmp_path) # Create zone zone = create_home_zone("Charlie") # Add templates table_template = ThingTemplate(name="table", description="A table", portable=False) chair_template = ThingTemplate(name="chair", description="A chair", portable=True) thing_templates["table"] = table_template thing_templates["chair"] = chair_template # Spawn furniture spawn_thing(table_template, zone, x=3, y=4) spawn_thing(chair_template, zone, x=3, y=5) # Save save_home_zone("Charlie", zone) # Clear registry and load zone_registry.clear() loaded = load_home_zone("Charlie") assert loaded is not None # Check furniture tables = [obj for obj in loaded._contents if obj.name == "table"] chairs = [obj for obj in loaded._contents if obj.name == "chair"] assert len(tables) == 1 assert tables[0].x == 3 assert tables[0].y == 4 assert len(chairs) == 1 assert chairs[0].x == 3 assert chairs[0].y == 5 def test_multiple_furniture_items(tmp_path): """Multiple furniture items save and load correctly.""" init_housing(tmp_path) zone = create_home_zone("Dave") # Add templates chair_template = ThingTemplate(name="chair", description="A chair", portable=True) thing_templates["chair"] = chair_template # Spawn multiple chairs spawn_thing(chair_template, zone, x=2, y=2) spawn_thing(chair_template, zone, x=3, y=2) spawn_thing(chair_template, zone, x=4, y=2) # Save save_home_zone("Dave", zone) # Load zone_registry.clear() loaded = load_home_zone("Dave") assert loaded is not None chairs = [obj for obj in loaded._contents if obj.name == "chair"] assert len(chairs) == 3 # Check positions positions = {(c.x, c.y) for c in chairs} assert positions == {(2, 2), (3, 2), (4, 2)} def test_load_unknown_template_skips(tmp_path, caplog): """Unknown template name in TOML is skipped with warning.""" caplog.set_level(logging.WARNING) init_housing(tmp_path) # Create zone _ = create_home_zone("Eve") zone_file = tmp_path / "eve.toml" # Add furniture with unknown template with open(zone_file) as f: content = f.read() content += """ [[furniture]] template = "unknown_thing" x = 1 y = 1 """ with open(zone_file, "w") as f: f.write(content) # Load zone_registry.clear() loaded = load_home_zone("Eve") assert loaded is not None # Check that no furniture was spawned things = [obj for obj in loaded._contents if isinstance(obj, Thing)] assert len(things) == 0 # Check that warning was logged assert "unknown_thing" in caplog.text.lower() def test_save_excludes_entities(tmp_path): """Entities in zone are NOT saved as furniture.""" init_housing(tmp_path) zone = create_home_zone("Frank") # Add an entity to the zone _ = Entity(name="test_mob", location=zone, x=5, y=5) # Save save_home_zone("Frank", zone) # Read the TOML zone_file = tmp_path / "frank.toml" with open(zone_file, "rb") as f: data = tomllib.load(f) # Furniture section should not exist or be empty furniture = data.get("furniture", []) assert len(furniture) == 0 def test_save_excludes_portals(tmp_path): """Portals are NOT saved as furniture.""" init_housing(tmp_path) zone = create_home_zone("Grace") # Add a portal to the zone _ = Portal( name="exit", description="An exit", location=zone, x=1, y=1, target_zone="overworld", target_x=10, target_y=10, ) # Save save_home_zone("Grace", zone) # Read the TOML zone_file = tmp_path / "grace.toml" with open(zone_file, "rb") as f: data = tomllib.load(f) # Furniture section should not exist or be empty furniture = data.get("furniture", []) assert len(furniture) == 0