Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

170 lines
5.6 KiB
Python

"""
Coverage tests for local_deep_research/utilities/llm_utils.py.
Focuses on paths not exercised by existing test files:
- _close_base_llm: close() delegation, Ollama httpx client closing,
non-Ollama _client skip, missing _client attr, missing httpx client
- fetch_ollama_models: verifies safe_get (not requests.get) is used
"""
from unittest.mock import Mock, patch
from local_deep_research.utilities.llm_utils import (
_close_base_llm,
fetch_ollama_models,
)
# ---------------------------------------------------------------------------
# _close_base_llm
# ---------------------------------------------------------------------------
class TestCloseBaseLlmDelegation:
"""When the LLM object has its own close() method defined on the type."""
def test_delegates_to_close_method(self):
"""Should call llm.close() when the type defines close."""
class ClosableLLM:
def close(self):
pass
llm = ClosableLLM()
llm.close = Mock()
_close_base_llm(llm)
llm.close.assert_called_once()
def test_does_not_inspect_client_after_close(self):
"""After calling close(), should return immediately without
inspecting _client."""
class ClosableLLM:
def close(self):
pass
llm = ClosableLLM()
llm.close = Mock()
llm._client = Mock()
_close_base_llm(llm)
llm.close.assert_called_once()
llm._client.close.assert_not_called()
class TestCloseBaseLlmOllamaClient:
"""When the LLM has an Ollama-module _client with an httpx _client."""
def test_closes_httpx_client_on_ollama(self):
"""Should close the nested httpx client on an Ollama _client."""
httpx_client = Mock()
httpx_client.close = Mock()
ollama_client = Mock()
ollama_client._client = httpx_client
type(ollama_client).__module__ = "ollama._client"
llm = Mock(spec=[])
llm._client = ollama_client
_close_base_llm(llm)
httpx_client.close.assert_called_once()
def test_skips_non_ollama_client_module(self):
"""Should NOT close _client._client when module is not ollama."""
httpx_client = Mock()
httpx_client.close = Mock()
non_ollama_client = Mock()
non_ollama_client._client = httpx_client
type(non_ollama_client).__module__ = "openai._http_client"
llm = Mock(spec=[])
llm._client = non_ollama_client
_close_base_llm(llm)
httpx_client.close.assert_not_called()
def test_no_client_attr_is_noop(self):
"""Should do nothing when llm has no _client attribute."""
llm = Mock(spec=[])
assert (
not hasattr(llm, "_client") or getattr(llm, "_client", None) is None
)
_close_base_llm(llm)
def test_ollama_client_without_httpx_client(self):
"""Should handle Ollama _client that has no nested _client (httpx)."""
ollama_client = Mock(spec=[])
type(ollama_client).__module__ = "ollama._client"
llm = Mock(spec=[])
llm._client = ollama_client
_close_base_llm(llm)
def test_ollama_httpx_client_without_close(self):
"""Should handle Ollama httpx _client that lacks close()."""
httpx_client = Mock(spec=[])
ollama_client = Mock()
ollama_client._client = httpx_client
type(ollama_client).__module__ = "ollama.core"
llm = Mock(spec=[])
llm._client = ollama_client
_close_base_llm(llm)
# ---------------------------------------------------------------------------
# fetch_ollama_models - verifies safe_get usage
# ---------------------------------------------------------------------------
class TestFetchOllamaModelsUsesSafeGet:
"""Verify that fetch_ollama_models calls safe_get, not requests.get."""
def test_calls_safe_get_not_requests_get(self):
"""safe_get should be called; requests.get should NOT."""
mock_safe_get = Mock()
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"models": [{"name": "llama3"}]}
mock_safe_get.return_value = mock_response
with patch(
"local_deep_research.utilities.llm_utils.safe_get",
mock_safe_get,
create=True,
):
with patch("local_deep_research.security.safe_get", mock_safe_get):
result = fetch_ollama_models("http://localhost:11434")
mock_safe_get.assert_called()
call_args = mock_safe_get.call_args
assert "localhost:11434/api/tags" in call_args.args[0]
assert call_args.kwargs["allow_localhost"] is True
assert call_args.kwargs["allow_private_ips"] is True
assert len(result) == 1
assert result[0]["value"] == "llama3"
def test_safe_get_exception_returns_empty_list(self):
"""When safe_get raises, should return [] gracefully."""
with patch(
"local_deep_research.security.safe_get",
side_effect=RuntimeError("SSRF blocked"),
):
result = fetch_ollama_models("http://evil.com:11434")
assert result == []
def test_safe_get_non_200_returns_empty(self):
"""Non-200 response from safe_get should yield empty list."""
mock_response = Mock()
mock_response.status_code = 503
with patch(
"local_deep_research.security.safe_get",
return_value=mock_response,
):
result = fetch_ollama_models("http://localhost:11434")
assert result == []