test: code-to10 v2 coverage additions (14 files, production code unchanged)
(cherry picked from commit 473338c35d59bfd50929542e7f08c0a4a22c5987)
This commit is contained in:
1 parent
2032043b60
commit
3a72ed3847
14 files changed
+8249
No files matched your search
@@ -0,0 +1,648 @@
|
||||
"""Coverage batch v2 (D4b): cloud/server.py small handler clusters,
|
||||
second half -- costs/relay, task lists, followups, threads, files,
|
||||
browser helpers, peer messages, reviews, skills/apps/MCP lists.
|
||||
|
||||
Same stubbing discipline as D2-D4: the method under test is never
|
||||
stubbed; host-CLI collaborators (_run_host_cli, _extract_*) are
|
||||
stubbed only to drive the handler's own error mapping.
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
|
||||
from cloud.server import (
|
||||
CloudControlPlaneHandler,
|
||||
_auth_store,
|
||||
init_db,
|
||||
)
|
||||
from cloud.sub2api import Sub2apiError as _Sub2apiError
|
||||
|
||||
|
||||
def _handler(db_path, upload_dir=None, tenants_root=None):
|
||||
h = CloudControlPlaneHandler.__new__(CloudControlPlaneHandler)
|
||||
h.server = SimpleNamespace(db_path=db_path, upload_dir=upload_dir,
|
||||
tenants_root=tenants_root)
|
||||
sent = []
|
||||
h._send_json = lambda status, obj, extra_headers=None: sent.append(
|
||||
(status, obj))
|
||||
h.sent = sent
|
||||
return h
|
||||
|
||||
|
||||
def _conn(db_path):
|
||||
c = sqlite3.connect(db_path)
|
||||
c.row_factory = sqlite3.Row
|
||||
return c
|
||||
|
||||
|
||||
class _ProxyConn:
|
||||
def __init__(self, real, fail_select_substr=None, fail_exc=None):
|
||||
self._real = real
|
||||
self._fail_select_substr = fail_select_substr
|
||||
self._fail_exc = fail_exc
|
||||
|
||||
def execute(self, sql, params=()):
|
||||
text = sql.strip()
|
||||
if (self._fail_select_substr and text.startswith("SELECT")
|
||||
and self._fail_select_substr in text):
|
||||
raise self._fail_exc
|
||||
return self._real.execute(sql, params)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._real, name)
|
||||
|
||||
|
||||
class _Base(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.db_path = os.path.join(self.tmp.name, "test.db")
|
||||
init_db(self.db_path)
|
||||
self.upload_dir = os.path.join(self.tmp.name, "uploads")
|
||||
os.makedirs(self.upload_dir, exist_ok=True)
|
||||
|
||||
def tearDown(self):
|
||||
self.tmp.cleanup()
|
||||
|
||||
def _h(self, body=None, err=None):
|
||||
h = _handler(self.db_path, upload_dir=self.upload_dir,
|
||||
tenants_root=self.tmp.name)
|
||||
h._read_json = lambda: (body, err)
|
||||
return h
|
||||
|
||||
def _project(self, pid="p1", owner="u1", repo_url=None):
|
||||
c = _conn(self.db_path)
|
||||
c.execute(
|
||||
"INSERT INTO projects(id, name, owner_id, created_at,"
|
||||
" repo_url, auto_pr) VALUES(?, ?, ?, ?, ?, 0)",
|
||||
(pid, "P", owner, time.time(), repo_url))
|
||||
c.commit()
|
||||
c.close()
|
||||
with _auth_store(self.db_path) as store:
|
||||
store.create_project(pid, owner)
|
||||
|
||||
def _member(self, pid, uid, role):
|
||||
c = _conn(self.db_path)
|
||||
c.execute(
|
||||
"INSERT OR REPLACE INTO project_members(project_id,"
|
||||
" user_id, role) VALUES(?, ?, ?)", (pid, uid, role))
|
||||
c.commit()
|
||||
c.close()
|
||||
|
||||
def _task(self, tid="t1", pid="p1", status="completed",
|
||||
created_by="u1", **extra):
|
||||
cols = ["id", "project_id", "prompt", "status", "created_by",
|
||||
"created_at", "updated_at"]
|
||||
vals = [tid, pid, "do work", status, created_by, time.time(),
|
||||
time.time()]
|
||||
for key, val in extra.items():
|
||||
cols.append(key)
|
||||
vals.append(val)
|
||||
c = _conn(self.db_path)
|
||||
c.execute(
|
||||
"INSERT INTO tasks(%s) VALUES(%s)"
|
||||
% (", ".join(cols), ", ".join("?" * len(cols))), vals)
|
||||
c.commit()
|
||||
c.close()
|
||||
|
||||
def _last(self, h):
|
||||
self.assertTrue(h.sent, "handler sent no response")
|
||||
return h.sent[-1]
|
||||
|
||||
|
||||
class _ClosingConn:
|
||||
def __init__(self):
|
||||
self.conn = mock.Mock()
|
||||
self.conn.close.side_effect = RuntimeError("close down")
|
||||
|
||||
|
||||
class CostRelayTestCase(_Base):
|
||||
def test_kb_search_q_required(self):
|
||||
h = self._h()
|
||||
h._kb_search("p1", {})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 400)
|
||||
self.assertIn("q required", payload["error"])
|
||||
|
||||
def test_kb_search_bad_top_k(self):
|
||||
h = self._h()
|
||||
h._kb_search("p1", {"q": ["x"], "top_k": ["abc"]})
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_kb_search_close_failure_swallowed(self):
|
||||
class _Store:
|
||||
def __init__(self, db_path):
|
||||
self.conn = _ClosingConn().conn
|
||||
|
||||
def search(self, project_id, q, top_k=5):
|
||||
return []
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server.KBStore", _Store):
|
||||
h._kb_search("p1", {"q": ["x"]})
|
||||
self.assertEqual(self._last(h)[0], 200)
|
||||
|
||||
def test_project_cost_bad_days(self):
|
||||
h = self._h()
|
||||
h._project_cost("p1", {"days": ["abc"]})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 400)
|
||||
self.assertIn("days must be an integer", payload["error"])
|
||||
|
||||
def test_project_cost_close_failure_swallowed(self):
|
||||
class _Store:
|
||||
def __init__(self, db_path):
|
||||
self._conn = mock.Mock()
|
||||
self._conn.close.side_effect = RuntimeError("down")
|
||||
|
||||
def dual_column_report_by_project(self, project_id):
|
||||
return {}
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server.CostStore", _Store):
|
||||
h._project_cost("p1", {})
|
||||
self.assertEqual(self._last(h)[0], 200)
|
||||
|
||||
def test_task_cost_close_failure_swallowed(self):
|
||||
self._project()
|
||||
self._task()
|
||||
class _Store:
|
||||
def __init__(self, db_path):
|
||||
self._conn = mock.Mock()
|
||||
self._conn.close.side_effect = RuntimeError("down")
|
||||
|
||||
def dual_column_report_by_task(self, task_id):
|
||||
return {}
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server.CostStore", _Store):
|
||||
h._task_cost("u1", "t1")
|
||||
self.assertEqual(self._last(h)[0], 200)
|
||||
|
||||
def test_relay_models_sub2api_generic_error_502(self):
|
||||
h = self._h()
|
||||
with mock.patch.dict(os.environ, {"CLOUD_GATEWAY": "sub2api"}), \
|
||||
mock.patch("cloud.server._validate_sub2api_key",
|
||||
side_effect=RuntimeError("net down")):
|
||||
h._relay_models("u1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_relay_models_sub2api_list_error_502(self):
|
||||
h = self._h()
|
||||
with mock.patch.dict(os.environ, {"CLOUD_GATEWAY": "sub2api"}), \
|
||||
mock.patch("cloud.server._validate_sub2api_key",
|
||||
return_value=("ok", "key", "sub", "kid")), \
|
||||
mock.patch("cloud.server._list_sub2api_models",
|
||||
side_effect=RuntimeError("gw down")):
|
||||
h._relay_models("u1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_models_overview_no_memberships_200(self):
|
||||
self._project()
|
||||
h = self._h()
|
||||
h._models_overview("u9")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(payload["model_stats"], [])
|
||||
|
||||
def test_models_overview_close_failure_swallowed(self):
|
||||
self._project()
|
||||
conn = mock.Mock()
|
||||
conn.execute.return_value.fetchall.return_value = []
|
||||
conn.close.side_effect = RuntimeError("down")
|
||||
|
||||
class _Store:
|
||||
def __init__(self, db_path):
|
||||
self._conn = conn
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server.CostStore", _Store):
|
||||
h._models_overview("u1")
|
||||
self.assertEqual(self._last(h)[0], 200)
|
||||
|
||||
|
||||
class ListsFollowupsTestCase(_Base):
|
||||
def test_list_tasks_project_required(self):
|
||||
h = self._h()
|
||||
h._list_tasks("u1", {})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 400)
|
||||
self.assertIn("project_id required", payload["error"])
|
||||
|
||||
def test_list_tasks_executor_filter_skips(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._list_tasks("u1", {"project_id": ["p1"],
|
||||
"executor": ["docker"]})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(payload["tasks"], [])
|
||||
|
||||
def test_goal_convert_queued_none(self):
|
||||
self._project()
|
||||
self._task(status="completed")
|
||||
h = self._h()
|
||||
self.assertIsNone(h._goal_convert_queued("t1", "做目标"))
|
||||
|
||||
def test_goal_revive_terminal_none(self):
|
||||
self._project()
|
||||
self._task(status="completed", resume_message="old")
|
||||
h = self._h()
|
||||
self.assertIsNone(h._goal_revive_terminal("t1", "做目标"))
|
||||
|
||||
def test_task_agents_remote_host_200(self):
|
||||
self._project()
|
||||
self._task(worktree_host="other-host")
|
||||
h = self._h()
|
||||
h._task_agents("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(payload["agents"], [])
|
||||
|
||||
def test_create_followup_convert_race_409(self):
|
||||
self._project()
|
||||
self._task(status="queued")
|
||||
h = self._h(body={"body": "/goal 做目标"})
|
||||
h._goal_convert_queued = lambda tid, target: None
|
||||
h._create_followup("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 409)
|
||||
self.assertIn("状态已变化", payload["error"])
|
||||
|
||||
def test_create_followup_revive_race_409(self):
|
||||
self._project()
|
||||
self._task(status="completed")
|
||||
h = self._h(body={"body": "/goal 做目标"})
|
||||
h._goal_revive_terminal = lambda tid, target: None
|
||||
h._create_followup("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 409)
|
||||
self.assertIn("状态已变化", payload["error"])
|
||||
|
||||
def test_append_direct_trace_remote_host_returns(self):
|
||||
h = self._h()
|
||||
h._append_direct_trace({"id": "t1", "project_id": "p1",
|
||||
"worktree_host": "other-host"},
|
||||
"fu1", "hi")
|
||||
|
||||
def test_append_direct_trace_tenant_dir(self):
|
||||
tenant_dir = os.path.join(self.tmp.name, "p1", "work",
|
||||
"host-traces")
|
||||
os.makedirs(tenant_dir, exist_ok=True)
|
||||
open(os.path.join(tenant_dir, "t1.jsonl"), "w").close()
|
||||
h = self._h()
|
||||
h._append_direct_trace({"id": "t1", "project_id": "p1"},
|
||||
"fu1", "hello tenant")
|
||||
with open(os.path.join(tenant_dir, "t1.jsonl")) as f:
|
||||
content = f.read()
|
||||
self.assertIn("followup-direct", content)
|
||||
|
||||
def test_append_direct_trace_write_failure_swallowed(self):
|
||||
h = self._h()
|
||||
with mock.patch("os.makedirs",
|
||||
side_effect=OSError("ro fs")):
|
||||
h._append_direct_trace({"id": "t1", "project_id": "p1"},
|
||||
"fu1", "hi")
|
||||
|
||||
def test_list_followups_missing_404(self):
|
||||
h = self._h()
|
||||
h._list_followups("u1", "ghost")
|
||||
self.assertEqual(self._last(h)[0], 404)
|
||||
|
||||
def test_list_followups_forbidden_403(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._list_followups("u9", "t1")
|
||||
self.assertEqual(self._last(h)[0], 403)
|
||||
|
||||
def test_task_timeline_forbidden_403(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._task_timeline("u9", "t1")
|
||||
self.assertEqual(self._last(h)[0], 403)
|
||||
|
||||
def test_task_files_200_cap(self):
|
||||
self._project()
|
||||
self._task()
|
||||
worktree = os.path.join(self.tmp.name, "p1", "work",
|
||||
"host-worktrees", "t1")
|
||||
os.makedirs(worktree, exist_ok=True)
|
||||
for i in range(201):
|
||||
with open(os.path.join(worktree, "f%03d.txt" % i), "w") as f:
|
||||
f.write("x")
|
||||
h = self._h()
|
||||
h._task_files("u1", "t1", {})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(len(payload["entries"]), 200)
|
||||
|
||||
def test_browser_worktree_for_cloud_409(self):
|
||||
self._project()
|
||||
self._task(executor="cloud")
|
||||
h = self._h()
|
||||
worktree, err = h._browser_worktree_for("u1", "t1")
|
||||
self.assertIsNone(worktree)
|
||||
self.assertEqual(err[0], 409)
|
||||
|
||||
def test_browser_body_dict_none_and_bad(self):
|
||||
h = self._h()
|
||||
self.assertIsNone(h._browser_body_dict(None))
|
||||
self.assertIsNone(h._browser_body_dict(b"not json"))
|
||||
self.assertEqual(h._browser_body_dict({"a": 1}), {"a": 1})
|
||||
|
||||
def test_browser_input_worktree_error(self):
|
||||
h = self._h()
|
||||
h._browser_worktree_for = lambda *a: (None, (404, "nope"))
|
||||
h._browser_input("u1", "t1", {})
|
||||
self.assertEqual(self._last(h)[0], 404)
|
||||
|
||||
def test_browser_input_state_error(self):
|
||||
h = self._h()
|
||||
h._browser_worktree_for = lambda *a: (self.tmp.name, None)
|
||||
with mock.patch("cloud.playwright_remote.read_state",
|
||||
return_value=(None, (502, "no session"))):
|
||||
h._browser_input("u1", "t1", {})
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_browser_input_write_os_error_503(self):
|
||||
h = self._h()
|
||||
h._browser_worktree_for = lambda *a: (self.tmp.name, None)
|
||||
with mock.patch("cloud.playwright_remote.read_state",
|
||||
return_value=({"url": ""}, None)), \
|
||||
mock.patch("cloud.playwright_remote.effective_holder",
|
||||
return_value="user"), \
|
||||
mock.patch("cloud.playwright_remote.append_input",
|
||||
side_effect=OSError("unreachable")):
|
||||
h._browser_input("u1", "t1", {"type": "type", "text": "hi"})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 503)
|
||||
self.assertIn("browser input unreachable", payload["error"])
|
||||
|
||||
def test_chat_history_forbidden_403(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._task_chat_history("u9", "t1")
|
||||
self.assertEqual(self._last(h)[0], 403)
|
||||
|
||||
def test_chat_history_legacy_query_failure_none(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._trace_snapshot = lambda task: ([], None)
|
||||
h._db = lambda: _ProxyConn(
|
||||
_conn(self.db_path),
|
||||
fail_select_substr="FROM audit_log",
|
||||
fail_exc=RuntimeError("audit down"))
|
||||
h._task_chat_history("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertIsNone(payload["legacy_continue_count"])
|
||||
|
||||
def test_peer_message_read_error(self):
|
||||
h = self._h(err="bad json")
|
||||
h._create_peer_message("u1", "t1")
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_peer_message_sender_forbidden_403(self):
|
||||
self._project()
|
||||
self._project(pid="p2", owner="u2")
|
||||
self._task(tid="t1", pid="p1")
|
||||
self._task(tid="t2", pid="p2", created_by="u2")
|
||||
h = self._h(body={"from_task_id": "t2", "body": "hi"})
|
||||
h._create_peer_message("u1", "t1")
|
||||
self.assertEqual(self._last(h)[0], 403)
|
||||
|
||||
def test_peer_message_body_too_long(self):
|
||||
self._project()
|
||||
self._task(tid="t1", pid="p1")
|
||||
self._task(tid="t2", pid="p1")
|
||||
h = self._h(body={"from_task_id": "t2", "body": "x" * 20001})
|
||||
h._create_peer_message("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 400)
|
||||
self.assertIn("body too long", payload["error"])
|
||||
|
||||
def test_list_peer_messages_missing_404(self):
|
||||
h = self._h()
|
||||
h._list_peer_messages("u1", "ghost")
|
||||
self.assertEqual(self._last(h)[0], 404)
|
||||
|
||||
def test_create_review_read_error(self):
|
||||
h = self._h(err="bad json")
|
||||
h._create_review("u1", "t1")
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_update_review_findings_read_error(self):
|
||||
h = self._h(err="bad json")
|
||||
h._update_review_findings("u1", "t1", "r1")
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_update_review_findings_missing_404(self):
|
||||
h = self._h(body={})
|
||||
h._update_review_findings("u1", "ghost", "r1")
|
||||
self.assertEqual(self._last(h)[0], 404)
|
||||
|
||||
|
||||
class ThreadListsTestCase(_Base):
|
||||
def test_thread_guard_docker_409(self):
|
||||
h = self._h()
|
||||
code, msg = h._thread_task_guard({"executor": "docker"})
|
||||
self.assertEqual(code, 409)
|
||||
|
||||
def test_thread_guard_remote_host_409(self):
|
||||
h = self._h()
|
||||
code, msg = h._thread_task_guard(
|
||||
{"executor": None, "worktree_host": "other-host"})
|
||||
self.assertEqual(code, 409)
|
||||
|
||||
def test_list_threads_no_tenant_home_502(self):
|
||||
h = self._h()
|
||||
threads, home, cwd, err, provider = h._list_task_threads(
|
||||
{"id": "t1", "project_id": None})
|
||||
self.assertEqual(err[0], 502)
|
||||
|
||||
def test_list_threads_extract_none_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, "garbage")), \
|
||||
mock.patch("cloud.server._extract_thread_list",
|
||||
return_value=None):
|
||||
threads, home, cwd, err, provider = h._list_task_threads(
|
||||
{"id": "t1", "project_id": "p1"})
|
||||
self.assertEqual(err[0], 502)
|
||||
self.assertIn("no thread list", err[1])
|
||||
|
||||
def test_task_thread_guard_409(self):
|
||||
self._project()
|
||||
self._task(executor="docker")
|
||||
h = self._h()
|
||||
h._task_thread("u1", "t1", {})
|
||||
self.assertEqual(self._last(h)[0], 409)
|
||||
|
||||
def test_task_thread_cli_failure_502(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._resolve_task_thread = lambda task: (
|
||||
{"id": "th1"}, "/tmp", "/tmp", None, None)
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._task_thread("u1", "t1", {})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 502)
|
||||
self.assertIn("host down", payload["error"])
|
||||
|
||||
def test_task_thread_no_thread_502(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h()
|
||||
h._resolve_task_thread = lambda task: (
|
||||
{"id": "th1"}, "/tmp", "/tmp", None, None)
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, "garbage")):
|
||||
h._task_thread("u1", "t1", {})
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 502)
|
||||
self.assertIn("no thread", payload["error"])
|
||||
|
||||
def test_task_thread_mutate_task_error(self):
|
||||
h = self._h()
|
||||
h._task_thread_mutate("u1", "ghost", ["archive"],
|
||||
"thread_archived", "detail")
|
||||
self.assertEqual(self._last(h)[0], 404)
|
||||
|
||||
def test_task_thread_rename_read_error(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h(err="bad json")
|
||||
h._task_thread_rename("u1", "t1")
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_task_thread_rename_name_too_long(self):
|
||||
self._project()
|
||||
self._task()
|
||||
h = self._h(body={"name": "x" * 201})
|
||||
h._task_thread_rename("u1", "t1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 400)
|
||||
self.assertIn("name too long", payload["error"])
|
||||
|
||||
def test_project_host_home_none(self):
|
||||
h = self._h()
|
||||
self.assertEqual(h._project_host_home(None), (None, None))
|
||||
|
||||
def _list_case(self, method):
|
||||
h = self._h()
|
||||
method(h, None)
|
||||
return h
|
||||
|
||||
def test_skills_list_home_none_502(self):
|
||||
h = self._h()
|
||||
h._skills_list(None)
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_skills_list_cli_failure_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._skills_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_skills_list_no_list_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, "garbage")):
|
||||
h._skills_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_apps_list_home_none_502(self):
|
||||
h = self._h()
|
||||
h._apps_list(None)
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_apps_list_cli_failure_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._apps_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_apps_list_no_list_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, "garbage")):
|
||||
h._apps_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_list_home_none_502(self):
|
||||
h = self._h()
|
||||
h._mcp_servers_list(None)
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_list_cli_failure_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._mcp_servers_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_list_no_list_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, "garbage")):
|
||||
h._mcp_servers_list("p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_list_next_cursor(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, {"servers": [],
|
||||
"nextCursor": "n1"})):
|
||||
h._mcp_servers_list("p1")
|
||||
code, payload = self._last(h)
|
||||
self.assertEqual(code, 200)
|
||||
self.assertEqual(payload["nextCursor"], "n1")
|
||||
|
||||
def test_mcp_reload_home_none_502(self):
|
||||
h = self._h()
|
||||
h._mcp_servers_reload("u1", None)
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_reload_cli_failure_502(self):
|
||||
h = self._h()
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._mcp_servers_reload("u1", "p1")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
|
||||
def test_mcp_oauth_login_read_error(self):
|
||||
h = self._h(err="bad json")
|
||||
h._mcp_server_oauth_login("u1", "p1", "srv")
|
||||
self.assertEqual(self._last(h)[0], 400)
|
||||
|
||||
def test_mcp_oauth_login_timeout_argv(self):
|
||||
h = self._h(body={"timeoutSecs": 30})
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(True, {"authorizationUrl": "u",
|
||||
"loginId": "l"})) as cli:
|
||||
h._mcp_server_oauth_login("u1", "p1", "srv")
|
||||
argv = cli.call_args[0][0]
|
||||
self.assertIn("--timeout-secs", argv)
|
||||
self.assertEqual(self._last(h)[0], 200)
|
||||
|
||||
def test_mcp_oauth_login_cli_failure_502(self):
|
||||
h = self._h(body={})
|
||||
with mock.patch("cloud.server._run_host_cli",
|
||||
return_value=(False, "host down")):
|
||||
h._mcp_server_oauth_login("u1", "p1", "srv")
|
||||
self.assertEqual(self._last(h)[0], 502)
|
||||
Reference in new issue
Block a user