Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
191 lines
6.6 KiB
Python
191 lines
6.6 KiB
Python
"""Unit tests for the Summary Reporter module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from scripts.pat_manager.models import CheckResult, PatEntry, PatStatus, RotationResult
|
|
from scripts.pat_manager.reporter import SummaryReporter
|
|
|
|
|
|
def make_entry(service: str = "GitLab", token_name: str = "ci-token") -> PatEntry:
|
|
"""Helper to create a PatEntry for testing."""
|
|
return PatEntry(
|
|
service=service,
|
|
token_name=token_name,
|
|
expiry_date="2025-08-15",
|
|
renewal_method="auto" if service == "GitLab" else "manual",
|
|
)
|
|
|
|
|
|
def make_result(
|
|
service: str = "GitLab",
|
|
token_name: str = "ci-token",
|
|
status: PatStatus = PatStatus.HEALTHY,
|
|
days_remaining: int | None = 45,
|
|
error_message: str | None = None,
|
|
) -> CheckResult:
|
|
"""Helper to create a CheckResult for testing."""
|
|
entry = make_entry(service, token_name)
|
|
return CheckResult(
|
|
entry=entry,
|
|
status=status,
|
|
days_remaining=days_remaining,
|
|
error_message=error_message,
|
|
)
|
|
|
|
|
|
class TestSummaryReporter:
|
|
"""Tests for SummaryReporter.report()."""
|
|
|
|
def test_report_header(self) -> None:
|
|
"""Report starts with the expected header."""
|
|
reporter = SummaryReporter()
|
|
report = reporter.report([], [])
|
|
assert "=== PAT Lifecycle Check Summary ===" in report
|
|
|
|
def test_report_contains_table_headers(self) -> None:
|
|
"""Report contains column headers."""
|
|
reporter = SummaryReporter()
|
|
results = [make_result()]
|
|
report = reporter.report(results, [])
|
|
assert "Service" in report
|
|
assert "Token Name" in report
|
|
assert "Status" in report
|
|
assert "Details" in report
|
|
|
|
def test_report_one_healthy_pat(self) -> None:
|
|
"""Report shows healthy PAT with days remaining."""
|
|
reporter = SummaryReporter()
|
|
results = [make_result(days_remaining=45)]
|
|
report = reporter.report(results, [])
|
|
assert "GitLab" in report
|
|
assert "ci-token" in report
|
|
assert "healthy" in report
|
|
assert "45 days remaining" in report
|
|
|
|
def test_report_expiring_soon_pat(self) -> None:
|
|
"""Report shows expiring soon PAT with days remaining."""
|
|
reporter = SummaryReporter()
|
|
results = [
|
|
make_result(
|
|
service="Jira",
|
|
token_name="jira-api",
|
|
status=PatStatus.EXPIRING_SOON,
|
|
days_remaining=7,
|
|
)
|
|
]
|
|
report = reporter.report(results, [])
|
|
assert "Jira" in report
|
|
assert "jira-api" in report
|
|
assert "expiring soon" in report
|
|
assert "7 days remaining" in report
|
|
|
|
def test_report_expired_pat(self) -> None:
|
|
"""Report shows expired PAT with 0 days remaining."""
|
|
reporter = SummaryReporter()
|
|
results = [
|
|
make_result(
|
|
service="Confluence",
|
|
token_name="confluence-bot",
|
|
status=PatStatus.EXPIRED,
|
|
days_remaining=0,
|
|
)
|
|
]
|
|
report = reporter.report(results, [])
|
|
assert "Confluence" in report
|
|
assert "confluence-bot" in report
|
|
assert "expired" in report
|
|
assert "0 days remaining" in report
|
|
|
|
def test_report_check_failed_pat(self) -> None:
|
|
"""Report shows check failed PAT with error message."""
|
|
reporter = SummaryReporter()
|
|
results = [
|
|
make_result(
|
|
service="OrgMyLife",
|
|
token_name="orchestrator-key",
|
|
status=PatStatus.CHECK_FAILED,
|
|
days_remaining=None,
|
|
error_message="Connection timeout",
|
|
)
|
|
]
|
|
report = reporter.report(results, [])
|
|
assert "OrgMyLife" in report
|
|
assert "orchestrator-key" in report
|
|
assert "check failed" in report
|
|
assert "Connection timeout" in report
|
|
|
|
def test_report_multiple_pats(self) -> None:
|
|
"""Report contains one line per PAT."""
|
|
reporter = SummaryReporter()
|
|
results = [
|
|
make_result(service="GitLab", token_name="token-a", days_remaining=45),
|
|
make_result(
|
|
service="Jira",
|
|
token_name="token-b",
|
|
status=PatStatus.EXPIRING_SOON,
|
|
days_remaining=7,
|
|
),
|
|
make_result(
|
|
service="Confluence",
|
|
token_name="token-c",
|
|
status=PatStatus.EXPIRED,
|
|
days_remaining=0,
|
|
),
|
|
]
|
|
report = reporter.report(results, [])
|
|
assert "token-a" in report
|
|
assert "token-b" in report
|
|
assert "token-c" in report
|
|
|
|
def test_report_no_rotation_section_when_empty(self) -> None:
|
|
"""Report omits rotation section when no rotations occurred."""
|
|
reporter = SummaryReporter()
|
|
results = [make_result()]
|
|
report = reporter.report(results, [])
|
|
assert "Rotation Results" not in report
|
|
|
|
def test_report_includes_rotation_section(self) -> None:
|
|
"""Report includes rotation section with successful rotations."""
|
|
reporter = SummaryReporter()
|
|
entry = make_entry("GitLab", "ci-pipeline-token")
|
|
results = [make_result()]
|
|
rotations = [
|
|
RotationResult(
|
|
success=True,
|
|
new_token="glpat-new-value",
|
|
new_expiry_date="2025-08-01",
|
|
error_message=None,
|
|
entry=entry,
|
|
old_expiry_date="2025-07-01",
|
|
)
|
|
]
|
|
report = reporter.report(results, rotations)
|
|
assert "--- Rotation Results ---" in report
|
|
assert "GitLab/ci-pipeline-token" in report
|
|
assert "Rotated successfully" in report
|
|
assert "old: 2025-07-01" in report
|
|
assert "new: 2025-08-01" in report
|
|
|
|
def test_report_excludes_failed_rotations(self) -> None:
|
|
"""Report rotation section only shows successful rotations."""
|
|
reporter = SummaryReporter()
|
|
results = [make_result()]
|
|
rotations = [
|
|
RotationResult(
|
|
success=False,
|
|
new_token=None,
|
|
new_expiry_date=None,
|
|
error_message="Rotation failed",
|
|
entry=make_entry("GitLab", "failed-token"),
|
|
old_expiry_date="2025-07-01",
|
|
)
|
|
]
|
|
report = reporter.report(results, rotations)
|
|
assert "Rotation Results" not in report
|
|
|
|
def test_report_empty_results(self) -> None:
|
|
"""Report handles empty results list gracefully."""
|
|
reporter = SummaryReporter()
|
|
report = reporter.report([], [])
|
|
assert "=== PAT Lifecycle Check Summary ===" in report
|