This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/loop/test_loop_guard_validate.py

164 lines
4.9 KiB
Python
Raw Normal View History

"""Tests for cycle_result.json validation in loop_guard.
Covers validate_cycle_result(), _load_cycle_result(), and _is_issue_open().
"""
from __future__ import annotations
import json
import time
from pathlib import Path
from unittest.mock import patch
import pytest
import scripts.loop_guard as lg
@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
"""Redirect loop_guard paths to tmp_path for isolation."""
monkeypatch.setattr(lg, "CYCLE_RESULT_FILE", tmp_path / "cycle_result.json")
monkeypatch.setattr(lg, "CYCLE_DURATION", 300)
monkeypatch.setattr(lg, "GITEA_API", "http://test:3000/api/v1")
monkeypatch.setattr(lg, "REPO_SLUG", "owner/repo")
def _write_cr(tmp_path, data: dict, age_seconds: float = 0) -> Path:
"""Write a cycle_result.json and optionally backdate it."""
p = tmp_path / "cycle_result.json"
p.write_text(json.dumps(data))
if age_seconds:
mtime = time.time() - age_seconds
import os
os.utime(p, (mtime, mtime))
return p
# --- _load_cycle_result ---
def test_load_cycle_result_missing(tmp_path):
assert lg._load_cycle_result() == {}
def test_load_cycle_result_valid(tmp_path):
_write_cr(tmp_path, {"issue": 42, "type": "fix"})
assert lg._load_cycle_result() == {"issue": 42, "type": "fix"}
def test_load_cycle_result_markdown_fenced(tmp_path):
p = tmp_path / "cycle_result.json"
p.write_text('```json\n{"issue": 99}\n```')
assert lg._load_cycle_result() == {"issue": 99}
def test_load_cycle_result_malformed(tmp_path):
p = tmp_path / "cycle_result.json"
p.write_text("not json at all")
assert lg._load_cycle_result() == {}
# --- _is_issue_open ---
def test_is_issue_open_true(monkeypatch):
monkeypatch.setattr(lg, "_get_token", lambda: "tok")
resp_data = json.dumps({"state": "open"}).encode()
class FakeResp:
def read(self):
return resp_data
def __enter__(self):
return self
def __exit__(self, *a):
pass
with patch("urllib.request.urlopen", return_value=FakeResp()):
assert lg._is_issue_open(42) is True
def test_is_issue_open_closed(monkeypatch):
monkeypatch.setattr(lg, "_get_token", lambda: "tok")
resp_data = json.dumps({"state": "closed"}).encode()
class FakeResp:
def read(self):
return resp_data
def __enter__(self):
return self
def __exit__(self, *a):
pass
with patch("urllib.request.urlopen", return_value=FakeResp()):
assert lg._is_issue_open(42) is False
def test_is_issue_open_no_token(monkeypatch):
monkeypatch.setattr(lg, "_get_token", lambda: "")
assert lg._is_issue_open(42) is None
def test_is_issue_open_api_error(monkeypatch):
monkeypatch.setattr(lg, "_get_token", lambda: "tok")
with patch("urllib.request.urlopen", side_effect=OSError("timeout")):
assert lg._is_issue_open(42) is None
# --- validate_cycle_result ---
def test_validate_no_file(tmp_path):
"""No file → returns False, no crash."""
assert lg.validate_cycle_result() is False
def test_validate_fresh_file_open_issue(tmp_path, monkeypatch):
"""Fresh file with open issue → kept."""
_write_cr(tmp_path, {"issue": 10})
monkeypatch.setattr(lg, "_is_issue_open", lambda n: True)
assert lg.validate_cycle_result() is False
assert (tmp_path / "cycle_result.json").exists()
def test_validate_stale_file_removed(tmp_path):
"""File older than 2× CYCLE_DURATION → removed."""
_write_cr(tmp_path, {"issue": 10}, age_seconds=700)
assert lg.validate_cycle_result() is True
assert not (tmp_path / "cycle_result.json").exists()
def test_validate_fresh_file_closed_issue(tmp_path, monkeypatch):
"""Fresh file referencing closed issue → removed."""
_write_cr(tmp_path, {"issue": 10})
monkeypatch.setattr(lg, "_is_issue_open", lambda n: False)
assert lg.validate_cycle_result() is True
assert not (tmp_path / "cycle_result.json").exists()
def test_validate_api_failure_keeps_file(tmp_path, monkeypatch):
"""API failure → file kept (graceful degradation)."""
_write_cr(tmp_path, {"issue": 10})
monkeypatch.setattr(lg, "_is_issue_open", lambda n: None)
assert lg.validate_cycle_result() is False
assert (tmp_path / "cycle_result.json").exists()
def test_validate_no_issue_field(tmp_path):
"""File without issue field → kept (only age check applies)."""
_write_cr(tmp_path, {"type": "fix"})
assert lg.validate_cycle_result() is False
assert (tmp_path / "cycle_result.json").exists()
def test_validate_stale_threshold_boundary(tmp_path, monkeypatch):
"""File just under threshold → kept (not stale yet)."""
_write_cr(tmp_path, {"issue": 10}, age_seconds=599)
monkeypatch.setattr(lg, "_is_issue_open", lambda n: True)
assert lg.validate_cycle_result() is False
assert (tmp_path / "cycle_result.json").exists()