Skip to content
Open
Show file tree
Hide file tree
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
9 changes: 6 additions & 3 deletions dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
from datetime import datetime
from urllib.parse import urlparse

DB_PATH = Path.home() / ".claude" / "usage.db"

Expand Down Expand Up @@ -1242,13 +1243,14 @@ def log_message(self, format, *args):
pass

def do_GET(self):
if self.path in ("/", "/index.html"):
path = urlparse(self.path).path
if path in ("/", "/index.html"):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(HTML_TEMPLATE.encode("utf-8"))

elif self.path == "/api/data":
elif path == "/api/data":
data = get_dashboard_data()
body = json.dumps(data).encode("utf-8")
self.send_response(200)
Expand All @@ -1262,7 +1264,8 @@ def do_GET(self):
self.end_headers()

def do_POST(self):
if self.path == "/api/rescan":
path = urlparse(self.path).path
if path == "/api/rescan":
# Full rebuild: delete DB and rescan from scratch.
# Pass DB_PATH / DEFAULT_PROJECTS_DIRS explicitly so tests that
# patch the module globals are honored (scan's defaults are
Expand Down
26 changes: 26 additions & 0 deletions tests/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ def test_index_returns_html(self):
self.assertEqual(resp.status, 200)
self.assertIn("text/html", resp.headers["Content-Type"])

def test_index_with_query_string_returns_html(self):
url = f"http://127.0.0.1:{self.port}/?range=30d"
with urllib.request.urlopen(url) as resp:
self.assertEqual(resp.status, 200)
self.assertIn("text/html", resp.headers["Content-Type"])
self.assertIn(b"Claude Code Usage Dashboard", resp.read())

def test_api_data_returns_json(self):
url = f"http://127.0.0.1:{self.port}/api/data"
with urllib.request.urlopen(url) as resp:
Expand All @@ -175,6 +182,14 @@ def test_api_data_returns_json(self):
# Should have expected keys (or error if no DB)
self.assertTrue("all_models" in data or "error" in data)

def test_api_data_with_query_string_returns_json(self):
url = f"http://127.0.0.1:{self.port}/api/data?range=all"
with urllib.request.urlopen(url) as resp:
self.assertEqual(resp.status, 200)
self.assertIn("application/json", resp.headers["Content-Type"])
data = json.loads(resp.read())
self.assertTrue("all_models" in data or "error" in data)

def test_api_rescan_returns_json(self):
url = f"http://127.0.0.1:{self.port}/api/rescan"
req = urllib.request.Request(url, method="POST")
Expand All @@ -186,6 +201,17 @@ def test_api_rescan_returns_json(self):
self.assertIn("updated", data)
self.assertIn("skipped", data)

def test_api_rescan_with_query_string_returns_json(self):
url = f"http://127.0.0.1:{self.port}/api/rescan?source=dashboard"
req = urllib.request.Request(url, method="POST")
with urllib.request.urlopen(req) as resp:
self.assertEqual(resp.status, 200)
self.assertIn("application/json", resp.headers["Content-Type"])
data = json.loads(resp.read())
self.assertIn("new", data)
self.assertIn("updated", data)
self.assertIn("skipped", data)

def test_404_for_unknown_path(self):
url = f"http://127.0.0.1:{self.port}/nonexistent"
try:
Expand Down
Loading