Replace bare open().read()/write() in tests with Path.read_text/
write_text (or json.loads over read_text); the five remaining
open(...).close() empty-file creations become Path.write_text('').
No assertion, predicate, or production code changed; no behaviour
change beyond closing the handle deterministically.
Long-function splits and server.py module split intentionally not
included: the longest functions (_dispatch, _create_task,
_dispatch_project, ~375-395 lines in a 10430-line server.py) are
central routing/creation paths, and splitting them is not low-risk;
see PR body.
171 lines
7.4 KiB
Python
171 lines
7.4 KiB
Python
"""多租户隔离单测。
|
||
|
||
只用临时目录与假 runner,不跑真实 Docker、不联网。
|
||
覆盖:租户目录播种(codex-home 只取 config.toml,绝不
|
||
复制 thread_history)、挂载按项目分开、网络名安全化、
|
||
执行器 per-call 挂载/网络覆盖、网络创建失败不降级。
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import tempfile
|
||
import unittest
|
||
from pathlib import Path
|
||
from types import SimpleNamespace
|
||
from unittest import mock
|
||
|
||
try:
|
||
from cloud.sandbox import DockerExecutor
|
||
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.sandbox import DockerExecutor
|
||
from cloud.server import init_db
|
||
from cloud.worker import Worker
|
||
|
||
|
||
def _ok_runner_factory(seen):
|
||
def fake(cmd, timeout=None):
|
||
seen.append(list(cmd))
|
||
return SimpleNamespace(returncode=0, stdout="", stderr="")
|
||
return fake
|
||
|
||
|
||
class TenantDirsTestCase(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)
|
||
# 模板 home:config.toml + 绝不能复制的 thread_history
|
||
# 与假密钥文件;模板 repo:sample.py + .git。
|
||
self.template_home = os.path.join(self.tmp.name, "tpl-home")
|
||
os.makedirs(self.template_home)
|
||
Path(os.path.join(self.template_home, "config.toml")).write_text(
|
||
'[model_provider.xcmapi]\nbase_url="https://xcmapi.org/v1"\n',
|
||
encoding="utf-8")
|
||
Path(os.path.join(self.template_home, "thread_history_1.sqlite")).write_text(
|
||
"OTHER TENANT HISTORY", encoding="utf-8")
|
||
Path(os.path.join(self.template_home, "auth.json")).write_text(
|
||
"SECRET", encoding="utf-8")
|
||
self.template_repo = os.path.join(self.tmp.name, "tpl-repo")
|
||
os.makedirs(os.path.join(self.template_repo, ".git"))
|
||
Path(os.path.join(self.template_repo, "sample.py")).write_text(
|
||
"def add(a, b):\n return a + b\n", encoding="utf-8")
|
||
|
||
def tearDown(self):
|
||
self.tmp.cleanup()
|
||
|
||
def _worker(self, **kwargs):
|
||
kwargs.setdefault("env", {"XCM_API_KEY": "test-placeholder"})
|
||
kwargs.setdefault("tenants_root",
|
||
os.path.join(self.tmp.name, "tenants"))
|
||
kwargs.setdefault("tenant_template_home", self.template_home)
|
||
kwargs.setdefault("tenant_template_repo", self.template_repo)
|
||
kwargs.setdefault("tenant_isolation", True)
|
||
return Worker(self.db_path, "/fake/host.py", **kwargs)
|
||
|
||
def test_dirs_seed_config_only_not_history(self):
|
||
worker = self._worker()
|
||
dirs = worker._tenant_dirs("projA")
|
||
home = dirs["codex_home"]
|
||
self.assertTrue(os.path.exists(os.path.join(home, "config.toml")))
|
||
self.assertFalse(os.path.exists(
|
||
os.path.join(home, "thread_history_1.sqlite")))
|
||
self.assertFalse(os.path.exists(os.path.join(home, "auth.json")))
|
||
# test-repo 副本有 sample.py,且是复制不是链接。
|
||
self.assertTrue(os.path.exists(
|
||
os.path.join(dirs["test_repo"], "sample.py")))
|
||
# 第二次调用不覆盖租户自己的改动。
|
||
marker = os.path.join(dirs["test_repo"], "mine.txt")
|
||
Path(marker).write_text("mine", encoding="utf-8")
|
||
worker._tenant_dirs("projA")
|
||
self.assertTrue(os.path.exists(marker))
|
||
|
||
def test_mounts_are_per_project(self):
|
||
worker = self._worker()
|
||
mounts_a = worker._tenant_mounts("projA")
|
||
mounts_b = worker._tenant_mounts("projB")
|
||
# 容器内路径相同(/app/work 等),宿主源不同项目不同。
|
||
cont_a = [m.split(":")[1] for m in mounts_a]
|
||
cont_b = [m.split(":")[1] for m in mounts_b]
|
||
self.assertEqual(cont_a, cont_b)
|
||
self.assertIn("/app/work", cont_a)
|
||
self.assertIn("/var/lib/codex-app-server/codex-home", cont_a)
|
||
hosts_a = {m.split(":")[0] for m in mounts_a}
|
||
hosts_b = {m.split(":")[0] for m in mounts_b}
|
||
self.assertFalse(hosts_a & hosts_b)
|
||
# 租户 A 的 work 里写文件,B 的挂载里看不到(宿主侧)。
|
||
Path(os.path.join(worker._tenant_dirs("projA")["work"],
|
||
"secret-a.txt")).write_text("SECRET-A", encoding="utf-8")
|
||
self.assertFalse(os.path.exists(os.path.join(
|
||
worker._tenant_dirs("projB")["work"], "secret-a.txt")))
|
||
|
||
def test_network_name_sanitized(self):
|
||
worker = self._worker()
|
||
self.assertEqual(worker._tenant_network("abc123"),
|
||
"codex-abc123")
|
||
self.assertEqual(worker._tenant_network("a b/c"),
|
||
"codex-a-b-c")
|
||
with self.assertRaises(ValueError):
|
||
worker._tenant_network("...")
|
||
|
||
def test_template_home_without_config_fails_loudly(self):
|
||
empty_home = os.path.join(self.tmp.name, "empty-home")
|
||
os.makedirs(empty_home)
|
||
worker = self._worker(tenant_template_home=empty_home)
|
||
with self.assertRaises(RuntimeError):
|
||
worker._tenant_dirs("projA")
|
||
|
||
|
||
class DockerTenantCommandTestCase(unittest.TestCase):
|
||
def test_per_call_mounts_network_and_drop_caps(self):
|
||
seen = []
|
||
static_dir = tempfile.mkdtemp()
|
||
tenant_dir = tempfile.mkdtemp()
|
||
with mock.patch("cloud.sandbox.shutil.which",
|
||
return_value="/usr/bin/docker"):
|
||
ex = DockerExecutor(
|
||
"/fake/host.py",
|
||
runner=_ok_runner_factory(seen),
|
||
env={"XCM_API_KEY": "test-placeholder"},
|
||
extra_mounts=["%s:/app/work:rw" % static_dir])
|
||
cmd = ex.build_run_command(
|
||
{"id": "t1", "worktree": "/tmp/wt"},
|
||
["echo", "hi"],
|
||
extra_mounts=["%s:/app/work:rw" % tenant_dir],
|
||
network="codex-projA")
|
||
mounts = [cmd[i + 1] for i, a in enumerate(cmd) if a == "-v"]
|
||
# per-call 挂载替换静态挂载,不是追加。
|
||
self.assertIn("%s:/app/work:rw" % tenant_dir, mounts)
|
||
self.assertNotIn("%s:/app/work:rw" % static_dir, mounts)
|
||
self.assertEqual(cmd[cmd.index("--network") + 1], "codex-projA")
|
||
self.assertIn("--cap-drop=ALL", cmd)
|
||
self.assertIn("--security-opt=no-new-privileges", cmd)
|
||
|
||
def test_ensure_network_already_exists_is_ok(self):
|
||
def exists_runner(cmd, timeout=None):
|
||
return SimpleNamespace(
|
||
returncode=1, stdout="",
|
||
stderr="Error response from daemon: network with name "
|
||
"codex-p already exists")
|
||
with mock.patch("cloud.sandbox.shutil.which",
|
||
return_value="/usr/bin/docker"):
|
||
ex = DockerExecutor("/fake/host.py", runner=exists_runner,
|
||
env={"XCM_API_KEY": "test-placeholder"})
|
||
self.assertTrue(ex.ensure_network("codex-p"))
|
||
|
||
def test_ensure_network_real_failure_is_false(self):
|
||
def fail_runner(cmd, timeout=None):
|
||
return SimpleNamespace(returncode=1, stdout="",
|
||
stderr="daemon not reachable")
|
||
with mock.patch("cloud.sandbox.shutil.which",
|
||
return_value="/usr/bin/docker"):
|
||
ex = DockerExecutor("/fake/host.py", runner=fail_runner,
|
||
env={"XCM_API_KEY": "test-placeholder"})
|
||
self.assertFalse(ex.ensure_network("codex-p"))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|