Add household support: multi-household isolation, each with own admin and users, 38 tests
This commit is contained in:
+70
-37
@@ -1,4 +1,4 @@
|
||||
"""Tests for meal periods, responses, and changed-mind logic."""
|
||||
"""Tests for meal periods, responses, changed-mind logic, and household scoping."""
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
@@ -10,10 +10,25 @@ from models import (
|
||||
ensure_meal_periods,
|
||||
get_dashboard_data,
|
||||
upsert_response,
|
||||
get_user_by_username,
|
||||
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():
|
||||
@@ -34,94 +49,112 @@ class TestMealPeriods:
|
||||
|
||||
class TestResponses:
|
||||
def test_first_response_no_changed_at(self, app):
|
||||
hh, user = _setup_household_and_user(app)
|
||||
with app.app_context():
|
||||
from models import create_user
|
||||
|
||||
user = create_user("test", "hash", is_admin=False)
|
||||
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():
|
||||
from models import create_user
|
||||
|
||||
user = create_user("flipper", "hash", is_admin=False)
|
||||
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():
|
||||
from models import create_user
|
||||
|
||||
user = create_user("flipper2", "hash", is_admin=False)
|
||||
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")
|
||||
|
||||
# changed_at should reflect the latest flip
|
||||
assert r2["changed_at"] is not None
|
||||
# The second flip should have a different timestamp (or at least not earlier)
|
||||
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():
|
||||
from models import create_user
|
||||
|
||||
user = create_user("same", "hash", is_admin=False)
|
||||
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 TestDashboardData:
|
||||
def test_dashboard_includes_all_users(self, app):
|
||||
class TestHouseholdScoping:
|
||||
def test_users_isolation(self, app):
|
||||
"""Users in different households should not see each other."""
|
||||
with app.app_context():
|
||||
from models import create_user
|
||||
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)
|
||||
|
||||
create_user("u1", "h1", is_admin=True)
|
||||
create_user("u2", "h2", is_admin=False)
|
||||
data1 = get_dashboard_data(date.today(), hh1["id"])
|
||||
data2 = get_dashboard_data(date.today(), hh2["id"])
|
||||
|
||||
data = get_dashboard_data(date.today())
|
||||
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
|
||||
assert data["users"][0]["username"] == "u1"
|
||||
assert data["users"][1]["username"] == "u2"
|
||||
# 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():
|
||||
from models import create_user
|
||||
|
||||
user = create_user("responder", "h", is_admin=True)
|
||||
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())
|
||||
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():
|
||||
from models import create_user
|
||||
hh = create_household("SilentHouse")
|
||||
create_user("silent", "h", hh["id"], is_admin=False)
|
||||
|
||||
create_user("silent", "h", is_admin=False)
|
||||
|
||||
data = get_dashboard_data(date.today())
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user