Normalize alias casing across command and persistence

This commit is contained in:
Jared Miller 2026-02-15 12:40:10 -05:00
parent 3d1279475e
commit 8424404d27
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 54 additions and 6 deletions

View file

@ -28,7 +28,7 @@ async def cmd_alias(player: Player, args: str) -> None:
# Check if this is a single-word lookup or a definition
parts = args.split(None, 1)
alias_name = parts[0]
alias_name = parts[0].lower()
if len(parts) == 1:
# Show single alias
@ -39,7 +39,7 @@ async def cmd_alias(player: Player, args: str) -> None:
return
# Create alias
expansion = parts[1]
expansion = parts[1].strip()
# Cannot alias over built-in commands
if alias_name in _registry:
@ -56,7 +56,7 @@ async def cmd_unalias(player: Player, args: str) -> None:
Usage:
unalias <name>
"""
alias_name = args.strip()
alias_name = args.strip().lower()
if not alias_name:
await player.send("Usage: unalias <name>\r\n")

View file

@ -502,7 +502,8 @@ def load_aliases(name: str, db_path: str | Path | None = None) -> dict[str, str]
(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()
return result

View file

@ -25,11 +25,11 @@ def test_save_and_load_aliases_roundtrip():
db_path = Path(tmpdir) / "test.db"
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)
loaded = load_aliases("goku", db_path)
assert loaded == aliases
assert loaded == {"pr": "punch right", "pl": "punch left", "l": "look"}
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")
@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
async def test_alias_list_with_aliases(player):
"""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")
@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
async def test_unalias_no_such_alias(player):
"""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
@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
@pytest.mark.asyncio
async def test_alias_expands_in_dispatch(player):
@ -151,6 +183,21 @@ async def test_alias_expands_in_dispatch(player):
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
async def test_alias_with_extra_args(player):
"""Alias expansion preserves additional arguments."""