kin: KIN-OBS-009 Task ID по категориям: PROJ-CAT-NUM (VDOL-SEC-001, VDOL-UI-003, VDOL-API-002, VDOL-INFRA-001, VDOL-BIZ-001). PM назначает категорию при создании задачи.
This commit is contained in:
parent
d50bd703ae
commit
81f974e6d3
3 changed files with 142 additions and 3 deletions
|
|
@ -1,8 +1,10 @@
|
|||
"""Tests for core/models.py — all functions, in-memory SQLite."""
|
||||
|
||||
import re
|
||||
import pytest
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
from core.models import TASK_CATEGORIES
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -330,3 +332,126 @@ def test_add_decision_if_new_skips_whitespace_duplicate(conn):
|
|||
result = models.add_decision_if_new(conn, "p1", "convention", " Run tests after each change ", "desc2")
|
||||
assert result is None
|
||||
assert len(models.get_decisions(conn, "p1")) == 1
|
||||
|
||||
|
||||
# -- next_task_id (KIN-OBS-009) --
|
||||
|
||||
def test_next_task_id_with_category_first(conn):
|
||||
"""Первая задача с category='SEC' → 'VDOL-SEC-001'."""
|
||||
models.create_project(conn, "vdol", "VDOL", "/vdol")
|
||||
task_id = models.next_task_id(conn, "vdol", category="SEC")
|
||||
assert task_id == "VDOL-SEC-001"
|
||||
|
||||
|
||||
def test_next_task_id_with_category_increments(conn):
|
||||
"""Вторая задача с category='SEC' → 'VDOL-SEC-002'."""
|
||||
models.create_project(conn, "vdol", "VDOL", "/vdol")
|
||||
models.create_task(conn, "VDOL-SEC-001", "vdol", "Task 1", category="SEC")
|
||||
task_id = models.next_task_id(conn, "vdol", category="SEC")
|
||||
assert task_id == "VDOL-SEC-002"
|
||||
|
||||
|
||||
def test_next_task_id_category_counters_independent(conn):
|
||||
"""Счётчики категорий независимы: SEC-002 не влияет на UI-001."""
|
||||
models.create_project(conn, "vdol", "VDOL", "/vdol")
|
||||
models.create_task(conn, "VDOL-SEC-001", "vdol", "Sec Task 1", category="SEC")
|
||||
models.create_task(conn, "VDOL-SEC-002", "vdol", "Sec Task 2", category="SEC")
|
||||
task_id = models.next_task_id(conn, "vdol", category="UI")
|
||||
assert task_id == "VDOL-UI-001"
|
||||
|
||||
|
||||
def test_next_task_id_without_category_backward_compat(conn):
|
||||
"""Задача без category → 'VDOL-001' (backward compat)."""
|
||||
models.create_project(conn, "vdol", "VDOL", "/vdol")
|
||||
task_id = models.next_task_id(conn, "vdol")
|
||||
assert task_id == "VDOL-001"
|
||||
|
||||
|
||||
def test_next_task_id_mixed_formats_no_collision(conn):
|
||||
"""Смешанный проект: счётчики старого и нового форматов не пересекаются."""
|
||||
models.create_project(conn, "kin", "KIN", "/kin")
|
||||
models.create_task(conn, "KIN-001", "kin", "Old style task")
|
||||
models.create_task(conn, "KIN-002", "kin", "Old style task 2")
|
||||
# Новый формат с категорией не мешает старому
|
||||
cat_id = models.next_task_id(conn, "kin", category="OBS")
|
||||
assert cat_id == "KIN-OBS-001"
|
||||
# Старый формат не мешает новому
|
||||
old_id = models.next_task_id(conn, "kin")
|
||||
assert old_id == "KIN-003"
|
||||
|
||||
|
||||
# -- Obsidian sync regex (KIN-OBS-009, решение #75) --
|
||||
|
||||
_OBSIDIAN_TASK_PATTERN = re.compile(
|
||||
r"^[-*]\s+\[([xX ])\]\s+([A-Z][A-Z0-9]*-(?:[A-Z][A-Z0-9]*-)?\d+)\s+(.+)$"
|
||||
)
|
||||
|
||||
|
||||
def test_obsidian_regex_matches_old_format():
|
||||
"""Старый формат KIN-001 матчится."""
|
||||
m = _OBSIDIAN_TASK_PATTERN.match("- [x] KIN-001 Fix login bug")
|
||||
assert m is not None
|
||||
assert m.group(2) == "KIN-001"
|
||||
|
||||
|
||||
def test_obsidian_regex_matches_new_format():
|
||||
"""Новый формат VDOL-SEC-001 матчится."""
|
||||
m = _OBSIDIAN_TASK_PATTERN.match("- [ ] VDOL-SEC-001 Security audit")
|
||||
assert m is not None
|
||||
assert m.group(2) == "VDOL-SEC-001"
|
||||
|
||||
|
||||
def test_obsidian_regex_matches_obs_format():
|
||||
"""Формат KIN-OBS-009 матчится (проверяем задачу этой фичи)."""
|
||||
m = _OBSIDIAN_TASK_PATTERN.match("* [X] KIN-OBS-009 Task ID по категориям")
|
||||
assert m is not None
|
||||
assert m.group(2) == "KIN-OBS-009"
|
||||
|
||||
|
||||
def test_obsidian_regex_no_match_lowercase():
|
||||
"""Нижний регистр не матчится."""
|
||||
assert _OBSIDIAN_TASK_PATTERN.match("- [x] proj-001 lowercase id") is None
|
||||
|
||||
|
||||
def test_obsidian_regex_no_match_numeric_prefix():
|
||||
"""Числовой префикс не матчится."""
|
||||
assert _OBSIDIAN_TASK_PATTERN.match("- [x] 123-abc invalid format") is None
|
||||
|
||||
|
||||
def test_obsidian_regex_done_state(conn):
|
||||
"""Статус done/pending корректно извлекается."""
|
||||
m_done = _OBSIDIAN_TASK_PATTERN.match("- [x] KIN-UI-003 Done task")
|
||||
m_pending = _OBSIDIAN_TASK_PATTERN.match("- [ ] KIN-UI-004 Pending task")
|
||||
assert m_done.group(1) == "x"
|
||||
assert m_pending.group(1) == " "
|
||||
|
||||
|
||||
# -- next_task_id для всех 12 категорий (KIN-OBS-009) --
|
||||
|
||||
@pytest.mark.parametrize("cat", TASK_CATEGORIES)
|
||||
def test_next_task_id_all_categories_generate_correct_format(conn, cat):
|
||||
"""next_task_id генерирует ID формата PROJ-CAT-001 для каждой из 12 категорий."""
|
||||
models.create_project(conn, "vdol", "VDOL", "/vdol")
|
||||
task_id = models.next_task_id(conn, "vdol", category=cat)
|
||||
assert task_id == f"VDOL-{cat}-001"
|
||||
|
||||
|
||||
# -- update_task category не ломает brief (KIN-OBS-009, решение #74) --
|
||||
|
||||
def test_update_task_category_preserves_brief(conn):
|
||||
"""update_task(category=...) не перетирает существующее поле brief."""
|
||||
models.create_project(conn, "p1", "P1", "/p1")
|
||||
models.create_task(conn, "P1-001", "p1", "Task", brief={"summary": "important context"})
|
||||
updated = models.update_task(conn, "P1-001", category="SEC")
|
||||
assert updated["category"] == "SEC"
|
||||
assert updated["brief"] == {"summary": "important context"}
|
||||
|
||||
|
||||
def test_update_task_category_preserves_status_and_priority(conn):
|
||||
"""update_task(category=...) не меняет остальные поля задачи."""
|
||||
models.create_project(conn, "p1", "P1", "/p1")
|
||||
models.create_task(conn, "P1-001", "p1", "Task", status="in_progress", priority=3)
|
||||
updated = models.update_task(conn, "P1-001", category="UI")
|
||||
assert updated["category"] == "UI"
|
||||
assert updated["status"] == "in_progress"
|
||||
assert updated["priority"] == 3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue