Skip to content

Commit 02e56c0

Browse files
committed
add some tests for dashboard
1 parent 0799152 commit 02e56c0

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pandas as pd
2+
from datetime import datetime, timedelta, timezone
3+
import pytest
4+
from batch_stats import calculate_batch_stats, _format_time_ago
5+
6+
@pytest.fixture
7+
def sample_df():
8+
"""Create a sample DataFrame for testing."""
9+
now = datetime.now(timezone.utc)
10+
return pd.DataFrame({
11+
'metric_name': ['test_metric1', 'test_metric1', 'test_metric2'],
12+
'metric_timestamp': [
13+
now.isoformat(),
14+
(now - timedelta(minutes=30)).isoformat(),
15+
(now - timedelta(hours=2)).isoformat()
16+
],
17+
'metric_value': [1.0, 2.0, 3.0],
18+
'metric_score': [0.5, 0.7, 0.3],
19+
'metric_alert': [1, 0, 1]
20+
})
21+
22+
def test_calculate_batch_stats_empty_df():
23+
"""Test stats calculation with empty DataFrame."""
24+
empty_df = pd.DataFrame()
25+
stats = calculate_batch_stats(empty_df, "test_batch")
26+
27+
assert stats["unique_metrics"] == 0
28+
assert stats["latest_timestamp"] == "No data"
29+
assert stats["avg_score"] == 0
30+
assert stats["alert_count"] == 0
31+
32+
def test_calculate_batch_stats(sample_df):
33+
"""Test stats calculation with sample data."""
34+
stats = calculate_batch_stats(sample_df, "test_batch")
35+
36+
assert stats["unique_metrics"] == 2 # test_metric1 and test_metric2
37+
assert "ago" in stats["latest_timestamp"] # Should contain time ago string
38+
assert stats["avg_score"] == 0.5 # Average of [0.5, 0.7, 0.3]
39+
assert stats["alert_count"] == 2 # Count of 1s in metric_alert
40+
41+
def test_format_time_ago():
42+
"""Test time ago formatting."""
43+
now = datetime.now(timezone.utc)
44+
45+
# Test minutes
46+
timestamp = (now - timedelta(minutes=5)).isoformat()
47+
assert "minute" in _format_time_ago(timestamp)
48+
49+
# Test hours
50+
timestamp = (now - timedelta(hours=3)).isoformat()
51+
assert "hour" in _format_time_ago(timestamp)
52+
53+
# Test days
54+
timestamp = (now - timedelta(days=2)).isoformat()
55+
assert "day" in _format_time_ago(timestamp)
56+
57+
def test_format_time_ago_no_data():
58+
"""Test handling of 'No data' timestamp."""
59+
assert _format_time_ago("No data") == "No data"

dashboard/tests/test_components.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pytest
2+
from components import create_batch_card, create_header
3+
4+
def test_create_batch_card():
5+
"""Test batch card creation."""
6+
stats = {
7+
"unique_metrics": 10,
8+
"latest_timestamp": "5 minutes ago",
9+
"avg_score": 0.75,
10+
"alert_count": 3
11+
}
12+
13+
card = create_batch_card("test_batch", stats)
14+
15+
# Test that the card contains key information
16+
card_str = str(card)
17+
assert "test_batch" in card_str
18+
assert "10 metrics" in card_str
19+
assert "5 minutes ago" in card_str
20+
assert "75.0%" in card_str # 0.75 formatted as percentage
21+
assert "3 alerts" in card_str
22+
23+
def test_create_header():
24+
"""Test header creation."""
25+
header = create_header()
26+
header_str = str(header)
27+
28+
# Test that header contains key elements
29+
assert "Anomstack" in header_str
30+
assert "Painless open source anomaly detection" in header_str
31+
assert "github.com/andrewm4894/anomstack" in header_str

dashboard/tests/test_routes.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import pytest
2+
from fasthtml.common import Div
3+
from app import app
4+
from routes import index, get_batch_view
5+
6+
@pytest.fixture
7+
def mock_app_state(monkeypatch):
8+
"""Mock application state for testing."""
9+
class MockState:
10+
dark_mode = False
11+
metric_batches = ["test_batch"]
12+
df_cache = {}
13+
specs_enabled = {"test_batch": {}}
14+
alert_max_n = {"test_batch": 100}
15+
stats_cache = {"test_batch": []}
16+
17+
monkeypatch.setattr(app, "state", MockState())
18+
return app.state
19+
20+
@pytest.fixture
21+
def mock_request():
22+
"""Mock HTTP request."""
23+
class MockRequest:
24+
def __init__(self, htmx=False):
25+
self.headers = {"HX-Request": "true"} if htmx else {}
26+
return MockRequest
27+
28+
def test_index_route_no_htmx(mock_app_state, mock_request):
29+
"""Test index route with regular request."""
30+
response = index(mock_request())
31+
32+
# Should return multiple elements for full page load
33+
assert len(response) > 1
34+
assert any("Anomstack" in str(elem) for elem in response)
35+
36+
def test_index_route_htmx(mock_app_state, mock_request):
37+
"""Test index route with HTMX request."""
38+
response = index(mock_request(htmx=True))
39+
40+
# Should return only main content
41+
assert isinstance(response, Div)
42+
assert response.attrs.get("id") == "main-content"
43+
44+
def test_batch_view(mock_app_state):
45+
"""Test batch view."""
46+
response = get_batch_view("test_batch", None)
47+
48+
# Should return a div containing charts container
49+
assert isinstance(response, Div)
50+
assert any("charts-container" in str(elem) for elem in response.children)

0 commit comments

Comments
 (0)