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
225 lines
7.6 KiB
Python
225 lines
7.6 KiB
Python
"""
|
||
Coverage tests for es_utils.ElasticsearchManager.
|
||
|
||
Focuses on paths not exercised by the existing test_es_utils.py:
|
||
- close() delegates to safe_close
|
||
- create_index with no mappings / no settings (default generation paths)
|
||
- index_document with refresh=False (default) vs True
|
||
- bulk_index_documents with id_field but field absent from doc
|
||
- search without highlight (highlight=False branch already covered; add coverage
|
||
for custom multi_match type expectation)
|
||
- index_file with explicit title_field=None
|
||
- index_directory with multiple glob patterns
|
||
"""
|
||
|
||
from unittest.mock import Mock, patch
|
||
|
||
import pytest
|
||
|
||
MODULE = "local_deep_research.utilities.es_utils"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Shared fixture
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def _make_manager():
|
||
"""Return an ElasticsearchManager with a fully mocked ES client."""
|
||
mock_client = Mock()
|
||
mock_client.info.return_value = {
|
||
"cluster_name": "test-cluster",
|
||
"version": {"number": "8.0.0"},
|
||
}
|
||
with patch(f"{MODULE}.Elasticsearch", return_value=mock_client):
|
||
from local_deep_research.utilities.es_utils import ElasticsearchManager
|
||
|
||
mgr = ElasticsearchManager()
|
||
return mgr
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# close()
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestClose:
|
||
def test_close_delegates_to_safe_close(self):
|
||
mgr = _make_manager()
|
||
with patch(
|
||
"local_deep_research.utilities.resource_utils.safe_close"
|
||
) as mock_safe_close:
|
||
mgr.close()
|
||
mock_safe_close.assert_called_once()
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# create_index – default mappings / settings paths
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestCreateIndexDefaults:
|
||
def test_creates_default_mappings_when_none_provided(self):
|
||
mgr = _make_manager()
|
||
mgr.client.indices.exists.return_value = False
|
||
mgr.client.indices.create.return_value = {"acknowledged": True}
|
||
|
||
result = mgr.create_index("my-index")
|
||
|
||
assert result is True
|
||
call_kwargs = mgr.client.indices.create.call_args[1]
|
||
# Default mappings should contain "properties"
|
||
assert "properties" in call_kwargs["mappings"]
|
||
|
||
def test_creates_default_settings_when_none_provided(self):
|
||
mgr = _make_manager()
|
||
mgr.client.indices.exists.return_value = False
|
||
mgr.client.indices.create.return_value = {"acknowledged": True}
|
||
|
||
mgr.create_index("my-index")
|
||
|
||
call_kwargs = mgr.client.indices.create.call_args[1]
|
||
assert "number_of_shards" in call_kwargs["settings"]
|
||
|
||
def test_custom_mappings_bypass_defaults(self):
|
||
mgr = _make_manager()
|
||
mgr.client.indices.exists.return_value = False
|
||
mgr.client.indices.create.return_value = {"acknowledged": True}
|
||
|
||
custom = {"properties": {"field_x": {"type": "keyword"}}}
|
||
mgr.create_index("idx", mappings=custom)
|
||
|
||
call_kwargs = mgr.client.indices.create.call_args[1]
|
||
assert call_kwargs["mappings"] == custom
|
||
|
||
def test_custom_settings_bypass_defaults(self):
|
||
mgr = _make_manager()
|
||
mgr.client.indices.exists.return_value = False
|
||
mgr.client.indices.create.return_value = {"acknowledged": True}
|
||
|
||
custom_settings = {"number_of_shards": 5, "number_of_replicas": 1}
|
||
mgr.create_index("idx", settings=custom_settings)
|
||
|
||
call_kwargs = mgr.client.indices.create.call_args[1]
|
||
assert call_kwargs["settings"] == custom_settings
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# index_document – refresh variants and document_id=None
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestIndexDocumentVariants:
|
||
def test_document_id_none_passes_none_to_client(self):
|
||
mgr = _make_manager()
|
||
mgr.client.index.return_value = {"_id": "auto-generated-id"}
|
||
|
||
result = mgr.index_document("idx", {"title": "doc"}, document_id=None)
|
||
|
||
assert result == "auto-generated-id"
|
||
call_kwargs = mgr.client.index.call_args[1]
|
||
assert call_kwargs["id"] is None
|
||
|
||
def test_refresh_false_is_default(self):
|
||
mgr = _make_manager()
|
||
mgr.client.index.return_value = {"_id": "abc"}
|
||
|
||
mgr.index_document("idx", {"title": "doc"})
|
||
|
||
call_kwargs = mgr.client.index.call_args[1]
|
||
assert call_kwargs["refresh"] is False
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# bulk_index_documents – id_field absent from doc
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestBulkIndexIdFieldAbsent:
|
||
def test_missing_id_field_docs_omit_id(self):
|
||
mgr = _make_manager()
|
||
|
||
with patch(f"{MODULE}.bulk", return_value=(2, 0)) as mock_bulk:
|
||
docs = [{"title": "no-id-field-here"}, {"title": "also-no-id"}]
|
||
result = mgr.bulk_index_documents("idx", docs, id_field="doc_id")
|
||
|
||
assert result == 2
|
||
actions = mock_bulk.call_args[0][1]
|
||
for action in actions:
|
||
assert "_id" not in action
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# search – query structure
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestSearchQueryStructure:
|
||
def test_search_uses_best_fields_type(self):
|
||
mgr = _make_manager()
|
||
mgr.client.search.return_value = {"hits": {"hits": []}}
|
||
|
||
mgr.search("idx", "quantum computing")
|
||
|
||
call_kwargs = mgr.client.search.call_args[1]
|
||
body = call_kwargs["body"]
|
||
assert body["query"]["multi_match"]["type"] == "best_fields"
|
||
|
||
def test_search_uses_tie_breaker(self):
|
||
mgr = _make_manager()
|
||
mgr.client.search.return_value = {"hits": {"hits": []}}
|
||
|
||
mgr.search("idx", "test")
|
||
|
||
body = mgr.client.search.call_args[1]["body"]
|
||
assert body["query"]["multi_match"]["tie_breaker"] == pytest.approx(0.3)
|
||
|
||
def test_search_highlight_uses_em_tags(self):
|
||
mgr = _make_manager()
|
||
mgr.client.search.return_value = {"hits": {"hits": []}}
|
||
|
||
mgr.search("idx", "test", highlight=True)
|
||
|
||
body = mgr.client.search.call_args[1]["body"]
|
||
assert body["highlight"]["pre_tags"] == ["<em>"]
|
||
assert body["highlight"]["post_tags"] == ["</em>"]
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# index_file – title_field=None branch
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestIndexFileTitleFieldNone:
|
||
def test_title_field_none_excludes_title_from_doc(self, tmp_path):
|
||
mgr = _make_manager()
|
||
|
||
mock_doc = Mock()
|
||
mock_doc.page_content = "file content"
|
||
mock_doc.metadata = {"source": "test"}
|
||
|
||
mock_loader = Mock()
|
||
mock_loader.load.return_value = [mock_doc]
|
||
mock_loader_class = Mock(return_value=mock_loader)
|
||
|
||
indexed_doc = {}
|
||
|
||
def _capture_index(index_name, document, refresh=False):
|
||
indexed_doc.update(document)
|
||
return "doc-id"
|
||
|
||
mgr.index_document = _capture_index
|
||
|
||
with patch(
|
||
"langchain_community.document_loaders.UnstructuredFileLoader",
|
||
mock_loader_class,
|
||
):
|
||
mgr.index_file(
|
||
"idx",
|
||
str(tmp_path / "report.txt"),
|
||
title_field=None,
|
||
)
|
||
|
||
# title should not be in the document when title_field is None
|
||
assert "title" not in indexed_doc
|