from __future__ import annotations import sys import tempfile import textwrap import types import unittest from pathlib import Path from unittest.mock import patch from claridoc.models import ProviderSpec from claridoc.providers.antigravity import AntigravityProvider from claridoc.providers.base import ProviderRequest from claridoc.providers.claude import ClaudeProvider from claridoc.providers.codex import CodexProvider class ProviderAdapterTests(unittest.TestCase): def _script(self, directory: Path, name: str, body: str) -> Path: path = directory / name path.write_text("#!/usr/bin/env python3\n" + textwrap.dedent(body), encoding="utf-8") path.chmod(0o755) return path def test_codex_adapter_reads_output_last_message(self) -> None: with tempfile.TemporaryDirectory() as temp: directory = Path(temp) script = self._script(directory, "fake_codex.py", r''' import json, pathlib, sys prompt = sys.stdin.read() index = sys.argv.index("--output-last-message") pathlib.Path(sys.argv[index + 1]).write_text(json.dumps({"ok": True, "prompt": prompt}), encoding="utf-8") ''') provider = CodexProvider(ProviderSpec(provider="codex", options={"binary": str(script)})) response = provider.generate(ProviderRequest("plan", "hello", directory)) self.assertIn('"ok": true', response.text) self.assertIn("hello", response.text) self.assertIn("--sandbox", response.command) self.assertIn("read-only", response.command) def test_claude_adapter_pipes_prompt(self) -> None: with tempfile.TemporaryDirectory() as temp: directory = Path(temp) script = self._script(directory, "fake_claude.py", r''' import sys data = sys.stdin.read() print("received:" + data) ''') provider = ClaudeProvider(ProviderSpec(provider="claude", options={"binary": str(script)})) response = provider.generate(ProviderRequest("draft", "payload", directory)) self.assertEqual(response.text, "received:payload") self.assertEqual(response.command[1:4], ["-p", "--output-format", "text"]) def test_antigravity_adapter_uses_sdk_contract_and_isolates_cwd(self) -> None: state: dict[str, object] = {} fake_google = types.ModuleType("google") fake_google.__path__ = [] # type: ignore[attr-defined] fake_sdk = types.ModuleType("google.antigravity") class FakeLocalAgentConfig: def __init__(self, **kwargs: object) -> None: state["config"] = kwargs class FakeResponse: async def text(self) -> str: return "sdk-response" class FakeAgent: def __init__(self, config: FakeLocalAgentConfig) -> None: state["agent_config"] = config async def __aenter__(self) -> "FakeAgent": state["cwd"] = str(Path.cwd()) return self async def __aexit__(self, exc_type: object, exc: object, traceback: object) -> None: return None async def chat(self, prompt: str) -> FakeResponse: state["prompt"] = prompt return FakeResponse() fake_sdk.Agent = FakeAgent # type: ignore[attr-defined] fake_sdk.LocalAgentConfig = FakeLocalAgentConfig # type: ignore[attr-defined] original_cwd = Path.cwd() with tempfile.TemporaryDirectory() as temp: directory = Path(temp).resolve() with patch.dict(sys.modules, {"google": fake_google, "google.antigravity": fake_sdk}): provider = AntigravityProvider( ProviderSpec( provider="antigravity", model="model-under-test", options={"config": {"temperature": 0.2}}, ) ) response = provider.generate(ProviderRequest("review", "inspect this", directory)) self.assertEqual(response.text, "sdk-response") self.assertEqual(state["prompt"], "inspect this") self.assertEqual(state["cwd"], str(directory)) self.assertEqual(state["config"], {"temperature": 0.2, "model": "model-under-test"}) self.assertEqual(Path.cwd(), original_cwd) def test_antigravity_doctor_handles_missing_sdk(self) -> None: provider = AntigravityProvider(ProviderSpec(provider="antigravity")) with patch("claridoc.providers.antigravity.importlib.util.find_spec", side_effect=ModuleNotFoundError): result = provider.check() self.assertFalse(result["available"]) if __name__ == "__main__": unittest.main()