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

213 lines
7.2 KiB
Python

"""
Behavioral tests for config/llm_config module.
Tests availability checks with snapshots, provider validation,
model/provider name cleaning, fallback model, and provider selection.
"""
import pytest
class TestApiKeyAvailabilityChecks:
"""Tests for is_*_available() functions with settings snapshots.
These functions check bool(api_key) from the snapshot — the core
logic is: present and non-empty → True, else → False.
"""
def test_openai_available_with_api_key(self):
from local_deep_research.llm.providers.implementations.openai import (
OpenAIProvider,
)
result = OpenAIProvider.is_available(
settings_snapshot={"llm.openai.api_key": "sk-test"}
)
assert result is True
def test_openai_unavailable_without_api_key(self):
from local_deep_research.llm.providers.implementations.openai import (
OpenAIProvider,
)
assert (
OpenAIProvider.is_available(
settings_snapshot={"search.tool": "searxng"}
)
is False
)
def test_openai_unavailable_with_empty_api_key(self):
"""Empty string API key is falsy — treated as missing."""
from local_deep_research.llm.providers.implementations.openai import (
OpenAIProvider,
)
result = OpenAIProvider.is_available(
settings_snapshot={"llm.openai.api_key": ""}
)
assert result is False
def test_anthropic_available_with_api_key(self):
from local_deep_research.llm.providers.implementations.anthropic import (
AnthropicProvider,
)
result = AnthropicProvider.is_available(
settings_snapshot={"llm.anthropic.api_key": "sk-ant-test"}
)
assert result is True
def test_anthropic_unavailable_without_api_key(self):
from local_deep_research.llm.providers.implementations.anthropic import (
AnthropicProvider,
)
assert (
AnthropicProvider.is_available(
settings_snapshot={"search.tool": "searxng"}
)
is False
)
def test_openai_endpoint_available_with_api_key(self):
from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
CustomOpenAIEndpointProvider,
)
result = CustomOpenAIEndpointProvider.is_available(
settings_snapshot={"llm.openai_endpoint.api_key": "key123"}
)
assert result is True
def test_openai_endpoint_unavailable_without_api_key(self):
from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
CustomOpenAIEndpointProvider,
)
assert (
CustomOpenAIEndpointProvider.is_available(
settings_snapshot={"search.tool": "searxng"}
)
is False
)
class TestProviderValidation:
"""Tests for provider validation in get_llm()."""
def test_invalid_provider_raises_value_error(self):
"""get_llm raises ValueError for unknown provider name."""
from local_deep_research.config.llm_config import get_llm
with pytest.raises(ValueError, match="Invalid provider"):
get_llm(
provider="nonexistent_provider",
settings_snapshot={"search.tool": "searxng"},
)
def test_error_message_lists_valid_providers(self):
"""ValueError message enumerates the auto-discovered providers.
The valid set is derived from auto-discovery (no hardcoded
VALID_PROVIDERS), so the error lists every discovered provider.
"""
from local_deep_research.config.llm_config import get_llm
from local_deep_research.llm.providers import (
get_discovered_provider_options,
)
from local_deep_research.llm.providers.base import normalize_provider
with pytest.raises(ValueError) as exc_info:
get_llm(
provider="bad_provider",
settings_snapshot={"search.tool": "searxng"},
)
message = str(exc_info.value)
for option in get_discovered_provider_options():
assert normalize_provider(option["value"]) in message
class TestModelAndProviderNameCleaning:
"""Tests for model_name and provider name cleaning in get_llm().
get_llm strips quotes, whitespace, and lowercases the provider name
before validation.
"""
def test_quoted_provider_name_cleaned(self):
"""Surrounding quotes are stripped: \"'none'\" → 'none' (valid but unimplemented)."""
import pytest
from local_deep_research.config.llm_config import get_llm
# 'none' is valid but has no implementation, so it raises ValueError
# The point is the quotes are stripped before validation
with pytest.raises(ValueError, match="No LLM provider configured"):
get_llm(
model_name="x",
provider="'none'",
settings_snapshot={"search.tool": "searxng"},
)
def test_whitespace_provider_name_cleaned(self):
"""Surrounding whitespace is stripped: ' none ' → 'none'."""
import pytest
from local_deep_research.config.llm_config import get_llm
with pytest.raises(ValueError, match="No LLM provider configured"):
get_llm(
model_name="x",
provider=" none ",
settings_snapshot={"search.tool": "searxng"},
)
def test_uppercase_provider_lowercased(self):
"""Provider name is lowercased: 'NONE' → 'none'."""
import pytest
from local_deep_research.config.llm_config import get_llm
with pytest.raises(ValueError, match="No LLM provider configured"):
get_llm(
model_name="x",
provider="NONE",
settings_snapshot={"search.tool": "searxng"},
)
def test_combined_quotes_whitespace_case_cleaning(self):
"""Combined cleaning: \" 'None' \" → 'none'."""
import pytest
from local_deep_research.config.llm_config import get_llm
with pytest.raises(ValueError, match="No LLM provider configured"):
get_llm(
model_name="x",
provider=" 'None' ",
settings_snapshot={"search.tool": "searxng"},
)
class TestGetSelectedLlmProvider:
"""Tests for get_selected_llm_provider() function."""
def test_returns_lowercase(self):
"""Provider value from snapshot is lowercased."""
from local_deep_research.config.llm_config import (
get_selected_llm_provider,
)
result = get_selected_llm_provider(
settings_snapshot={"llm.provider": "OpenAI"}
)
assert result == "openai"
def test_default_is_ollama(self):
"""Default provider is 'ollama' when not specified in snapshot."""
from local_deep_research.config.llm_config import (
get_selected_llm_provider,
)
result = get_selected_llm_provider(
settings_snapshot={"search.tool": "searxng"}
)
assert result == "ollama"