项目文件夹

文件
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

223 行
7.8 KiB
Python

"""
Coverage tests for local_deep_research/llm/providers/auto_discovery.py
Focuses on paths not covered by tests/llm_providers/test_auto_discovery.py:
- ProviderInfo.__init__ with and without optional class attributes
- ProviderInfo._generate_display_name() for cloud=True, cloud=False
- ProviderInfo.to_dict() shape
- ProviderDiscovery singleton behaviour
- ProviderDiscovery.get_provider_info() triggers discover_providers if not done
- ProviderDiscovery.get_provider_class() returns None for unknown key
- ProviderDiscovery.get_provider_options() sorts by label
- get_available_discovered_provider_options() filters unavailable providers
- Module-level convenience functions delegate correctly
"""
from unittest.mock import MagicMock
from local_deep_research.llm.providers.auto_discovery import (
ProviderInfo,
ProviderDiscovery,
discover_providers,
get_discovered_provider_options,
get_available_discovered_provider_options,
get_provider_class,
)
# ---------------------------------------------------------------------------
# Minimal provider class stubs
# ---------------------------------------------------------------------------
def _make_provider_class(
provider_name="TestProv",
provider_key="TESTPROV",
company_name=None,
is_cloud=True,
requires_auth=True,
):
cls = MagicMock()
cls.__name__ = f"{provider_name}Provider"
cls.provider_name = provider_name
cls.provider_key = provider_key
if company_name is not None:
cls.company_name = company_name
else:
del cls.company_name # force AttributeError → getattr default
cls.is_cloud = is_cloud
cls.requires_auth_for_models = MagicMock(return_value=requires_auth)
cls.is_available = MagicMock(return_value=True)
return cls
# ---------------------------------------------------------------------------
# ProviderInfo
# ---------------------------------------------------------------------------
class TestProviderInfo:
def test_basic_attributes_set(self):
cls = _make_provider_class(
provider_name="Alpha", provider_key="ALPHA", is_cloud=True
)
info = ProviderInfo(cls)
assert info.provider_name == "Alpha"
assert info.provider_key == "ALPHA"
assert info.is_cloud is True
def test_display_name_cloud(self):
cls = _make_provider_class(is_cloud=True)
info = ProviderInfo(cls)
assert "Cloud" in info.display_name or "☁️" in info.display_name
def test_display_name_local(self):
cls = _make_provider_class(is_cloud=False)
info = ProviderInfo(cls)
assert "Local" in info.display_name or "💻" in info.display_name
def test_to_dict_shape(self):
cls = _make_provider_class(
provider_name="Beta", provider_key="BETA", is_cloud=True
)
info = ProviderInfo(cls)
d = info.to_dict()
assert "value" in d
assert "label" in d
assert "is_cloud" in d
assert d["value"] == "BETA"
def test_provider_key_falls_back_to_class_name(self):
"""When provider_key is not an attribute, falls back to class name."""
cls = MagicMock(spec=[])
cls.provider_name = "Gamma"
cls.__name__ = "GammaProvider"
cls.is_cloud = True
cls.requires_auth_for_models = MagicMock(return_value=True)
info = ProviderInfo(cls)
assert info.provider_key # must be some non-empty string
# ---------------------------------------------------------------------------
# ProviderDiscovery singleton
# ---------------------------------------------------------------------------
class TestProviderDiscoverySingleton:
def test_same_instance_returned(self):
d1 = ProviderDiscovery()
d2 = ProviderDiscovery()
assert d1 is d2
# ---------------------------------------------------------------------------
# get_provider_info
# ---------------------------------------------------------------------------
class TestGetProviderInfo:
def test_unknown_key_returns_none(self):
discovery = ProviderDiscovery()
# Force providers to be discovered first
discovery.discover_providers()
result = discovery.get_provider_info("NONEXISTENT_PROVIDER_XYZ")
assert result is None
def test_triggers_discover_if_not_done(self):
"""get_provider_info() discovers providers if not already done."""
discovery = ProviderDiscovery()
discovery._discovered = False
# Just verifying it runs without error
result = discovery.get_provider_info("NONEXISTENT_XYZ")
assert result is None # not found, but no crash
# ---------------------------------------------------------------------------
# get_provider_class
# ---------------------------------------------------------------------------
class TestGetProviderClass:
def test_unknown_key_returns_none(self):
result = get_provider_class("TOTALLY_NONEXISTENT_KEY_ZZZ")
assert result is None
# ---------------------------------------------------------------------------
# get_provider_options sorting
# ---------------------------------------------------------------------------
class TestGetProviderOptions:
def test_options_sorted_by_label(self):
discovery = ProviderDiscovery()
# Inject two fake providers with known labels
cls_z = _make_provider_class(provider_name="Zebra", provider_key="Z")
cls_a = _make_provider_class(provider_name="Apple", provider_key="A")
info_z = ProviderInfo(cls_z)
info_a = ProviderInfo(cls_a)
original = dict(discovery._providers)
discovery._providers = {"Z": info_z, "A": info_a}
discovery._discovered = True
try:
options = discovery.get_provider_options()
labels = [o["label"] for o in options]
assert labels == sorted(labels)
finally:
discovery._providers = original
# ---------------------------------------------------------------------------
# get_available_provider_options – filters unavailable
# ---------------------------------------------------------------------------
class TestGetAvailableProviderOptions:
def test_unavailable_providers_filtered_out(self):
discovery = ProviderDiscovery()
cls_ok = _make_provider_class(provider_name="OK", provider_key="OK")
cls_ok.is_available = MagicMock(return_value=True)
cls_bad = _make_provider_class(provider_name="BAD", provider_key="BAD")
cls_bad.is_available = MagicMock(return_value=False)
info_ok = ProviderInfo(cls_ok)
info_ok.provider_class = cls_ok
info_bad = ProviderInfo(cls_bad)
info_bad.provider_class = cls_bad
original = dict(discovery._providers)
original_disc = discovery._discovered
discovery._providers = {"OK": info_ok, "BAD": info_bad}
discovery._discovered = True
try:
options = discovery.get_available_provider_options()
values = [o["value"] for o in options]
assert "OK" in values
assert "BAD" not in values
finally:
discovery._providers = original
discovery._discovered = original_disc
# ---------------------------------------------------------------------------
# Module-level convenience functions
# ---------------------------------------------------------------------------
class TestModuleLevelFunctions:
def test_discover_providers_returns_dict(self):
result = discover_providers()
assert isinstance(result, dict)
def test_get_discovered_provider_options_returns_list(self):
result = get_discovered_provider_options()
assert isinstance(result, list)
def test_get_available_discovered_provider_options_returns_list(self):
result = get_available_discovered_provider_options()
assert isinstance(result, list)