"""Ścieżka agentowa (LlmBrain) przepuszczona przez atrapę serwera OpenAI. Tryb offline sprawdza reguły; ten test sprawdza to, czego reguły nie dotykają: budowę agentów z definicji APM, wywoływanie sandboxowanych narzędzi przez model i parsowanie strukturalnych wyjść. Bez GPU, bez sieci, tak samo na Linuksie i macOS. """ from __future__ import annotations import threading import pytest from mock_server import serve from agentic_codemod.schemas import ChangeRequest, TaskType from agentic_codemod.workflow.runner import run_pipeline @pytest.fixture() def mock_llm(): httpd = serve(host="127.0.0.1", port=0) thread = threading.Thread(target=httpd.serve_forever, daemon=True) thread.start() host, port = httpd.server_address[:2] try: yield f"http://{host}:{port}/v1" finally: httpd.shutdown() thread.join(timeout=5) @pytest.fixture() def llm_settings(settings, mock_llm): settings.provider = "openai_like" settings.base_url = mock_llm settings.api_key = "mock" for role in ("planner", "coder", "reviewer", "scribe"): setattr(settings.models, role, "mock/agentic-codemod") return settings @pytest.fixture() def request_upgrade(acme_app): return ChangeRequest( task_type=TaskType.SDK_UPGRADE, repo_path=str(acme_app), package="acme-sdk", module_name="acme", from_version="1.4.2", to_version="2.1.0", ) def test_przebieg_z_agentami_konczy_sie_zielona_weryfikacja(request_upgrade, llm_settings, acme_app): manifest = run_pipeline(request_upgrade, settings=llm_settings, mode="llm", in_place=True) assert manifest.status == "success", manifest.errors assert manifest.mode == "llm" assert manifest.verifications and manifest.verifications[-1].passed assert set(manifest.changed_files) == {"pyproject.toml", "src/acme_app/notifier.py"} migrated = (acme_app / "src/acme_app/notifier.py").read_text(encoding="utf-8") assert "AcmeClient(api_key=api_key, base_url=endpoint)" in migrated assert "client.send(" not in migrated def test_strukturalne_wyjscia_agentow_sa_parsowane(request_upgrade, llm_settings): manifest = run_pipeline(request_upgrade, settings=llm_settings, mode="llm", in_place=True) assert manifest.profile and manifest.profile.module_name == "acme" assert manifest.plan and manifest.plan.actionable_edits[0].path == "src/acme_app/notifier.py" assert manifest.review and manifest.review.verdict == "approve" assert manifest.merge_request and manifest.merge_request.title.startswith("build(deps):") def test_agent_uzywa_sandboxowanych_narzedzi(request_upgrade, llm_settings): manifest = run_pipeline(request_upgrade, settings=llm_settings, mode="llm", in_place=True) used = {entry["tool"] for entry in manifest.tool_calls} assert {"read_file", "replace_in_file", "run_verification"} <= used