raw ยท 6073 bytes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | import os import shutil import subprocess import sys import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "scripts")) import denylist_check as D # noqa: E402 ENV = {**os.environ, "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t", "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t", "GIT_CONFIG_GLOBAL": "/dev/null", "GIT_CONFIG_NOSYSTEM": "1"} def git(cwd, *args, check=True): return subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True, env=ENV, check=check) class ParseTest(unittest.TestCase): def test_words_comments_exemptions(self): words, exempt = D.parse("# c\nSecretProj\n\n Bob \n!bobcat\n") self.assertEqual(words, [(2, "secretproj"), (4, "bob")]) self.assertEqual(exempt, ["bobcat"]) def test_hits_case_insensitive_and_exempt(self): words, exempt = [(1, "bob"), (2, "secretproj")], ["bobcat"] self.assertEqual(D.hits("a BOB and a Bobcat", words, exempt), [1]) self.assertEqual(D.hits("only a bobcat here", words, exempt), []) self.assertEqual(D.hits("x SecretProj y", words, exempt), [2]) self.assertEqual(D.hits("clean", words, exempt), []) class HookTest(unittest.TestCase): """Real temp repo with the committed hook dir; commits through git itself.""" def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.repo = Path(self.tmp.name) / "r" self.repo.mkdir() git(self.repo, "init", "-q", "-b", "master") shutil.copytree(ROOT / ".githooks", self.repo / ".githooks") (self.repo / "scripts").mkdir() shutil.copy(ROOT / "scripts" / "denylist_check.py", self.repo / "scripts") (self.repo / ".gitignore").write_text("publish-denylist.local\n") git(self.repo, "config", "core.hooksPath", ".githooks") (self.repo / "publish-denylist.local").write_text("# private\nSecretProj\n!secretprojector\n") self.put("a.txt", "hello\n") git(self.repo, "add", ".githooks", "scripts", ".gitignore") r = self.commit("init") self.assertEqual(r.returncode, 0, r.stderr) def tearDown(self): self.tmp.cleanup() def put(self, path, text): f = self.repo / path f.parent.mkdir(parents=True, exist_ok=True) f.write_text(text) git(self.repo, "add", path) def commit(self, msg, cwd=None): return git(cwd or self.repo, "commit", "-q", "-m", msg, check=False) def head(self, cwd=None): return git(cwd or self.repo, "log", "--format=%s").stdout.splitlines() def test_content_hit_blocks_without_printing_word(self): self.put("b.txt", "x\nsee secretproj docs\n") r = self.commit("add b") self.assertEqual(r.returncode, 1) self.assertIn("b.txt:2: word #2", r.stderr) self.assertNotIn("secretproj", r.stderr.lower()) self.assertEqual(self.head(), ["init"]) def test_path_hit_blocks(self): self.put("docs/SECRETPROJ-notes.md", "clean\n") r = self.commit("add notes") self.assertEqual(r.returncode, 1) self.assertIn("path docs/***-notes.md: word #2", r.stderr) self.assertNotIn("secretproj", r.stderr.lower()) def test_message_hit_blocks(self): self.put("b.txt", "clean\n") r = self.commit("port from SecretProj") self.assertEqual(r.returncode, 1) self.assertIn("commit message:1: word #2", r.stderr) self.assertEqual(self.head(), ["init"]) def test_clean_and_exempt_pass(self): self.put("b.txt", "the secretprojector is fine\n") r = self.commit("clean msg") self.assertEqual(r.returncode, 0, r.stderr) self.assertEqual(self.head(), ["clean msg", "init"]) def test_only_added_lines_count(self): (self.repo / "publish-denylist.local").write_text("") # let an old hit in self.put("old.txt", "SecretProj legacy\n") self.assertEqual(self.commit("old").returncode, 0) (self.repo / "publish-denylist.local").write_text("SecretProj\n") self.put("old.txt", "SecretProj legacy\nnew clean line\n") r = self.commit("touch old") self.assertEqual(r.returncode, 0, r.stderr) def test_missing_denylist_warns_not_fails(self): (self.repo / "publish-denylist.local").unlink() self.put("b.txt", "SecretProj\n") r = self.commit("no list") self.assertEqual(r.returncode, 0, r.stderr) self.assertIn("warning: no publish-denylist.local", r.stderr) def test_linked_worktree_uses_main_tree_list(self): wt = Path(self.tmp.name) / "wt" git(self.repo, "worktree", "add", "-q", str(wt), "-b", "topic") self.assertFalse((wt / "publish-denylist.local").exists()) (wt / "c.txt").write_text("SecretProj\n") git(wt, "add", "c.txt") r = self.commit("wt", cwd=wt) self.assertEqual(r.returncode, 1) self.assertIn("c.txt:1: word #2", r.stderr) def test_extra_list_from_git_config_merged(self): glob = Path(self.tmp.name) / "global.txt" glob.write_text("Alpha\n") git(self.repo, "config", "--add", "denylist.file", str(glob)) self.put("b.txt", "alpha\n") r = self.commit("b") self.assertEqual(r.returncode, 1) self.assertIn("b.txt:1: global.txt word #1", r.stderr) self.assertNotIn("alpha", r.stderr.lower()) def test_tree_mode_scans_tracked_files(self): (self.repo / "publish-denylist.local").write_text("") self.put("old.txt", "x SecretProj\n") self.assertEqual(self.commit("old").returncode, 0) (self.repo / "publish-denylist.local").write_text("SecretProj\n") r = subprocess.run([sys.executable, "scripts/denylist_check.py", "tree"], cwd=self.repo, capture_output=True, text=True, env=ENV) self.assertEqual(r.returncode, 1) self.assertIn("old.txt:1: word #1", r.stderr) if __name__ == "__main__": unittest.main() |