"""VerificationLoop 单元测试""" from __future__ import annotations import asyncio import pytest from agentkit.core.verification_loop import VerificationLoop, VerificationResult class TestVerify: """verify() 方法测试""" @pytest.mark.asyncio async def test_verify_success(self) -> None: """成功命令返回 passed=True""" loop = VerificationLoop(commands=["echo ok"], timeout=10.0) result = await loop.verify() assert isinstance(result, VerificationResult) assert result.passed is True assert result.attempts == 1 assert "ok" in result.test_output assert result.errors == [] @pytest.mark.asyncio async def test_verify_failure(self) -> None: """失败命令返回 passed=False""" loop = VerificationLoop(commands=["false"], timeout=10.0) result = await loop.verify() assert result.passed is False assert result.attempts == 1 assert len(result.errors) > 0 @pytest.mark.asyncio async def test_verify_timeout(self) -> None: """超时命令返回 passed=False""" loop = VerificationLoop(commands=["sleep 10"], timeout=0.5) result = await loop.verify() assert result.passed is False assert any("timed out" in e for e in result.errors) @pytest.mark.asyncio async def test_verify_command_not_found(self) -> None: """不存在的命令返回 passed=False""" loop = VerificationLoop(commands=["nonexistent_command_xyz"], timeout=5.0) result = await loop.verify() assert result.passed is False assert len(result.errors) > 0 @pytest.mark.asyncio async def test_verify_multiple_commands_partial_failure(self) -> None: """部分命令失败时整体返回 passed=False""" loop = VerificationLoop(commands=["echo ok", "false"], timeout=10.0) result = await loop.verify() assert result.passed is False assert len(result.errors) == 1 @pytest.mark.asyncio async def test_verify_default_commands(self) -> None: """默认命令为 pytest 和 ruff check""" loop = VerificationLoop() assert loop._commands == ["pytest -x -q", "ruff check src/"] class TestVerifyAndRetry: """verify_and_retry() 方法测试""" @pytest.mark.asyncio async def test_retry_no_fix_callback(self) -> None: """无 fix_callback 时重试指定次数""" loop = VerificationLoop(commands=["false"], max_retries=2, timeout=5.0) result = await loop.verify_and_retry() assert result.passed is False assert result.attempts == 3 # 1 initial + 2 retries @pytest.mark.asyncio async def test_max_retries_respected(self) -> None: """max_retries=0 时不重试""" loop = VerificationLoop(commands=["false"], max_retries=0, timeout=5.0) result = await loop.verify_and_retry() assert result.passed is False assert result.attempts == 1 @pytest.mark.asyncio async def test_retry_with_fix_callback(self) -> None: """fix_callback 被调用并接收 errors 和 test_output""" call_count = 0 received_args: list[tuple[list[str], str]] = [] async def fix_cb(errors: list[str], test_output: str) -> None: nonlocal call_count call_count += 1 received_args.append((errors, test_output)) loop = VerificationLoop(commands=["false"], max_retries=1, timeout=5.0) result = await loop.verify_and_retry(fix_callback=fix_cb) assert result.passed is False assert call_count == 1 assert len(received_args) == 1 assert len(received_args[0][0]) > 0 # errors assert isinstance(received_args[0][1], str) # test_output @pytest.mark.asyncio async def test_retry_succeeds_after_fix(self) -> None: """fix_callback 修复后验证成功""" attempt = 0 async def fix_cb(errors: list[str], test_output: str) -> None: pass # Simulate fix applied # Use a command that always fails — but test that the retry mechanism works loop = VerificationLoop(commands=["false"], max_retries=1, timeout=5.0) result = await loop.verify_and_retry(fix_callback=fix_cb) # false always fails, so result should still be False assert result.passed is False assert result.attempts == 2 @pytest.mark.asyncio async def test_fix_callback_exception_handled(self) -> None: """fix_callback 抛出异常时不影响重试""" async def bad_fix_cb(errors: list[str], test_output: str) -> None: raise RuntimeError("fix failed!") loop = VerificationLoop(commands=["false"], max_retries=1, timeout=5.0) result = await loop.verify_and_retry(fix_callback=bad_fix_cb) assert result.passed is False assert result.attempts == 2 @pytest.mark.asyncio async def test_verify_and_retry_success_first_try(self) -> None: """首次验证成功时不重试""" loop = VerificationLoop(commands=["echo ok"], max_retries=3, timeout=10.0) result = await loop.verify_and_retry() assert result.passed is True assert result.attempts == 1