""" Tests for the check-absolute-module-paths pre-commit hook. Ensures the hook correctly detects absolute module paths (local_deep_research.*) that should be relative imports, while allowing legitimate uses. """ import ast import json import sys from importlib import import_module from pathlib import Path # Add the pre-commit hooks directory to path HOOKS_DIR = Path(__file__).parent.parent.parent / ".pre-commit-hooks" sys.path.insert(0, str(HOOKS_DIR)) # Import the checker from the hook (must be after sys.path modification) hook_module = import_module("check-absolute-module-paths") # noqa: E402 AbsoluteModulePathChecker = hook_module.AbsoluteModulePathChecker LEGITIMATE_ABSOLUTE_REFS = hook_module.LEGITIMATE_ABSOLUTE_REFS class TestAbsoluteModulePathChecker: """Tests for the AbsoluteModulePathChecker AST visitor.""" def _check_code(self, code: str, filename: str = "src/module.py") -> list: """Helper to check code and return errors.""" tree = ast.parse(code) checker = AbsoluteModulePathChecker(filename) checker.visit(tree) return checker.errors # --- Detection tests --- def test_detects_web_search_engine_absolute_path(self): """Should detect absolute paths in the web_search_engines subpackage.""" code = """ module_path = "local_deep_research.web_search_engines.engines.search_engine_brave" """ errors = self._check_code(code) assert len(errors) == 1 assert "local_deep_research.web_search_engines" in errors[0][1] def test_detects_llm_absolute_path(self): """Should detect absolute paths in the llm subpackage.""" code = """ path = "local_deep_research.llm.providers.implementations.openai_provider" """ errors = self._check_code(code) assert len(errors) == 1 assert "local_deep_research.llm" in errors[0][1] def test_detects_arbitrary_subpackage_absolute_path(self): """Should detect absolute paths in any subpackage.""" code = """ x = "local_deep_research.some_new_package.foo.bar" """ errors = self._check_code(code) assert len(errors) == 1 def test_detects_multiple_violations(self): """Should detect multiple violations in one file.""" code = """ a = "local_deep_research.web_search_engines.engines.local_embedding_manager" b = "local_deep_research.web_search_engines.engines.search_engine_brave" c = "local_deep_research.llm.providers.implementations.openai_provider" """ errors = self._check_code(code) assert len(errors) == 3 def test_reports_correct_line_numbers(self): """Should report the correct line number for violations.""" code = """ # Some comment x = 1 module_path = "local_deep_research.web_search_engines.engines.foo" """ errors = self._check_code(code) assert len(errors) == 1 assert errors[0][0] == 5 # Line 5 # --- Allowance tests --- def test_allows_relative_paths(self): """Should not flag relative module paths.""" code = """ module_path = ".engines.search_engine_brave" """ errors = self._check_code(code) assert len(errors) == 0 def test_allows_legitimate_absolute_refs(self): """Should not flag known legitimate absolute references.""" for ref in LEGITIMATE_ABSOLUTE_REFS: code = f''' x = "{ref}" ''' errors = self._check_code(code) assert len(errors) == 0, f"Legitimate ref '{ref}' was flagged" def test_allows_unrelated_strings(self): """Should not flag strings that don't start with local_deep_research.""" code = """ name = "some_other_package.module" url = "https://example.com" msg = "This is a message" """ errors = self._check_code(code) assert len(errors) == 0 def test_allows_bare_package_name(self): """Should not flag 'local_deep_research' without a dot-suffix.""" code = """ name = "local_deep_research" """ errors = self._check_code(code) assert len(errors) == 0 def test_allows_non_string_constants(self): """Should not flag integer or other non-string constants.""" code = """ x = 42 y = 3.14 z = True """ errors = self._check_code(code) assert len(errors) == 0 class TestIsFileAllowed: """Tests for the _is_file_allowed() function.""" def test_allows_test_directory(self): """Files under tests/ should be allowed.""" assert hook_module._is_file_allowed("tests/test_something.py") is True assert ( hook_module._is_file_allowed("tests/security/test_whitelist.py") is True ) def test_allows_pre_commit_hooks_directory(self): """Files under .pre-commit-hooks/ should be allowed.""" assert ( hook_module._is_file_allowed( ".pre-commit-hooks/check-absolute-module-paths.py" ) is True ) def test_allows_module_whitelist(self): """module_whitelist.py should be allowed.""" assert ( hook_module._is_file_allowed( "src/local_deep_research/security/module_whitelist.py" ) is True ) def test_allows_test_prefix_basename(self): """Files with test_ prefix should be allowed.""" assert hook_module._is_file_allowed("src/test_something.py") is True def test_allows_test_suffix_basename(self): """Files with _test.py suffix should be allowed.""" assert hook_module._is_file_allowed("src/something_test.py") is True def test_rejects_normal_source_file(self): """Normal source files should NOT be allowed.""" assert ( hook_module._is_file_allowed("src/local_deep_research/config.py") is False ) def test_no_false_positive_on_tests_in_filename(self): """'tests' in a filename (not dir) should not allow the file.""" # 'my_tests_helper.py' has 'tests' in the name but isn't a test file # and isn't in a tests/ directory assert hook_module._is_file_allowed("src/my_tests_helper.py") is False def test_no_false_positive_on_module_whitelist_substring(self): """module_whitelist_extra.py should NOT match module_whitelist.py.""" assert ( hook_module._is_file_allowed( "src/security/module_whitelist_extra.py" ) is False ) def test_no_false_positive_on_test_in_middle_of_name(self): """'contest_results.py' should not match test_ prefix.""" assert hook_module._is_file_allowed("src/contest_results.py") is False class TestCheckPythonFile: """Integration tests for check_python_file().""" def test_clean_file_passes(self, tmp_path): """A file with only relative paths should pass.""" clean = tmp_path / "clean_module.py" clean.write_text(""" config = { "module_path": ".engines.search_engine_brave", "class_name": "BraveSearchEngine", } """) assert hook_module.check_python_file(str(clean)) is True def test_violation_file_fails(self, tmp_path): """A file with absolute paths should fail.""" bad = tmp_path / "bad_module.py" bad.write_text(""" config = { "module_path": "local_deep_research.web_search_engines.engines.search_engine_brave", "class_name": "BraveSearchEngine", } """) assert hook_module.check_python_file(str(bad)) is False def test_legitimate_ref_passes(self, tmp_path): """A file using a legitimate absolute ref should pass.""" legit = tmp_path / "legit_module.py" legit.write_text(""" from importlib import resources data_dir = resources.files("local_deep_research.defaults.settings") """) assert hook_module.check_python_file(str(legit)) is True def test_syntax_error_passes(self, tmp_path): """Files with syntax errors should pass (let other tools handle them).""" broken = tmp_path / "broken.py" broken.write_text("def bad(:\n pass") assert hook_module.check_python_file(str(broken)) is True def test_test_file_always_passes(self, tmp_path): """Test files should always pass even with absolute paths.""" test_file = tmp_path / "test_whitelist.py" test_file.write_text(""" path = "local_deep_research.web_search_engines.engines.foo" """) assert hook_module.check_python_file(str(test_file)) is True def test_nonexistent_file_fails(self): """A non-existent file should fail (returns False).""" assert ( hook_module.check_python_file("/nonexistent/path/file.py") is False ) class TestCheckJsonFile: """Integration tests for check_json_file().""" def test_clean_json_passes(self, tmp_path): """A JSON file with relative module_path should pass.""" clean = tmp_path / "engine.json" clean.write_text( json.dumps( { "module_path": ".engines.search_engine_brave", "class_name": "BraveSearchEngine", } ) ) assert hook_module.check_json_file(str(clean)) is True def test_absolute_module_path_in_json_fails(self, tmp_path): """A JSON file with absolute module_path should fail.""" bad = tmp_path / "bad_engine.json" bad.write_text( json.dumps( { "module_path": "local_deep_research.web_search_engines.engines.search_engine_brave", "class_name": "BraveSearchEngine", } ) ) assert hook_module.check_json_file(str(bad)) is False def test_nested_module_path_in_json_fails(self, tmp_path): """A nested JSON module_path with absolute path should fail.""" bad = tmp_path / "nested.json" bad.write_text( json.dumps( { "engines": { "brave": { "module_path": "local_deep_research.web_search_engines.engines.search_engine_brave", "class_name": "BraveSearchEngine", } } } ) ) assert hook_module.check_json_file(str(bad)) is False def test_absolute_path_in_non_module_path_key_passes(self, tmp_path): """Absolute paths in non-module_path keys should NOT trigger (no false positive).""" ok = tmp_path / "description.json" ok.write_text( json.dumps( { "description": "Uses local_deep_research.web_search_engines.engines.foo internally", "module_path": ".engines.search_engine_brave", } ) ) assert hook_module.check_json_file(str(ok)) is True def test_invalid_json_fails(self, tmp_path): """Invalid JSON should fail gracefully (returns False).""" bad = tmp_path / "broken.json" bad.write_text("{invalid json}") assert hook_module.check_json_file(str(bad)) is False def test_legitimate_ref_in_module_path_passes(self, tmp_path): """Legitimate absolute refs in module_path should pass.""" legit = tmp_path / "legit.json" legit.write_text( json.dumps( {"module_path": "local_deep_research.web_search_engines"} ) ) assert hook_module.check_json_file(str(legit)) is True def test_absolute_full_search_module_in_json_fails(self, tmp_path): """A JSON file with absolute full_search_module should fail.""" bad = tmp_path / "bad_full_search.json" bad.write_text( json.dumps( { "full_search_module": "local_deep_research.web_search_engines.engines.full_search", "full_search_class": "FullSearchResults", } ) ) assert hook_module.check_json_file(str(bad)) is False def test_relative_full_search_module_in_json_passes(self, tmp_path): """A JSON file with relative full_search_module should pass.""" ok = tmp_path / "ok_full_search.json" ok.write_text( json.dumps( { "full_search_module": ".engines.full_search", "full_search_class": "FullSearchResults", } ) ) assert hook_module.check_json_file(str(ok)) is True def test_module_path_in_list_of_dicts(self, tmp_path): """Should detect absolute paths in lists of engine configs.""" bad = tmp_path / "engines_list.json" bad.write_text( json.dumps( [ { "module_path": ".engines.good", "class_name": "Good", }, { "module_path": "local_deep_research.web_search_engines.engines.bad", "class_name": "Bad", }, ] ) ) assert hook_module.check_json_file(str(bad)) is False