feat(datastore): support --kind flag and add 15m duplicate suppression

This commit is contained in:
Moritz Graf
2026-10-09 10:28:11 +02:00
parent 77a0e56d96
commit 6a93539a97
2 changed files with 303 additions and 7 deletions
+190
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import datetime
import json
import os
import shutil
import tempfile
from pathlib import Path
@@ -21,6 +22,7 @@ def temp_datastore():
if schema_src.is_dir():
shutil.copytree(schema_src, data_dir / "_schema")
(data_dir / "_meta.json").write_text("{}")
engine = DatastoreEngine(data_dir=data_dir)
yield engine
shutil.rmtree(temp_dir)
@@ -384,3 +386,191 @@ def test_sleep_over_midnight_attribution(temp_datastore: DatastoreEngine):
assert "sleep" not in thu_summary or thu_summary["sleep"] is None
def test_duplicate_suppression_nutrition(temp_datastore: DatastoreEngine):
"""Verifies that duplicate intake events within 15 minutes are suppressed idempotently."""
start_time = "2026-10-09T08:00:00+02:00"
payload = {
"item": "Protein Porridge",
"qty": 300,
"unit": "g",
"protein_g": 35.0,
"kcal": 400.0,
}
# 1. First logging
res1 = temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start=start_time,
payload=payload,
)
assert res1["status"] == "success"
assert res1.get("duplicate") is not True
first_id = res1["event"]["id"]
# 2. Duplicate logging 5 minutes later
res2 = temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start="2026-10-09T08:05:00+02:00",
payload=payload,
)
assert res2["status"] == "success"
assert res2.get("duplicate") is True
assert res2["event"]["id"] == first_id
# Verify DuckDB summary: only 1 item counted
sum1 = temp_datastore.get_summary(subject="moritz", date_str="2026-10-09")
assert sum1["nutrition"]["item_count"] == 1
assert sum1["nutrition"]["total_kcal"] == 400.0
# 3. Explicit bypass with allow_duplicate=True
res3 = temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start="2026-10-09T08:06:00+02:00",
payload=payload,
allow_duplicate=True,
)
assert res3["status"] == "success"
assert res3.get("duplicate") is not True
assert res3["event"]["id"] != first_id
# Verify DuckDB summary: 2 items counted
sum2 = temp_datastore.get_summary(subject="moritz", date_str="2026-10-09")
assert sum2["nutrition"]["item_count"] == 2
assert sum2["nutrition"]["total_kcal"] == 800.0
# 4. Different item within 15 minutes is NOT suppressed
res4 = temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start="2026-10-09T08:07:00+02:00",
payload={"item": "Apfel", "qty": 150, "kcal": 80.0},
)
assert res4.get("duplicate") is not True
# 5. Same item outside 15 minutes window is NOT suppressed
res5 = temp_datastore.add_event(
kind="nutrition.intake",
subject="moritz",
actor="agent:koch",
start="2026-10-09T09:30:00+02:00",
payload=payload,
)
assert res5.get("duplicate") is not True
def test_duplicate_suppression_sleep(temp_datastore: DatastoreEngine):
"""Verifies that starting the same sleep session within 15 minutes returns existing open session."""
res1 = temp_datastore.add_event(
kind="sleep.session",
subject="mila",
actor="agent:hebamme",
start="2026-10-09T13:00:00+02:00",
payload={"type": "nap", "location": "bett"},
)
assert res1["status"] == "success"
first_id = res1["event"]["id"]
# Repeat 3 minutes later: should return existing open session without throwing ValueError
res2 = temp_datastore.add_event(
kind="sleep.session",
subject="mila",
actor="agent:hebamme",
start="2026-10-09T13:03:00+02:00",
payload={"type": "nap", "location": "bett"},
)
assert res2["status"] == "success"
assert res2.get("duplicate") is True
assert res2["event"]["id"] == first_id
# Stop session
temp_datastore.stop_event(
kind="sleep.session",
subject="mila",
end_time="2026-10-09T14:00:00+02:00",
)
def test_cli_kind_flag_and_positional(temp_datastore: DatastoreEngine):
"""Verifies CLI accepts both positional and --kind flag for add and stop commands."""
import subprocess
import sys
from pathlib import Path
script_path = Path(__file__).resolve().parent.parent / "scripts" / "datastore.py"
data_dir = str(temp_datastore.data_dir)
# 1. Add using --kind flag
proc_add = subprocess.run(
[
sys.executable,
str(script_path),
"add",
"--kind",
"nutrition.intake",
"--subject",
"moritz",
"--payload",
'{"item": "CLI Snack", "qty": 50, "kcal": 100}',
"--start",
"2026-10-09T15:00:00+02:00",
],
env=dict(os.environ, OPENCLAW_DATA_DIR=data_dir),
capture_output=True,
text=True,
)
assert proc_add.returncode == 0
data = json.loads(proc_add.stdout)
assert data["status"] == "success"
assert data["event"]["kind"] == "nutrition.intake"
# 2. Add open sleep using positional kind
proc_sleep_start = subprocess.run(
[
sys.executable,
str(script_path),
"add",
"sleep.session",
"--subject",
"mila",
"--payload",
'{"type": "nap"}',
"--start",
"2026-10-09T15:00:00+02:00",
],
env=dict(os.environ, OPENCLAW_DATA_DIR=data_dir),
capture_output=True,
text=True,
)
assert proc_sleep_start.returncode == 0
# 3. Stop sleep using --kind flag
proc_sleep_stop = subprocess.run(
[
sys.executable,
str(script_path),
"stop",
"--kind",
"sleep.session",
"--subject",
"mila",
"--end",
"2026-10-09T16:00:00+02:00",
],
env=dict(os.environ, OPENCLAW_DATA_DIR=data_dir),
capture_output=True,
text=True,
)
assert proc_sleep_stop.returncode == 0
stop_data = json.loads(proc_sleep_stop.stdout)
assert stop_data["status"] == "success"
assert stop_data["duration_minutes"] == 60