Files
infrapuzzle/projects/duckdb-backend/tests/test_api_curated.py
T

206 lines
6.6 KiB
Python

import pytest
from fastapi.testclient import TestClient
def test_mila_dashboard_endpoint(client: TestClient):
resp = client.get("/duckdb-api/v1/mila/dashboard")
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "ok"
assert data["metadata"]["subject"] == "mila"
assert "live_status" in data
assert "today" in data
assert "growth" in data
def test_moritz_dashboard_endpoint(client: TestClient):
resp = client.get("/duckdb-api/v1/moritz/dashboard")
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "ok"
assert data["metadata"]["subject"] == "moritz"
assert "today" in data
assert "yesterday" in data
assert "seven_days" in data
assert "thirty_days" in data
def test_healthz_and_status_endpoints(client: TestClient):
resp_h = client.get("/healthz")
assert resp_h.status_code == 200
assert resp_h.json()["engine_ready"] is True
resp_s = client.get("/duckdb-api/v1/status")
assert resp_s.status_code == 200
data_s = resp_s.json()
assert "status" in data_s
assert "table_counts" in data_s
assert data_s["table_counts"]["sleep"] > 0
def test_mila_history_includes_diaper_only_days(client: TestClient):
# Log a diaper change for a specific day without sleep
client.post("/duckdb-api/v1/events", json={
"kind": "diaper.change",
"subject": "mila",
"start": "2026-10-12T12:00:00+02:00",
"payload": {"pee": True, "poo": True},
})
resp = client.get("/duckdb-api/v1/mila/dashboard")
assert resp.status_code == 200
data = resp.json()
history = data["history"]
dates = [h["date"] for h in history]
assert "2026-10-12" in dates
def test_moritz_seven_days_includes_garmin_days(client: TestClient):
resp = client.get("/duckdb-api/v1/moritz/dashboard")
assert resp.status_code == 200
data = resp.json()
days = data["seven_days"]["days"]
assert len(days) > 0
# Every returned day has a valid date and steps or expenditure
for d in days:
assert d["date"] is not None
assert "expenditure_kcal" in d
def test_mila_dashboard_parent_cockpit_and_norms(client: TestClient):
resp = client.get("/duckdb-api/v1/mila/dashboard")
assert resp.status_code == 200
data = resp.json()
# Parent cockpit live fields
live = data["live_status"]
assert "is_sleeping" in live
assert "active_sleep_duration_str" in live
assert "wake_window_minutes" in live
assert "last_feed_since_str" in live
assert "last_diaper_since_str" in live
assert "last_stool_str" in live
# Science age norms
assert "age_norms" in data
norms = data["age_norms"]
assert norms["age_months"] >= 6.0
assert norms["wake_window_min_minutes"] == 135
assert norms["wake_window_max_minutes"] == 180
assert norms["feed_interval_min_minutes"] == 180
# Ensure IDs on items
today = data["today"]
for f in today["feedings"]:
assert "id" in f
for d in today["diapers"]:
assert "id" in d
def test_day_detail_and_summary_endpoints(client: TestClient):
# Test mila day detail
resp_mila = client.get("/duckdb-api/v1/mila/day/2026-10-12")
assert resp_mila.status_code == 200
assert resp_mila.json()["metadata"]["today_date"] == "2026-10-12"
# Test moritz day detail
resp_moritz = client.get("/duckdb-api/v1/moritz/day/2026-10-12")
assert resp_moritz.status_code == 200
assert "today" in resp_moritz.json()
# Test summary
resp_sum = client.get("/duckdb-api/v1/summary/mila?date=2026-10-12")
assert resp_sum.status_code == 200
sum_data = resp_sum.json()
assert sum_data["subject"] == "mila"
assert sum_data["date"] == "2026-10-12"
assert "nutrition" in sum_data
def test_entities_and_places_endpoints(client: TestClient):
# Put and list entity
client.post("/duckdb-api/v1/entities", json={
"collection": "places",
"record": {
"id": "test-biergarten",
"name": "Test Biergarten",
"category": "biergarten",
"lat": 49.02,
"lon": 12.09,
}
})
resp_places = client.get("/duckdb-api/v1/entities/places")
assert resp_places.status_code == 200
places = resp_places.json()
assert any(p["id"] == "test-biergarten" for p in places)
# Places near search
resp_near = client.get("/duckdb-api/v1/places/near?lat=49.02&lon=12.09&radius_m=500")
assert resp_near.status_code == 200
near = resp_near.json()
assert len(near) > 0
assert near[0]["id"] == "test-biergarten"
def test_garmin_sync_and_duplicate_guard(client: TestClient):
# Duplicate guard test
post_data = {
"kind": "feeding.bottle",
"subject": "mila",
"start": "2026-10-12T10:00:00+02:00",
"payload": {"consumed_ml": 180, "milk_type": "pre"},
}
r1 = client.post("/duckdb-api/v1/events", json=post_data)
assert r1.status_code == 200
assert r1.json()["status"] == "success"
assert "feedback" in r1.json()
# Second post within 15min should be duplicate_skipped
r2 = client.post("/duckdb-api/v1/events", json=post_data)
assert r2.status_code == 200
assert r2.json()["status"] == "duplicate_skipped"
# Garmin sync test
garmin_payload = {
"daily": [{
"calendarDate": "2026-10-15",
"totalKilocalories": 2400,
"activeKilocalories": 600,
"bmrKilocalories": 1800,
"totalSteps": 11000,
"restingHeartRate": 52,
}],
"sleep": [],
"activities": [],
}
r_sync = client.post("/duckdb-api/v1/garmin/sync", json=garmin_payload)
assert r_sync.status_code == 200
assert r_sync.json()["status"] == "success"
assert r_sync.json()["synced_daily"] == 1
# Test alias support (daily_summary and sleep_data dicts)
garmin_alias_payload = {
"date": "2026-10-16",
"daily_summary": {
"calendarDate": "2026-10-16",
"totalKilocalories": 2550,
"activeKilocalories": 650,
"bmrKilocalories": 1900,
"totalSteps": 12000,
"restingHeartRate": 50,
},
"sleep_data": {
"dailySleepDTO": {
"calendarDate": "2026-10-16",
"sleepTimeSeconds": 28800,
}
},
"activities": [],
}
r_alias = client.post("/duckdb-api/v1/garmin/sync", json=garmin_alias_payload)
assert r_alias.status_code == 200
assert r_alias.json()["status"] == "success"
assert r_alias.json()["synced_daily"] == 1
assert r_alias.json()["synced_sleep"] == 1