Files
Harbor/tests/test_install.py
KeelrLeek dbfd1720c5 Harbor as of 2026-10-05: the coordination hub for the Open Sea's decomp team
Messages, threads, acknowledgements, claims, bands, tasks, review items and
stop flags for a team of AI agents; the chat page and board; host hooks and
MCP server; autopilot; tools/watch.py. Put under version control after the
weekend's changes so they have history and can be rolled back. Local only.
2026-10-05 10:44:23 -05:00

156 lines
7.9 KiB
Python

"""Installer tests against a fake user home (HARBOR_INSTALL_HOME) - real host configs are never touched."""
import json
import os
import shutil
import tomllib
import unittest
import yaml
from helpers import make_home, temp_dir
from harbor import install
CODEX_CONFIG = '''model = "gpt"
notify = ["x.exe", "turn-ended"]
[features]
[mcp_servers.node_repl]
command = "node"
[tui.model_availability_nux]
seen = true
'''
DSH_PATCH = '''# Your patch layer
- id: ui-chat
name: "@deepseek-ai/dsh-client-ui-chat"
config:
transcriptView: standard
- id: tools
config:
mode: !!js process.env.DSH_TOOLS_MODE
'''
class Installer(unittest.TestCase):
def setUp(self):
self.dir = temp_dir()
self.home = self.dir / "user"
(self.home / ".codex").mkdir(parents=True)
(self.home / ".codex" / "config.toml").write_text(CODEX_CONFIG, encoding="utf-8")
(self.home / ".codex" / "AGENTS.md").write_text("# My rules\n\nBe brief.\n", encoding="utf-8")
(self.home / ".config" / "opencode").mkdir(parents=True)
(self.home / ".config" / "opencode" / "opencode.jsonc").write_text('{\n "$schema": "https://opencode.ai/config.json"\n}', encoding="utf-8")
(self.home / ".dsh" / "profiles" / "desktop").mkdir(parents=True)
(self.home / ".dsh" / "profiles" / "desktop" / "cordis.patch.yml").write_text(DSH_PATCH, encoding="utf-8")
self.harbor_home = self.dir / "harbor-home"
make_home(self.harbor_home)
self.saved = {k: os.environ.get(k) for k in ("HARBOR_INSTALL_HOME", "HARBOR_HOME", "HARBOR_PROJECT", "APPDATA")}
os.environ.update(HARBOR_INSTALL_HOME=str(self.home), HARBOR_HOME=str(self.harbor_home),
APPDATA=str(self.dir / "appdata"))
os.environ.pop("HARBOR_PROJECT", None)
self.cwd = os.getcwd()
os.chdir(self.dir) # resolve projects from the test folder, not from wherever the suite runs
def tearDown(self):
os.chdir(self.cwd)
for k, v in self.saved.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
shutil.rmtree(self.dir, ignore_errors=True)
def read(self, *parts):
return (self.home.joinpath(*parts)).read_text(encoding="utf-8")
def test_codex_install_is_idempotent_and_reversible(self):
install.install("codex", python="py -3.12")
config = tomllib.loads(self.read(".codex", "config.toml"))
server = config["mcp_servers"]["harbor"]
self.assertEqual(server["env"]["HARBOR_AGENT"], "Codex")
self.assertEqual(server["args"][-3:], ["--as", "Codex", "mcp"])
self.assertEqual(config["mcp_servers"]["node_repl"]["command"], "node") # untouched
self.assertTrue(config["tui"]["model_availability_nux"]["seen"])
hooks = json.loads(self.read(".codex", "hooks.json"))["hooks"]
self.assertEqual(sorted(hooks), ["PreToolUse", "SessionStart", "Stop", "UserPromptSubmit"])
self.assertIn("hook pre-tool --host codex", hooks["PreToolUse"][0]["hooks"][0]["command"])
self.assertIn("Be brief.", self.read(".codex", "AGENTS.md"))
self.assertIn("you are **Codex**", self.read(".codex", "AGENTS.md"))
once = self.read(".codex", "config.toml")
report = install.install("codex", python="py -3.12")
self.assertEqual(self.read(".codex", "config.toml"), once)
self.assertTrue(all(line.startswith("unchanged") for line in report if line.startswith(("unchanged", "update", "create"))))
install.install("codex", remove=True)
self.assertEqual(self.read(".codex", "config.toml"), CODEX_CONFIG)
self.assertEqual(self.read(".codex", "AGENTS.md"), "# My rules\n\nBe brief.\n")
self.assertFalse((self.home / ".codex" / "hooks.json").exists())
backups = list((self.harbor_home / "backups" / "integrations").rglob("config.toml"))
self.assertTrue(backups)
def test_codex_keeps_other_hooks(self):
(self.home / ".codex" / "hooks.json").write_text(json.dumps(
{"hooks": {"Stop": [{"hooks": [{"type": "command", "command": "mine.exe"}]}]}}), encoding="utf-8")
install.install("codex", python="py -3.12")
stop = json.loads(self.read(".codex", "hooks.json"))["hooks"]["Stop"]
self.assertEqual(len(stop), 2)
install.install("codex", remove=True)
self.assertEqual(json.loads(self.read(".codex", "hooks.json")),
{"hooks": {"Stop": [{"hooks": [{"type": "command", "command": "mine.exe"}]}]}})
def test_opencode(self):
install.install("opencode", python="py -3.12")
config = json.loads(self.read(".config", "opencode", "opencode.jsonc"))
self.assertEqual(config["$schema"], "https://opencode.ai/config.json")
self.assertEqual(config["mcp"]["harbor"]["environment"], {"HARBOR_AGENT": "OpenCode", "HARBOR_HOST": "opencode"})
plugin = self.read(".config", "opencode", "plugin", "harbor.js")
self.assertIn('const AGENT = "OpenCode";', plugin)
self.assertIn('const COMMAND = ["py", "-3.12", "-B",', plugin)
self.assertIn("export const HarborPlugin", plugin)
install.install("opencode", remove=True)
self.assertEqual(json.loads(self.read(".config", "opencode", "opencode.jsonc")),
{"$schema": "https://opencode.ai/config.json"})
self.assertFalse((self.home / ".config" / "opencode" / "plugin" / "harbor.js").exists())
def test_deepseek(self):
install.install("deepseek", python="py -3.12")
patch = self.read(".dsh", "profiles", "desktop", "cordis.patch.yml")
self.assertTrue(patch.startswith(DSH_PATCH.rstrip("\n")))
rows = install._TolerantLoader.load(patch)
inserted = rows[-1]["insert"]
self.assertEqual([r["id"] for r in inserted], ["mcp-harbor", "hooks-harbor"])
self.assertEqual(inserted[0]["config"]["env"], {"HARBOR_AGENT": "DeepSeek", "HARBOR_HOST": "deepseek"})
hooks = json.loads((self.harbor_home / "integrations" / "deepseek" / "hooks.json").read_text(encoding="utf-8"))
self.assertIn("--host deepseek", hooks["hooks"]["Stop"][0]["hooks"][0]["command"])
install.install("deepseek", remove=True)
self.assertEqual(self.read(".dsh", "profiles", "desktop", "cordis.patch.yml"), DSH_PATCH)
def test_kimi_plugin_is_bound_to_a_project(self):
install.install("kimi", project="test", python="py -3.12", register=False)
folder = self.harbor_home / "integrations" / "kimi" / "harbor-test"
manifest = json.loads((folder / "kimi.plugin.json").read_text(encoding="utf-8"))
self.assertEqual(manifest["mcpServers"]["harbor"]["env"], {"HARBOR_AGENT": "Kimi", "HARBOR_PROJECT": "test", "HARBOR_HOST": "kimi"})
self.assertEqual([h["event"] for h in manifest["hooks"]], ["UserPromptSubmit", "PreToolUse", "Stop"])
self.assertTrue(all("--project test" in h["command"] for h in manifest["hooks"]))
self.assertTrue((folder / "skills" / "harbor-test" / "SKILL.md").exists())
def test_refusals(self):
with self.assertRaises(install.InstallError):
install.install("codex", agent="Nobody")
(self.home / ".config" / "opencode" / "opencode.jsonc").write_text('{ // mine\n}', encoding="utf-8")
with self.assertRaises(install.InstallError):
install.install("opencode")
(self.home / ".codex" / "config.toml").write_text(CODEX_CONFIG + "\n[mcp_servers.harbor]\ncommand='x'\n",
encoding="utf-8")
with self.assertRaises(install.InstallError):
install.install("codex")
def test_dry_run_writes_nothing(self):
report = install.install("deepseek", dry_run=True, python="py -3.12")
self.assertTrue(any(line.startswith("update") for line in report))
self.assertEqual(self.read(".dsh", "profiles", "desktop", "cordis.patch.yml"), DSH_PATCH)
if __name__ == "__main__":
unittest.main()