import json import os import subprocess import tempfile import unittest from pathlib import Path class EntrypointConfigTests(unittest.TestCase): @staticmethod def _repo_root() -> Path: return Path(__file__).resolve().parents[1] @classmethod def _entrypoint_path(cls) -> Path: return cls._repo_root() / "scripts" / "openclaw-entrypoint.sh" @staticmethod def _write_fake_openclaw(bin_dir: Path) -> None: fake_openclaw = bin_dir / "openclaw" fake_openclaw.write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") fake_openclaw.chmod(0o755) def _run_gateway(self, extra_env=None): if extra_env is None: extra_env = {} with tempfile.TemporaryDirectory() as tmp: tmp_path = Path(tmp) state_dir = tmp_path / "state" home_dir = tmp_path / "home" workspace_dir = state_dir / "workspace" logs_dir = tmp_path / "logs" bin_dir = tmp_path / "bin" config_path = state_dir / "openclaw.json" state_dir.mkdir(parents=True) home_dir.mkdir(parents=True) workspace_dir.mkdir(parents=True) logs_dir.mkdir(parents=True) bin_dir.mkdir(parents=True) self._write_fake_openclaw(bin_dir) env = os.environ.copy() for key in ("OPENCLAW_LLM_BASE_URL", "OPENCLAW_LLM_MODEL", "OPENCLAW_LLM_API_KEY"): env.pop(key, None) env.update( { "PATH": f"{bin_dir}:{env.get('PATH', '')}", "OPENCLAW_HOME": str(home_dir), "OPENCLAW_STATE_DIR": str(state_dir), "OPENCLAW_CONFIG_PATH": str(config_path), "OPENCLAW_WORKSPACE_DIR": str(workspace_dir), "OPENCLAW_STDOUT_LOG_PATH": str(logs_dir / "gateway.stdout.log"), "OPENCLAW_STDERR_LOG_PATH": str(logs_dir / "gateway.stderr.log"), "OPENCLAW_BACKUP_ENABLED": "false", "OPENCLAW_SSHX_AUTO_START": "false", } ) env.update(extra_env) result = subprocess.run( [str(self._entrypoint_path()), "gateway"], cwd=self._repo_root(), env=env, capture_output=True, text=True, check=True, ) with config_path.open("r", encoding="utf-8") as fh: config = json.load(fh) return config, result def test_gateway_configures_custom_model_when_llm_env_present(self): config, _ = self._run_gateway( { "OPENCLAW_LLM_BASE_URL": "https://example.com/v1", "OPENCLAW_LLM_MODEL": "demo-model", "OPENCLAW_LLM_API_KEY": "demo-key", } ) self.assertEqual(config["agents"]["defaults"]["model"]["primary"], "thirdparty/demo-model") self.assertEqual( config["agents"]["defaults"]["models"]["thirdparty/demo-model"]["alias"], "Default", ) self.assertEqual(config["models"]["mode"], "merge") self.assertEqual(config["models"]["providers"]["thirdparty"]["baseUrl"], "${OPENCLAW_LLM_BASE_URL}") self.assertEqual(config["models"]["providers"]["thirdparty"]["apiKey"], "${OPENCLAW_LLM_API_KEY}") self.assertEqual(config["models"]["providers"]["thirdparty"]["api"], "openai-completions") self.assertEqual( config["models"]["providers"]["thirdparty"]["models"], [{"id": "demo-model", "name": "demo-model"}], ) def test_gateway_skips_custom_model_when_llm_env_missing(self): config, result = self._run_gateway() defaults = config.get("agents", {}).get("defaults", {}) providers = config.get("models", {}).get("providers", {}) self.assertNotIn("model", defaults) self.assertNotIn("models", defaults) self.assertNotIn("thirdparty", providers) self.assertIn("OPENCLAW_SSHX_AUTO_START=true", result.stderr) if __name__ == "__main__": unittest.main()