← Files NGS Analysis WorkbenchARCHIVED FILE
tests/test_mcp_ui.py
20.1 KB · Sep 30, 2026 · 23:20 UTC
from __future__ import annotations
import asyncio
import json
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import create_autospec, patch
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
MCP_ROOT = PLUGIN_ROOT / "mcp"
if str(MCP_ROOT) not in sys.path:
sys.path.insert(0, str(MCP_ROOT))
from ngs_app_mcp import app as global_app # noqa: E402
from ngs_workbench_daemon import server as daemon_server # noqa: E402
from ngs_workbench_mcp import app, runs, ui # noqa: E402
def _write_fake_codex_python(executable: Path) -> None:
fake_python_source = (
f"#!{sys.executable}\n"
"import json, os, sys\n"
"arguments = sys.argv[1:]\n"
"print(json.dumps({'arguments': arguments, 'executable': sys.argv[0]}))\n"
)
executable.parent.mkdir(parents=True)
executable.write_text(
f"#!{sys.executable}\n"
"import json, os, sys\n"
"from pathlib import Path\n"
"arguments = sys.argv[1:]\n"
"with Path(os.environ['FAKE_PYTHON_LOG']).open('a', encoding='utf-8') as log:\n"
" log.write(json.dumps(arguments) + '\\n')\n"
"if arguments[:2] == ['-m', 'venv']:\n"
" python = Path(arguments[-1]) / 'bin' / 'python'\n"
" python.parent.mkdir(parents=True, exist_ok=True)\n"
f" python.write_text({fake_python_source!r}, encoding='utf-8')\n"
" python.chmod(0o755)\n"
"elif arguments[:2] == ['-m', 'pip']:\n"
" print('Installing Workbench requirements')\n"
"else:\n"
" raise SystemExit(f'unexpected fake Python arguments: {arguments!r}')\n",
encoding="utf-8",
)
executable.chmod(0o755)
class McpUiContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.tools = {tool.name: tool for tool in asyncio.run(app.mcp.list_tools())}
cls.resources = {
str(resource.uri): resource for resource in asyncio.run(app.mcp.list_resources())
}
cls.app_tools = {tool.name: tool for tool in asyncio.run(global_app.mcp.list_tools())}
cls.app_resources = {
str(resource.uri): resource for resource in asyncio.run(global_app.mcp.list_resources())
}
def test_full_workbench_is_the_only_app_global_entrypoint(self) -> None:
tool = self.app_tools["open_ngs_workbench"]
meta = tool.meta
self.assertEqual(tool.title, "NGS Analysis Workbench")
self.assertEqual(meta["ui"]["resourceUri"], global_app.APP_RESOURCE_URI)
self.assertEqual(meta["ui"]["visibility"], ["app"])
self.assertTrue(meta["ui"]["supportsFullscreen"])
self.assertEqual(meta["openai/ui"], {"entrypoints": [{"type": "global"}]})
self.assertNotIn("openai/outputTemplate", meta)
self.assertNotIn("ui/resourceUri", meta)
global_entrypoints = [
tool.name
for tool in [*self.tools.values(), *self.app_tools.values()]
if tool.meta.get("openai/ui", {}).get("entrypoints") == [{"type": "global"}]
]
self.assertEqual(global_entrypoints, ["open_ngs_workbench"])
resource_meta = self.app_resources[global_app.APP_RESOURCE_URI].meta
self.assertTrue(resource_meta["ui"]["supportsFullscreen"])
self.assertEqual(
resource_meta["ui"]["permissions"],
{"clipboardWrite": {}},
)
plan_meta = self.resources[ui.PLAN_REVIEW_RESOURCE_URI].meta
self.assertNotIn("permissions", plan_meta["ui"])
self.assertTrue(plan_meta["openai/widgetAccessible"])
run_meta = self.resources[ui.RUN_STATUS_RESOURCE_URI].meta
self.assertNotIn("permissions", run_meta["ui"])
self.assertTrue(run_meta["openai/widgetAccessible"])
def test_workflow_planner_mounts_only_the_passive_plan_app(self) -> None:
self.assertNotIn("plan_workflow", self.tools)
for name in ("plan_nextflow", "plan_snakemake"):
with self.subTest(tool=name):
meta = self.tools[name].meta
self.assertEqual(meta["ui"]["resourceUri"], ui.PLAN_REVIEW_RESOURCE_URI)
self.assertEqual(meta["ui"]["visibility"], ["model"])
self.assertEqual(meta["openai/outputTemplate"], ui.PLAN_REVIEW_RESOURCE_URI)
self.assertNotIn("openai/widgetAccessible", meta)
def test_workflow_planners_expose_only_engine_specific_configuration(self) -> None:
nextflow = self.tools["plan_nextflow"].inputSchema["properties"]
snakemake = self.tools["plan_snakemake"].inputSchema["properties"]
self.assertTrue({"workflow_id", "params_file", "profile"}.issubset(nextflow))
self.assertTrue({"config_file", "cores"}.issubset(snakemake))
self.assertTrue(
{"engine", "config_file", "cores", "revision", "workflow_source"}.isdisjoint(nextflow)
)
self.assertTrue(
{
"engine",
"params_file",
"profile",
"revision",
"workflow_parameters",
"workflow_source",
}.isdisjoint(snakemake)
)
self.assertTrue(nextflow["params_file"]["description"])
self.assertTrue(snakemake["config_file"]["description"])
def test_execute_plan_is_identity_only_and_mounts_run_receipt(self) -> None:
tool = self.tools["execute_plan"]
self.assertEqual(
set(tool.inputSchema["properties"]),
{"plan_name", "plan_id", "plan_checksum"},
)
self.assertEqual(set(tool.inputSchema["required"]), set(tool.inputSchema["properties"]))
self.assertEqual(tool.meta["ui"]["resourceUri"], ui.RUN_STATUS_RESOURCE_URI)
self.assertEqual(tool.meta["ui"]["visibility"], ["model"])
self.assertNotIn("openai/widgetAccessible", tool.meta)
self.assertNotIn("start_nfcore_run", self.tools)
self.assertNotIn("start_snakemake_run", self.tools)
def test_workflow_catalog_exposes_entry_and_version_lifecycle(self) -> None:
save = self.tools["save_workflow"]
self.assertEqual(
set(save.inputSchema["required"]), {"workflow_id", "name", "engine", "source"}
)
self.assertEqual(
set(save.inputSchema["properties"]),
{"workflow_id", "name", "engine", "source", "description"},
)
self.assertTrue(save.inputSchema["properties"]["source"].get("oneOf"))
self.assertNotIn("remove_workflow", self.tools)
self.assertTrue(
{
"update_workflow",
"list_workflow_versions",
"activate_workflow_version",
"archive_workflow",
"restore_workflow",
}.issubset(self.tools)
)
def test_workbench_tools_are_model_only(self) -> None:
for name in (
"list_workflows",
"get_runtime_environment",
"check_nextflow_readiness",
"check_snakemake_readiness",
"list_ngs_runs",
"get_ngs_run",
"update_ngs_run_analysis_summary",
):
with self.subTest(tool=name):
meta = self.tools[name].meta
self.assertEqual(meta, {"ui": {"visibility": ["model"]}})
self.assertNotIn("open_ngs_workbench", self.tools)
self.assertNotIn("list_compute_targets", self.tools)
self.assertEqual(
self.tools["observe_ngs_run"].meta,
{"ui": {"visibility": ["model", "app"]}},
)
def test_global_app_tools_are_app_only(self) -> None:
self.assertEqual(
set(self.app_tools),
{
"open_ngs_workbench",
"list_workflows",
"list_compute_target_summaries",
"list_ngs_runs",
"list_ngs_run_lineages",
"get_ngs_run",
"observe_ngs_run",
"get_ngs_run_report",
},
)
for tool in self.app_tools.values():
with self.subTest(tool=tool.name):
self.assertEqual(tool.meta["ui"]["visibility"], ["app"])
def test_global_app_history_routes_lineage_queries_and_filters(self) -> None:
with patch(
"ngs_workbench_mcp.runs.list_registry_run_lineages",
return_value={"ok": True, "runs": []},
) as list_lineages:
asyncio.run(global_app.mcp.call_tool("list_ngs_run_lineages", {"limit": 12}))
list_lineages.assert_called_once_with(limit=12)
filters = {
"first_run_id": "run-a",
"limit": 200,
}
with patch(
"ngs_workbench_mcp.runs.list_registry_runs",
return_value={"ok": True, "runs": []},
) as list_runs:
asyncio.run(global_app.mcp.call_tool("list_ngs_runs", filters))
list_runs.assert_called_once_with(**filters, statuses=None, binding=None, pipeline=None)
def test_compute_target_list_projects_only_app_safe_inventory(self) -> None:
safe_target = {
"target_id": "research-slurm",
"title": "Research Slurm",
"provider": "ngs-compute",
"controller_transport": "ssh",
"executor": "slurm",
"workspace_access": "remote_filesystem",
"description": "Run a workflow controller through SSH.",
"workspace_root": "/shared/rosalind",
"executor_configuration": {"partition": "genomics"},
}
raw_target = {
**safe_target,
"config_hash": "sha256:private-identity",
"host_access": {"alias": "research-login"},
}
with patch.object(daemon_server, "_list_targets", return_value={"targets": [raw_target]}):
result = daemon_server.list_target_summaries()
self.assertEqual(result, {"count": 1, "targets": [safe_target]})
def test_global_app_reads_only_the_daemon_owned_summary(self) -> None:
with patch.object(
daemon_server,
"list_target_summaries",
return_value={"count": 0, "targets": []},
) as list_summaries:
result = global_app.list_compute_target_summaries()
self.assertEqual(result, {"count": 0, "targets": []})
list_summaries.assert_called_once_with()
def test_global_app_lists_runs_across_the_registry(self) -> None:
expected = {"count": 0, "runs": []}
list_runs = create_autospec(runs.list_registry_runs, return_value=expected)
with patch.object(runs, "list_registry_runs", list_runs):
result = global_app.list_ngs_runs()
self.assertEqual(result, expected)
list_runs.assert_called_once_with(
statuses=None,
binding=None,
pipeline=None,
first_run_id=None,
limit=50,
)
def test_run_cancellation_is_not_visible_to_embedded_apps(self) -> None:
self.assertEqual(
self.tools["cancel_ngs_run"].meta,
{"ui": {"visibility": ["model"]}},
)
def test_all_ui_resources_are_registered(self) -> None:
self.assertEqual(set(self.app_resources), {global_app.APP_RESOURCE_URI})
self.assertNotIn(global_app.APP_RESOURCE_URI, self.resources)
self.assertIn(ui.PLAN_REVIEW_RESOURCE_URI, self.resources)
self.assertIn(ui.RUN_STATUS_RESOURCE_URI, self.resources)
self.assertTrue(global_app.ngs_app().lstrip().lower().startswith("<!doctype html>"))
self.assertIn(ui.LEGACY_RUN_REVIEW_RESOURCE_URI, self.resources)
def test_legacy_run_review_resource_preserves_the_read_only_receipt(self) -> None:
legacy_resource = self.resources[ui.LEGACY_RUN_REVIEW_RESOURCE_URI]
run_resource = self.resources[ui.RUN_STATUS_RESOURCE_URI]
legacy_contents = list(
asyncio.run(app.mcp.read_resource(ui.LEGACY_RUN_REVIEW_RESOURCE_URI))
)
run_contents = list(asyncio.run(app.mcp.read_resource(ui.RUN_STATUS_RESOURCE_URI)))
self.assertEqual(legacy_resource.meta, run_resource.meta)
self.assertNotIn("permissions", legacy_resource.meta["ui"])
self.assertTrue(legacy_resource.meta["openai/widgetAccessible"])
self.assertEqual(len(legacy_contents), 1)
self.assertEqual(legacy_contents[0].content, run_contents[0].content)
self.assertIn("Execution blocked", legacy_contents[0].content)
def test_bundled_inline_resource_renders_execution_failures_as_blocked(self) -> None:
inline_resource = ui.read_run_review_html()
self.assertIn("Execution blocked", inline_resource)
self.assertIn(
"Refresh the runtime environment, then create and review a new execution plan.",
inline_resource,
)
def test_only_execute_plan_uses_native_prompt_approval(self) -> None:
config = json.loads((PLUGIN_ROOT / ".mcp.json").read_text(encoding="utf-8"))
app_server = config["mcpServers"]["ngs-app"]
server = config["mcpServers"]["ngs-analysis-workbench"]
self.assertEqual(app_server["args"], ["ngs_app_mcp"])
self.assertEqual(app_server["default_tools_approval_mode"], "auto")
self.assertNotIn("tools", app_server)
self.assertEqual(server["default_tools_approval_mode"], "auto")
self.assertEqual(server["tools"], {"execute_plan": {"approval_mode": "prompt"}})
def test_mcp_server_prefers_bundled_python_over_uv_and_reuses_ready_environment(self) -> None:
config = json.loads((PLUGIN_ROOT / ".mcp.json").read_text(encoding="utf-8"))
server = config["mcpServers"]["ngs-analysis-workbench"]
with tempfile.TemporaryDirectory() as temporary:
user_home = Path(temporary) / "home"
bundled_python = (
user_home
/ ".cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3"
)
_write_fake_codex_python(bundled_python)
user_bin = user_home / ".local" / "bin"
user_bin.mkdir(parents=True)
for name in ("uv", "python3"):
executable = user_bin / name
executable.write_text("#!/bin/sh\nexit 99\n", encoding="utf-8")
executable.chmod(0o755)
fake_python_log = Path(temporary) / "python.jsonl"
codex_home = Path(temporary) / "codex-home"
environment = {
**os.environ,
"CODEX_HOME": str(codex_home),
"FAKE_PYTHON_LOG": str(fake_python_log),
"HOME": str(user_home),
"PATH": str(user_bin),
"NGS_ANALYSIS_WORKBENCH_UV": str(user_bin / "uv"),
}
cold_result = subprocess.run(
[server["command"], *server["args"]],
cwd=MCP_ROOT,
env=environment,
capture_output=True,
check=True,
text=True,
)
warm_result = subprocess.run(
[server["command"], "ngs_compute_mcp"],
cwd=MCP_ROOT,
env=environment,
capture_output=True,
check=True,
text=True,
)
python_launches = [
json.loads(line)
for line in fake_python_log.read_text(encoding="utf-8").splitlines()
]
expected_venv = (
codex_home
/ "cache/ngs-analysis-workbench/venvs"
/ (MCP_ROOT / "PLUGIN_VENV_VERSION").read_text(encoding="utf-8").strip()
)
expected_python = expected_venv / "bin" / "python"
self.assertTrue((expected_venv / ".ready").is_file())
self.assertEqual(len(python_launches), 2)
self.assertTrue(cold_result.stderr)
self.assertEqual(
json.loads(cold_result.stdout),
{"arguments": ["-m", "ngs_workbench_mcp"], "executable": str(expected_python)},
)
self.assertEqual(
json.loads(warm_result.stdout),
{"arguments": ["-m", "ngs_compute_mcp"], "executable": str(expected_python)},
)
environment.pop("HOME")
environment.pop("NGS_ANALYSIS_WORKBENCH_UV")
alternate_codex_home = Path(temporary) / "codex-home-without-home"
environment["CODEX_HOME"] = str(alternate_codex_home)
runtime_dependencies = bundled_python.parents[2]
environment["PATH"] = (
f"{user_bin}:{runtime_dependencies / 'bin' / 'override'}:/usr/bin:/bin"
)
without_home_result = subprocess.run(
[server["command"], *server["args"]],
cwd=MCP_ROOT,
env=environment,
capture_output=True,
check=True,
text=True,
)
self.assertEqual(
json.loads(without_home_result.stdout)["arguments"],
["-m", "ngs_workbench_mcp"],
)
system_python = Path(temporary) / "system-bin" / "python3"
_write_fake_codex_python(system_python)
environment["HOME"] = str(Path(temporary) / "home-without-bundled-python")
environment["CODEX_HOME"] = str(Path(temporary) / "codex-home-system-python")
environment["PATH"] = f"{system_python.parent}:{user_bin}:/usr/bin:/bin"
system_python_result = subprocess.run(
[server["command"], *server["args"]],
cwd=MCP_ROOT,
env=environment,
capture_output=True,
check=True,
text=True,
)
self.assertEqual(
json.loads(system_python_result.stdout)["arguments"],
["-m", "ngs_workbench_mcp"],
)
def test_preparation_is_nested_in_workflow_planner(self) -> None:
for name in ("plan_nextflow", "plan_snakemake"):
with self.subTest(tool=name):
preparation_schema = self.tools[name].inputSchema["properties"]["preparation"]
self.assertIn("anyOf", preparation_schema)
self.assertNotIn("plan_setup", self.tools)
self.assertNotIn("list_public_demo_setups", self.tools)
self.assertNotIn("plan_public_demo_setup", self.tools)
self.assertNotIn("execute_setup_plan", self.tools)
def test_inline_plan_stays_static_and_only_receipts_poll_run_status(self) -> None:
source = (PLUGIN_ROOT / "src" / "inline" / "RunReviewApp.tsx").read_text(encoding="utf-8")
client = (PLUGIN_ROOT / "src" / "inline" / "client.ts").read_text(encoding="utf-8")
self.assertEqual(source.count("<button"), 1)
self.assertIn("onClick={onRefresh}", source)
self.assertIn('client.call("observe_ngs_run"', source)
self.assertNotIn("lookupPlannedRun", source)
self.assertNotIn("restoredPlanRun", source)
self.assertLess(source.index("if (plan) {"), source.index("if (run) {"))
self.assertIn('"Refresh"', source)
self.assertNotIn("Approve and start", source)
self.assertIn("callServerTool", client)
self.assertNotIn('client.call("execute_plan"', source)
def test_full_workbench_has_no_plan_or_execution_path(self) -> None:
hook = (PLUGIN_ROOT / "src" / "hooks" / "useNgsWorkbench.ts").read_text(encoding="utf-8")
client_contract = (PLUGIN_ROOT / "src" / "mcp" / "ngs.ts").read_text(encoding="utf-8")
self.assertNotIn("plan_nextflow", hook + client_contract)
self.assertNotIn("plan_snakemake", hook + client_contract)
self.assertNotIn("execute_plan", hook + client_contract)
self.assertFalse((PLUGIN_ROOT / "src" / "components" / "RunConfiguration.tsx").exists())
def test_default_prompts_fit_the_app_server_contract(self) -> None:
manifest = json.loads(
(PLUGIN_ROOT / ".codex-plugin" / "plugin.json").read_text(encoding="utf-8")
)
prompts = manifest["interface"]["defaultPrompt"]
self.assertGreater(len(prompts), 0)
self.assertLessEqual(len(prompts), 3)
self.assertTrue(all(prompt.strip() for prompt in prompts))
self.assertTrue(all(len(prompt) <= 128 for prompt in prompts))
if __name__ == "__main__":
unittest.main()
SHA-256: a8c928520f921c9c7741f2ec2e0578ed57f46bc6172cfe32baca014afb57a3e1