File size: 5,523 Bytes
db14e6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
"""Always-on verification of the top featured MCP servers against the sandbox.

This fetches the top entries from the **real** Matrix Hub API (no hardcoded
ids) and runs the sandbox-testability verifier on each, so we continuously know
which featured servers can be trialed in the no-secrets sandbox and which are
tagged "not available" (and why).

Run:
    cd hf && python -m pytest tests/test_featured_sandbox.py -v -s

Network: hits https://api.matrixhub.io. If unreachable, network-dependent tests
skip (so the suite stays green offline). Set MATRIXLAB_SANDBOX_LIVE=1 and
MATRIXLAB_SANDBOX_URL=<worker> to additionally start real sandbox sessions for
the testable servers.
"""
from __future__ import annotations

import os
import sys
import time
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))  # hf/
from app.plan import verify, is_testable  # noqa: E402

try:
    import requests
except Exception:  # pragma: no cover
    requests = None

HUB = os.environ.get("MATRIX_HUB_BASE", "https://api.matrixhub.io").rstrip("/")
TOP_N = int(os.environ.get("FEATURED_TOP_N", "5"))


def _get(path: str):
    if requests is None:
        pytest.skip("requests not installed")
    try:
        r = requests.get(HUB + path, timeout=15, headers={"Accept": "application/json"})
        r.raise_for_status()
        return r.json()
    except Exception as e:  # network / hub down -> skip, keep suite green offline
        pytest.skip(f"Matrix Hub unreachable ({HUB}): {e}")


def _featured_top():
    """Top featured entries from the real catalog (mirrors the homepage feed)."""
    data = _get(f"/catalog?limit={TOP_N}&sort=quality")
    items = data.get("items") or data.get("entities") or []
    return items[:TOP_N]


# --- always-on: the real top-5 featured servers ----------------------------

def test_real_api_returns_featured():
    items = _featured_top()
    assert items, "real Matrix Hub returned no featured items"
    assert all(it.get("id") for it in items), "featured items missing ids"


def test_verifier_classifies_top5_featured():
    """The verifier must produce a clear testable/not verdict for every one of
    the live top-5 featured servers (this is the 'tag' the UI shows)."""
    items = _featured_top()
    print(f"\nSandbox testability of top {len(items)} featured (live from {HUB}):")
    for it in items:
        v = verify(it)
        tag = "● available" if v["testable"] else "○ not available"
        print(f"  {tag:16} {it.get('name') or it.get('id')}")
        print(f"      id={it.get('id')}")
        print(f"      reason: {v['reason']}")
        if v["testable"]:
            print(f"      run: {v['start_command']}")
        # contract: every entry yields a boolean verdict + a non-empty reason
        assert isinstance(v["testable"], bool)
        assert v["reason"]
        if v["testable"]:
            assert v["start_command"] and v["runtime"] in ("node", "python")


# --- deterministic verifier contract (no network) --------------------------

@pytest.mark.parametrize("entity,expected", [
    ({"id": "mcp.io-github-modelcontextprotocol-servers.filesystem",
      "name": "Filesystem", "source_url": "https://github.com/modelcontextprotocol/servers"}, True),
    ({"id": "x", "name": "Time", "source_url": "https://github.com/x/mcp-server-time"}, True),
    ({"id": "tool.io-github-mindstone-mcp-server-retell-ai.5425b3de29",
      "name": "Retell AI", "source_url": "https://github.com/mindstone/retell"}, False),
    ({"id": "tool.x", "name": "Microsoft Office", "source_url": "https://github.com/x/office"}, False),
    ({"id": "mcp.some-unknown-server", "name": "Unknown", "source_url": "https://github.com/x/y"}, False),
])
def test_verifier_contract(entity, expected):
    assert is_testable(entity) is expected


def test_saas_reason_mentions_credentials():
    v = verify({"id": "x", "name": "Retell AI", "source_url": ""})
    assert v["testable"] is False
    assert "credential" in v["reason"].lower()


# --- opt-in: actually run the testable servers in a live sandbox -----------

@pytest.mark.skipif(os.environ.get("MATRIXLAB_SANDBOX_LIVE") != "1",
                    reason="set MATRIXLAB_SANDBOX_LIVE=1 (+ MATRIXLAB_SANDBOX_URL) to run live")
def test_live_sandbox_runs_testable_featured():
    base = os.environ.get("MATRIXLAB_SANDBOX_URL", "http://127.0.0.1:7860").rstrip("/")
    # Use a known-good packaged server (proves the install->run->tools path).
    plan = verify({"id": "filesystem", "name": "Filesystem",
                   "source_url": "https://github.com/modelcontextprotocol/servers"})
    assert plan["testable"]
    r = requests.post(f"{base}/mcp/sessions", timeout=30, json={
        "entity_id": "test-filesystem", "runtime": plan["runtime"],
        "start_command": plan["start_command"], "transport": "stdio", "ttl_seconds": 120,
    })
    r.raise_for_status()
    sid = r.json()["session_id"]
    try:
        status = ""
        for _ in range(40):
            status = requests.get(f"{base}/mcp/sessions/{sid}", timeout=15).json()["status"]
            if status in ("running", "failed", "timeout", "install_failed", "start_failed"):
                break
            time.sleep(2)
        assert status == "running", f"sandbox status={status}"
        tools = requests.get(f"{base}/mcp/sessions/{sid}/tools", timeout=15).json().get("tools", [])
        assert len(tools) > 0, "no tools discovered"
    finally:
        requests.delete(f"{base}/mcp/sessions/{sid}", timeout=15)