feat(datastore): add relative --since duration flag and overnight sleep test

This commit is contained in:
Moritz Graf
2026-10-09 09:25:04 +02:00
parent 0489ef7d62
commit 77a0e56d96
3 changed files with 147 additions and 1 deletions
+91
View File
@@ -293,3 +293,94 @@ def test_garmin_daily_and_daily_balance(temp_datastore: DatastoreEngine):
assert row["balance_kcal"] == -744.0 # 2146 - 2890
assert row["balance_status"] == "Defizit"
def test_add_event_with_since(temp_datastore: DatastoreEngine):
"""Verifies that relative offsets like '15m' calculate start time deterministically in Berlin timezone."""
from zoneinfo import ZoneInfo
berlin_tz = ZoneInfo("Europe/Berlin")
before = datetime.datetime.now(berlin_tz)
res = temp_datastore.add_event(
kind="sleep.session",
subject="mila",
actor="agent:hebamme",
since="15m",
payload={"type": "nap", "location": "bett"},
)
after = datetime.datetime.now(berlin_tz)
assert res["status"] == "success"
event = res["event"]
assert event["end"] is None
start_dt = datetime.datetime.fromisoformat(event["start"])
assert start_dt.tzinfo is not None
# Verify Berlin timezone offset (+02:00 in CEST or +01:00 in CET)
assert start_dt.utcoffset() == before.utcoffset()
# The logged start must be ~15 minutes before the test run
expected_approx = before - datetime.timedelta(minutes=15)
assert abs((start_dt - expected_approx).total_seconds()) < 5
# Test error if both start and since are provided
with pytest.raises(ValueError, match="Cannot specify both 'since' and 'start'"):
temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start="2026-10-06T12:00:00+02:00",
since="10m",
payload={"item": "Snack", "qty": 10},
)
# Test invalid since format
with pytest.raises(ValueError, match="Unrecognized duration format"):
temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
since="invalid_format",
payload={"item": "Snack", "qty": 10},
)
def test_sleep_over_midnight_attribution(temp_datastore: DatastoreEngine):
"""Verifies that overnight sleep (Wed evening -> Thu morning) stays attributed to Wednesday."""
# Sleep starts Wednesday 19:15
res_start = temp_datastore.add_event(
kind="sleep.session",
subject="mila",
actor="agent:hebamme",
start="2026-10-07T19:15:00+02:00",
payload={"type": "night", "location": "bett"},
)
assert res_start["status"] == "success"
# Sleep ends Thursday 07:00
res_stop = temp_datastore.stop_event(
kind="sleep.session",
subject="mila",
end_time="2026-10-08T07:00:00+02:00",
)
assert res_stop["status"] == "success"
# Duration: 11 hours 45 min = 705 minutes
assert res_stop["duration_minutes"] == 705
# Check v_sleep view: log_date must be the day it STARTED (Wednesday 2026-10-07)
rows = temp_datastore.query("SELECT log_date, duration_minutes, sleep_type FROM v_sleep WHERE subject = 'mila'")
assert len(rows) == 1
assert rows[0]["log_date"] == "2026-10-07"
assert rows[0]["sleep_type"] == "night"
assert rows[0]["duration_minutes"] == 705
# Check daily summary for Wednesday: must contain the full 11.75 hours night sleep
wed_summary = temp_datastore.get_summary(subject="mila", date_str="2026-10-07")
assert "sleep" in wed_summary
assert wed_summary["sleep"]["night_sleep_minutes"] == 705
assert wed_summary["sleep"]["total_sleep_hours"] == 11.75
# Check daily summary for Thursday: must NOT contain Wednesday night's sleep
thu_summary = temp_datastore.get_summary(subject="mila", date_str="2026-10-08")
assert "sleep" not in thu_summary or thu_summary["sleep"] is None