Files
handbook/docker/packages/mcp_proxy/tests/test_viewer.py
T

125 lines
4.9 KiB
Python
Raw Normal View History

2026-06-24 12:44:34 -07:00
"""Tests for the viewer reverse-proxy app."""
import httpx
import pytest
from starlette.testclient import TestClient
from mcp_proxy.viewer import _DISPLAY_NAMES, _render_shell, create_viewer_app
# ---------------------------------------------------------------------------
# Shell rendering
# ---------------------------------------------------------------------------
class TestRenderShell:
def test_empty_registry(self):
html = _render_shell({})
assert "No services available" in html
def test_single_service(self):
html = _render_shell({"jira": 9000})
assert "Services" in html
assert "1 services" in html
assert 'data-service="jira"' in html
assert 'src="/__viewer__/set?app=jira"' in html
def test_multiple_services(self):
html = _render_shell({"jira": 9000, "slack": 9001, "google_mail": 9002})
assert "3 services" in html
for name in ("jira", "slack", "google_mail"):
assert f'data-service="{name}"' in html
def test_display_names_used(self):
html = _render_shell({"jira": 9000})
assert _DISPLAY_NAMES["jira"] in html # "Issues"
def test_unknown_service_gets_title_case(self):
html = _render_shell({"my_custom_service": 9000})
assert "My Custom Service" in html
def test_services_sorted(self):
html = _render_shell({"slack": 9001, "jira": 9000})
jira_pos = html.index('data-service="jira"')
slack_pos = html.index('data-service="slack"')
assert jira_pos < slack_pos
def test_first_service_is_default_iframe(self):
html = _render_shell({"slack": 9001, "jira": 9000})
# sorted → jira comes first
assert 'src="/__viewer__/set?app=jira"' in html
# ---------------------------------------------------------------------------
# Viewer app routes
# ---------------------------------------------------------------------------
class TestViewerApp:
@pytest.fixture
def app(self):
return create_viewer_app(
service_registry={"test_svc": 9999, "other": 8888},
proxy_token="secret123",
)
@pytest.fixture
def client(self, app):
return TestClient(app, raise_server_exceptions=False)
def test_index_returns_html(self, client):
resp = client.get("/")
assert resp.status_code == 200
assert "Services" in resp.text
def test_reverse_proxy_unknown_service_returns_404(self, client):
resp = client.get("/__viewer__/set?app=nonexistent")
assert resp.status_code == 404
assert "Unknown service" in resp.text
def test_iframe_fallback_uses_server_state_when_cookie_blocked(self, app, monkeypatch):
"""When embedded in an iframe, browsers may block the cookie. The
viewer should fall back to server-side state when any referer is
present (indicating a redirect, not a direct navigation)."""
async def mock_request(self, *args, **kwargs):
return httpx.Response(200, text="service content")
monkeypatch.setattr(httpx.AsyncClient, "request", mock_request)
client = TestClient(app, raise_server_exceptions=False)
# First, select a service via /__viewer__/set to populate server state.
resp = client.get("/__viewer__/set?app=test_svc", follow_redirects=False)
assert resp.status_code == 307
# Now simulate an iframe request with NO cookie but a referer present
# (browser stripped the path but kept the origin).
resp = client.get(
"/",
headers={"referer": "http://some-outer-host/"},
cookies={}, # no cookie — blocked by browser
)
# Should proxy to the service, not re-render the shell
assert resp.status_code == 200
assert "service content" in resp.text
assert "Services" not in resp.text # not the shell HTML
def test_iframe_fallback_not_triggered_without_referer(self, client):
"""Without a referer, the server state fallback should NOT activate —
direct navigation should always show the shell."""
resp = client.get("/", cookies={})
assert resp.status_code == 200
assert "Services" in resp.text
def test_reverse_proxy_connection_error_returns_502(self, app, monkeypatch):
# Mock httpx to simulate a connection error regardless of port state
async def mock_request(self, *args, **kwargs):
raise httpx.ConnectError("Connection refused")
monkeypatch.setattr(httpx.AsyncClient, "request", mock_request)
client = TestClient(app, raise_server_exceptions=False)
# Set the service cookie first, then make a proxied request
client.cookies.set("__viewer_service", "test_svc")
resp = client.get("/some-page", headers={"referer": "http://testserver/"})
assert resp.status_code == 502
assert "Service unavailable" in resp.text