"""Tests for TOML verb support in thing templates.""" import textwrap import pytest from mudlib.player import Player from mudlib.things import ( Container, ThingTemplate, load_thing_template, spawn_thing, ) from mudlib.zone import Zone # Test handler functions async def _test_verb_handler(obj, player, args): """Test verb handler that sends a message.""" await player.send(f"verb on {obj.name} with args={args}\r\n") async def _another_test_handler(obj, player, args): """Another test handler.""" await player.send(f"another verb on {obj.name}\r\n") @pytest.fixture def test_zone(): terrain = [["." for _ in range(10)] for _ in range(10)] return Zone(name="testzone", width=10, height=10, terrain=terrain) @pytest.fixture def player(mock_reader, mock_writer, test_zone): return Player( name="TestPlayer", x=5, y=5, reader=mock_reader, writer=mock_writer, location=test_zone, ) def test_thing_template_default_empty_verbs(): """ThingTemplate has empty verbs dict by default.""" template = ThingTemplate(name="test", description="a test") assert template.verbs == {} def test_load_template_with_verbs(tmp_path): """load_thing_template parses [verbs] section from TOML.""" toml = tmp_path / "key.toml" toml.write_text( textwrap.dedent("""\ name = "key" description = "a brass key" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" """) ) template = load_thing_template(toml) assert template.verbs == {"unlock": "tests.test_toml_verbs:_test_verb_handler"} def test_load_template_without_verbs_section(tmp_path): """load_thing_template works without [verbs] section (backwards compatible).""" toml = tmp_path / "rock.toml" toml.write_text( textwrap.dedent("""\ name = "rock" description = "a smooth stone" portable = true """) ) template = load_thing_template(toml) assert template.verbs == {} def test_load_template_with_multiple_verbs(tmp_path): """load_thing_template handles multiple verbs.""" toml = tmp_path / "multi.toml" toml.write_text( textwrap.dedent("""\ name = "door" description = "a wooden door" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" open = "tests.test_toml_verbs:_another_test_handler" """) ) template = load_thing_template(toml) assert len(template.verbs) == 2 assert "unlock" in template.verbs assert "open" in template.verbs def test_spawn_thing_registers_verbs(test_zone, tmp_path): """spawn_thing registers verbs on spawned instance.""" toml = tmp_path / "key.toml" toml.write_text( textwrap.dedent("""\ name = "key" description = "a brass key" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" """) ) template = load_thing_template(toml) thing = spawn_thing(template, test_zone, x=5, y=5) assert thing.has_verb("unlock") def test_spawn_container_registers_verbs(test_zone, tmp_path): """spawn_thing registers verbs on Container instances too.""" toml = tmp_path / "chest.toml" toml.write_text( textwrap.dedent("""\ name = "chest" description = "a wooden chest" capacity = 10 [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" """) ) template = load_thing_template(toml) container = spawn_thing(template, test_zone, x=5, y=5) assert isinstance(container, Container) assert container.has_verb("unlock") @pytest.mark.asyncio async def test_spawned_verb_handler_receives_correct_args(test_zone, player, tmp_path): """Spawned thing's verb handler can be called and receives (player, args).""" toml = tmp_path / "key.toml" toml.write_text( textwrap.dedent("""\ name = "key" description = "a brass key" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" """) ) template = load_thing_template(toml) thing = spawn_thing(template, test_zone, x=5, y=5) # Call the verb handler handler = thing.get_verb("unlock") assert handler is not None await handler(player, "door") # Check that the message was sent player.writer.write.assert_called() call_args = player.writer.write.call_args[0][0] assert "verb on key with args=door" in call_args @pytest.mark.asyncio async def test_verb_handler_receives_correct_object_instance( test_zone, player, tmp_path ): """Handler receives the correct object instance as first arg (via partial).""" toml = tmp_path / "key.toml" toml.write_text( textwrap.dedent("""\ name = "brass key" description = "a brass key" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" """) ) template = load_thing_template(toml) thing = spawn_thing(template, test_zone, x=5, y=5) # Call the verb handler handler = thing.get_verb("unlock") assert handler is not None await handler(player, "chest") # Check that the object name appears in the message (verifying correct object) player.writer.write.assert_called() call_args = player.writer.write.call_args[0][0] assert "verb on brass key with args=chest" in call_args @pytest.mark.asyncio async def test_multiple_verbs_all_registered(test_zone, player, tmp_path): """Multiple verbs in TOML all get registered.""" toml = tmp_path / "door.toml" toml.write_text( textwrap.dedent("""\ name = "door" description = "a wooden door" [verbs] unlock = "tests.test_toml_verbs:_test_verb_handler" open = "tests.test_toml_verbs:_another_test_handler" """) ) template = load_thing_template(toml) thing = spawn_thing(template, test_zone, x=5, y=5) # Both verbs should be registered assert thing.has_verb("unlock") assert thing.has_verb("open") # Both should work unlock_handler = thing.get_verb("unlock") assert unlock_handler is not None await unlock_handler(player, "test1") player.writer.write.assert_called() open_handler = thing.get_verb("open") assert open_handler is not None await open_handler(player, "test2") call_args = player.writer.write.call_args[0][0] assert "another verb on door" in call_args def test_bad_handler_ref_skipped(test_zone): """A TOML verb with bad handler ref is skipped, not a crash.""" template = ThingTemplate( name="bad", description="oops", portable=True, verbs={"zap": "nonexistent.module:fake_handler"}, ) thing = spawn_thing(template, test_zone, x=5, y=5) assert not thing.has_verb("zap")