"""RH-01~07: Retry Handler — 分层重试 + heal/simple 分离""" import sys, os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) from hina.retry import RetryHandler, HEALING_FIXES from data.diff_result import VerificationRun def _vr(status="PASS", build_log=""): vr = VerificationRun(status=status, program="TEST") if build_log: vr.debug = {"cobol_build": {"log": build_log}} return vr def test_immediate_pass(): """RH-01: 1次 PASS → heal=0, simple=0""" h = RetryHandler() vr = h.run(lambda: _vr("PASS")) assert vr.status == "PASS" assert vr.heal_retry == 0 assert vr.simple_retry == 0 def test_heal_recovery(): """RH-02: BLOCKED(not found) → heal修复→PASS""" calls = [0] def fn(): calls[0] += 1 if calls[0] == 1: return _vr("BLOCKED", build_log="file not found: libcob.so") return _vr("PASS") h = RetryHandler() vr = h.run(fn) assert vr.status == "PASS" assert vr.heal_retry >= 1 assert vr.simple_retry == 0 def test_simple_retry(): """RH-03: BLOCKED→重试→PASS (无 heal 匹配)""" calls = [0] def fn(): calls[0] += 1 if calls[0] == 1: return _vr("BLOCKED", build_log="some random error") return _vr("PASS") h = RetryHandler() vr = h.run(fn) assert vr.status == "PASS" assert vr.simple_retry >= 1 def test_max_retries_exceeded(): """RH-04: 全部失败 → FATAL""" h = RetryHandler(max_heal=1, max_simple=1) vr = h.run(lambda: _vr("BLOCKED")) assert vr.status == "FATAL" assert vr.exit_code == 4 def test_quality_warn_no_retry(): """RH-05: QUALITY_WARN → 立即返回 不重试""" h = RetryHandler() vr = h.run(lambda: _vr("QUALITY_WARN")) assert vr.status == "QUALITY_WARN" assert vr.heal_retry == 0 assert vr.simple_retry == 0 def test_heal_fails_then_simple(): """RH-06: heal 尝试但仍然 BLOCKED → 回退 simple""" calls = [0] def fn(): calls[0] += 1 return _vr("BLOCKED", build_log="file not found: libcob.so") h = RetryHandler(max_heal=2, max_simple=2) vr = h.run(fn) assert vr.status == "FATAL" # 应已消耗所有 heal+simple assert vr.heal_retry + vr.simple_retry >= 1 def test_concurrent_count_separation(): """RH-07: heal 和 simple 计数互不影响""" h = RetryHandler(max_heal=2, max_simple=2) calls = [0, False] # [count, callable flag] def fn(): calls[0] += 1 if calls[0] == 1: return _vr("BLOCKED", build_log="file not found: libcob.so") return _vr("PASS") h._try_set_env = lambda k, v: None # no-op fix # Mock fix to succeed on first heal original_fix = HEALING_FIXES["compile_error"]["fix"] HEALING_FIXES["compile_error"]["fix"] = lambda: None try: vr = h.run(fn) assert vr.heal_retry >= 0 assert vr.simple_retry >= 0 # heal 和 simple 的计数不会混淆 finally: HEALING_FIXES["compile_error"]["fix"] = original_fix def test_history_records(): """所有 VR 被记录到 history""" h = RetryHandler(max_heal=0, max_simple=2) results = [] def fn(): vr = _vr("BLOCKED") if len(results) < 2 else _vr("PASS") results.append(vr) return vr h.run(fn) assert len(h.history) >= 2