kin/tests/test_kin_docs_002_regression.py
2026-03-19 19:06:18 +02:00

149 lines
6.9 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-DOCS-002 — Standardise all agent prompts.
Acceptance criteria:
1. pytest green (checked by running this suite)
2. No file in agents/prompts/ contains the old '## Output format' section header
3. Every prompt file contains '## Return Format'
4. Every prompt file contains the full standard structure:
## Working Mode, ## Focus On, ## Quality Checks, ## Return Format, ## Constraints
"""
from pathlib import Path
import pytest
PROMPTS_DIR = Path(__file__).parent.parent / "agents" / "prompts"
# Single source of truth for required sections (decision #920)
REQUIRED_SECTIONS = [
"## Working Mode",
"## Focus On",
"## Quality Checks",
"## Return Format",
"## Constraints",
]
# Files excluded from standard-structure checks (decision #917/#918)
# All 9 previously excluded files now contain all 5 required sections — list is empty.
# Guard-тест TestExclusionListIsEmpty (decision #929) не даст тихо добавить файлы обратно.
EXCLUDED_FROM_STRUCTURE_CHECK = []
def _prompt_files():
return sorted(PROMPTS_DIR.glob("*.md"))
def _active_prompt_files():
"""Prompt files not in the exclusion list (decision #918)."""
return [f for f in _prompt_files() if f.name not in EXCLUDED_FROM_STRUCTURE_CHECK]
_ACTIVE_PROMPT_NAMES = [f.name for f in _active_prompt_files()]
# ---------------------------------------------------------------------------
# AC-2: No legacy '## Output format' section
# ---------------------------------------------------------------------------
class TestNoLegacyOutputFormatSection:
"""Проверяет отсутствие устаревшей секции '## Output format' во всех промптах."""
def test_no_prompt_contains_old_output_format_header(self):
"""Ни один файл agents/prompts/*.md не содержит '## Output format'."""
files_with_old_header = [
f.name
for f in _prompt_files()
if "## Output format" in f.read_text(encoding="utf-8")
]
assert files_with_old_header == [], (
f"Файлы с устаревшей секцией '## Output format': {files_with_old_header}"
)
# ---------------------------------------------------------------------------
# AC-3: Every prompt contains '## Return Format'
# ---------------------------------------------------------------------------
class TestAllPromptsContainReturnFormat:
"""Проверяет наличие секции '## Return Format' во всех промптах."""
def test_return_format_count_equals_prompt_count(self):
"""Число промптов с '## Return Format' равно общему числу промптов."""
all_files = _prompt_files()
files_with_rf = [f for f in all_files if "## Return Format" in f.read_text(encoding="utf-8")]
assert len(files_with_rf) == len(all_files), (
f"Промптов всего: {len(all_files)}, "
f"с '## Return Format': {len(files_with_rf)}. "
f"Без секции: {[f.name for f in all_files if f not in files_with_rf]}"
)
@pytest.mark.parametrize("prompt_file", _ACTIVE_PROMPT_NAMES)
def test_each_prompt_has_return_format(self, prompt_file):
"""Каждый промпт-файл содержит секцию '## Return Format'."""
content = (PROMPTS_DIR / prompt_file).read_text(encoding="utf-8")
assert "## Return Format" in content, (
f"{prompt_file} не содержит секцию '## Return Format'"
)
# ---------------------------------------------------------------------------
# AC-4: Full standard structure in every active prompt (decision #917-#920)
# ---------------------------------------------------------------------------
class TestAllPromptsContainStandardStructure:
"""Проверяет наличие полного набора обязательных секций во всех активных промптах.
Requirements:
1. Каждый активный промпт содержит все 5 секций из REQUIRED_SECTIONS в правильном порядке
"""
@pytest.mark.parametrize("section", REQUIRED_SECTIONS)
@pytest.mark.parametrize("prompt_file", _ACTIVE_PROMPT_NAMES)
def test_prompt_has_required_section(self, section, prompt_file):
"""Каждый активный промпт содержит каждую из 5 обязательных секций."""
content = (PROMPTS_DIR / prompt_file).read_text(encoding="utf-8")
assert section in content, (
f"{prompt_file!r} не содержит обязательную секцию {section!r}"
)
# ---------------------------------------------------------------------------
# Sanity: prompt count stays at 25
# ---------------------------------------------------------------------------
class TestPromptCount:
"""Проверяет, что число промптов не изменилось неожиданно."""
def test_prompt_count_is_26(self):
"""В agents/prompts/ ровно 26 файлов .md."""
count = len(_prompt_files())
assert count == 26, ( # 26 промптов — актуально на 2026-03-19, +knowledge_synthesizer (KIN-DOCS-003, см. git log agents/prompts/)
f"Ожидалось 26 промптов, найдено {count}. "
"Если добавлен новый промпт — обнови этот тест."
)
# ---------------------------------------------------------------------------
# Guard: exclusion list must stay empty (decision #929)
# ---------------------------------------------------------------------------
class TestExclusionListIsEmpty:
"""Регрессионный guard против молчаливого роста EXCLUDED_FROM_STRUCTURE_CHECK.
Если нужно добавить файл обратно в exclusion — этот тест заставит явно
обосновать причину и обновить его (decision #929).
"""
def test_exclusion_list_is_empty(self):
"""EXCLUDED_FROM_STRUCTURE_CHECK должен оставаться пустым.
Все 9 ранее excluded файлов содержат все 5 стандартных секций.
Добавление файла в exclusion лишает его регрессионной защиты (decision #921).
Чтобы добавить файл — сначала обоснуй причину и обнови этот тест.
"""
assert EXCLUDED_FROM_STRUCTURE_CHECK == [], (
f"EXCLUDED_FROM_STRUCTURE_CHECK должен быть пустым, "
f"но содержит: {EXCLUDED_FROM_STRUCTURE_CHECK}. "
"Добавление файла в exclusion лишает его регрессионной защиты (decision #921)."
)