"""cloud/tree.py 单测:结构、忽略、符号链接、截断、渲染。 不联网、不跑模型;只建临时目录。 """ import os import sys import tempfile import unittest from pathlib import Path try: from cloud.tree import build_tree, render_tree_text except ImportError: sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from cloud.tree import build_tree, render_tree_text class TreeTestCase(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.root = Path(self.tmp.name) (self.root / "pkg").mkdir() (self.root / "pkg" / "deep").mkdir() (self.root / "pkg" / "mod.py").write_text("x", encoding="utf-8") (self.root / "pkg" / "deep" / "leaf.txt").write_text("y", encoding="utf-8") (self.root / "README.md").write_text("# hi\n", encoding="utf-8") (self.root / ".git").mkdir() (self.root / ".git" / "config").write_text("x", encoding="utf-8") (self.root / "node_modules").mkdir() (self.root / "node_modules" / "pkg").mkdir() os.symlink("/etc/passwd", self.root / "esc") def tearDown(self): self.tmp.cleanup() def _names(self, tree): return [(e["name"], e["type"]) for e in tree["entries"]] def test_root_structure_dirs_first_and_ignore(self): tree = build_tree(str(self.root)) # 目录在前、同类按名:pkg(目录)在文件之前;.git 与 # node_modules 一律不出现;符号链接列出但不跟进。 self.assertEqual(self._names(tree), [("pkg", "dir"), ("esc", "symlink"), ("README.md", "file")]) self.assertFalse(tree["truncated"]) pkg = tree["entries"][0] self.assertEqual([c["name"] for c in pkg["children"]], ["deep", "mod.py"]) self.assertEqual(pkg["children"][0]["children"][0]["name"], "leaf.txt") def test_max_depth_cuts_children_with_flag(self): tree = build_tree(str(self.root), max_depth=1) pkg = tree["entries"][0] self.assertEqual(pkg["children"], []) self.assertTrue(pkg["children_truncated"]) tree2 = build_tree(str(self.root), max_depth=2) deep = tree2["entries"][0]["children"][0] self.assertEqual(deep["children"], []) self.assertTrue(deep["children_truncated"]) def test_max_entries_truncates_deterministically(self): def total(entries): return sum(1 + total(e.get("children") or []) for e in entries) tree = build_tree(str(self.root), max_entries=2) self.assertTrue(tree["truncated"]) # 条数按整棵树计(含子层):pkg + deep = 2 条即停, # pkg 的 children 因此半截且 children_truncated=True。 self.assertEqual(total(tree["entries"]), 2) self.assertTrue(tree["entries"][0]["children_truncated"]) # 再次跑同一输入,结果必须相同(截断不可随机)。 again = build_tree(str(self.root), max_entries=2) self.assertEqual(tree, again) def test_subdir_and_size(self): tree = build_tree(str(self.root), path="pkg") self.assertEqual(tree["path"], "pkg") mod = [e for e in tree["entries"] if e["name"] == "mod.py"][0] self.assertEqual(mod["path"], "pkg/mod.py") self.assertEqual(mod["size"], 1) def test_escape_file_and_missing(self): with self.assertRaises(ValueError): build_tree(str(self.root), path="../outside") with self.assertRaises(ValueError): build_tree(str(self.root), path="/etc/passwd") with self.assertRaises(FileNotFoundError): build_tree(str(self.root), path="nope") with self.assertRaises(NotADirectoryError): build_tree(str(self.root), path="README.md") # 指向 worktree 外的符号链接作 path 时同样逃逸。 with self.assertRaises(ValueError): build_tree(str(self.root), path="esc") def test_render_text(self): tree = build_tree(str(self.root), max_depth=2) text = render_tree_text(tree) self.assertIn("pkg/", text) self.assertIn("mod.py", text) self.assertIn("esc@", text) self.assertNotIn(".git", text) self.assertIn("…", text) # max_depth=2 时 deep 的子层被截断 if __name__ == "__main__": unittest.main()