""" Tests for embeddings_config module. These tests verify the get_embedding_function and related configuration functions. """ import pytest from unittest.mock import Mock, patch, MagicMock class TestGetEmbeddingFunction: """Tests for get_embedding_function.""" def test_get_embedding_function_exists(self): """Verify get_embedding_function can be imported.""" from local_deep_research.embeddings.embeddings_config import ( get_embedding_function, ) assert callable(get_embedding_function) def test_get_embedding_function_returns_callable(self): """get_embedding_function should return a callable embed_documents method.""" from local_deep_research.embeddings.embeddings_config import ( get_embedding_function, ) # Mock the get_embeddings function mock_embeddings = MagicMock() mock_embeddings.embed_documents = Mock(return_value=[[0.1, 0.2, 0.3]]) with patch( "local_deep_research.embeddings.embeddings_config.get_embeddings", return_value=mock_embeddings, ): func = get_embedding_function( provider="sentence_transformers", model_name="test-model" ) # Should return the embed_documents method assert callable(func) # Should be the embed_documents method specifically result = func(["test text"]) assert result == [[0.1, 0.2, 0.3]] mock_embeddings.embed_documents.assert_called_once_with( ["test text"] ) def test_get_embedding_function_passes_parameters(self): """get_embedding_function should pass all parameters to get_embeddings.""" from local_deep_research.embeddings.embeddings_config import ( get_embedding_function, ) mock_embeddings = MagicMock() mock_embeddings.embed_documents = Mock() with patch( "local_deep_research.embeddings.embeddings_config.get_embeddings", return_value=mock_embeddings, ) as mock_get_embeddings: settings = {"key": "value"} get_embedding_function( provider="ollama", model_name="nomic-embed-text", settings_snapshot=settings, extra_param="extra", ) mock_get_embeddings.assert_called_once_with( provider="ollama", model="nomic-embed-text", settings_snapshot=settings, extra_param="extra", ) class TestGetEmbeddings: """Tests for get_embeddings function.""" def test_get_embeddings_exists(self): """Verify get_embeddings can be imported.""" from local_deep_research.embeddings.embeddings_config import ( get_embeddings, ) assert callable(get_embeddings) def test_get_embeddings_validates_provider(self): """get_embeddings should raise ValueError for invalid provider.""" from local_deep_research.embeddings.embeddings_config import ( get_embeddings, ) with pytest.raises(ValueError, match="Invalid embedding provider"): get_embeddings(provider="invalid_provider") class TestAvailableProviders: """Tests for provider availability functions.""" def test_valid_embedding_providers_list(self): """VALID_EMBEDDING_PROVIDERS should contain expected providers.""" from local_deep_research.embeddings.embeddings_config import ( VALID_EMBEDDING_PROVIDERS, ) assert "sentence_transformers" in VALID_EMBEDDING_PROVIDERS assert "ollama" in VALID_EMBEDDING_PROVIDERS assert "openai" in VALID_EMBEDDING_PROVIDERS def test_get_available_embedding_providers_exists(self): """Verify get_available_embedding_providers can be imported.""" from local_deep_research.embeddings.embeddings_config import ( get_available_embedding_providers, ) assert callable(get_available_embedding_providers) class TestSnapshotlessEgressGate: """get_embeddings() with no settings_snapshot can't read the embeddings.require_local toggle, so it fails closed: only the localhost-default providers may proceed. A snapshot-less get_embeddings(provider="openai") must NOT silently ship the local corpus to a cloud embedder. Regression (PR #4300 review): the gate was `if settings_snapshot is not None:` — the entire check was skipped when snapshot was None, so a snapshot-less cloud-provider call proceeded unchecked. Source: src/local_deep_research/embeddings/embeddings_config.py """ def test_openai_without_snapshot_is_denied(self): from local_deep_research.embeddings.embeddings_config import ( get_embeddings, ) from local_deep_research.security.egress.policy import ( PolicyDeniedError, ) with pytest.raises(PolicyDeniedError) as excinfo: get_embeddings(provider="openai", settings_snapshot=None) assert excinfo.value.decision.reason == "no_snapshot_for_provider" def test_local_default_providers_are_allowlisted(self): """sentence_transformers and ollama are the localhost-default embedding providers — they must pass the snapshot-less gate (i.e. NOT raise the no_snapshot_for_provider denial).""" from local_deep_research.security.egress.policy import ( _LOCAL_DEFAULT_EMBEDDING_PROVIDERS, ) assert "sentence_transformers" in _LOCAL_DEFAULT_EMBEDDING_PROVIDERS assert "ollama" in _LOCAL_DEFAULT_EMBEDDING_PROVIDERS # Cloud providers must NOT be on the allow-list. assert "openai" not in _LOCAL_DEFAULT_EMBEDDING_PROVIDERS def test_sentence_transformers_without_snapshot_passes_gate(self): """The local default must get past the egress gate without a snapshot. We patch the provider class so the test asserts the POLICY decision, not the heavy model load.""" from local_deep_research.embeddings.embeddings_config import ( get_embeddings, ) from local_deep_research.security.egress.policy import ( PolicyDeniedError, ) sentinel = object() fake_provider = Mock() fake_provider.return_value.create_embeddings.return_value = sentinel with patch( "local_deep_research.embeddings.embeddings_config._get_provider_classes", return_value={"sentence_transformers": fake_provider}, ): try: get_embeddings( provider="sentence_transformers", settings_snapshot=None, ) except PolicyDeniedError: pytest.fail( "sentence_transformers must pass the snapshot-less gate" ) except Exception: # Any non-policy error means we got PAST the gate, which is # all this test asserts. pass