fix(profiles): normalize profile IDs for Kanban assignees and lookups
- Add normalize_profile_name() for lowercase canonical IDs and Default alias - Use canonical names in create/delete/rename/export/import/set_active paths - Canonicalize Kanban assignee on create/assign, list filter, and worker spawn - Tests for mixed-case assignees and profile resolution (fixes #18498)
This commit is contained in:
@@ -252,6 +252,22 @@ def test_assign_reassigns_when_not_running(kanban_home):
|
||||
assert kb.get_task(conn, t).assignee == "b"
|
||||
|
||||
|
||||
def test_assignee_normalized_to_lowercase_on_create_and_assign(kanban_home):
|
||||
"""Dashboard/CLI may pass title-cased profile labels; DB + spawn use canonical id."""
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(conn, title="cased", assignee="Jules")
|
||||
assert kb.get_task(conn, tid).assignee == "jules"
|
||||
assert kb.assign_task(conn, tid, "Librarian")
|
||||
assert kb.get_task(conn, tid).assignee == "librarian"
|
||||
|
||||
|
||||
def test_list_tasks_assignee_filter_case_insensitive(kanban_home):
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(conn, title="q", assignee="jules")
|
||||
found = kb.list_tasks(conn, assignee="Jules")
|
||||
assert len(found) == 1 and found[0].id == tid
|
||||
|
||||
|
||||
def test_archive_hides_from_default_list(kanban_home):
|
||||
with kb.connect() as conn:
|
||||
t = kb.create_task(conn, title="x")
|
||||
|
||||
@@ -15,6 +15,7 @@ from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
|
||||
from hermes_cli.profiles import (
|
||||
normalize_profile_name,
|
||||
validate_profile_name,
|
||||
get_profile_dir,
|
||||
create_profile,
|
||||
@@ -58,6 +59,24 @@ def profile_env(tmp_path, monkeypatch):
|
||||
# TestValidateProfileName
|
||||
# ===================================================================
|
||||
|
||||
class TestNormalizeProfileName:
|
||||
"""Tests for normalize_profile_name()."""
|
||||
|
||||
def test_title_case_normalized(self):
|
||||
assert normalize_profile_name("Jules") == "jules"
|
||||
assert normalize_profile_name(" Librarian ") == "librarian"
|
||||
|
||||
def test_default_case_insensitive(self):
|
||||
assert normalize_profile_name("Default") == "default"
|
||||
assert normalize_profile_name("DEFAULT") == "default"
|
||||
|
||||
def test_empty_raises(self):
|
||||
with pytest.raises(ValueError, match="cannot be empty"):
|
||||
normalize_profile_name("")
|
||||
with pytest.raises(ValueError, match="cannot be empty"):
|
||||
normalize_profile_name(" ")
|
||||
|
||||
|
||||
class TestValidateProfileName:
|
||||
"""Tests for validate_profile_name()."""
|
||||
|
||||
@@ -66,7 +85,10 @@ class TestValidateProfileName:
|
||||
# Should not raise
|
||||
validate_profile_name(name)
|
||||
|
||||
@pytest.mark.parametrize("name", ["UPPER", "has space", ".hidden", "-leading"])
|
||||
def test_uppercase_accepted_via_normalization(self):
|
||||
validate_profile_name("Jules")
|
||||
|
||||
@pytest.mark.parametrize("name", ["has space", ".hidden", "-leading"])
|
||||
def test_invalid_names_rejected(self, name):
|
||||
with pytest.raises(ValueError):
|
||||
validate_profile_name(name)
|
||||
@@ -107,6 +129,10 @@ class TestGetProfileDir:
|
||||
result = get_profile_dir("coder")
|
||||
assert result == tmp_path / ".hermes" / "profiles" / "coder"
|
||||
|
||||
def test_named_profile_matching_is_case_insensitive(self, profile_env):
|
||||
tmp_path = profile_env
|
||||
assert get_profile_dir("Coder") == tmp_path / ".hermes" / "profiles" / "coder"
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# TestCreateProfile
|
||||
|
||||
Reference in New Issue
Block a user