""" Tests for the check-pathlib-usage pre-commit hook. Covers the alias-bypass regression: ``import os as `` followed by ``.path.*`` used to slip past the AST matcher because it only looked for the literal name ``os``. That is how a ``_os.path.join(...)`` landed in #4880 and had to be caught by human review. """ import subprocess import sys import tempfile from pathlib import Path HOOK_SCRIPT = ( Path(__file__).parent.parent.parent / ".pre-commit-hooks" / "check-pathlib-usage.py" ) def _run_hook( content: str, filename: str = "service.py" ) -> subprocess.CompletedProcess: with tempfile.TemporaryDirectory() as d: path = Path(d) / filename path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content) return subprocess.run( [sys.executable, str(HOOK_SCRIPT), str(path)], capture_output=True, text=True, ) class TestPlainOsPath: """Regression guards: the pre-existing ``os.path.*`` detection still works.""" def test_plain_os_path_join_is_caught(self): result = _run_hook("import os\nx = os.path.join('a', 'b')\n") assert result.returncode == 1 assert "os.path.join" in result.stdout def test_from_os_import_path_is_caught(self): result = _run_hook("from os import path\n") assert result.returncode == 1 assert "from os import path" in result.stdout def test_from_os_path_import_join_is_caught(self): result = _run_hook("from os.path import join\n") assert result.returncode == 1 assert "from os.path import join" in result.stdout def test_expandvars_is_allowed(self): """os.path.expandvars has no pathlib equivalent and must not be flagged.""" result = _run_hook("import os\nx = os.path.expandvars('$HOME')\n") assert result.returncode == 0 class TestAliasedOsImport: """The bug: ``import os as `` used to bypass the matcher.""" def test_aliased_os_path_join_is_caught(self): """The exact pattern that slipped into #4880.""" result = _run_hook("import os as _os\nx = _os.path.join('a', 'b')\n") assert result.returncode == 1 assert "os.path.join" in result.stdout def test_aliased_os_path_exists_is_caught(self): result = _run_hook( "import os as os_module\n" "if os_module.path.exists('/x'):\n" " pass\n" ) assert result.returncode == 1 assert "os.path.exists" in result.stdout def test_aliased_expandvars_is_allowed(self): """Alias bypass must not extend the expandvars carve-out to new code — but expandvars itself stays allowed regardless of how os is bound.""" result = _run_hook( "import os as _os\nx = _os.path.expandvars('$HOME')\n" ) assert result.returncode == 0 def test_multiple_aliases_in_one_import(self): """``import os, sys`` style — os is still tracked.""" result = _run_hook("import os, sys\nx = os.path.join('a', 'b')\n") assert result.returncode == 1 assert "os.path.join" in result.stdout class TestNoFalsePositives: """Aliased ``os`` imports that do NOT touch ``.path`` must stay silent.""" def test_aliased_os_chmod_only(self): result = _run_hook("import os as _os\n_os.chmod('/tmp/x', 0o644)\n") assert result.returncode == 0 def test_aliased_os_environ_only(self): result = _run_hook( "import os as os_module\nci = os_module.environ.get('CI')\n" ) assert result.returncode == 0 def test_clean_pathlib_code(self): result = _run_hook("from pathlib import Path\np = Path('a') / 'b'\n") assert result.returncode == 0 def test_no_os_import_at_all(self): result = _run_hook("x = 'hello'\nprint(x)\n") assert result.returncode == 0 class TestAllowedFilesBypass: """The ALLOWED_FILES list in the hook must still suppress violations for the legacy exceptions.""" def test_log_utils_is_exempt(self): result = _run_hook( "import os\nx = os.path.join('a', 'b')\n", filename="src/local_deep_research/utilities/log_utils.py", ) assert result.returncode == 0 def test_paths_config_is_exempt(self): result = _run_hook( "import os\nx = os.path.join('a', 'b')\n", filename="src/local_deep_research/config/paths.py", ) assert result.returncode == 0