Files

161 lines
6.6 KiB
Python

"""Tests for meal periods, responses, changed-mind logic, and household scoping."""
from datetime import date, datetime
import pytest
from models import (
init_db,
get_db,
ensure_meal_periods,
get_dashboard_data,
upsert_response,
create_user,
create_household,
get_all_households,
get_household_users,
)
# ── Helpers ───────────────────────────────────────────────────────────────────
def _setup_household_and_user(app, username="test", household_name="Test House"):
"""Create a household + user. Returns (household, user)."""
with app.app_context():
hh = create_household(household_name)
user = create_user(username, "hash", hh["id"], is_admin=True)
return hh, user
# ── Tests ─────────────────────────────────────────────────────────────────────
class TestMealPeriods:
def test_ensure_creates_periods(self, app):
with app.app_context():
periods = ensure_meal_periods(date(2025, 6, 15))
assert len(periods) == 2
types = {p["meal_type"] for p in periods}
assert types == {"lunch", "dinner"}
for p in periods:
assert p["date"] == "2025-06-15"
def test_ensure_is_idempotent(self, app):
with app.app_context():
p1 = ensure_meal_periods(date(2025, 6, 15))
p2 = ensure_meal_periods(date(2025, 6, 15))
assert p1[0]["id"] == p2[0]["id"]
assert p1[1]["id"] == p2[1]["id"]
class TestResponses:
def test_first_response_no_changed_at(self, app):
hh, user = _setup_household_and_user(app)
with app.app_context():
periods = ensure_meal_periods(date.today())
lunch = periods[0]
resp = upsert_response(user["id"], lunch["id"], "yes")
assert resp["status"] == "yes"
assert resp["changed_at"] is None
def test_flip_yes_to_no_sets_changed_at(self, app):
hh, user = _setup_household_and_user(app)
with app.app_context():
periods = ensure_meal_periods(date.today())
lunch = periods[0]
upsert_response(user["id"], lunch["id"], "yes")
resp = upsert_response(user["id"], lunch["id"], "no")
assert resp["status"] == "no"
assert resp["changed_at"] is not None
def test_flip_twice_updates_changed_at(self, app):
hh, user = _setup_household_and_user(app, "flipper")
with app.app_context():
periods = ensure_meal_periods(date.today())
lunch = periods[0]
upsert_response(user["id"], lunch["id"], "yes")
r1 = upsert_response(user["id"], lunch["id"], "no")
r2 = upsert_response(user["id"], lunch["id"], "yes")
assert r2["changed_at"] is not None
assert r2["changed_at"] >= r1["changed_at"]
def test_same_status_no_change(self, app):
hh, user = _setup_household_and_user(app, "same")
with app.app_context():
periods = ensure_meal_periods(date.today())
lunch = periods[0]
r1 = upsert_response(user["id"], lunch["id"], "yes")
r2 = upsert_response(user["id"], lunch["id"], "yes")
assert r1["status"] == r2["status"]
assert r1["changed_at"] == r2["changed_at"]
class TestHouseholdScoping:
def test_users_isolation(self, app):
"""Users in different households should not see each other."""
with app.app_context():
hh1 = create_household("House Alpha")
hh2 = create_household("House Beta")
u1 = create_user("alice_a", "h", hh1["id"], is_admin=True)
u2 = create_user("bob_b", "h", hh2["id"], is_admin=True)
data1 = get_dashboard_data(date.today(), hh1["id"])
data2 = get_dashboard_data(date.today(), hh2["id"])
assert len(data1["users"]) == 1
assert data1["users"][0]["username"] == "alice_a"
assert len(data2["users"]) == 1
assert data2["users"][0]["username"] == "bob_b"
def test_join_household_starts_fresh(self, app):
"""New joiner sees household members but no prior responses."""
with app.app_context():
hh = create_household("Shared")
u1 = create_user("alice", "h", hh["id"], is_admin=True)
# Alice responds
periods = ensure_meal_periods(date.today())
lunch = next(p for p in periods if p["meal_type"] == "lunch")
upsert_response(u1["id"], lunch["id"], "yes")
# Bob joins
u2 = create_user("bob", "h", hh["id"], is_admin=False)
data = get_dashboard_data(date.today(), hh["id"])
assert len(data["users"]) == 2
# Bob's default status
bob_resp = [u for u in data["users"] if u["username"] == "bob"][0]
assert bob_resp["responses"]["lunch"]["status"] == "not_answered"
class TestDashboardData:
def test_dashboard_includes_all_household_users(self, app):
with app.app_context():
hh = create_household("DashHouse")
create_user("u1", "h1", hh["id"], is_admin=True)
create_user("u2", "h2", hh["id"], is_admin=False)
data = get_dashboard_data(date.today(), hh["id"])
assert len(data["users"]) == 2
usernames = {u["username"] for u in data["users"]}
assert usernames == {"u1", "u2"}
def test_dashboard_shows_responses(self, app):
with app.app_context():
hh = create_household("RespHouse")
user = create_user("responder", "h", hh["id"], is_admin=True)
periods = ensure_meal_periods(date.today())
lunch = next(p for p in periods if p["meal_type"] == "lunch")
upsert_response(user["id"], lunch["id"], "yes")
data = get_dashboard_data(date.today(), hh["id"])
resp = data["users"][0]["responses"]["lunch"]
assert resp["status"] == "yes"
def test_dashboard_defaults_not_answered(self, app):
with app.app_context():
hh = create_household("SilentHouse")
create_user("silent", "h", hh["id"], is_admin=False)
data = get_dashboard_data(date.today(), hh["id"])
resp = data["users"][0]["responses"]["dinner"]
assert resp["status"] == "not_answered"
assert resp["changed_at"] is None