| 1 | from __future__ import annotations
|
| 2 |
|
| 3 | import os
|
| 4 | import shutil
|
| 5 | import subprocess
|
| 6 | import sys
|
| 7 | import tempfile
|
| 8 | import unittest
|
| 9 | from pathlib import Path
|
| 10 | from unittest import mock
|
| 11 |
|
| 12 | sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
| 13 |
|
| 14 | from oakbench.environment import ENV_ISOLATION_VERSION, base_env # noqa: E402
|
| 15 |
|
| 16 |
|
| 17 | class EnvironmentPolicyTest(unittest.TestCase):
|
| 18 | def test_inherited_git_plumbing_cannot_redirect_any_lane(self):
|
| 19 | poison = {"GIT_DIR": "/wrong", "GIT_WORK_TREE": "/wrong", "GIT_INDEX_FILE": "/wrong",
|
| 20 | "GIT_OBJECT_DIRECTORY": "/wrong", "GIT_CONFIG_COUNT": "1",
|
| 21 | "GIT_CONFIG_KEY_0": "core.hooksPath", "GIT_CONFIG_VALUE_0": "/wrong",
|
| 22 | "GIT_CONFIG_GLOBAL": "/wrong", "GIT_NO_REPLACE_OBJECTS": "0"}
|
| 23 | with mock.patch.dict(os.environ, poison):
|
| 24 | env = base_env()
|
| 25 | for key in poison.keys() - {"GIT_CONFIG_GLOBAL", "GIT_NO_REPLACE_OBJECTS"}:
|
| 26 | self.assertFalse(key in env, key)
|
| 27 | self.assertEqual(env["GIT_CONFIG_GLOBAL"], os.devnull)
|
| 28 | self.assertEqual(env["GIT_NO_REPLACE_OBJECTS"], "1")
|
| 29 |
|
| 30 | @unittest.skipUnless(shutil.which("git"), "Git required")
|
| 31 | def test_real_git_initializes_requested_checkout_with_poisoned_host_environment(self):
|
| 32 | with tempfile.TemporaryDirectory() as tmp:
|
| 33 | target, poison = Path(tmp) / "target", Path(tmp) / "poison"
|
| 34 | target.mkdir(); poison.mkdir()
|
| 35 | with mock.patch.dict(os.environ, {"GIT_DIR": str(poison / ".git"), "GIT_WORK_TREE": str(poison)}):
|
| 36 | process = subprocess.run([shutil.which("git"), "init", "-q"], cwd=target,
|
| 37 | env=base_env(), capture_output=True, timeout=20)
|
| 38 | self.assertEqual(process.returncode, 0, process.stderr)
|
| 39 | self.assertTrue((target / ".git").is_dir())
|
| 40 | self.assertFalse((poison / ".git").exists())
|
| 41 |
|
| 42 | def test_base_env_isolates_host_home_git_config_and_locale(self) -> None:
|
| 43 | env = base_env()
|
| 44 | bench_home = Path(env["HOME"])
|
| 45 |
|
| 46 | self.assertNotEqual(bench_home, Path.home())
|
| 47 | self.assertTrue(bench_home.is_dir())
|
| 48 | self.assertEqual(env["XDG_CONFIG_HOME"], str(bench_home / ".config"))
|
| 49 | self.assertEqual(env["XDG_CACHE_HOME"], str(bench_home / ".cache"))
|
| 50 | self.assertEqual(env["ZDOTDIR"], str(bench_home))
|
| 51 | self.assertEqual(env["BASH_ENV"], os.devnull)
|
| 52 | self.assertEqual(env["ENV"], os.devnull)
|
| 53 | self.assertEqual(env["GIT_CONFIG_GLOBAL"], os.devnull)
|
| 54 | self.assertEqual(env["GIT_CONFIG_NOSYSTEM"], "1")
|
| 55 | self.assertEqual(env["LC_ALL"], "C")
|
| 56 | self.assertEqual(env["LANG"], "C")
|
| 57 | self.assertEqual(env["TZ"], "UTC")
|
| 58 | self.assertEqual(env["OAK_BENCH_ENV_ISOLATION_VERSION"], ENV_ISOLATION_VERSION)
|
| 59 |
|
| 60 | def test_base_env_accepts_explicit_stable_home(self) -> None:
|
| 61 | with tempfile.TemporaryDirectory() as tmp:
|
| 62 | home = Path(tmp) / "bench-home"
|
| 63 | first = base_env(home=home)
|
| 64 | second = base_env(home=home)
|
| 65 | self.assertEqual(first["HOME"], second["HOME"])
|
| 66 | self.assertEqual(first["XDG_CONFIG_HOME"], str(home / ".config"))
|
| 67 |
|
| 68 | def test_base_env_preserves_existing_path_and_explicit_tokens(self) -> None:
|
| 69 | old_token = os.environ.get("OAK_API_KEY")
|
| 70 | try:
|
| 71 | os.environ["OAK_API_KEY"] = "test-token"
|
| 72 | env = base_env()
|
| 73 | finally:
|
| 74 | if old_token is None:
|
| 75 | os.environ.pop("OAK_API_KEY", None)
|
| 76 | else:
|
| 77 | os.environ["OAK_API_KEY"] = old_token
|
| 78 |
|
| 79 | self.assertEqual(env.get("PATH"), os.environ.get("PATH"))
|
| 80 | self.assertEqual(env["OAK_API_KEY"], "test-token")
|
| 81 |
|
| 82 |
|
| 83 | if __name__ == "__main__":
|
| 84 | unittest.main()
|