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
369 lines
11 KiB
Python
369 lines
11 KiB
Python
"""
|
|
Tests for llm/llm_registry.py
|
|
|
|
Tests cover:
|
|
- LLMRegistry class methods
|
|
- Global registry functions
|
|
- Thread safety
|
|
- Case-insensitive registration
|
|
"""
|
|
|
|
import threading
|
|
from unittest.mock import Mock
|
|
|
|
|
|
class TestLLMRegistryInit:
|
|
"""Tests for LLMRegistry initialization."""
|
|
|
|
def test_init_creates_empty_registry(self):
|
|
"""Test that initialization creates empty registry."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
assert registry.list_registered() == []
|
|
|
|
def test_init_creates_lock(self):
|
|
"""Test that initialization creates threading lock."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
assert registry._lock is not None
|
|
assert isinstance(registry._lock, type(threading.Lock()))
|
|
|
|
|
|
class TestLLMRegistryRegister:
|
|
"""Tests for LLMRegistry.register method."""
|
|
|
|
def test_register_llm_instance(self):
|
|
"""Test registering an LLM instance."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("test-llm", mock_llm)
|
|
|
|
assert registry.is_registered("test-llm")
|
|
assert registry.get("test-llm") is mock_llm
|
|
|
|
def test_register_llm_factory(self):
|
|
"""Test registering an LLM factory function."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
factory = Mock(return_value=Mock())
|
|
|
|
registry.register("test-factory", factory)
|
|
|
|
assert registry.is_registered("test-factory")
|
|
assert registry.get("test-factory") is factory
|
|
|
|
def test_register_normalizes_name_to_lowercase(self):
|
|
"""Test that registration normalizes names to lowercase."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("Test-LLM", mock_llm)
|
|
|
|
assert registry.is_registered("test-llm")
|
|
assert registry.is_registered("TEST-LLM")
|
|
assert registry.is_registered("Test-LLM")
|
|
|
|
def test_register_overwrites_existing(self):
|
|
"""Test that registering overwrites existing LLM with same name."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm1 = Mock(name="llm1")
|
|
mock_llm2 = Mock(name="llm2")
|
|
|
|
registry.register("test", mock_llm1)
|
|
registry.register("test", mock_llm2)
|
|
|
|
assert registry.get("test") is mock_llm2
|
|
|
|
|
|
class TestLLMRegistryUnregister:
|
|
"""Tests for LLMRegistry.unregister method."""
|
|
|
|
def test_unregister_removes_llm(self):
|
|
"""Test that unregister removes the LLM."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("test", mock_llm)
|
|
assert registry.is_registered("test")
|
|
|
|
registry.unregister("test")
|
|
assert not registry.is_registered("test")
|
|
|
|
def test_unregister_nonexistent_does_nothing(self):
|
|
"""Test that unregistering non-existent LLM doesn't raise."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
# Should not raise
|
|
registry.unregister("nonexistent")
|
|
|
|
def test_unregister_is_case_insensitive(self):
|
|
"""Test that unregister is case-insensitive."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("Test-LLM", mock_llm)
|
|
registry.unregister("TEST-LLM")
|
|
|
|
assert not registry.is_registered("test-llm")
|
|
|
|
|
|
class TestLLMRegistryGet:
|
|
"""Tests for LLMRegistry.get method."""
|
|
|
|
def test_get_returns_registered_llm(self):
|
|
"""Test that get returns the registered LLM."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("test", mock_llm)
|
|
|
|
assert registry.get("test") is mock_llm
|
|
|
|
def test_get_returns_none_for_nonexistent(self):
|
|
"""Test that get returns None for non-existent LLM."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
assert registry.get("nonexistent") is None
|
|
|
|
def test_get_is_case_insensitive(self):
|
|
"""Test that get is case-insensitive."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("Test-LLM", mock_llm)
|
|
|
|
assert registry.get("test-llm") is mock_llm
|
|
assert registry.get("TEST-LLM") is mock_llm
|
|
|
|
|
|
class TestLLMRegistryIsRegistered:
|
|
"""Tests for LLMRegistry.is_registered method."""
|
|
|
|
def test_is_registered_returns_true_for_registered(self):
|
|
"""Test that is_registered returns True for registered LLM."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
|
|
registry.register("test", mock_llm)
|
|
|
|
assert registry.is_registered("test") is True
|
|
|
|
def test_is_registered_returns_false_for_nonexistent(self):
|
|
"""Test that is_registered returns False for non-existent LLM."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
assert registry.is_registered("nonexistent") is False
|
|
|
|
|
|
class TestLLMRegistryListRegistered:
|
|
"""Tests for LLMRegistry.list_registered method."""
|
|
|
|
def test_list_registered_returns_empty_for_new_registry(self):
|
|
"""Test that list_registered returns empty list for new registry."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
assert registry.list_registered() == []
|
|
|
|
def test_list_registered_returns_all_names(self):
|
|
"""Test that list_registered returns all registered names."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
registry.register("llm1", Mock())
|
|
registry.register("llm2", Mock())
|
|
registry.register("llm3", Mock())
|
|
|
|
names = registry.list_registered()
|
|
assert len(names) == 3
|
|
assert "llm1" in names
|
|
assert "llm2" in names
|
|
assert "llm3" in names
|
|
|
|
|
|
class TestLLMRegistryClear:
|
|
"""Tests for LLMRegistry.clear method."""
|
|
|
|
def test_clear_removes_all_llms(self):
|
|
"""Test that clear removes all registered LLMs."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
|
|
registry.register("llm1", Mock())
|
|
registry.register("llm2", Mock())
|
|
registry.register("llm3", Mock())
|
|
|
|
assert len(registry.list_registered()) == 3
|
|
|
|
registry.clear()
|
|
|
|
assert registry.list_registered() == []
|
|
|
|
|
|
class TestGlobalRegistryFunctions:
|
|
"""Tests for global registry public API functions."""
|
|
|
|
def test_register_llm_function(self):
|
|
"""Test global register_llm function."""
|
|
from local_deep_research.llm.llm_registry import (
|
|
register_llm,
|
|
is_llm_registered,
|
|
clear_llm_registry,
|
|
)
|
|
|
|
try:
|
|
mock_llm = Mock()
|
|
register_llm("global-test", mock_llm)
|
|
assert is_llm_registered("global-test")
|
|
finally:
|
|
clear_llm_registry()
|
|
|
|
def test_unregister_llm_function(self):
|
|
"""Test global unregister_llm function."""
|
|
from local_deep_research.llm.llm_registry import (
|
|
register_llm,
|
|
unregister_llm,
|
|
is_llm_registered,
|
|
clear_llm_registry,
|
|
)
|
|
|
|
try:
|
|
register_llm("global-test", Mock())
|
|
assert is_llm_registered("global-test")
|
|
|
|
unregister_llm("global-test")
|
|
assert not is_llm_registered("global-test")
|
|
finally:
|
|
clear_llm_registry()
|
|
|
|
def test_get_llm_from_registry_function(self):
|
|
"""Test global get_llm_from_registry function."""
|
|
from local_deep_research.llm.llm_registry import (
|
|
register_llm,
|
|
get_llm_from_registry,
|
|
clear_llm_registry,
|
|
)
|
|
|
|
try:
|
|
mock_llm = Mock()
|
|
register_llm("global-test", mock_llm)
|
|
|
|
assert get_llm_from_registry("global-test") is mock_llm
|
|
finally:
|
|
clear_llm_registry()
|
|
|
|
def test_list_registered_llms_function(self):
|
|
"""Test global list_registered_llms function."""
|
|
from local_deep_research.llm.llm_registry import (
|
|
register_llm,
|
|
list_registered_llms,
|
|
clear_llm_registry,
|
|
)
|
|
|
|
try:
|
|
register_llm("test1", Mock())
|
|
register_llm("test2", Mock())
|
|
|
|
names = list_registered_llms()
|
|
assert "test1" in names
|
|
assert "test2" in names
|
|
finally:
|
|
clear_llm_registry()
|
|
|
|
def test_clear_llm_registry_function(self):
|
|
"""Test global clear_llm_registry function."""
|
|
from local_deep_research.llm.llm_registry import (
|
|
register_llm,
|
|
list_registered_llms,
|
|
clear_llm_registry,
|
|
)
|
|
|
|
register_llm("test1", Mock())
|
|
register_llm("test2", Mock())
|
|
|
|
clear_llm_registry()
|
|
|
|
assert list_registered_llms() == []
|
|
|
|
|
|
class TestThreadSafety:
|
|
"""Tests for thread safety of LLMRegistry."""
|
|
|
|
def test_concurrent_registration(self):
|
|
"""Test that concurrent registrations are thread-safe."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
errors = []
|
|
|
|
def register_llm(name):
|
|
try:
|
|
registry.register(name, Mock())
|
|
except Exception as e:
|
|
errors.append(e)
|
|
|
|
threads = [
|
|
threading.Thread(target=register_llm, args=(f"llm-{i}",))
|
|
for i in range(100)
|
|
]
|
|
|
|
for t in threads:
|
|
t.start()
|
|
for t in threads:
|
|
t.join()
|
|
|
|
assert len(errors) == 0
|
|
assert len(registry.list_registered()) == 100
|
|
|
|
def test_concurrent_get(self):
|
|
"""Test that concurrent gets are thread-safe."""
|
|
from local_deep_research.llm.llm_registry import LLMRegistry
|
|
|
|
registry = LLMRegistry()
|
|
mock_llm = Mock()
|
|
registry.register("test", mock_llm)
|
|
results = []
|
|
|
|
def get_llm():
|
|
result = registry.get("test")
|
|
results.append(result)
|
|
|
|
threads = [threading.Thread(target=get_llm) for _ in range(100)]
|
|
|
|
for t in threads:
|
|
t.start()
|
|
for t in threads:
|
|
t.join()
|
|
|
|
assert len(results) == 100
|
|
assert all(r is mock_llm for r in results)
|