kin: KIN-069 Frontend: цветные бейджи категорий и фильтр по категории в канбане
This commit is contained in:
parent
d627c1ba77
commit
8007960332
1 changed files with 152 additions and 0 deletions
|
|
@ -312,3 +312,155 @@ class TestAutoResolvePendingActions:
|
|||
results = auto_resolve_pending_actions(conn, "VDOL-001", [])
|
||||
assert results == []
|
||||
mock_claude.assert_not_called()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# KIN-068 — category наследуется при создании followup и manual задач
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestNextTaskIdWithCategory:
|
||||
"""_next_task_id с category генерирует ID в формате PROJ-CAT-NNN."""
|
||||
|
||||
@pytest.mark.parametrize("category,expected_prefix", [
|
||||
("SEC", "VDOL-SEC-"),
|
||||
("UI", "VDOL-UI-"),
|
||||
("API", "VDOL-API-"),
|
||||
("INFRA", "VDOL-INFRA-"),
|
||||
("BIZ", "VDOL-BIZ-"),
|
||||
])
|
||||
def test_with_category_produces_cat_format(self, conn, category, expected_prefix):
|
||||
"""_next_task_id с category возвращает PROJ-CAT-NNN."""
|
||||
result = _next_task_id(conn, "vdol", category=category)
|
||||
assert result.startswith(expected_prefix)
|
||||
suffix = result[len(expected_prefix):]
|
||||
assert suffix.isdigit() and len(suffix) == 3
|
||||
|
||||
def test_with_none_category_produces_plain_format(self, conn):
|
||||
"""_next_task_id без category возвращает PROJ-NNN (backward compat)."""
|
||||
result = _next_task_id(conn, "vdol", category=None)
|
||||
# VDOL-001 already exists → next is VDOL-002
|
||||
assert result == "VDOL-002"
|
||||
parts = result.split("-")
|
||||
assert len(parts) == 2
|
||||
assert parts[1].isdigit()
|
||||
|
||||
def test_first_cat_task_is_001(self, conn):
|
||||
"""Первая задача категории всегда получает номер 001."""
|
||||
result = _next_task_id(conn, "vdol", category="DB")
|
||||
assert result == "VDOL-DB-001"
|
||||
|
||||
def test_cat_counter_is_per_category(self, conn):
|
||||
"""Счётчик независим для каждой категории."""
|
||||
models.create_task(conn, "VDOL-SEC-001", "vdol", "Security task", category="SEC")
|
||||
assert _next_task_id(conn, "vdol", category="SEC") == "VDOL-SEC-002"
|
||||
assert _next_task_id(conn, "vdol", category="UI") == "VDOL-UI-001"
|
||||
|
||||
|
||||
class TestFollowupCategoryInheritance:
|
||||
"""Регрессионный тест KIN-068: followup задачи наследуют category родителя."""
|
||||
|
||||
@pytest.mark.parametrize("category", ["SEC", "UI", "API", "INFRA", "BIZ", None])
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_generate_followups_followup_inherits_category(
|
||||
self, mock_claude, category, conn
|
||||
):
|
||||
"""Followup задача наследует category родительской задачи (включая None)."""
|
||||
# Установить category на родительской задаче
|
||||
models.update_task(conn, "VDOL-001", category=category)
|
||||
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Followup task", "type": "feature", "priority": 3},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
|
||||
result = generate_followups(conn, "VDOL-001")
|
||||
|
||||
assert len(result["created"]) == 1
|
||||
followup = result["created"][0]
|
||||
|
||||
# category должен совпадать с родительской задачей
|
||||
assert followup["category"] == category
|
||||
|
||||
# ID должен иметь правильный формат
|
||||
if category:
|
||||
assert followup["id"].startswith(f"VDOL-{category}-"), (
|
||||
f"Ожидался ID вида VDOL-{category}-NNN, получен {followup['id']!r}"
|
||||
)
|
||||
else:
|
||||
# Без категории: старый формат VDOL-NNN
|
||||
parts = followup["id"].split("-")
|
||||
assert len(parts) == 2, (
|
||||
f"Ожидался ID вида VDOL-NNN (2 части), получен {followup['id']!r}"
|
||||
)
|
||||
assert parts[1].isdigit()
|
||||
|
||||
@pytest.mark.parametrize("category", ["SEC", "UI", "API", "INFRA", "BIZ", None])
|
||||
def test_resolve_pending_action_manual_task_inherits_category(
|
||||
self, category, conn
|
||||
):
|
||||
"""manual_task при resolve_pending_action наследует category родителя."""
|
||||
models.update_task(conn, "VDOL-001", category=category)
|
||||
|
||||
action = {
|
||||
"type": "permission_fix",
|
||||
"original_item": {
|
||||
"title": "Fix manually",
|
||||
"type": "hotfix",
|
||||
"priority": 4,
|
||||
"brief": "Apply permissions fix",
|
||||
},
|
||||
}
|
||||
result = resolve_pending_action(conn, "VDOL-001", action, "manual_task")
|
||||
|
||||
assert result is not None
|
||||
assert result["category"] == category
|
||||
|
||||
if category:
|
||||
assert result["id"].startswith(f"VDOL-{category}-"), (
|
||||
f"Ожидался ID вида VDOL-{category}-NNN, получен {result['id']!r}"
|
||||
)
|
||||
else:
|
||||
parts = result["id"].split("-")
|
||||
assert len(parts) == 2
|
||||
assert parts[1].isdigit()
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_generate_followups_sec_category_id_format(self, mock_claude, conn):
|
||||
"""Регрессионный тест KIN-068: followup задача с category=SEC получает ID VDOL-SEC-001."""
|
||||
models.update_task(conn, "VDOL-001", category="SEC")
|
||||
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([{"title": "Fix SQL injection", "priority": 2}]),
|
||||
"returncode": 0,
|
||||
}
|
||||
|
||||
result = generate_followups(conn, "VDOL-001")
|
||||
|
||||
assert len(result["created"]) == 1
|
||||
followup = result["created"][0]
|
||||
assert followup["id"] == "VDOL-SEC-001"
|
||||
assert followup["category"] == "SEC"
|
||||
|
||||
@patch("agents.runner._run_claude")
|
||||
def test_generate_followups_multiple_followups_same_category(self, mock_claude, conn):
|
||||
"""Несколько followup задач с одной category получают инкрементальные номера."""
|
||||
models.update_task(conn, "VDOL-001", category="API")
|
||||
|
||||
mock_claude.return_value = {
|
||||
"output": json.dumps([
|
||||
{"title": "Add auth header", "priority": 2},
|
||||
{"title": "Add rate limit", "priority": 3},
|
||||
]),
|
||||
"returncode": 0,
|
||||
}
|
||||
|
||||
result = generate_followups(conn, "VDOL-001")
|
||||
|
||||
assert len(result["created"]) == 2
|
||||
ids = [t["id"] for t in result["created"]]
|
||||
assert ids[0] == "VDOL-API-001"
|
||||
assert ids[1] == "VDOL-API-002"
|
||||
for t in result["created"]:
|
||||
assert t["category"] == "API"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue