baton/tests/test_arch_004.py

177 lines
8 KiB
Python
Raw Normal View History

"""
Tests for BATON-ARCH-004: Переименование ADR-002-offline-pattern.md.
Acceptance criteria:
1. No file named ADR-002-offline-pattern*.md exists in docs/adr/.
2. No references to 'ADR-002-offline-pattern' anywhere in docs/ and ARCHITECTURE.md.
3. No dangling bare 'ADR-002' references in docs/, ARCHITECTURE.md, or tests/.
4. ADR-007-offline-queue-v2.md exists in docs/adr/.
5. tech_report.md references ADR-007 (not ADR-002).
6. ADR-006 references ADR-007 (not ADR-002).
7. ARCHITECTURE.md references ADR-007 (not ADR-002) for offline-related rows.
"""
from __future__ import annotations
import re
from pathlib import Path
PROJECT_ROOT = Path(__file__).parent.parent
ADR_DIR = PROJECT_ROOT / "docs" / "adr"
ARCHITECTURE_MD = PROJECT_ROOT / "ARCHITECTURE.md"
TECH_REPORT_MD = PROJECT_ROOT / "docs" / "tech_report.md"
ADR_006 = ADR_DIR / "ADR-006-offline-ios-constraints.md"
ADR_007 = ADR_DIR / "ADR-007-offline-queue-v2.md"
# ---------------------------------------------------------------------------
# Criterion 1 — old ADR-002-offline-pattern file must not exist
# ---------------------------------------------------------------------------
# Criterion 1 superseded by BATON-ARCH-014: ADR-002-offline-pattern.md now exists
# as a legitimate new ADR document.
# ---------------------------------------------------------------------------
# Criterion 2 — no stale 'ADR-002-offline-pattern' textual references in docs/
# ---------------------------------------------------------------------------
def _all_md_in_docs() -> list[Path]:
return list((PROJECT_ROOT / "docs").rglob("*.md"))
def test_no_adr_002_offline_pattern_in_docs() -> None:
"""Ни один файл в docs/ не должен содержать строку 'ADR-002-offline-pattern'."""
for path in _all_md_in_docs():
content = path.read_text(encoding="utf-8")
assert "ADR-002-offline-pattern" not in content, (
f"Найдена устаревшая ссылка 'ADR-002-offline-pattern' в {path.relative_to(PROJECT_ROOT)}"
)
# test_no_adr_002_offline_pattern_in_architecture_md superseded by BATON-ARCH-014:
# ARCHITECTURE.md now legitimately links to ADR-002-offline-pattern.md.
# test_no_bare_adr_002_in_docs superseded: ADR-002-offline-pattern.md is a valid new ADR.
# test_no_bare_adr_002_in_architecture_md superseded: [ADR-002] is now a valid table row.
# ---------------------------------------------------------------------------
# Criterion 3 — no dangling bare ADR-002 references in test files
# ---------------------------------------------------------------------------
def test_no_bare_adr_002_in_tests() -> None:
"""Файлы тестов (кроме легитимных исключений) не должны содержать голую метку 'ADR-002'."""
pattern = re.compile(r"\bADR-002\b")
# Легитимные исключения: файлы, документирующие задачи, которые явно работают с ADR-002.
_ALLOWED = {
Path(__file__).resolve(), # test_arch_004.py: задача по переименованию
(PROJECT_ROOT / "tests" / "test_arch_014.py").resolve(), # задача по созданию ADR-002
}
for path in (PROJECT_ROOT / "tests").glob("*.py"):
if path.resolve() in _ALLOWED:
continue
content = path.read_text(encoding="utf-8")
assert not pattern.search(content), (
f"Найдена висячая ссылка 'ADR-002' в {path.relative_to(PROJECT_ROOT)}"
)
# ---------------------------------------------------------------------------
# Criterion 4 — ADR-007-offline-queue-v2.md exists
# ---------------------------------------------------------------------------
def test_adr_007_offline_queue_file_exists() -> None:
"""Файл ADR-007-offline-queue-v2.md должен существовать в docs/adr/."""
assert ADR_007.is_file(), (
f"Переименованный файл ADR-007-offline-queue-v2.md не найден в {ADR_DIR}"
)
# ---------------------------------------------------------------------------
# Criterion 5 — tech_report.md references ADR-007
# ---------------------------------------------------------------------------
def test_tech_report_references_adr_007() -> None:
"""docs/tech_report.md должен содержать ссылку на ADR-007."""
content = TECH_REPORT_MD.read_text(encoding="utf-8")
assert "ADR-007" in content, (
"tech_report.md не ссылается на ADR-007 (переименованный offline-pattern)"
)
# ---------------------------------------------------------------------------
# Criterion 6 — ADR-006 references ADR-007 (not ADR-002)
# ---------------------------------------------------------------------------
def test_adr_006_references_adr_007() -> None:
"""ADR-006-offline-ios-constraints.md должен ссылаться на ADR-007."""
content = ADR_006.read_text(encoding="utf-8")
assert "ADR-007" in content, (
"ADR-006 не содержит ссылки на ADR-007"
)
def test_adr_006_has_no_adr_002_references() -> None:
"""ADR-006-offline-ios-constraints.md не должен ссылаться на ADR-002."""
content = ADR_006.read_text(encoding="utf-8")
assert not re.search(r"\bADR-002\b", content), (
"ADR-006 всё ещё содержит ссылку 'ADR-002'"
)
# ---------------------------------------------------------------------------
# Criterion 7 — ARCHITECTURE.md references ADR-007 for offline rows
# ---------------------------------------------------------------------------
def test_architecture_md_references_adr_007_for_service_worker() -> None:
"""ARCHITECTURE.md должен ссылаться на ADR-007 в строке Service Worker."""
content = ARCHITECTURE_MD.read_text(encoding="utf-8")
sw_line = next(
(line for line in content.splitlines() if "Service Worker" in line), None
)
assert sw_line is not None, "Строка 'Service Worker' не найдена в ARCHITECTURE.md"
assert "ADR-007" in sw_line, (
f"Строка Service Worker в ARCHITECTURE.md не содержит ADR-007: {sw_line!r}"
)
def test_architecture_md_references_adr_007_for_offline() -> None:
"""ARCHITECTURE.md должен ссылаться на ADR-007 в строке Offline."""
content = ARCHITECTURE_MD.read_text(encoding="utf-8")
offline_line = next(
(line for line in content.splitlines() if line.startswith("| Offline")), None
)
assert offline_line is not None, "Строка '| Offline' не найдена в ARCHITECTURE.md"
assert "ADR-007" in offline_line, (
f"Строка Offline в ARCHITECTURE.md не содержит ADR-007: {offline_line!r}"
)
# ---------------------------------------------------------------------------
# Criterion 8 — ADR-007 строка, помечающая #1001 устаревшим, содержит ACTION:
# ---------------------------------------------------------------------------
def test_adr_007_stale_reference_has_action_item() -> None:
"""Строка ADR-007, ссылающаяся на решение #1001, должна содержать маркер ACTION:.
Конвенция #1049: все ссылки на устаревшие решения обязаны быть оформлены как
явный ACTION item, а не как пассивная заметка.
"""
content = ADR_007.read_text(encoding="utf-8")
lines_with_1001 = [line for line in content.splitlines() if "#1001" in line]
assert lines_with_1001, (
"ADR-007 не содержит ни одной строки со ссылкой на решение #1001"
)
has_action = any(re.search(r"ACTION:", line) for line in lines_with_1001)
assert has_action, (
"Строка, помечающая решение #1001 устаревшим, не содержит явного маркера ACTION: "
"— нарушение конвенции #1049"
)