268 lines
7.1 KiB
Python
268 lines
7.1 KiB
Python
"""Tests for player housing system."""
|
|
|
|
import tomllib
|
|
|
|
import pytest
|
|
|
|
from mudlib.housing import (
|
|
HOME_HEIGHT,
|
|
HOME_SPAWN_X,
|
|
HOME_SPAWN_Y,
|
|
HOME_WIDTH,
|
|
_home_zone_name,
|
|
create_home_zone,
|
|
get_or_create_home,
|
|
init_housing,
|
|
load_home_zone,
|
|
save_home_zone,
|
|
)
|
|
from mudlib.zones import get_zone, zone_registry
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clean_zone_registry():
|
|
"""Clear zone registry between tests."""
|
|
saved = dict(zone_registry)
|
|
zone_registry.clear()
|
|
yield
|
|
zone_registry.clear()
|
|
zone_registry.update(saved)
|
|
|
|
|
|
def test_init_housing_creates_directory(tmp_path):
|
|
"""init_housing() creates the zones directory."""
|
|
zones_dir = tmp_path / "zones"
|
|
assert not zones_dir.exists()
|
|
|
|
init_housing(zones_dir)
|
|
|
|
assert zones_dir.exists()
|
|
assert zones_dir.is_dir()
|
|
|
|
|
|
def test_home_zone_name():
|
|
"""_home_zone_name() returns correct format."""
|
|
assert _home_zone_name("Alice") == "home:alice"
|
|
assert _home_zone_name("bob") == "home:bob"
|
|
assert _home_zone_name("Charlie") == "home:charlie"
|
|
|
|
|
|
def test_create_home_zone(tmp_path):
|
|
"""create_home_zone() creates a zone with correct properties."""
|
|
init_housing(tmp_path)
|
|
|
|
zone = create_home_zone("Alice")
|
|
|
|
# Check basic properties
|
|
assert zone.name == "home:alice"
|
|
assert zone.description == "Alice's home"
|
|
assert zone.width == HOME_WIDTH
|
|
assert zone.height == HOME_HEIGHT
|
|
assert zone.toroidal is False
|
|
assert zone.spawn_x == HOME_SPAWN_X
|
|
assert zone.spawn_y == HOME_SPAWN_Y
|
|
assert zone.safe is True
|
|
assert zone.impassable == {"#", "^", "~"}
|
|
|
|
# Check terrain dimensions
|
|
assert len(zone.terrain) == HOME_HEIGHT
|
|
assert all(len(row) == HOME_WIDTH for row in zone.terrain)
|
|
|
|
# Check border is walls
|
|
for x in range(HOME_WIDTH):
|
|
assert zone.terrain[0][x] == "#" # top
|
|
assert zone.terrain[HOME_HEIGHT - 1][x] == "#" # bottom
|
|
for y in range(HOME_HEIGHT):
|
|
assert zone.terrain[y][0] == "#" # left
|
|
assert zone.terrain[y][HOME_WIDTH - 1] == "#" # right
|
|
|
|
# Check interior is grass
|
|
for y in range(1, HOME_HEIGHT - 1):
|
|
for x in range(1, HOME_WIDTH - 1):
|
|
assert zone.terrain[y][x] == "."
|
|
|
|
# Check spawn point is passable
|
|
assert zone.terrain[HOME_SPAWN_Y][HOME_SPAWN_X] == "."
|
|
|
|
|
|
def test_create_registers_zone(tmp_path):
|
|
"""create_home_zone() registers the zone."""
|
|
init_housing(tmp_path)
|
|
|
|
zone = create_home_zone("Bob")
|
|
|
|
registered = get_zone("home:bob")
|
|
assert registered is zone
|
|
|
|
|
|
def test_save_home_zone(tmp_path):
|
|
"""save_home_zone() writes a valid TOML file."""
|
|
init_housing(tmp_path)
|
|
|
|
create_home_zone("Charlie")
|
|
zone_file = tmp_path / "charlie.toml"
|
|
|
|
assert zone_file.exists()
|
|
|
|
# Verify TOML is valid and contains expected data
|
|
with open(zone_file, "rb") as f:
|
|
data = tomllib.load(f)
|
|
|
|
assert data["name"] == "home:charlie"
|
|
assert data["description"] == "Charlie's home"
|
|
assert data["width"] == HOME_WIDTH
|
|
assert data["height"] == HOME_HEIGHT
|
|
assert data["toroidal"] is False
|
|
assert data["spawn_x"] == HOME_SPAWN_X
|
|
assert data["spawn_y"] == HOME_SPAWN_Y
|
|
assert data["safe"] is True
|
|
|
|
# Check terrain
|
|
rows = data["terrain"]["rows"]
|
|
assert len(rows) == HOME_HEIGHT
|
|
assert all(len(row) == HOME_WIDTH for row in rows)
|
|
|
|
# Check impassable
|
|
assert set(data["terrain"]["impassable"]["tiles"]) == {"#", "^", "~"}
|
|
|
|
|
|
def test_load_home_zone(tmp_path):
|
|
"""load_home_zone() reads a zone from disk."""
|
|
init_housing(tmp_path)
|
|
|
|
# Create and save a zone
|
|
_ = create_home_zone("Dave")
|
|
|
|
# Clear registry
|
|
zone_registry.clear()
|
|
|
|
# Load it back
|
|
loaded = load_home_zone("Dave")
|
|
|
|
assert loaded is not None
|
|
assert loaded.name == "home:dave"
|
|
assert loaded.description == "Dave's home"
|
|
assert loaded.width == HOME_WIDTH
|
|
assert loaded.height == HOME_HEIGHT
|
|
assert loaded.toroidal is False
|
|
assert loaded.spawn_x == HOME_SPAWN_X
|
|
assert loaded.spawn_y == HOME_SPAWN_Y
|
|
assert loaded.safe is True
|
|
assert loaded.impassable == {"#", "^", "~"}
|
|
|
|
|
|
def test_load_registers_zone(tmp_path):
|
|
"""load_home_zone() registers the zone."""
|
|
init_housing(tmp_path)
|
|
|
|
create_home_zone("Eve")
|
|
zone_registry.clear()
|
|
|
|
loaded = load_home_zone("Eve")
|
|
|
|
registered = get_zone("home:eve")
|
|
assert registered is loaded
|
|
|
|
|
|
def test_load_nonexistent_returns_none(tmp_path):
|
|
"""load_home_zone() returns None if file doesn't exist."""
|
|
init_housing(tmp_path)
|
|
|
|
result = load_home_zone("Nobody")
|
|
|
|
assert result is None
|
|
|
|
|
|
def test_round_trip(tmp_path):
|
|
"""Create -> save -> load produces equivalent zone."""
|
|
init_housing(tmp_path)
|
|
|
|
original = create_home_zone("Frank")
|
|
zone_registry.clear()
|
|
|
|
loaded = load_home_zone("Frank")
|
|
assert loaded is not None
|
|
|
|
# Compare all fields
|
|
assert loaded.name == original.name
|
|
assert loaded.description == original.description
|
|
assert loaded.width == original.width
|
|
assert loaded.height == original.height
|
|
assert loaded.toroidal == original.toroidal
|
|
assert loaded.spawn_x == original.spawn_x
|
|
assert loaded.spawn_y == original.spawn_y
|
|
assert loaded.safe == original.safe
|
|
assert loaded.impassable == original.impassable
|
|
|
|
# Compare terrain
|
|
assert len(loaded.terrain) == len(original.terrain)
|
|
for loaded_row, orig_row in zip(loaded.terrain, original.terrain, strict=True):
|
|
assert loaded_row == orig_row
|
|
|
|
|
|
def test_get_or_create_home_creates_new(tmp_path):
|
|
"""get_or_create_home() creates a zone on first call."""
|
|
init_housing(tmp_path)
|
|
|
|
zone = get_or_create_home("Grace")
|
|
|
|
assert zone.name == "home:grace"
|
|
assert zone.description == "Grace's home"
|
|
|
|
|
|
def test_get_or_create_home_returns_existing_from_registry(tmp_path):
|
|
"""get_or_create_home() returns existing zone from registry."""
|
|
init_housing(tmp_path)
|
|
|
|
first = get_or_create_home("Hank")
|
|
second = get_or_create_home("Hank")
|
|
|
|
assert second is first
|
|
|
|
|
|
def test_get_or_create_home_loads_from_disk(tmp_path):
|
|
"""get_or_create_home() loads from disk if not in registry."""
|
|
init_housing(tmp_path)
|
|
|
|
create_home_zone("Iris")
|
|
zone_registry.clear()
|
|
|
|
loaded = get_or_create_home("Iris")
|
|
|
|
assert loaded.name == "home:iris"
|
|
|
|
|
|
def test_case_insensitive_zone_names(tmp_path):
|
|
"""Zone names are lowercased consistently."""
|
|
init_housing(tmp_path)
|
|
|
|
zone1 = create_home_zone("JACK")
|
|
zone2 = create_home_zone("Jack")
|
|
zone3 = create_home_zone("jack")
|
|
|
|
# All should reference the same zone name
|
|
assert zone1.name == "home:jack"
|
|
assert zone2.name == "home:jack"
|
|
assert zone3.name == "home:jack"
|
|
|
|
|
|
def test_save_preserves_modifications(tmp_path):
|
|
"""save_home_zone() preserves modifications to terrain."""
|
|
init_housing(tmp_path)
|
|
|
|
zone = create_home_zone("Kate")
|
|
|
|
# Modify terrain
|
|
zone.terrain[2][2] = "~" # add water
|
|
zone.terrain[3][3] = "^" # add mountain
|
|
|
|
# Save modifications
|
|
save_home_zone("Kate", zone)
|
|
|
|
# Load and verify
|
|
zone_registry.clear()
|
|
loaded = load_home_zone("Kate")
|
|
assert loaded is not None
|
|
|
|
assert loaded.terrain[2][2] == "~"
|
|
assert loaded.terrain[3][3] == "^"
|