mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-03-28 17:42:56 +01:00
- Renamed network route, template, and tests to dashboard - Added logo.svg for favicon and navbar branding - Created reusable Jinja2 icon macros for navigation and UI elements - Updated home page hero layout with centered content and larger logo - Added Map button alongside Dashboard button in hero section - Navigation menu items now display icons before labels Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
207 lines
7.4 KiB
Python
207 lines
7.4 KiB
Python
"""End-to-end tests for the full MeshCore Hub flow.
|
|
|
|
These tests require Docker Compose services to be running:
|
|
|
|
docker compose -f tests/e2e/docker-compose.test.yml up -d
|
|
pytest tests/e2e/
|
|
docker compose -f tests/e2e/docker-compose.test.yml down -v
|
|
|
|
The tests verify:
|
|
1. API health endpoints
|
|
2. Web dashboard health endpoints
|
|
3. Node listing and retrieval
|
|
4. Message listing
|
|
5. Statistics endpoint
|
|
6. Command sending (admin only)
|
|
"""
|
|
|
|
import httpx
|
|
|
|
|
|
class TestHealthEndpoints:
|
|
"""Test health check endpoints."""
|
|
|
|
def test_api_health(self, api_client: httpx.Client) -> None:
|
|
"""Test API basic health endpoint."""
|
|
response = api_client.get("/health")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "healthy"
|
|
assert "version" in data
|
|
|
|
def test_api_ready(self, api_client: httpx.Client) -> None:
|
|
"""Test API readiness endpoint with database check."""
|
|
response = api_client.get("/health/ready")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "ready"
|
|
assert data["database"] == "connected"
|
|
|
|
def test_web_health(self, web_client: httpx.Client) -> None:
|
|
"""Test Web dashboard health endpoint."""
|
|
response = web_client.get("/health")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "healthy"
|
|
|
|
def test_web_ready(self, web_client: httpx.Client) -> None:
|
|
"""Test Web dashboard readiness with API connectivity."""
|
|
response = web_client.get("/health/ready")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "ready"
|
|
assert data["api"] == "connected"
|
|
|
|
|
|
class TestAPIEndpoints:
|
|
"""Test API data endpoints."""
|
|
|
|
def test_list_nodes(self, api_client: httpx.Client) -> None:
|
|
"""Test listing nodes."""
|
|
response = api_client.get("/api/v1/nodes")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "total" in data
|
|
assert isinstance(data["items"], list)
|
|
|
|
def test_list_messages(self, api_client: httpx.Client) -> None:
|
|
"""Test listing messages."""
|
|
response = api_client.get("/api/v1/messages")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "total" in data
|
|
|
|
def test_list_advertisements(self, api_client: httpx.Client) -> None:
|
|
"""Test listing advertisements."""
|
|
response = api_client.get("/api/v1/advertisements")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "total" in data
|
|
|
|
def test_get_stats(self, api_client: httpx.Client) -> None:
|
|
"""Test getting network statistics."""
|
|
response = api_client.get("/api/v1/stats")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_nodes" in data
|
|
assert "active_nodes" in data
|
|
assert "total_messages" in data
|
|
|
|
def test_list_telemetry(self, api_client: httpx.Client) -> None:
|
|
"""Test listing telemetry records."""
|
|
response = api_client.get("/api/v1/telemetry")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
|
|
def test_list_trace_paths(self, api_client: httpx.Client) -> None:
|
|
"""Test listing trace paths."""
|
|
response = api_client.get("/api/v1/trace-paths")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
|
|
def test_list_members(self, api_client: httpx.Client) -> None:
|
|
"""Test listing members."""
|
|
response = api_client.get("/api/v1/members")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "total" in data
|
|
|
|
|
|
class TestWebDashboard:
|
|
"""Test web dashboard pages."""
|
|
|
|
def test_home_page(self, web_client: httpx.Client) -> None:
|
|
"""Test home page loads."""
|
|
response = web_client.get("/")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_dashboard_page(self, web_client: httpx.Client) -> None:
|
|
"""Test dashboard page loads."""
|
|
response = web_client.get("/dashboard")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_nodes_page(self, web_client: httpx.Client) -> None:
|
|
"""Test nodes listing page loads."""
|
|
response = web_client.get("/nodes")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_messages_page(self, web_client: httpx.Client) -> None:
|
|
"""Test messages page loads."""
|
|
response = web_client.get("/messages")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_map_page(self, web_client: httpx.Client) -> None:
|
|
"""Test map page loads."""
|
|
response = web_client.get("/map")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_members_page(self, web_client: httpx.Client) -> None:
|
|
"""Test members page loads."""
|
|
response = web_client.get("/members")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
|
|
class TestAuthentication:
|
|
"""Test API authentication."""
|
|
|
|
def test_read_access_with_read_key(self, api_client: httpx.Client) -> None:
|
|
"""Test read access works with read key."""
|
|
response = api_client.get("/api/v1/nodes")
|
|
assert response.status_code == 200
|
|
|
|
def test_admin_access_with_admin_key(self, admin_client: httpx.Client) -> None:
|
|
"""Test admin access works with admin key."""
|
|
# Admin key should have read access
|
|
response = admin_client.get("/api/v1/nodes")
|
|
assert response.status_code == 200
|
|
|
|
|
|
class TestCommands:
|
|
"""Test command endpoints (requires admin access)."""
|
|
|
|
def test_send_message_requires_admin(self, api_client: httpx.Client) -> None:
|
|
"""Test that send message requires admin key."""
|
|
response = api_client.post(
|
|
"/api/v1/commands/send-message",
|
|
json={
|
|
"destination": "0" * 64,
|
|
"text": "Test message",
|
|
},
|
|
)
|
|
# Read key should not have admin access
|
|
assert response.status_code == 403
|
|
|
|
def test_send_channel_message_admin(self, admin_client: httpx.Client) -> None:
|
|
"""Test sending channel message with admin key."""
|
|
response = admin_client.post(
|
|
"/api/v1/commands/send-channel-message",
|
|
json={
|
|
"channel_idx": 0,
|
|
"text": "Test channel message",
|
|
},
|
|
)
|
|
# Should succeed with admin key (202 = accepted for processing)
|
|
assert response.status_code == 202
|
|
|
|
def test_send_advertisement_admin(self, admin_client: httpx.Client) -> None:
|
|
"""Test sending advertisement with admin key."""
|
|
response = admin_client.post(
|
|
"/api/v1/commands/send-advertisement",
|
|
json={
|
|
"flood": False,
|
|
},
|
|
)
|
|
assert response.status_code == 202
|