Files
shd-mcp-plugin/tests/test_widget_artifact.py
T
markkats cc63037e1f
Validate SHD MCP plugin / validate (push) Has been cancelled
feat: add SHD widget suite
2026-08-24 21:46:52 +03:00

98 lines
5.6 KiB
Python

import hashlib
import json
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
WIDGETS = {
"active-projects": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "active-projects" / "v1",
"documents": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "documents" / "v1",
"tasks": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "tasks" / "v1",
"finance": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "finance" / "v1",
"crm": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "crm" / "v1",
"discussions": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "discussions" / "v1",
"notifications": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notifications" / "v1",
"scheduling": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "scheduling" / "v1",
"inventory": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "inventory" / "v1",
"agents": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "agents" / "v1",
"status-page": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "status-page" / "v1",
"project-db": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-db" / "v1",
"notes": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notes" / "v1",
"project-overview": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-overview" / "v1",
"files": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "files" / "v1",
"proposals": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "proposals" / "v1",
"terms": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "terms" / "v1",
"activity": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "activity" / "v1",
"organizations": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "organizations" / "v1",
"gitea": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "gitea" / "v1",
"schemes": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schemes" / "v1",
"task-kanban": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "task-kanban" / "v1",
"team-workload": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "team-workload" / "v1",
"project-timeline": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-timeline" / "v1",
"crm-funnel": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "crm-funnel" / "v1",
"finance-dashboard": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "finance-dashboard" / "v1",
"proposal-approvals": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "proposal-approvals" / "v1",
"documents-completeness": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "documents-completeness" / "v1",
"agents-health": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "agents-health" / "v1",
"schedule-calendar": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schedule-calendar" / "v1",
"acl-matrix": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "acl-matrix" / "v1",
"inventory-warnings": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "inventory-warnings" / "v1",
"gitea-board": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "gitea-board" / "v1",
"schemes-progress": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schemes-progress" / "v1",
"notes-tree": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notes-tree" / "v1",
"project-db-preview": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-db-preview" / "v1",
}
class WidgetArtifactTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.artifacts = {}
for name, widget in WIDGETS.items():
manifest = json.loads((widget / "manifest.json").read_text(encoding="utf-8"))
resource = next(item for item in manifest["resources"] if item["file"] == "index.html")
cls.artifacts[name] = {
"widget": widget,
"manifest": manifest,
"resource": resource,
"html": (widget / "index.html").read_text(encoding="utf-8"),
}
def test_manifest_and_checksums_match_artifact(self):
for name, artifact in self.artifacts.items():
widget = artifact["widget"]
manifest = artifact["manifest"]
resource = artifact["resource"]
html = artifact["html"]
digest = hashlib.sha256(html.encode("utf-8")).hexdigest()
self.assertEqual(resource["uri"], f"ui://shd/{name}/v1.html", name)
self.assertEqual(resource["mediaType"], "text/html;profile=mcp-app", name)
self.assertEqual(resource["bytes"], len(html.encode("utf-8")), name)
self.assertEqual(resource["sha256"], digest, name)
checksum = (widget / "SHA256SUMS").read_text(encoding="utf-8").strip().split()
self.assertEqual(checksum, [digest, "index.html"], name)
license_path = (widget / manifest["license"]).resolve()
self.assertEqual(license_path, (ROOT / "LICENSE").resolve(), name)
source_path = (ROOT / manifest["source"]["path"]).resolve()
self.assertEqual(source_path, (widget / "index.html").resolve(), name)
def test_widget_uses_portable_bridge_and_no_external_script(self):
for name, artifact in self.artifacts.items():
for marker in (
"ui/initialize",
"ui/notifications/initialized",
"ui/notifications/tool-input",
"ui/notifications/tool-result",
"tools/call",
"window.openai",
):
self.assertIn(marker, artifact["html"], f"{name}: {marker}")
self.assertNotIn("<script src=", artifact["html"].lower(), name)
if __name__ == "__main__":
unittest.main()