Files
headquarter/tests/docs/test_architecture.py
T
Fusion 61c5554c42
CI / API CI (push) Failing after 35s
CI / Web CI (push) Failing after 2m11s
feat(FN-019): draft enhanced architecture.md with all 18 required sections (+4 more)
Commits merged:
- docs(FN-019): complete Step 6 — documentation index, conversation handoff, project brief, and build config
- test(FN-019): complete Step 4 — doc validation tests, project-brief.md, and fix mvp-scope placeholders
- feat(FN-019): complete Step 3 — draft mvp-scope.md with milestones, dependency order, and open questions
- docs(FN-019): fix auth callback flow, Python syntax, dev bypass clarity, add AccessProvider protocol and type stubs
- feat(FN-019): complete Step 2 — draft enhanced architecture.md with all 18 required sections

Files changed:
docs/README.md                  |   11 +-
 docs/architecture.md            | 1185 ++++++++++++++++++++++++++++++++++-----
 docs/conversation-handoff.md    |   68 +++
 docs/mvp-scope.md               |  184 ++++++
 docs/project-brief.md           |   31 +
 package.json                    |    3 +
 tests/docs/__init__.py          |    0
 tests/docs/test_architecture.py |  120 ++++
 8 files changed, 1444 insertions(+), 158 deletions(-)

Fusion-Task-Id: FN-019
2026-05-14 10:56:20 +02:00

121 lines
4.8 KiB
Python

"""Automated validation suite for documentation structural and content requirements."""
import re
from pathlib import Path
import pytest
DOCS_DIR = Path(__file__).resolve().parent.parent.parent / "docs"
ROOT_DIR = Path(__file__).resolve().parent.parent.parent
class TestArchitectureDoc:
def test_file_exists_and_is_non_empty(self) -> None:
path = DOCS_DIR / "architecture.md"
assert path.exists(), "docs/architecture.md must exist"
assert path.stat().st_size > 0, "docs/architecture.md must be non-empty"
def test_contains_required_section_headers(self) -> None:
path = DOCS_DIR / "architecture.md"
content = path.read_text()
required_sections = [
"## 1. Overview",
"## 2. System Context",
"## 3. Component Boundaries",
"## 4. PostgreSQL Domain Model",
"## 5. Git Provider Abstraction",
"## 6. Repository Credential Model",
"## 7. Tool Manifest Model",
"## 8. Tool Spawn Lifecycle",
"## 9. Docker Runtime Abstraction",
"## 10. Traefik Subdomain Routing Model",
"## 11. Storage Layout",
"## 12. Authentik OIDC Auth Flow",
"## 13. Security Considerations",
"## 14. Extension Points",
"## 15. Environment Assumptions",
"## 16. Technology Boundaries",
"## 17. Acceptance Criteria for Architecture Compliance",
"## 18. Deferred Decisions",
]
missing = [s for s in required_sections if s not in content]
assert not missing, f"Missing required sections: {missing}"
def test_contains_required_references(self) -> None:
path = DOCS_DIR / "architecture.md"
content = path.read_text()
required_refs = [
"GitProvider",
"RuntimeProvider",
"AccessProvider",
"ToolManifest",
"Authentik",
"Traefik",
"Portainer",
"PostgreSQL",
]
missing = [r for r in required_refs if r not in content]
assert not missing, f"Missing required references: {missing}"
class TestMvpScopeDoc:
def test_file_exists_and_is_non_empty(self) -> None:
path = DOCS_DIR / "mvp-scope.md"
assert path.exists(), "docs/mvp-scope.md must exist"
assert path.stat().st_size > 0, "docs/mvp-scope.md must be non-empty"
def test_contains_required_sections(self) -> None:
path = DOCS_DIR / "mvp-scope.md"
content = path.read_text()
required_sections = [
"## 1. Product Vision",
"## 2. MVP User Journeys",
"## 3. In-Scope",
"## 4. Out-of-Scope",
"## 5. MVP Milestones",
"## 6. Dependency Order",
"## 7. Definition of MVP Done",
"## 8. Open Questions",
]
missing = [s for s in required_sections if s not in content]
assert not missing, f"Missing required sections: {missing}"
class TestAllDocs:
def test_no_todo_or_fixme_in_docs(self) -> None:
markdown_files = list(DOCS_DIR.rglob("*.md"))
assert markdown_files, "No markdown files found in docs/"
violations = []
for path in markdown_files:
content = path.read_text()
if re.search(r"\bTODO\b", content, re.IGNORECASE):
violations.append(f"{path.name} contains TODO")
if re.search(r"\bFIXME\b", content, re.IGNORECASE):
violations.append(f"{path.name} contains FIXME")
assert not violations, f"Documentation contains placeholders: {violations}"
def test_internal_links_point_to_existing_files(self) -> None:
markdown_files = list(DOCS_DIR.rglob("*.md"))
link_pattern = re.compile(r"\]\(([^)]+)\)")
violations = []
for path in markdown_files:
content = path.read_text()
for match in link_pattern.finditer(content):
link = match.group(1)
# Skip external URLs and anchors
if link.startswith("http") or link.startswith("#") or link.startswith("mailto:"):
continue
# Resolve relative to the docs directory or repo root
if link.startswith("docs/"):
target = ROOT_DIR / link
elif link.startswith("../"):
target = path.parent / link
elif link.startswith("./"):
target = path.parent / link
else:
# Assume relative to docs dir for bare paths like "architecture.md"
target = DOCS_DIR / link
if not target.exists():
violations.append(f"{path.name}: broken link to '{link}'")
assert not violations, f"Broken internal links found: {violations}"