diff --git a/frontend/electron/main.cjs b/frontend/electron/main.cjs index 26bbfa4..12599ef 100644 --- a/frontend/electron/main.cjs +++ b/frontend/electron/main.cjs @@ -161,13 +161,26 @@ function stopSidecar() { async function apiIsRunning(timeoutMs = 250) { try { - await fetchStatus(timeoutMs) + await fetchHealth(timeoutMs) return true } catch { return false } } +function fetchHealth(timeoutMs = 1000) { + return new Promise((resolve, reject) => { + const request = http.get(`${apiBase}/health`, (response) => { + response.resume() + response.statusCode === 200 + ? resolve() + : reject(new Error(`status ${response.statusCode}`)) + }) + request.on("error", reject) + request.setTimeout(timeoutMs, () => request.destroy(new Error("timeout"))) + }) +} + function fetchStatus(timeoutMs = 1000) { return new Promise((resolve, reject) => { const request = http.get(`${apiBase}/status`, (response) => { diff --git a/src/resolve_time_tracker/api.py b/src/resolve_time_tracker/api.py index 20a17f0..8a84e36 100644 --- a/src/resolve_time_tracker/api.py +++ b/src/resolve_time_tracker/api.py @@ -382,6 +382,10 @@ def create_app( ) app.state.api = api + @app.get("/health") + def health() -> dict[str, bool]: + return {"ok": True} + @app.get("/status") def status() -> dict[str, Any]: return api.refresh() diff --git a/tests/test_api.py b/tests/test_api.py index 553e0a4..b8db41d 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2,7 +2,7 @@ import unittest from datetime import datetime, timezone from pathlib import Path -from unittest.mock import patch +from unittest.mock import Mock, patch from fastapi.testclient import TestClient @@ -296,6 +296,19 @@ def test_allows_frontend_origin(self): self.assertEqual("*", response.headers["access-control-allow-origin"]) + def test_health_does_not_poll_resolve(self): + with tempfile.TemporaryDirectory() as tmp: + engine = Mock() + with SQLiteStore( + Path(tmp) / "tracker.sqlite3", check_same_thread=False + ) as store: + response = TestClient(create_app(store, tracking_engine=engine)).get( + "/health" + ) + + self.assertEqual({"ok": True}, response.json()) + engine.poll.assert_not_called() + def test_run_api_starts_tracking_api_with_resolve_bridge(self): with tempfile.TemporaryDirectory() as tmp: with (