"""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