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
273 lines
9.8 KiB
Python
273 lines
9.8 KiB
Python
"""
|
|
Edge-case tests for loader_registry module.
|
|
|
|
Focuses on:
|
|
- get_loader_for_path returning None when constructor raises
|
|
- Special loader kwargs (TSV tab delimiter, TXT autodetect, notebook outputs, Evernote)
|
|
- Optional loader flag gating (HAS_ODT/EPUB/RTF/RST/ORG/IMAGE_LOADER)
|
|
- Path handling (string conversion, uppercase extension)
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_loader_for_path -- constructor raises returns None
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestGetLoaderForPathConstructorException:
|
|
"""When the loader class constructor raises, get_loader_for_path returns None."""
|
|
|
|
def test_runtime_error_in_constructor_returns_none(self):
|
|
"""A RuntimeError during loader instantiation yields None."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
class _BoomLoader:
|
|
def __init__(self, *args, **kwargs):
|
|
raise RuntimeError("boom")
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{".txt": {"loader_class": _BoomLoader, "loader_kwargs": {}}},
|
|
):
|
|
result = get_loader_for_path("/fake/path/file.txt")
|
|
assert result is None
|
|
|
|
def test_type_error_in_constructor_returns_none(self):
|
|
"""A TypeError during loader instantiation yields None."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
class _BadSigLoader:
|
|
def __init__(self):
|
|
pass
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{".csv": {"loader_class": _BadSigLoader, "loader_kwargs": {}}},
|
|
):
|
|
result = get_loader_for_path("/fake/data.csv")
|
|
assert result is None
|
|
|
|
def test_value_error_in_constructor_returns_none(self):
|
|
"""A ValueError during loader instantiation yields None."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
class _ValErrLoader:
|
|
def __init__(self, *args, **kwargs):
|
|
raise ValueError("invalid arg")
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{".md": {"loader_class": _ValErrLoader, "loader_kwargs": {}}},
|
|
):
|
|
result = get_loader_for_path("/fake/readme.md")
|
|
assert result is None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Special loader kwargs
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSpecialLoaderKwargs:
|
|
"""Verify that specific extensions carry the correct special kwargs."""
|
|
|
|
def test_tsv_has_tab_delimiter(self):
|
|
"""TSV entry uses CSVLoader with tab delimiter."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
)
|
|
|
|
entry = LOADER_REGISTRY[".tsv"]
|
|
assert entry["loader_kwargs"]["csv_args"]["delimiter"] == "\t"
|
|
|
|
def test_txt_has_autodetect_encoding(self):
|
|
"""TXT entry specifies autodetect_encoding=True."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
)
|
|
|
|
kwargs = LOADER_REGISTRY[".txt"]["loader_kwargs"]
|
|
assert kwargs["autodetect_encoding"] is True
|
|
assert kwargs["encoding"] == "utf-8"
|
|
|
|
def test_notebook_include_outputs(self):
|
|
"""Notebook entry has include_outputs=True and remove_newline=True."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
)
|
|
|
|
kwargs = LOADER_REGISTRY[".ipynb"]["loader_kwargs"]
|
|
assert kwargs["include_outputs"] is True
|
|
assert kwargs["remove_newline"] is True
|
|
|
|
def test_evernote_not_single_document(self):
|
|
"""Evernote entry has load_single_document=False."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
)
|
|
|
|
kwargs = LOADER_REGISTRY[".enex"]["loader_kwargs"]
|
|
assert kwargs["load_single_document"] is False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Optional loader flag gating (module-level HAS_* flags)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestOptionalLoaderFlagGating:
|
|
"""Test that HAS_* flags control whether optional extensions are registered."""
|
|
|
|
def test_odt_absent_when_flag_false(self):
|
|
"""When HAS_ODT_LOADER is False, .odt must not be in the registry."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
if not mod.HAS_ODT_LOADER:
|
|
assert ".odt" not in mod.LOADER_REGISTRY
|
|
else:
|
|
pytest.skip("HAS_ODT_LOADER is True in this environment")
|
|
|
|
def test_odt_present_when_flag_true(self):
|
|
"""When HAS_ODT_LOADER is True, .odt must be in the registry."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
if mod.HAS_ODT_LOADER:
|
|
assert ".odt" in mod.LOADER_REGISTRY
|
|
assert (
|
|
mod.LOADER_REGISTRY[".odt"]["loader_class"].__name__
|
|
== "UnstructuredODTLoader"
|
|
)
|
|
else:
|
|
pytest.skip("HAS_ODT_LOADER is False in this environment")
|
|
|
|
def test_epub_gated_by_flag(self):
|
|
"""EPUB registration matches HAS_EPUB_LOADER flag."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
assert (".epub" in mod.LOADER_REGISTRY) == mod.HAS_EPUB_LOADER
|
|
|
|
def test_rtf_gated_by_flag(self):
|
|
"""RTF registration matches HAS_RTF_LOADER flag."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
assert (".rtf" in mod.LOADER_REGISTRY) == mod.HAS_RTF_LOADER
|
|
|
|
def test_rst_gated_by_flag(self):
|
|
"""RST registration matches HAS_RST_LOADER flag."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
assert (".rst" in mod.LOADER_REGISTRY) == mod.HAS_RST_LOADER
|
|
|
|
def test_org_gated_by_flag(self):
|
|
"""Org-mode registration matches HAS_ORG_LOADER flag."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
assert (".org" in mod.LOADER_REGISTRY) == mod.HAS_ORG_LOADER
|
|
|
|
def test_image_extensions_gated_by_flag(self):
|
|
"""Image extensions are present iff the loader class AND the OCR
|
|
runtime dependency (pytesseract + tesseract binary) are available."""
|
|
import local_deep_research.document_loaders.loader_registry as mod
|
|
|
|
image_exts = [
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".tiff",
|
|
".tif",
|
|
".bmp",
|
|
".heic",
|
|
]
|
|
expected = mod.HAS_IMAGE_LOADER and mod.HAS_OCR_DEP
|
|
for ext in image_exts:
|
|
assert (ext in mod.LOADER_REGISTRY) == expected, (
|
|
f"{ext} registration does not match "
|
|
f"HAS_IMAGE_LOADER={mod.HAS_IMAGE_LOADER} "
|
|
f"HAS_OCR_DEP={mod.HAS_OCR_DEP}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Path handling -- string conversion and uppercase extension
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestPathHandling:
|
|
"""Test that get_loader_for_path handles various path forms correctly."""
|
|
|
|
def test_string_path_is_converted_to_pathlib(self):
|
|
"""A plain string path should work identically to a Path object."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
mock_loader_instance = MagicMock()
|
|
mock_class = MagicMock(return_value=mock_loader_instance)
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{
|
|
".txt": {
|
|
"loader_class": mock_class,
|
|
"loader_kwargs": {"encoding": "utf-8"},
|
|
}
|
|
},
|
|
):
|
|
result = get_loader_for_path("/some/dir/notes.txt")
|
|
assert result is mock_loader_instance
|
|
mock_class.assert_called_once_with(
|
|
"/some/dir/notes.txt", encoding="utf-8"
|
|
)
|
|
|
|
def test_uppercase_extension_resolves_correctly(self):
|
|
"""A file with .CSV extension should still resolve via .lower()."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
mock_loader_instance = MagicMock()
|
|
mock_class = MagicMock(return_value=mock_loader_instance)
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{".csv": {"loader_class": mock_class, "loader_kwargs": {}}},
|
|
):
|
|
result = get_loader_for_path("/data/REPORT.CSV")
|
|
assert result is mock_loader_instance
|
|
mock_class.assert_called_once_with("/data/REPORT.CSV")
|
|
|
|
def test_path_object_suffix_extracted(self):
|
|
"""Path object suffix is used for extension lookup, not the full name."""
|
|
from local_deep_research.document_loaders.loader_registry import (
|
|
LOADER_REGISTRY,
|
|
get_loader_for_path,
|
|
)
|
|
|
|
mock_loader_instance = MagicMock()
|
|
mock_class = MagicMock(return_value=mock_loader_instance)
|
|
|
|
with patch.dict(
|
|
LOADER_REGISTRY,
|
|
{".json": {"loader_class": mock_class, "loader_kwargs": {}}},
|
|
):
|
|
result = get_loader_for_path(Path("/tmp/config.backup.json"))
|
|
assert result is mock_loader_instance
|
|
call_args = mock_class.call_args
|
|
assert call_args[0][0].endswith("config.backup.json")
|