Files
btcprice/tests/test_health.py
T
magnus 0c86cf3b59
pull-request / validate (pull_request) Successful in 12s
Show midnight change below heading
2026-09-17 14:53:05 +02:00

110 lines
3.8 KiB
Python

import httpx
from fastapi.testclient import TestClient
from app import main
class StubAsyncClient:
def __init__(self, response=None, error=None):
self.response = response
self.error = error
self.requested_url = None
self.timeout = None
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, traceback):
return None
async def get(self, url, timeout):
self.requested_url = url
self.timeout = timeout
if self.error:
raise self.error
return self.response
def test_health_is_independent_of_btcdata():
response = TestClient(main.app).get("/api/health")
assert response.status_code == 200
assert response.json() == {"status": "up"}
def test_home_updates_title_from_latest_price():
response = TestClient(main.app).get("/")
assert response.status_code == 200
assert "new MutationObserver(updateTitleFromPrice)" in response.text
assert "currentPrice - midnightPrice" in response.text
assert "document.title = price + ' | ' + formattedChange" in response.text
assert '<h1 class="text-3xl font-bold text-gray-900">BTC Price</h1>' in response.text
assert "page-title" not in response.text
assert 'id="midnight-change"' in response.text
assert "midnightChange.textContent = formattedChange" in response.text
assert "since 00:00" not in response.text
assert "change > 0 ? 'text-green-600' : change < 0 ? 'text-red-600'" in response.text
assert "formatPriceChange(change)" in response.text
assert "midnight.setHours(0, 0, 0, 0)" in response.text
def test_price_fragment_exposes_formatted_price_for_title():
html = main.price_json_to_html(
'{"price": 67432.1, "prev_price": 67400, "recorded_at": ""}'
)
assert 'data-title-price="$67.432,10"' in html
assert 'data-price-value="67432.1"' in html
assert ">$67.432,10</p>" in html
def test_ready_when_btcdata_is_healthy(monkeypatch):
request = httpx.Request("GET", "http://btcdata.test/api/health")
upstream = httpx.Response(200, request=request)
client = StubAsyncClient(response=upstream)
monkeypatch.setattr(main, "BTCDATA_URL", "http://btcdata.test")
monkeypatch.setattr(main.httpx, "AsyncClient", lambda: client)
response = TestClient(main.app).get("/api/ready")
assert response.status_code == 200
assert response.json() == {"status": "ready"}
assert client.requested_url == "http://btcdata.test/api/health"
assert client.timeout == main.READINESS_TIMEOUT_SECONDS
def test_not_ready_when_btcdata_is_unavailable(monkeypatch):
request = httpx.Request("GET", "http://btcdata.test/api/health")
error = httpx.ConnectError("connection refused", request=request)
client = StubAsyncClient(error=error)
monkeypatch.setattr(main, "BTCDATA_URL", "http://btcdata.test")
monkeypatch.setattr(main.httpx, "AsyncClient", lambda: client)
response = TestClient(main.app).get("/api/ready")
assert response.status_code == 503
assert response.json() == {"status": "not ready"}
assert "btcdata.test" not in response.text
def test_not_ready_when_btcdata_returns_an_error(monkeypatch):
request = httpx.Request("GET", "http://btcdata.test/api/health")
upstream = httpx.Response(503, request=request)
client = StubAsyncClient(response=upstream)
monkeypatch.setattr(main.httpx, "AsyncClient", lambda: client)
response = TestClient(main.app).get("/api/ready")
assert response.status_code == 503
assert response.json() == {"status": "not ready"}
def test_version_endpoint(monkeypatch):
monkeypatch.setattr(main, "APP_VERSION", "abc1234")
response = TestClient(main.app).get("/api/version")
assert response.status_code == 200
assert response.json() == {"version": "abc1234"}