"""Tests for shared-home MCP script sync (cloud/shared_home_mcp.py). No production path is ever touched here: every sync target is a temp dir, and the server-resolution tests monkeypatch the resolver inputs instead of reading the repo's host-config-host.json (which points at the production shared home). Coverage: - the 7-file list is the single source of truth (exact set); - missing files are copied; - differing files are refreshed, same files are untouched (mtime unchanged); - missing source files do not crash and are reported; - an uncreatable target and a per-file unwritable destination (a directory in place of a file, root-proof) do not crash and do not block the other files; - server resolution: local worker home wins, tenants-root parent is the fallback; - server main() actually calls the sync at startup and survives a sync error (fake server, no real port, no real DB). """ import os import sys import tempfile import unittest from pathlib import Path from unittest import mock sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from shared_home_mcp import SHARED_HOME_MCP_FILES # noqa: E402 from shared_home_mcp import sync_shared_home_mcp # noqa: E402 try: from cloud.server import _resolve_shared_home_mcp from cloud.server import main as _server_main _SERVER_OK = True except ImportError: sys.path.insert(0, str(Path(__file__).resolve().parents[2])) try: from cloud.server import _resolve_shared_home_mcp from cloud.server import main as _server_main _SERVER_OK = True except ImportError: _resolve_shared_home_mcp = None _server_main = None _SERVER_OK = False def _write_source(source_dir, contents): for name, data in contents.items(): with open(os.path.join(source_dir, name), "wb") as fh: fh.write(data) class SharedHomeMcpSyncTest(unittest.TestCase): def setUp(self): self._tmp = tempfile.TemporaryDirectory() self.source = os.path.join(self._tmp.name, "cloud") self.target = os.path.join(self._tmp.name, "shared-home") os.makedirs(self.source) self.contents = {name: ("# %s v2\n" % name).encode() for name in SHARED_HOME_MCP_FILES} def tearDown(self): self._tmp.cleanup() def test_file_list_is_exactly_the_seven(self): self.assertEqual(len(SHARED_HOME_MCP_FILES), 7) self.assertEqual(len(set(SHARED_HOME_MCP_FILES)), 7) self.assertEqual(set(SHARED_HOME_MCP_FILES), { "playwright_mcp.py", "playwright_browser.py", "playwright_remote.py", "websearch_mcp.py", "websearch.py", "imagegen_mcp.py", "imagegen.py", }) def test_missing_files_are_copied(self): _write_source(self.source, self.contents) result = sync_shared_home_mcp(self.target, self.source) self.assertEqual(sorted(result["copied"]), sorted(SHARED_HOME_MCP_FILES)) self.assertEqual(result["refreshed"], []) self.assertEqual(result["unchanged"], []) self.assertEqual(result["missing"], []) self.assertIsNone(result["error"]) for name, data in self.contents.items(): with open(os.path.join(self.target, name), "rb") as fh: self.assertEqual(fh.read(), data) def test_differing_files_are_refreshed(self): _write_source(self.source, self.contents) os.makedirs(self.target) for name in SHARED_HOME_MCP_FILES: with open(os.path.join(self.target, name), "wb") as fh: fh.write(b"# old\n") result = sync_shared_home_mcp(self.target, self.source) self.assertEqual(sorted(result["refreshed"]), sorted(SHARED_HOME_MCP_FILES)) self.assertEqual(result["copied"], []) self.assertIsNone(result["error"]) def test_same_files_are_untouched(self): _write_source(self.source, self.contents) os.makedirs(self.target) mtimes = {} for name, data in self.contents.items(): path = os.path.join(self.target, name) with open(path, "wb") as fh: fh.write(data) os.utime(path, (1000000000, 1000000000)) mtimes[name] = os.stat(path).st_mtime result = sync_shared_home_mcp(self.target, self.source) self.assertEqual(sorted(result["unchanged"]), sorted(SHARED_HOME_MCP_FILES)) self.assertEqual(result["copied"], []) self.assertEqual(result["refreshed"], []) for name, mtime in mtimes.items(): self.assertEqual(os.stat(os.path.join(self.target, name)).st_mtime, mtime) def test_missing_source_does_not_crash(self): # Source dir exists but is empty: every file is missing. result = sync_shared_home_mcp(self.target, self.source) self.assertEqual(sorted(result["missing"]), sorted(SHARED_HOME_MCP_FILES)) self.assertEqual(result["copied"], []) self.assertIsNotNone(result["error"]) self.assertIn("missing source", result["error"]) def test_uncreatable_target_does_not_crash(self): _write_source(self.source, self.contents) # Target path is an existing file, not a directory: # makedirs fails even as root, and must not raise. blocker = os.path.join(self._tmp.name, "blocker") with open(blocker, "wb") as fh: fh.write(b"x") result = sync_shared_home_mcp(blocker, self.source) self.assertEqual(result["copied"], []) self.assertIsNotNone(result["error"]) def test_one_unwritable_file_does_not_block_others(self): _write_source(self.source, self.contents) os.makedirs(self.target) bad = SHARED_HOME_MCP_FILES[0] os.makedirs(os.path.join(self.target, bad)) result = sync_shared_home_mcp(self.target, self.source) self.assertEqual(len(result["copied"]), len(SHARED_HOME_MCP_FILES) - 1) self.assertIn(bad, result["error"]) for name in SHARED_HOME_MCP_FILES[1:]: self.assertTrue(os.path.isfile(os.path.join(self.target, name))) def test_no_shared_home_does_not_crash(self): result = sync_shared_home_mcp(None, self.source) self.assertIsNotNone(result["error"]) self.assertEqual(result["copied"], []) @unittest.skipUnless(_SERVER_OK, "cloud.server import unavailable") class SharedHomeMcpServerTest(unittest.TestCase): def test_local_worker_home_wins(self): import cloud.server as server_mod with mock.patch.object(server_mod, "_local_worker_codex_home", return_value="/tmp/fake-shared"): self.assertEqual(_resolve_shared_home_mcp(object()), "/tmp/fake-shared") def test_tenants_root_parent_is_fallback(self): import cloud.server as server_mod class _FakeServer: tenants_root = "/tmp/fake-root/tenants" with mock.patch.object(server_mod, "_local_worker_codex_home", return_value=None): self.assertEqual(_resolve_shared_home_mcp(_FakeServer()), "/tmp/fake-root/codex-home") def test_main_calls_sync_and_survives_sync_error(self): import cloud.server as server_mod class _FakeServer: rate_limiter = None server_address = ("127.0.0.1", 0) def serve_forever(self): raise KeyboardInterrupt calls = {} fake = _FakeServer() def _fake_sync(target): calls["target"] = target return {"copied": [], "refreshed": [], "unchanged": [], "missing": [], "error": "boom (test)"} with mock.patch.object(server_mod, "create_server", return_value=fake), \ mock.patch.object(server_mod, "_resolve_shared_home_mcp", return_value="/tmp/fake-shared"), \ mock.patch.object(server_mod, "_sync_shared_home_mcp", side_effect=_fake_sync), \ mock.patch.object(server_mod, "start_rate_limit_sweeper"): # No exception: sync error is warning-only, and the # KeyboardInterrupt from serve_forever ends main(). _server_main(["--db", "/tmp/fake.db", "--port", "0"]) self.assertEqual(calls.get("target"), "/tmp/fake-shared") if __name__ == "__main__": unittest.main()