mud/tests/test_furniture.py

271 lines
6.7 KiB
Python

"""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