Files
codex-app-server/cloud/tests/test_worker_tree_injection.py
host ce7fb3afd6 feat: project directory tree + first-round snapshot
- cloud/tree.py: structured worktree tree (dirs first, ignore
  .git/node_modules, symlinks not followed, entry/depth
  truncation flags, text render for injection)
- GET /tasks/{id}/tree with /files auth + worktree resolution,
  max_depth 1..5, max_entries 1..500, escape 400, missing 404,
  cloud/cross-host 409
- MCP get_task_tree translating to the new endpoint
- worker first-round injection of a repo-checkout top-level
  snapshot (2 layers, 120 entries) after repo resolution,
  labelled as checkout snapshot not worktree; skipped for
  coordinator/resume/fork, silent when no host source
- docs/project-tree.md; tests for tree, endpoint, injection,
  MCP translation
2026-10-30 14:46:11 +08:00

111 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""worker 首轮目录树注入单测:只改内存副本,跳过规则与来源。
不跑真实模型、不派发:直接调 _apply_tree_injection,Worker
用 test_worker 同款最小构造(db 在临时目录、假 host.py)。
"""
import os
import sys
import tempfile
import unittest
from pathlib import Path
try:
from cloud.server import init_db
from cloud.worker import Worker
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.server import init_db
from cloud.worker import Worker
def _completed_runner(cmd):
from types import SimpleNamespace
return SimpleNamespace(returncode=0, stdout="ok", stderr="")
class TreeInjectionTestCase(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
self.repo = os.path.join(self.tmp.name, "repo")
os.makedirs(os.path.join(self.repo, "pkg"))
Path(self.repo, "pkg", "mod.py").write_text("x", encoding="utf-8")
Path(self.repo, "README.md").write_text("hi", encoding="utf-8")
os.makedirs(os.path.join(self.repo, ".git"))
self.worker = Worker(
self.db_path, "/fake/host.py",
runner=_completed_runner,
env={"XCM_API_KEY": "test-placeholder"})
def tearDown(self):
self.tmp.cleanup()
def _task(self, **over):
task = {"id": "t1", "project_id": "p1", "prompt": "做一件事",
"repo": self.repo}
task.update(over)
return task
def test_injects_snapshot_and_keeps_prompt(self):
out = self.worker._apply_tree_injection(self._task())
self.assertTrue(out["_tree_injected"])
self.assertTrue(out["prompt"].endswith("做一件事"))
self.assertIn("[项目目录结构:仓库检出顶层快照", out["prompt"])
self.assertIn("pkg/", out["prompt"])
self.assertIn("mod.py", out["prompt"])
self.assertNotIn(".git", out["prompt"])
# 原任务不被改动:注入只改内存副本。
original = self._task()
self.worker._apply_tree_injection(original)
self.assertEqual(original["prompt"], "做一件事")
self.assertNotIn("_tree_injected", original)
def test_goal_task_updates_goal_too(self):
out = self.worker._apply_tree_injection(
self._task(goal_mode=1, goal="目标"))
self.assertIn("[项目目录结构", out["goal"])
self.assertTrue(out["goal"].endswith("目标"))
def test_skip_coordinator_resume_fork_and_no_source(self):
for over in ({"coordinator_parts": [{"name": "a"}]},
{"_resume": True},
{"_goal_resume_breakpoint": True},
{"forked_from": "parent"}):
out = self.worker._apply_tree_injection(self._task(**over))
self.assertEqual(out["prompt"], "做一件事", over)
self.assertNotIn("_tree_injected", out)
out = self.worker._apply_tree_injection(
self._task(repo="/nonexistent/repo"))
self.assertEqual(out["prompt"], "做一件事")
self.assertNotIn("_tree_injected", out)
def test_tenant_missing_checkout_with_repo_url_never_falls_back(self):
# 租户 + 有 repo_url + 检出缺失时,绝不回落到 test_repo
# 模板快照(那会误导);repo_url 为空时才回落。
from cloud.sandbox import DockerExecutor
test_repo = os.path.join(self.tmp.name, "test-repo")
os.makedirs(test_repo)
Path(test_repo, "sample.py").write_text("x", encoding="utf-8")
self.worker.tenant_isolation = True
self.worker.executor = DockerExecutor.__new__(DockerExecutor)
self.worker.tenants_root = os.path.join(self.tmp.name, "tenants")
self.worker._tenant_dirs = lambda pid: {"test_repo": test_repo}
task = self._task(repo="/var/lib/codex-app-server/repos/p1")
self.worker._project_repo_url = lambda pid: "https://example.invalid/r.git"
self.assertIsNone(self.worker._tree_snapshot_source(task))
self.worker._project_repo_url = lambda pid: None
self.assertEqual(self.worker._tree_snapshot_source(task), test_repo)
def test_empty_repo_no_section(self):
empty = os.path.join(self.tmp.name, "empty")
os.makedirs(empty)
out = self.worker._apply_tree_injection(self._task(repo=empty))
self.assertEqual(out["prompt"], "做一件事")
self.assertNotIn("_tree_injected", out)
if __name__ == "__main__":
unittest.main()