"""Worker wiring for web search MCP: tenant seeding + egress filter. No Docker, no network: _tenant_dirs runs against temp dirs, and the egress check only reads the repo filter file. """ 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 REPO_ROOT = Path(__file__).resolve().parents[2] class WorkerWebsearchTestCase(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.template_home = os.path.join(self.tmp.name, "tpl-home") os.makedirs(self.template_home) open(os.path.join(self.template_home, "config.toml"), "w").write('[model_provider.xcmapi]\n' 'base_url="https://xcmapi.org/v1"\n') self.template_repo = os.path.join(self.tmp.name, "tpl-repo") os.makedirs(os.path.join(self.template_repo, ".git")) open(os.path.join(self.template_repo, "sample.py"), "w").write("def add(a, b):\n return a + b\n") 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_flag_off_is_old_behaviour(self): worker = self._worker() self.assertFalse(worker.websearch_mcp) dirs = worker._tenant_dirs("projA") config = open(os.path.join(dirs["codex_home"], "config.toml")).read() self.assertNotIn("[mcp_servers.websearch]", config) self.assertFalse(os.path.exists( os.path.join(dirs["codex_home"], "websearch_mcp.py"))) def test_flag_on_seeds_container_path_and_scripts(self): worker = self._worker(websearch_mcp=True) dirs = worker._tenant_dirs("projA") home = dirs["codex_home"] config = open(os.path.join(home, "config.toml")).read() self.assertIn("[mcp_servers.websearch]", config) # Container path, not the host temp path: this config is # read inside the task container, where the tenant home is # mounted at /var/lib/codex-app-server/codex-home. self.assertIn("/var/lib/codex-app-server/codex-home/" "websearch_mcp.py", config) self.assertNotIn(self.tmp.name, config) self.assertTrue(os.path.exists( os.path.join(home, "websearch.py"))) self.assertTrue(os.path.exists( os.path.join(home, "websearch_mcp.py"))) # The copied MCP script is the real one (not a stub). script = open(os.path.join(home, "websearch_mcp.py")).read() self.assertIn("web_search", script) self.assertIn("web_fetch", script) def test_old_tenant_merges_on_next_task(self): worker = self._worker() dirs = worker._tenant_dirs("projA") config_path = os.path.join(dirs["codex_home"], "config.toml") self.assertNotIn("[mcp_servers.websearch]", open(config_path).read()) # Flag flips on later (deploy enables it): next task's # _tenant_dirs must merge, not require a new project. worker2 = self._worker(websearch_mcp=True) worker2._tenant_dirs("projA") config = open(config_path).read() self.assertIn("[mcp_servers.websearch]", config) self.assertIn("xcmapi.org", config) self.assertEqual(config.count("[mcp_servers.websearch]"), 1) # Third call stays idempotent. worker2._tenant_dirs("projA") config = open(config_path).read() self.assertEqual(config.count("[mcp_servers.websearch]"), 1) class EgressFilterTestCase(unittest.TestCase): def test_search_domains_present_in_basic_form(self): lines = open(REPO_ROOT / "deploy" / "egress" / "tinyproxy-filter").read().splitlines() entries = [ln for ln in lines if ln and not ln.startswith("#")] # No extended-regex syntax in any entry: + ? | and bare # parens are literal in BASIC and would break the allowlist. for entry in entries: self.assertNotIn("+", entry) self.assertNotIn("?", entry) self.assertNotIn("|", entry) self.assertNotIn("(", entry.replace("\\(", "")) self.assertNotIn(")", entry.replace("\\)", "")) expected = [ r"^\(.*\.\)*perplexity\.ai$", r"^\(.*\.\)*exa\.ai$", r"^\(.*\.\)*tavily\.com$", r"^\(.*\.\)*search\.brave\.com$", r"^\(.*\.\)*duckduckgo\.com$", r"^searx\.diante\.me$", ] for want in expected: self.assertIn(want, entries, "missing filter entry: %s" % want) if __name__ == "__main__": unittest.main()