feat(datastore): add relative --since duration flag and overnight sleep test
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user