Files
meshcore-hub/tests/test_api/test_dashboard.py
2025-12-08 19:37:45 +00:00

249 lines
9.5 KiB
Python

"""Tests for dashboard API routes."""
from datetime import datetime, timedelta, timezone
import pytest
from meshcore_hub.common.models import Advertisement, Message, Node
class TestDashboardStats:
"""Tests for GET /dashboard/stats endpoint."""
def test_get_stats_empty(self, client_no_auth):
"""Test getting stats with empty database."""
response = client_no_auth.get("/api/v1/dashboard/stats")
assert response.status_code == 200
data = response.json()
assert data["total_nodes"] == 0
assert data["active_nodes"] == 0
assert data["total_messages"] == 0
assert data["messages_today"] == 0
assert data["total_advertisements"] == 0
assert data["channel_message_counts"] == {}
def test_get_stats_with_data(
self, client_no_auth, sample_node, sample_message, sample_advertisement
):
"""Test getting stats with data in database."""
response = client_no_auth.get("/api/v1/dashboard/stats")
assert response.status_code == 200
data = response.json()
assert data["total_nodes"] == 1
assert data["active_nodes"] == 1 # Node was just created
assert data["total_messages"] == 1
assert data["total_advertisements"] == 1
class TestDashboardHtml:
"""Tests for GET /dashboard endpoint."""
def test_dashboard_html_response(self, client_no_auth):
"""Test dashboard returns HTML."""
response = client_no_auth.get("/api/v1/dashboard")
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
assert "<!DOCTYPE html>" in response.text
assert "MeshCore Hub Dashboard" in response.text
def test_dashboard_contains_stats(
self, client_no_auth, sample_node, sample_message
):
"""Test dashboard HTML contains stat values."""
response = client_no_auth.get("/api/v1/dashboard")
assert response.status_code == 200
# Check that stats are present
assert "Total Nodes" in response.text
assert "Active Nodes" in response.text
assert "Total Messages" in response.text
def test_dashboard_contains_recent_data(self, client_no_auth, sample_node):
"""Test dashboard HTML contains recent nodes."""
response = client_no_auth.get("/api/v1/dashboard")
assert response.status_code == 200
assert "Recent Nodes" in response.text
# The node name should appear in the table
assert sample_node.name in response.text
class TestDashboardActivity:
"""Tests for GET /dashboard/activity endpoint."""
@pytest.fixture
def past_advertisement(self, api_db_session):
"""Create an advertisement from yesterday (since today is excluded)."""
yesterday = datetime.now(timezone.utc) - timedelta(days=1)
advert = Advertisement(
public_key="abc123def456abc123def456abc123de",
name="TestNode",
adv_type="REPEATER",
received_at=yesterday,
)
api_db_session.add(advert)
api_db_session.commit()
api_db_session.refresh(advert)
return advert
def test_get_activity_empty(self, client_no_auth):
"""Test getting activity with empty database."""
response = client_no_auth.get("/api/v1/dashboard/activity")
assert response.status_code == 200
data = response.json()
assert data["days"] == 30
assert len(data["data"]) == 30
# All counts should be 0
for point in data["data"]:
assert point["count"] == 0
assert "date" in point
def test_get_activity_custom_days(self, client_no_auth):
"""Test getting activity with custom days parameter."""
response = client_no_auth.get("/api/v1/dashboard/activity?days=7")
assert response.status_code == 200
data = response.json()
assert data["days"] == 7
assert len(data["data"]) == 7
def test_get_activity_max_days(self, client_no_auth):
"""Test that activity is capped at 90 days."""
response = client_no_auth.get("/api/v1/dashboard/activity?days=365")
assert response.status_code == 200
data = response.json()
assert data["days"] == 90
assert len(data["data"]) == 90
def test_get_activity_with_data(self, client_no_auth, past_advertisement):
"""Test getting activity with advertisement in database.
Note: Activity endpoints exclude today's data to avoid showing
incomplete stats early in the day.
"""
response = client_no_auth.get("/api/v1/dashboard/activity")
assert response.status_code == 200
data = response.json()
# At least one day should have a count > 0
total_count = sum(point["count"] for point in data["data"])
assert total_count >= 1
class TestMessageActivity:
"""Tests for GET /dashboard/message-activity endpoint."""
@pytest.fixture
def past_message(self, api_db_session):
"""Create a message from yesterday (since today is excluded)."""
yesterday = datetime.now(timezone.utc) - timedelta(days=1)
message = Message(
message_type="direct",
pubkey_prefix="abc123",
text="Hello World",
received_at=yesterday,
)
api_db_session.add(message)
api_db_session.commit()
api_db_session.refresh(message)
return message
def test_get_message_activity_empty(self, client_no_auth):
"""Test getting message activity with empty database."""
response = client_no_auth.get("/api/v1/dashboard/message-activity")
assert response.status_code == 200
data = response.json()
assert data["days"] == 30
assert len(data["data"]) == 30
# All counts should be 0
for point in data["data"]:
assert point["count"] == 0
assert "date" in point
def test_get_message_activity_custom_days(self, client_no_auth):
"""Test getting message activity with custom days parameter."""
response = client_no_auth.get("/api/v1/dashboard/message-activity?days=7")
assert response.status_code == 200
data = response.json()
assert data["days"] == 7
assert len(data["data"]) == 7
def test_get_message_activity_max_days(self, client_no_auth):
"""Test that message activity is capped at 90 days."""
response = client_no_auth.get("/api/v1/dashboard/message-activity?days=365")
assert response.status_code == 200
data = response.json()
assert data["days"] == 90
assert len(data["data"]) == 90
def test_get_message_activity_with_data(self, client_no_auth, past_message):
"""Test getting message activity with message in database.
Note: Activity endpoints exclude today's data to avoid showing
incomplete stats early in the day.
"""
response = client_no_auth.get("/api/v1/dashboard/message-activity")
assert response.status_code == 200
data = response.json()
# At least one day should have a count > 0
total_count = sum(point["count"] for point in data["data"])
assert total_count >= 1
class TestNodeCountHistory:
"""Tests for GET /dashboard/node-count endpoint."""
@pytest.fixture
def past_node(self, api_db_session):
"""Create a node from yesterday (since today is excluded)."""
yesterday = datetime.now(timezone.utc) - timedelta(days=1)
node = Node(
public_key="abc123def456abc123def456abc123de",
name="Test Node",
adv_type="REPEATER",
first_seen=yesterday,
last_seen=yesterday,
created_at=yesterday,
)
api_db_session.add(node)
api_db_session.commit()
api_db_session.refresh(node)
return node
def test_get_node_count_empty(self, client_no_auth):
"""Test getting node count with empty database."""
response = client_no_auth.get("/api/v1/dashboard/node-count")
assert response.status_code == 200
data = response.json()
assert data["days"] == 30
assert len(data["data"]) == 30
# All counts should be 0
for point in data["data"]:
assert point["count"] == 0
assert "date" in point
def test_get_node_count_custom_days(self, client_no_auth):
"""Test getting node count with custom days parameter."""
response = client_no_auth.get("/api/v1/dashboard/node-count?days=7")
assert response.status_code == 200
data = response.json()
assert data["days"] == 7
assert len(data["data"]) == 7
def test_get_node_count_max_days(self, client_no_auth):
"""Test that node count is capped at 90 days."""
response = client_no_auth.get("/api/v1/dashboard/node-count?days=365")
assert response.status_code == 200
data = response.json()
assert data["days"] == 90
assert len(data["data"]) == 90
def test_get_node_count_with_data(self, client_no_auth, past_node):
"""Test getting node count with node in database.
Note: Activity endpoints exclude today's data to avoid showing
incomplete stats early in the day.
"""
response = client_no_auth.get("/api/v1/dashboard/node-count")
assert response.status_code == 200
data = response.json()
# At least one day should have a count > 0 (cumulative)
# The last day should have count >= 1
assert data["data"][-1]["count"] >= 1