""" 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")