import json from io import BytesIO from unittest.mock import patch from routes import canvas class _Handler: command = "GET" headers = {} def __init__(self): self.body = b"" self.status = None self.sent_headers = {} self.rfile = BytesIO() @property def wfile(self): handler = self class _Writer: def write(self, data): handler.body += data return _Writer() def send_response(self, status): self.status = status def send_header(self, key, value): self.sent_headers[key] = value def end_headers(self): pass def test_tools_manifest_exposes_cmo_canvas_surface(): handler = _Handler() assert canvas._handle_tools(handler, None) is True payload = json.loads(handler.body) names = {tool["name"] for tool in payload["tools"]} assert handler.status == 200 assert payload["tool_surface"] == "cmo-canvas-v0" assert "canvas.generate_screen" in names assert "canvas.command" in names assert "canvas.generate_from_image" in names assert "canvas.update_screen" in names assert "canvas.record_variant" in names assert "canvas.connect_prototype_edge" in names assert "canvas.export_project" in names assert "upstream-hermes-webui-edits" in payload["non_goals"] def test_canvas_proxy_rejects_unlisted_paths(): handler = _Handler() parsed = type("Parsed", (), {"query": "path=/api/admin/secrets"})() assert canvas._handle_proxy(handler, parsed) is True payload = json.loads(handler.body) assert handler.status == 400 assert payload["ok"] is False def test_canvas_status_reports_offline_service_without_failing_request(): handler = _Handler() with patch("routes.canvas.urllib.request.urlopen", side_effect=OSError("offline")): assert canvas._handle_status(handler, None) is True payload = json.loads(handler.body) assert handler.status == 200 assert payload["ok"] is False assert payload["service"] == "canva-editor"