"""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] == "^"