Files
Timmy-time-dashboard/tests/loop/test_cycle_retro.py
Timmy Time 5b569af383
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled
[loop-cycle] fix: consume cycle_result.json after reading (#897) (#898)
2026-03-22 01:38:07 +00:00

136 lines
4.8 KiB
Python

"""Tests for scripts/cycle_retro.py issue auto-detection."""
from __future__ import annotations
# Import the module under test — it's a script so we import the helpers directly
import importlib
import subprocess
from pathlib import Path
from unittest.mock import patch
import pytest
SCRIPTS_DIR = Path(__file__).resolve().parent.parent.parent / "scripts"
@pytest.fixture(autouse=True)
def _add_scripts_to_path(monkeypatch):
monkeypatch.syspath_prepend(str(SCRIPTS_DIR))
@pytest.fixture()
def mod():
"""Import cycle_retro as a module."""
return importlib.import_module("cycle_retro")
class TestDetectIssueFromBranch:
def test_kimi_issue_branch(self, mod):
with patch.object(subprocess, "check_output", return_value="kimi/issue-492\n"):
assert mod.detect_issue_from_branch() == 492
def test_plain_issue_branch(self, mod):
with patch.object(subprocess, "check_output", return_value="issue-123\n"):
assert mod.detect_issue_from_branch() == 123
def test_issue_slash_number(self, mod):
with patch.object(subprocess, "check_output", return_value="fix/issue/55\n"):
assert mod.detect_issue_from_branch() == 55
def test_no_issue_in_branch(self, mod):
with patch.object(subprocess, "check_output", return_value="main\n"):
assert mod.detect_issue_from_branch() is None
def test_feature_branch(self, mod):
with patch.object(subprocess, "check_output", return_value="feature/add-widget\n"):
assert mod.detect_issue_from_branch() is None
def test_git_not_available(self, mod):
with patch.object(subprocess, "check_output", side_effect=FileNotFoundError):
assert mod.detect_issue_from_branch() is None
def test_git_fails(self, mod):
with patch.object(
subprocess,
"check_output",
side_effect=subprocess.CalledProcessError(1, "git"),
):
assert mod.detect_issue_from_branch() is None
class TestConsumeOnce:
"""cycle_result.json must be deleted after reading."""
def test_cycle_result_deleted_after_read(self, mod, tmp_path):
"""After _load_cycle_result() data is consumed in main(), the file is deleted."""
result_file = tmp_path / "cycle_result.json"
result_file.write_text('{"issue": 42, "type": "bug"}')
with (
patch.object(mod, "CYCLE_RESULT_FILE", result_file),
patch.object(mod, "RETRO_FILE", tmp_path / "retro" / "cycles.jsonl"),
patch.object(mod, "SUMMARY_FILE", tmp_path / "retro" / "summary.json"),
patch.object(mod, "EPOCH_COUNTER_FILE", tmp_path / "retro" / ".epoch_counter"),
patch(
"sys.argv",
["cycle_retro", "--cycle", "1", "--success", "--main-green", "--duration", "60"],
),
):
mod.main()
assert not result_file.exists(), "cycle_result.json should be deleted after consumption"
def test_cycle_result_not_deleted_when_empty(self, mod, tmp_path):
"""If cycle_result.json doesn't exist, no error occurs."""
result_file = tmp_path / "nonexistent_result.json"
with (
patch.object(mod, "CYCLE_RESULT_FILE", result_file),
patch.object(mod, "RETRO_FILE", tmp_path / "retro" / "cycles.jsonl"),
patch.object(mod, "SUMMARY_FILE", tmp_path / "retro" / "summary.json"),
patch.object(mod, "EPOCH_COUNTER_FILE", tmp_path / "retro" / ".epoch_counter"),
patch(
"sys.argv",
[
"cycle_retro",
"--cycle",
"1",
"--success",
"--main-green",
"--duration",
"60",
"--issue",
"10",
],
),
):
mod.main() # Should not raise
class TestBackfillExtractIssueNumber:
"""Tests for backfill_retro.extract_issue_number PR-number filtering."""
@pytest.fixture()
def backfill(self):
return importlib.import_module("backfill_retro")
def test_body_has_issue(self, backfill):
assert backfill.extract_issue_number("fix: foo (#491)", "Fixes #490", pr_number=491) == 490
def test_title_skips_pr_number(self, backfill):
assert backfill.extract_issue_number("fix: foo (#491)", "", pr_number=491) is None
def test_title_with_issue_and_pr(self, backfill):
# [loop-cycle-538] refactor: ... (#459) (#481)
assert (
backfill.extract_issue_number(
"[loop-cycle-538] refactor: remove dead airllm (#459) (#481)",
"",
pr_number=481,
)
== 459
)
def test_no_pr_number_provided(self, backfill):
assert backfill.extract_issue_number("fix: foo (#491)", "") == 491