Files
AgapHost/mood/tests/test_store.py
alvis d37801806d services: add mood, moodtracker, overleaf, personal-sensing; update ollama
Compose and supporting code for four services that had been running or
prototyped without their config tracked here, per the repo convention that
agap_git holds the compose + config while application source lives in each
service's own Gitea repo.

Only placeholder credentials are included: mood/.env.example and
moodtracker/.env.example ship dummy values, and overleaf/variables.env carries
app name and feature flags only. Real values stay in Vaultwarden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 04:42:58 +00:00

113 lines
3.8 KiB
Python

import json
import os
import sys
import tempfile
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src import store
def _fresh_db():
path = os.path.join(tempfile.mkdtemp(), "t.sqlite")
conn = store.connect(path)
store.init_db(conn)
return conn
def _row(id_, ts, mood, tags, note="", affirmation=""):
return {"id": id_, "ts": ts, "mood": mood, "tags": json.dumps(tags),
"note": note, "affirmation": affirmation}
def test_upsert_idempotent():
conn = _fresh_db()
rows = [
_row(1, "2026-07-01T08:00:00+00:00", 4, ["calm"]),
_row(2, "2026-07-02T08:00:00+00:00", 2, ["sad", "tired"]),
]
store.upsert_entries(conn, rows)
store.upsert_entries(conn, rows) # re-run same rows
count = conn.execute("SELECT COUNT(*) c FROM mood_entries").fetchone()["c"]
assert count == 2 # no duplication despite double ingest
def test_upsert_updates_on_conflict():
conn = _fresh_db()
rows = [_row(1, "2026-07-01T08:00:00+00:00", 4, ["calm"], note="first")]
store.upsert_entries(conn, rows)
rows[0]["note"] = "corrected"
store.upsert_entries(conn, rows)
got = conn.execute("SELECT note FROM mood_entries WHERE source_id=1").fetchone()["note"]
assert got == "corrected"
def test_sync_state_high_water_mark():
conn = _fresh_db()
store.set_sync_state(conn, "moodtracker_entries", 5)
store.set_sync_state(conn, "moodtracker_entries", 3) # older cursor must not regress
assert store.get_last_synced_id(conn, "moodtracker_entries") == 5
store.set_sync_state(conn, "moodtracker_entries", 9)
assert store.get_last_synced_id(conn, "moodtracker_entries") == 9
def test_summary_and_daily_mood():
conn = _fresh_db()
rows = [
_row(1, "2026-07-01T08:00:00+00:00", 4, ["calm"]),
_row(2, "2026-07-01T20:00:00+00:00", 2, ["tired"]),
_row(3, "2026-07-02T08:00:00+00:00", 5, ["happy"]),
]
store.upsert_entries(conn, rows)
s = store.summary(conn)
assert s["entries"] == 3
assert s["coverage"]["earliest"] is not None
daily = store.daily_mood(conn, days=30)
by_day = {d["day"]: d for d in daily}
assert by_day["2026-07-01"]["entries"] == 2
assert by_day["2026-07-01"]["avg_mood"] == 3.0 # (4+2)/2
assert by_day["2026-07-02"]["avg_mood"] == 5.0
def test_tag_breakdown():
conn = _fresh_db()
rows = [
_row(1, "2026-07-01T08:00:00+00:00", 5, ["happy", "energetic"]),
_row(2, "2026-07-02T08:00:00+00:00", 1, ["sad", "tired"]),
_row(3, "2026-07-03T08:00:00+00:00", 2, ["tired"]),
]
store.upsert_entries(conn, rows)
tags = store.tag_breakdown(conn, days=90, min_count=2)
by_tag = {t["tag"]: t for t in tags}
assert by_tag["tired"]["count"] == 2
assert by_tag["tired"]["avg_mood"] == 1.5
assert "happy" not in by_tag # min_count=2 filters singletons
def test_correlate_with_series():
conn = _fresh_db()
rows = [
_row(1, "2026-07-01T08:00:00+00:00", 5, []),
_row(2, "2026-07-02T08:00:00+00:00", 4, []),
_row(3, "2026-07-03T08:00:00+00:00", 2, []),
_row(4, "2026-07-04T08:00:00+00:00", 1, []),
]
store.upsert_entries(conn, rows)
# perfectly correlated external series (e.g. "hours slept")
external = {
"2026-07-01": 8.0, "2026-07-02": 7.0,
"2026-07-03": 5.0, "2026-07-04": 4.0,
}
result = store.correlate_with_series(conn, external, days=30)
assert result["n"] == 4
assert result["r"] > 0.99 # near-perfect positive correlation
def test_correlate_too_few_points():
conn = _fresh_db()
store.upsert_entries(conn, [_row(1, "2026-07-01T08:00:00+00:00", 3, [])])
result = store.correlate_with_series(conn, {"2026-07-01": 5.0}, days=30)
assert result["n"] == 1
assert result["r"] is None