reachy_mini_minder / tests /test_database_onboarding.py
Boopster's picture
feat: Introduce an onboarding review step and remove the initial welcome step from the onboarding flow.
fb88636
"""Tests for new database methods: user profile and scheduled medications."""
import pytest
from reachy_mini_conversation_app.database import MiniMinderDB
@pytest.fixture
def db() -> MiniMinderDB:
"""Create an in-memory database for testing."""
return MiniMinderDB(":memory:")
# -----------------------------------------------------------------------------
# User Profile Tests
# -----------------------------------------------------------------------------
def test_get_or_create_profile(db: MiniMinderDB) -> None:
"""Profile should be created on first access."""
profile = db.get_or_create_profile()
assert profile is not None
assert profile["id"] == 1
assert profile["onboarding_completed"] == 0
assert profile["onboarding_step"] == "name"
def test_get_or_create_profile_idempotent(db: MiniMinderDB) -> None:
"""Getting profile multiple times should return same data."""
profile1 = db.get_or_create_profile()
profile2 = db.get_or_create_profile()
assert profile1["id"] == profile2["id"]
assert profile1["created_at"] == profile2["created_at"]
def test_update_profile(db: MiniMinderDB) -> None:
"""Profile updates should persist."""
db.update_profile(
{
"display_name": "Alice",
"onboarding_completed": 1,
"onboarding_step": "done",
}
)
profile = db.get_or_create_profile()
assert profile["display_name"] == "Alice"
assert profile["onboarding_completed"] == 1
assert profile["onboarding_step"] == "done"
def test_is_onboarding_complete_default(db: MiniMinderDB) -> None:
"""Onboarding should not be complete by default."""
assert db.is_onboarding_complete() is False
def test_is_onboarding_complete_after_update(db: MiniMinderDB) -> None:
"""Onboarding should be complete after marking it."""
db.update_profile({"onboarding_completed": 1})
assert db.is_onboarding_complete() is True
# -----------------------------------------------------------------------------
# Scheduled Medications Tests
# -----------------------------------------------------------------------------
def test_add_scheduled_medication(db: MiniMinderDB) -> None:
"""Add and retrieve a scheduled medication."""
med_id = db.add_scheduled_medication(
{
"medication_name": "Ibuprofen",
"dose": "400mg",
"frequency": "twice daily",
"times_of_day": ["morning", "evening"],
}
)
assert med_id == 1
meds = db.get_scheduled_medications()
assert len(meds) == 1
assert meds[0]["medication_name"] == "Ibuprofen"
assert meds[0]["dose"] == "400mg"
assert meds[0]["times_of_day"] == ["morning", "evening"]
def test_add_multiple_medications(db: MiniMinderDB) -> None:
"""Add multiple medications."""
db.add_scheduled_medication({"medication_name": "Med A"})
db.add_scheduled_medication({"medication_name": "Med B"})
db.add_scheduled_medication({"medication_name": "Med C"})
meds = db.get_scheduled_medications()
assert len(meds) == 3
names = [m["medication_name"] for m in meds]
assert "Med A" in names
assert "Med B" in names
assert "Med C" in names
def test_scheduled_medication_with_reminders(db: MiniMinderDB) -> None:
"""Medications can have reminder times."""
db.add_scheduled_medication(
{
"medication_name": "Blood Pressure Med",
"reminder_enabled": 1,
"reminder_times": ["07:55", "19:55"],
}
)
meds = db.get_scheduled_medications()
assert len(meds) == 1
assert meds[0]["reminder_enabled"] == 1
assert meds[0]["reminder_times"] == ["07:55", "19:55"]
def test_update_scheduled_medication(db: MiniMinderDB) -> None:
"""Update an existing medication."""
med_id = db.add_scheduled_medication(
{
"medication_name": "Aspirin",
"dose": "100mg",
}
)
success = db.update_scheduled_medication(
med_id,
{
"dose": "200mg",
"frequency": "once daily",
},
)
assert success is True
meds = db.get_scheduled_medications()
assert meds[0]["dose"] == "200mg"
assert meds[0]["frequency"] == "once daily"
def test_deactivate_scheduled_medication(db: MiniMinderDB) -> None:
"""Deactivated medications should not appear in active list."""
med_id = db.add_scheduled_medication({"medication_name": "Old Med"})
success = db.deactivate_scheduled_medication(med_id)
assert success is True
# Should not appear in active list
active_meds = db.get_scheduled_medications(active_only=True)
assert len(active_meds) == 0
# Should appear in full list
all_meds = db.get_scheduled_medications(active_only=False)
assert len(all_meds) == 1
assert all_meds[0]["active"] == 0
def test_update_nonexistent_medication(db: MiniMinderDB) -> None:
"""Updating a nonexistent medication should return False."""
success = db.update_scheduled_medication(999, {"dose": "100mg"})
# Note: SQLite UPDATE returns 0 rows affected, so this returns False
assert success is False
# -----------------------------------------------------------------------------
# Upsert Scheduled Medication Tests
# -----------------------------------------------------------------------------
def test_upsert_scheduled_medication_new(db: MiniMinderDB) -> None:
"""Upsert should create a new row when no match exists."""
med_id, is_new = db.upsert_scheduled_medication(
{"medication_name": "Aspirin", "dose": "100mg"}
)
assert is_new is True
assert med_id >= 1
meds = db.get_scheduled_medications()
assert len(meds) == 1
assert meds[0]["medication_name"] == "Aspirin"
def test_upsert_scheduled_medication_existing(db: MiniMinderDB) -> None:
"""Upsert should update existing row and not create a duplicate."""
# First call: creates the row
med_id_1, is_new_1 = db.upsert_scheduled_medication(
{"medication_name": "Amitriptyline"}
)
assert is_new_1 is True
# Second call: same name, adds dose — should update
med_id_2, is_new_2 = db.upsert_scheduled_medication(
{"medication_name": "Amitriptyline", "dose": "10mg"}
)
assert is_new_2 is False
assert med_id_2 == med_id_1
# Third call: same name, adds frequency — should update again
med_id_3, is_new_3 = db.upsert_scheduled_medication(
{"medication_name": "Amitriptyline", "dose": "10mg", "frequency": "once daily"}
)
assert is_new_3 is False
assert med_id_3 == med_id_1
# Only one row should exist with all the merged data
meds = db.get_scheduled_medications()
assert len(meds) == 1
assert meds[0]["dose"] == "10mg"
assert meds[0]["frequency"] == "once daily"
def test_upsert_case_insensitive(db: MiniMinderDB) -> None:
"""Upsert should match medication names case-insensitively."""
db.upsert_scheduled_medication({"medication_name": "Aspirin"})
med_id, is_new = db.upsert_scheduled_medication(
{"medication_name": "aspirin", "dose": "200mg"}
)
assert is_new is False
meds = db.get_scheduled_medications()
assert len(meds) == 1
def test_schema_includes_new_tables(db: MiniMinderDB) -> None:
"""The new tables should exist."""
conn = db._get_conn()
tables = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
).fetchall()
table_names = [t["name"] for t in tables]
assert "user_profile" in table_names
assert "scheduled_medications" in table_names