kin/tests/test_arch_002.py

121 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Regression tests for KIN-ARCH-002.
Проблема: функция create_project_with_phases имела нестабильную сигнатуру —
параметр path с дефолтом на позиции 4, после чего шли обязательные параметры
(description, selected_roles), что могло приводить к SyntaxError при инвалидации
.pyc-кеша в Python 3.14+.
Фикс: параметры path переносится после обязательных ИЛИ изолируется через *
(keyword-only) — текущий код использует * для description/selected_roles.
Тесты покрывают:
1. Вызов с path как позиционным аргументом (текущая конвенция в тестах)
2. Вызов с path=... как keyword-аргументом (безопасная конвенция)
3. Вызов без path=None (дефолт работает)
4. Нет SyntaxError при импорте core.phases (regression guard)
5. Стабильность числа тестов: полный suite запускается без collection errors
"""
import pytest
from core.db import init_db
from core import models
from core.phases import create_project_with_phases
@pytest.fixture
def conn():
c = init_db(db_path=":memory:")
yield c
c.close()
# ---------------------------------------------------------------------------
# KIN-ARCH-002 — regression: signature stability of create_project_with_phases
# ---------------------------------------------------------------------------
def test_arch_002_import_core_phases_no_syntax_error():
"""KIN-ARCH-002: импорт core.phases не вызывает SyntaxError."""
import core.phases # noqa: F401 — если упадёт SyntaxError, тест падает
def test_arch_002_path_as_positional_arg(conn):
"""KIN-ARCH-002: path передаётся как позиционный аргумент (4-я позиция).
Текущая конвенция во всех тестах и в web/api.py.
Регрессионная защита: изменение сигнатуры не должно сломать этот вызов.
"""
result = create_project_with_phases(
conn, "arch002a", "Project A", "/some/path",
description="Описание A", selected_roles=["business_analyst"],
)
assert result["project"]["id"] == "arch002a"
assert len(result["phases"]) == 2 # business_analyst + architect
def test_arch_002_path_as_keyword_arg(conn):
"""KIN-ARCH-002: path передаётся как keyword-аргумент.
Рекомендуемая конвенция по итогам debugger-расследования.
Гарантирует, что будущий рефакторинг сигнатуры не сломает код.
"""
result = create_project_with_phases(
conn, "arch002b", "Project B",
description="Описание B",
selected_roles=["tech_researcher"],
path="/keyword/path",
)
assert result["project"]["id"] == "arch002b"
assert result["project"]["path"] == "/keyword/path"
def test_arch_002_path_none_without_operations_raises(conn):
"""KIN-ARCH-002: path=None для non-operations проекта → IntegrityError из БД (CHECK constraint)."""
import sqlite3
with pytest.raises(sqlite3.IntegrityError, match="CHECK constraint"):
create_project_with_phases(
conn, "arch002fail", "Fail",
description="D",
selected_roles=["marketer"],
path=None,
)
def test_arch_002_phases_count_is_deterministic(conn):
"""KIN-ARCH-002: при каждом вызове создаётся ровно N+1 фаз (N researchers + architect)."""
for idx, (roles, expected_count) in enumerate([
(["business_analyst"], 2),
(["business_analyst", "tech_researcher"], 3),
(["business_analyst", "market_researcher", "legal_researcher"], 4),
]):
project_id = f"arch002_det_{idx}"
result = create_project_with_phases(
conn, project_id, f"Project {len(roles)}",
description="Det test",
selected_roles=roles,
path=f"/tmp/det/{idx}",
)
assert len(result["phases"]) == expected_count, (
f"roles={roles}: ожидали {expected_count} фаз, "
f"получили {len(result['phases'])}"
)
def test_arch_002_first_phase_active_regardless_of_call_convention(conn):
"""KIN-ARCH-002: первая фаза всегда active независимо от способа передачи path."""
# Positional convention
r1 = create_project_with_phases(
conn, "p_pos", "P pos", "/pos",
description="D", selected_roles=["business_analyst"],
)
assert r1["phases"][0]["status"] == "active"
assert r1["phases"][0]["task_id"] is not None
# Keyword convention
r2 = create_project_with_phases(
conn, "p_kw", "P kw",
description="D", selected_roles=["business_analyst"], path="/kw",
)
assert r2["phases"][0]["status"] == "active"
assert r2["phases"][0]["task_id"] is not None