import json, os, subprocess, sys, tempfile, unittest ROOT = os.path.dirname(os.path.abspath(__file__)) SCRIPT = os.path.join(ROOT, "dedup.py") PY = sys.executable def make_tree(tmp): d = os.path.join(tmp, "t") os.makedirs(d) a = os.path.join(d, "a.txt"); b = os.path.join(d, "b.txt") with open(a, "w") as f: f.write("hello world\n" * 100) with open(b, "w") as f: f.write("hello world\n" * 100) with open(os.path.join(d, "c.txt"), "w") as f: f.write("unique content") open(os.path.join(d, "e1"), "w").close() open(os.path.join(d, "e2"), "w").close() sub = os.path.join(d, "sub"); os.makedirs(sub) with open(os.path.join(sub, "dup.txt"), "w") as f: f.write("hello world\n" * 100) return d def run(args): return subprocess.run([PY, SCRIPT] + args, capture_output=True, text=True) class Tests(unittest.TestCase): def test_finds_duplicates(self): with tempfile.TemporaryDirectory() as td: d = make_tree(td) r = run([d, "--json"]) self.assertEqual(r.returncode, 0, r.stderr) groups = json.loads(r.stdout) sizes = sorted(len(g["files"]) for g in groups) self.assertEqual(sizes, [2, 3], sizes) for g in groups: self.assertEqual(len(g["hash"]), 64) def test_no_duplicates(self): with tempfile.TemporaryDirectory() as td: d = os.path.join(td, "t2"); os.makedirs(d) with open(os.path.join(d, "x"), "w") as f: f.write("x") r = run([d]) self.assertEqual(r.returncode, 0) self.assertIn("no duplicates", r.stdout) def test_min_size_filters(self): with tempfile.TemporaryDirectory() as td: d = make_tree(td) r = run([d, "--min-size", "100000", "--json"]) self.assertEqual(r.returncode, 0) self.assertEqual(json.loads(r.stdout), []) def test_missing_dir(self): r = run(["/no/such/dir/xyz"]) self.assertEqual(r.returncode, 2) self.assertIn("not a directory", r.stderr) def test_symlink_skipped_by_default(self): with tempfile.TemporaryDirectory() as td: d = make_tree(td) link = os.path.join(d, "link.txt") os.symlink(os.path.join(d, "a.txt"), link) r = run([d, "--json"]) groups = json.loads(r.stdout) content_group = [g for g in groups if g["size"] > 0][0] self.assertEqual(len(content_group["files"]), 3) self.assertNotIn(link, content_group["files"]) def test_follow_symlinks(self): with tempfile.TemporaryDirectory() as td: d = make_tree(td) link = os.path.join(d, "link.txt") os.symlink(os.path.join(d, "a.txt"), link) r = run([d, "--follow-symlinks", "--json"]) groups = json.loads(r.stdout) content_group = [g for g in groups if g["size"] > 0][0] self.assertEqual(len(content_group["files"]), 4) if __name__ == "__main__": unittest.main()