"""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