| from __future__ import annotations |
|
|
| import logging |
|
|
| import pytest |
|
|
| from dovla_cil.vlm.client import VLMClient, VLMParseError |
|
|
|
|
| def test_mock_mode_returns_deterministic_json(monkeypatch: pytest.MonkeyPatch) -> None: |
| monkeypatch.setenv("OPENCLAUDE_MOCK", "1") |
| monkeypatch.setenv("OPENCLAUDE_MODEL", "mock-model") |
|
|
| client = VLMClient(api_key="test-secret") |
| first = client.chat_json("system", "user", schema_hint={"answer": "string"}) |
| second = client.chat_json("system", "user", schema_hint={"answer": "string"}) |
|
|
| assert first == second |
| assert first["mock"] is True |
| assert first["model"] == "mock-model" |
| assert first["schema_keys"] == ["answer"] |
|
|
|
|
| def test_chat_json_parses_fenced_json() -> None: |
| class StaticTextClient(VLMClient): |
| def chat_text(self, system: str, user: str) -> str: |
| del system, user |
| return '```json\n{"answer": 7, "ok": true}\n```' |
|
|
| client = StaticTextClient(api_key="test-secret", model="model") |
| assert client.chat_json("system", "user") == {"answer": 7, "ok": True} |
|
|
|
|
| def test_chat_json_extracts_first_json_object() -> None: |
| class StaticTextClient(VLMClient): |
| def chat_text(self, system: str, user: str) -> str: |
| del system, user |
| return 'Here is the result: {"answer": {"nested": true}}. Thanks.' |
|
|
| client = StaticTextClient(api_key="test-secret", model="model") |
| assert client.chat_json("system", "user") == {"answer": {"nested": True}} |
|
|
|
|
| def test_parse_error_redacts_secret() -> None: |
| class BadTextClient(VLMClient): |
| def chat_text(self, system: str, user: str) -> str: |
| del system, user |
| return "not json test-secret" |
|
|
| client = BadTextClient(api_key="test-secret", model="model") |
| with pytest.raises(VLMParseError) as exc_info: |
| client.chat_json("system", "user") |
| assert "test-secret" not in str(exc_info.value) |
| assert "***REDACTED***" in str(exc_info.value) |
|
|
|
|
| def test_repr_and_retry_logs_redact_secret( |
| caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch |
| ) -> None: |
| class FailingClient(VLMClient): |
| def _request_once(self, *, messages, response_format): |
| del messages, response_format |
| raise RuntimeError(f"boom {self.api_key}") |
|
|
| monkeypatch.setattr("dovla_cil.vlm.client.time.sleep", lambda _seconds: None) |
| client = FailingClient(api_key="test-secret", model="model", max_retries=1) |
|
|
| assert "test-secret" not in repr(client) |
| with caplog.at_level(logging.WARNING, logger="dovla_cil.vlm.client"): |
| with pytest.raises(Exception) as exc_info: |
| client.chat_text("system", "user") |
|
|
| assert "test-secret" not in caplog.text |
| assert "test-secret" not in str(exc_info.value) |
| assert "***REDACTED***" in caplog.text |
|
|