from pathlib import Path import pytest from engine.character import Character from engine.defs import DefRegistry from engine.dev_config import DevConfig from engine.dev_tools import DevFactionMenu, DevSpawnMenu, DevTurretMenu from engine.inventory import Inventory DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs" @pytest.fixture() def registry(): return DefRegistry.load(DEFS_DIR) def make_human(registry, **kwargs) -> Character: return Character(species_id="human", default_body_parts=registry.new_default_body_parts("human"), **kwargs) # --- DevSpawnMenu ------------------------------------------------------------------------------- def test_menu_defaults_to_every_loaded_non_secret_item_def_sorted(registry): menu = DevSpawnMenu(registry) expected = sorted(def_id for def_id, item_def in registry.item_defs.items() if not item_def.secret) assert menu.item_ids == expected assert menu.selected_item_id() == menu.item_ids[0] def test_menu_excludes_secret_items(registry): menu = DevSpawnMenu(registry) assert "cyberdeck_the_miro_model_3a" not in menu.item_ids def test_cycle_wraps_around(registry): menu = DevSpawnMenu(registry) menu.cycle(-1) assert menu.selected_item_id() == menu.item_ids[-1] def test_spawn_into_places_the_item_when_inventory_has_room(registry): menu = DevSpawnMenu(registry) character = make_human(registry) character.inventory = Inventory(4, 4) assert menu.spawn_into(character) is True assert len(character.inventory.items()) == 1 def test_spawn_into_fails_without_an_inventory(registry): menu = DevSpawnMenu(registry) character = make_human(registry) assert menu.spawn_into(character) is False def test_spawn_into_fails_when_inventory_is_full(registry): menu = DevSpawnMenu(registry) character = make_human(registry) character.inventory = Inventory(1, 1) item_def = registry.get_item_def(menu.selected_item_id()) if item_def.width > 1 or item_def.height > 1: pytest.skip("first item def doesn't fit a 1x1 inventory to begin with") assert menu.spawn_into(character) is True assert menu.spawn_into(character) is False # --- DevTurretMenu -------------------------------------------------------------------------------- def test_turret_menu_defaults_to_every_turret_entity_def_sorted(registry): menu = DevTurretMenu(registry) assert menu.entity_def_ids == ["turret_autocannon", "turret_pistol"] assert menu.selected_entity_def_id() == "turret_autocannon" def test_turret_menu_excludes_non_turret_entity_defs(registry): menu = DevTurretMenu(registry) assert "player" not in menu.entity_def_ids assert "npc_reptilian" not in menu.entity_def_ids assert "companion_dog" not in menu.entity_def_ids def test_turret_menu_cycle_wraps_around(registry): menu = DevTurretMenu(registry) menu.cycle(-1) assert menu.selected_entity_def_id() == menu.entity_def_ids[-1] def test_turret_menu_selected_entity_def_id_is_none_when_empty(registry): menu = DevTurretMenu(registry) menu.entity_def_ids = [] assert menu.selected_entity_def_id() is None # --- DevFactionMenu -------------------------------------------------------------------------------- def test_type_char_builds_up_the_input_text(): menu = DevFactionMenu() menu.type_char("h") menu.type_char("i") assert menu.input_text == "hi" def test_backspace_removes_the_last_typed_character(): menu = DevFactionMenu() menu.type_char("h") menu.type_char("i") menu.backspace() assert menu.input_text == "h" def test_typing_is_capped_at_max_faction_tag_length(): menu = DevFactionMenu() for ch in "x" * 40: menu.type_char(ch) assert len(menu.input_text) == 24 def test_add_tag_appends_the_typed_text_and_clears_the_input(registry): menu = DevFactionMenu() character = make_human(registry) menu.type_char("h") menu.type_char("o") menu.type_char("s") menu.type_char("t") menu.type_char("i") menu.type_char("l") menu.type_char("e") assert menu.add_tag(character) is True assert character.factions == ["hostile"] assert menu.input_text == "" def test_add_tag_trims_whitespace(registry): menu = DevFactionMenu() character = make_human(registry) for ch in " crew ": menu.type_char(ch) menu.add_tag(character) assert character.factions == ["crew"] def test_add_tag_is_a_noop_for_blank_input(registry): menu = DevFactionMenu() character = make_human(registry) for ch in " ": menu.type_char(ch) assert menu.add_tag(character) is False assert character.factions == [] def test_add_tag_is_a_noop_for_a_duplicate(registry): menu = DevFactionMenu() character = make_human(registry, factions=["crew"]) for ch in "crew": menu.type_char(ch) assert menu.add_tag(character) is False assert character.factions == ["crew"] def test_cycle_selected_wraps_around(registry): menu = DevFactionMenu() character = make_human(registry, factions=["a", "b", "c"]) menu.cycle_selected(character, -1) assert menu.selected_index == 2 def test_cycle_selected_is_a_noop_without_any_tags(registry): menu = DevFactionMenu() character = make_human(registry) menu.cycle_selected(character, 1) assert menu.selected_index == 0 def test_remove_selected_removes_the_highlighted_tag(registry): menu = DevFactionMenu() character = make_human(registry, factions=["a", "b", "c"]) menu.selected_index = 1 assert menu.remove_selected(character) is True assert character.factions == ["a", "c"] def test_remove_selected_clamps_the_index_after_removing_the_last_tag(registry): menu = DevFactionMenu() character = make_human(registry, factions=["a", "b"]) menu.selected_index = 1 menu.remove_selected(character) assert character.factions == ["a"] assert menu.remove_selected(character) is True assert character.factions == [] assert menu.selected_index == 0 def test_remove_selected_is_a_noop_without_any_tags(registry): menu = DevFactionMenu() character = make_human(registry) assert menu.remove_selected(character) is False # --- DevConfig ------------------------------------------------------------------------------------ def test_dev_config_defaults_to_disabled_when_file_missing(tmp_path): config = DevConfig.load(tmp_path / "does_not_exist.conf") assert config.enabled is False def test_dev_config_toggle_flips_the_flag(): config = DevConfig() config.toggle() assert config.enabled is True config.toggle() assert config.enabled is False def test_dev_config_save_and_reload_round_trips(tmp_path): path = tmp_path / "dev.conf" config = DevConfig(enabled=True) config.save(path) reloaded = DevConfig.load(path) assert reloaded.enabled is True