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", [])
|
results = auto_resolve_pending_actions(conn, "VDOL-001", [])
|
||||||
assert results == []
|
assert results == []
|
||||||
mock_claude.assert_not_called()
|
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