Files
mi-fitness-health/tests/test_view_api.py

75 lines
3.9 KiB
Python

import pytest
from fastapi.testclient import TestClient
from mi_fitness_mcp import api
from mi_fitness_mcp.services.query_service import QueryService
from mi_fitness_mcp.storage import Database
from test_view_queries import insert
@pytest.fixture
def api_client(tmp_path, monkeypatch):
db = Database(tmp_path / "synthetic.db")
monkeypatch.setenv("MI_FITNESS_API_KEY", "synthetic-api-key")
context = api.UserContext("owner", "synthetic-unused", "cn", query_service=QueryService(db, "owner"))
monkeypatch.setattr(api.app.state, "db", db, raising=False)
monkeypatch.setattr(api.app.state, "default_context", context, raising=False)
client = TestClient(api.app, base_url="http://localhost", headers={"X-API-Key": "synthetic-api-key"})
yield client, db
client.close()
def test_view_auth_validation_and_legacy_compatibility(api_client):
client, db = api_client
insert(db, "heart_rate_samples", id="sample", timestamp="2026-10-15T02:00:00Z", bpm=72, sample_type="passive")
query = "?start_date=2026-10-15&end_date=2026-10-15"
for path in ["/api/views/heartrate", "/api/views/heartrate/records", "/api/views/heartrate/export", "/api/overview"]:
request = client.build_request("GET", path + query)
request.headers.pop("X-API-Key")
assert client.send(request).status_code == 401
result = client.get("/api/views/heartrate" + query)
assert result.status_code == 200
assert result.json()["statistics"]["primary_mean"] == 72
assert result.json()["page"]["size"] == 10
assert result.json()["range"] == {"start": "2026-10-15", "end": "2026-10-15"}
old = client.get("/api/heart-rate" + query)
assert old.status_code == 200
assert old.json() == {"status": "ok", "count": 1, "data": [dict(timestamp="2026-10-15T02:00:00Z", bpm=72, sample_type="passive")]}
page = client.get("/api/views/heartrate/records" + query).json()
assert set(page) == {"status", "kind", "range", "page"}
overview = client.get("/api/overview" + query).json()
assert overview["status"] == "ok"
assert overview["range"] == {"start": "2026-10-15", "end": "2026-10-15"}
assert overview["data"]["resting_mean_bpm"] is None
assert set(overview["data"]) == {"steps", "active_kcal", "distance_m", "resting_mean_bpm", "mean_daily_asleep_minutes", "mean_spo2_pct", "mean_stress", "activity_days", "sleep_days", "resting_count", "spo2_count"}
@pytest.mark.parametrize("changes", [dict(kind="unknown"), dict(start_date="2026-02-30"), dict(start_date="2026-10-16"),
dict(start_date="2000-01-01"), dict(page=0), dict(page=1000001), dict(limit=0), dict(limit=-1),
dict(pressure_level="invalid"), dict(page="1.5"), dict(include_naps="invalid")])
def test_invalid_view_query_rejected(api_client, changes):
client, _ = api_client
values = dict(kind="stress", start_date="2026-10-15", end_date="2026-10-15") | changes
kind = values.pop("kind")
response = client.get(f"/api/views/{kind}", params=values)
assert response.status_code in (400, 422)
assert "detail" in response.json()
def test_blank_filters_and_empty_values_are_supported(api_client):
client, _ = api_client
response = client.get("/api/views/stress", params=dict(start_date="2026-10-15", end_date="2026-10-15", sample_type="", pressure_level=""))
assert response.status_code == 200
assert response.json()["statistics"]["primary_mean"] is None
assert response.json()["page"]["rows"] == []
def test_records_endpoint_does_not_reaggregate(api_client, monkeypatch):
client, _ = api_client
from mi_fitness_mcp.storage.view_queries import ViewRepository
def forbidden(*args, **kwargs):
raise AssertionError("翻页不能重新聚合")
monkeypatch.setattr(ViewRepository, "statistics", forbidden)
monkeypatch.setattr(ViewRepository, "points", forbidden)
assert client.get("/api/views/stress/records", params=dict(start_date="2026-10-15", end_date="2026-10-15", page=2)).status_code == 200