Normalize alias casing across command and persistence
This commit is contained in:
parent
3d1279475e
commit
8424404d27
3 changed files with 54 additions and 6 deletions
|
|
@ -28,7 +28,7 @@ async def cmd_alias(player: Player, args: str) -> None:
|
||||||
|
|
||||||
# Check if this is a single-word lookup or a definition
|
# Check if this is a single-word lookup or a definition
|
||||||
parts = args.split(None, 1)
|
parts = args.split(None, 1)
|
||||||
alias_name = parts[0]
|
alias_name = parts[0].lower()
|
||||||
|
|
||||||
if len(parts) == 1:
|
if len(parts) == 1:
|
||||||
# Show single alias
|
# Show single alias
|
||||||
|
|
@ -39,7 +39,7 @@ async def cmd_alias(player: Player, args: str) -> None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create alias
|
# Create alias
|
||||||
expansion = parts[1]
|
expansion = parts[1].strip()
|
||||||
|
|
||||||
# Cannot alias over built-in commands
|
# Cannot alias over built-in commands
|
||||||
if alias_name in _registry:
|
if alias_name in _registry:
|
||||||
|
|
@ -56,7 +56,7 @@ async def cmd_unalias(player: Player, args: str) -> None:
|
||||||
Usage:
|
Usage:
|
||||||
unalias <name>
|
unalias <name>
|
||||||
"""
|
"""
|
||||||
alias_name = args.strip()
|
alias_name = args.strip().lower()
|
||||||
|
|
||||||
if not alias_name:
|
if not alias_name:
|
||||||
await player.send("Usage: unalias <name>\r\n")
|
await player.send("Usage: unalias <name>\r\n")
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,8 @@ def load_aliases(name: str, db_path: str | Path | None = None) -> dict[str, str]
|
||||||
(name,),
|
(name,),
|
||||||
)
|
)
|
||||||
|
|
||||||
result = {alias: expansion for alias, expansion in cursor.fetchall()}
|
# Normalize keys to lowercase so dispatch can match consistently.
|
||||||
|
result = {alias.lower(): expansion for alias, expansion in cursor.fetchall()}
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ def test_save_and_load_aliases_roundtrip():
|
||||||
db_path = Path(tmpdir) / "test.db"
|
db_path = Path(tmpdir) / "test.db"
|
||||||
init_db(db_path)
|
init_db(db_path)
|
||||||
|
|
||||||
aliases = {"pr": "punch right", "pl": "punch left", "l": "look"}
|
aliases = {"PR": "punch right", "pl": "punch left", "l": "look"}
|
||||||
save_aliases("goku", aliases, db_path)
|
save_aliases("goku", aliases, db_path)
|
||||||
|
|
||||||
loaded = load_aliases("goku", db_path)
|
loaded = load_aliases("goku", db_path)
|
||||||
assert loaded == aliases
|
assert loaded == {"pr": "punch right", "pl": "punch left", "l": "look"}
|
||||||
|
|
||||||
|
|
||||||
def test_load_aliases_empty():
|
def test_load_aliases_empty():
|
||||||
|
|
@ -76,6 +76,16 @@ async def test_alias_create(player):
|
||||||
player.writer.write.assert_called_with("Alias set: pr -> punch right\r\n")
|
player.writer.write.assert_called_with("Alias set: pr -> punch right\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_alias_normalizes_name_to_lowercase(player):
|
||||||
|
"""Alias names are normalized so dispatch lookup is consistent."""
|
||||||
|
from mudlib.commands.alias import cmd_alias
|
||||||
|
|
||||||
|
await cmd_alias(player, "PR punch right")
|
||||||
|
assert "pr" in player.aliases
|
||||||
|
assert "PR" not in player.aliases
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_alias_list_with_aliases(player):
|
async def test_alias_list_with_aliases(player):
|
||||||
"""alias with no args lists all aliases."""
|
"""alias with no args lists all aliases."""
|
||||||
|
|
@ -113,6 +123,16 @@ async def test_unalias_removes_alias(player):
|
||||||
player.writer.write.assert_called_with("Alias removed: pr\r\n")
|
player.writer.write.assert_called_with("Alias removed: pr\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_unalias_is_case_insensitive(player):
|
||||||
|
"""unalias should remove aliases regardless of case."""
|
||||||
|
from mudlib.commands.alias import cmd_unalias
|
||||||
|
|
||||||
|
player.aliases = {"pr": "punch right"}
|
||||||
|
await cmd_unalias(player, "PR")
|
||||||
|
assert "pr" not in player.aliases
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_unalias_no_such_alias(player):
|
async def test_unalias_no_such_alias(player):
|
||||||
"""unalias on non-existent alias shows error."""
|
"""unalias on non-existent alias shows error."""
|
||||||
|
|
@ -135,6 +155,18 @@ async def test_alias_cannot_override_builtin(player):
|
||||||
assert "look" not in player.aliases
|
assert "look" not in player.aliases
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_alias_builtin_collision_is_case_insensitive(player):
|
||||||
|
"""Built-in collision checks should apply regardless of alias casing."""
|
||||||
|
import mudlib.commands.look # noqa: F401 - needed to register look command
|
||||||
|
from mudlib.commands.alias import cmd_alias
|
||||||
|
|
||||||
|
await cmd_alias(player, "LOOK punch right")
|
||||||
|
player.writer.write.assert_called_with(
|
||||||
|
"Cannot alias over built-in command: look\r\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Dispatch integration tests
|
# Dispatch integration tests
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_alias_expands_in_dispatch(player):
|
async def test_alias_expands_in_dispatch(player):
|
||||||
|
|
@ -151,6 +183,21 @@ async def test_alias_expands_in_dispatch(player):
|
||||||
assert called_with == ["hello"]
|
assert called_with == ["hello"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_alias_expands_in_dispatch_case_insensitive_key(player):
|
||||||
|
"""Stored aliases with uppercase keys still load/use as lowercase."""
|
||||||
|
called_with = []
|
||||||
|
|
||||||
|
async def test_handler(p, args):
|
||||||
|
called_with.append(args)
|
||||||
|
|
||||||
|
register(CommandDefinition("testcmd", test_handler))
|
||||||
|
player.aliases["pr"] = "testcmd"
|
||||||
|
|
||||||
|
await dispatch(player, "PR hello")
|
||||||
|
assert called_with == ["hello"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_alias_with_extra_args(player):
|
async def test_alias_with_extra_args(player):
|
||||||
"""Alias expansion preserves additional arguments."""
|
"""Alias expansion preserves additional arguments."""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue