import os import subprocess from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from typing import Literal import pytest pytestmark = pytest.mark.skipif( not os.environ.get("TIER2_SANDBOX_TESTS"), reason="opt-in: report writer test off by default; set TIER2_SANDBOX_TESTS=1", ) from scripts.tier2.write_report import ( compute_report_path, compute_stopped_flag_path, TaskResult, write_failure_report, ) from scripts.tier2.failcount import FailcountState def test_report_path_under_tier2_failures_dir(monkeypatch, tmp_path) -> None: monkeypatch.setenv("TIER2_FAILURES_DIR", str(tmp_path / "failures")) path = compute_report_path("my_track", datetime(2026, 6, 16, 12, 0, 0, tzinfo=timezone.utc)) assert path.parent == tmp_path / "failures" assert path.name.startswith("my_track_") assert path.name.endswith(".md") def test_stopped_flag_path_under_tier2_failures_dir(monkeypatch, tmp_path) -> None: monkeypatch.setenv("TIER2_FAILURES_DIR", str(tmp_path / "failures")) path = compute_stopped_flag_path("my_track") assert path.parent == tmp_path / "failures" assert path.name == "my_track.STOPPED" def test_report_has_7_sections(monkeypatch, tmp_path) -> None: monkeypatch.setenv("TIER2_FAILURES_DIR", str(tmp_path / "failures")) now = datetime(2026, 6, 16, 12, 0, 0, tzinfo=timezone.utc) started = datetime(2026, 6, 16, 11, 30, 0, tzinfo=timezone.utc) path = write_failure_report( track_name="my_track", branch_name="tier2/my_track", started_at=started, stopped_at=now, give_up_signal="red_phase_failures >= 3", completed_tasks=[ TaskResult(task_id="1.1", phase="Commit", commit_sha="abc1234", summary="did thing 1"), ], current_task=TaskResult( task_id="1.2", phase="Red", commit_sha="", summary="writing test", error="AssertionError: expected 5, got 3", ), last_failures=["AssertionError on test_foo", "ImportError on test_bar"], state=FailcountState(red_phase_failures=3, green_phase_failures=0, no_progress_started_at=started), repo_path=Path("."), ) content = path.read_text(encoding="utf-8") assert "## 1. Header" in content assert "## 2. Tasks Completed" in content assert "## 3. Current Task" in content assert "## 4. Last 3 Failures" in content assert "## 5. Failcount State" in content assert "## 6. Git State" in content assert "## 7. Recommendation" in content def test_stopped_flag_created(monkeypatch, tmp_path) -> None: monkeypatch.setenv("TIER2_FAILURES_DIR", str(tmp_path / "failures")) now = datetime(2026, 6, 16, 12, 0, 0, tzinfo=timezone.utc) started = datetime(2026, 6, 16, 11, 30, 0, tzinfo=timezone.utc) write_failure_report( track_name="my_track", branch_name="tier2/my_track", started_at=started, stopped_at=now, give_up_signal="green_phase_failures >= 3", completed_tasks=[], current_task=TaskResult(task_id="1.1", phase="Green", commit_sha="", summary="", error=""), last_failures=[], state=FailcountState(), repo_path=Path("."), ) flag = compute_stopped_flag_path("my_track") assert flag.exists() def test_recommend_green_phase_stuck() -> None: from scripts.tier2.write_report import _recommend state = FailcountState(red_phase_failures=0, green_phase_failures=3) rec = _recommend(state, None) assert "green-phase" in rec.lower() def test_truncate_long_text() -> None: from scripts.tier2.write_report import _truncate long_text = "\n".join(f"line {i}" for i in range(100)) truncated = _truncate(long_text, max_lines=10) assert "line 0" in truncated assert "line 9" in truncated assert "truncated" in truncated assert "90 more lines" in truncated def test_truncate_short_text_unchanged() -> None: from scripts.tier2.write_report import _truncate text = "short\ntext" assert _truncate(text) == text def test_git_log_fallback_on_subprocess_error(monkeypatch, tmp_path) -> None: from scripts.tier2.write_report import _git_log_for_branch import scripts.tier2.write_report as wr def fake_run(*args, **kwargs): raise FileNotFoundError("no git") monkeypatch.setattr(wr.subprocess, "run", fake_run) result = _git_log_for_branch("any", tmp_path) assert "not available" in result.lower()