Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions dream-server/extensions/services/dashboard-api/tests/test_routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ def test_workflows_requires_auth(test_client):
assert resp.status_code == 401


def test_agents_metrics_requires_auth(test_client):
"""GET /api/agents/metrics without auth header → 401."""
resp = test_client.get("/api/agents/metrics")
assert resp.status_code == 401


def test_agents_cluster_requires_auth(test_client):
"""GET /api/agents/cluster without auth header → 401."""
resp = test_client.get("/api/agents/cluster")
assert resp.status_code == 401


def test_agents_throughput_requires_auth(test_client):
"""GET /api/agents/throughput without auth header → 401."""
resp = test_client.get("/api/agents/throughput")
assert resp.status_code == 401


# ---------------------------------------------------------------------------
# Setup router
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -309,3 +327,136 @@ def test_api_service_tokens_authenticated(test_client):
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, dict)


# ---------------------------------------------------------------------------
# Agents router
# ---------------------------------------------------------------------------


def test_agents_metrics_authenticated(test_client):
"""GET /api/agents/metrics with auth → 200, returns agent metrics with seeded data."""
from agent_monitor import agent_metrics, throughput

# Seed non-default values to test actual aggregation
agent_metrics.session_count = 5
agent_metrics.tokens_per_second = 123.45
throughput.add_sample(100.0)
throughput.add_sample(150.0)

resp = test_client.get("/api/agents/metrics", headers=test_client.auth_headers)
assert resp.status_code == 200
data = resp.json()
assert "agent" in data
assert "cluster" in data
assert "throughput" in data

# Verify seeded values are reflected in response
assert data["agent"]["session_count"] == 5
assert data["agent"]["tokens_per_second"] == 123.45
assert data["throughput"]["current"] == 150.0
assert data["throughput"]["peak"] == 150.0


def test_agents_cluster_authenticated(test_client):
"""GET /api/agents/cluster with auth → 200, returns cluster status with mocked data."""

async def _fake_subprocess(*args, **kwargs):
"""Mock subprocess that returns a 2-node cluster with 1 healthy node."""
proc = MagicMock()
cluster_response = b'{"nodes": [{"id": "node1", "healthy": true}, {"id": "node2", "healthy": false}]}'
proc.communicate = AsyncMock(return_value=(cluster_response, b""))
proc.returncode = 0
return proc

with patch("asyncio.create_subprocess_exec", side_effect=_fake_subprocess):
resp = test_client.get("/api/agents/cluster", headers=test_client.auth_headers)

assert resp.status_code == 200
data = resp.json()
assert "nodes" in data
assert "total_gpus" in data
assert "active_gpus" in data
assert "failover_ready" in data

# Verify parsed cluster data
assert data["total_gpus"] == 2
assert data["active_gpus"] == 1
assert data["failover_ready"] is False # Only 1 healthy node, need >1 for failover


def test_agents_cluster_failover_ready(test_client):
"""GET /api/agents/cluster with 2 healthy nodes → failover_ready is True."""

async def _fake_subprocess(*args, **kwargs):
"""Mock subprocess that returns a 2-node cluster with both nodes healthy."""
proc = MagicMock()
cluster_response = b'{"nodes": [{"id": "node1", "healthy": true}, {"id": "node2", "healthy": true}]}'
proc.communicate = AsyncMock(return_value=(cluster_response, b""))
proc.returncode = 0
return proc

with patch("asyncio.create_subprocess_exec", side_effect=_fake_subprocess):
resp = test_client.get("/api/agents/cluster", headers=test_client.auth_headers)

assert resp.status_code == 200
data = resp.json()
assert data["total_gpus"] == 2
assert data["active_gpus"] == 2
assert data["failover_ready"] is True # 2 healthy nodes enables failover


def test_agents_metrics_html_xss_escaping(test_client):
"""GET /api/agents/metrics.html escapes HTML special chars to prevent XSS."""
from agent_monitor import agent_metrics, throughput

# Inject XSS payload into agent metrics
agent_metrics.session_count = 999
throughput.add_sample(42.0)

# Mock cluster status with XSS payload in node data
async def _fake_subprocess(*args, **kwargs):
proc = MagicMock()
# Node ID contains script tag
cluster_response = b'{"nodes": [{"id": "<script>alert(1)</script>", "healthy": true}]}'
proc.communicate = AsyncMock(return_value=(cluster_response, b""))
proc.returncode = 0
return proc

with patch("asyncio.create_subprocess_exec", side_effect=_fake_subprocess):
resp = test_client.get("/api/agents/metrics.html", headers=test_client.auth_headers)

assert resp.status_code == 200
html_content = resp.text

# Verify HTML special chars are escaped
assert "<script>" not in html_content
assert "&lt;script&gt;" in html_content or "alert(1)" not in html_content
# Verify legitimate content is present
assert "999" in html_content # session_count
assert "42.0" in html_content # throughput


def test_agents_throughput_authenticated(test_client):
"""GET /api/agents/throughput with auth → 200, returns throughput stats with real data."""
from agent_monitor import throughput

# Seed throughput data to test actual behavior
throughput.add_sample(42.0)
throughput.add_sample(55.0)
throughput.add_sample(38.0)

resp = test_client.get("/api/agents/throughput", headers=test_client.auth_headers)
assert resp.status_code == 200
data = resp.json()
assert "current" in data
assert "average" in data
assert "peak" in data
assert "history" in data

# Verify calculated stats
assert data["current"] == 38.0 # Last sample
assert data["peak"] == 55.0 # Max of all samples
assert data["average"] == (42.0 + 55.0 + 38.0) / 3 # Average of all samples
assert len(data["history"]) == 3

Loading