Files

189 lines
7.1 KiB
Python

"""Integration tests for HTTP routes."""
from datetime import date
class TestDashboardRoute:
def test_dashboard_requires_login(self, client):
resp = client.get("/dashboard", follow_redirects=True)
assert b"Log In" in resp.data
def test_dashboard_shows_today(self, auth_client):
resp = auth_client.get("/dashboard")
assert resp.status_code == 200
assert b"Today" in resp.data
def test_dashboard_renders_users(self, auth_client):
resp = auth_client.get("/dashboard")
# bob and alice were registered in the same household
assert b"alice" in resp.data
assert b"bob" in resp.data
assert b"Lunch" in resp.data
assert b"Dinner" in resp.data
def test_dashboard_hides_other_households(self, auth_client):
"""Bob should NOT see users from other households."""
# Register another user in a different household
auth_client.get("/logout")
auth_client.post("/register", data={
"username": "stranger",
"password": "stranger1",
"confirm": "stranger1",
"household_action": "create",
"new_household_name": "Stranger House",
})
# Log back in as bob (Test Household)
auth_client.get("/logout")
auth_client.post("/login", data={
"username": "bob",
"password": "bobpass",
})
resp = auth_client.get("/dashboard")
assert resp.status_code == 200
assert b"stranger" not in resp.data
assert b"Stranger House" not in resp.data
class TestRespondRoute:
def test_respond_sets_status(self, auth_client):
resp = auth_client.post("/respond", data={
"meal_type": "lunch",
"status": "yes",
}, follow_redirects=True)
assert resp.status_code == 200
assert b"Home" in resp.data
def test_respond_changes_mind(self, auth_client):
auth_client.post("/respond", data={
"meal_type": "dinner",
"status": "yes",
})
resp = auth_client.post("/respond", data={
"meal_type": "dinner",
"status": "no",
}, follow_redirects=True)
assert resp.status_code == 200
def test_respond_invalid_meal_type(self, auth_client):
resp = auth_client.post("/respond", data={
"meal_type": "breakfast",
"status": "yes",
}, follow_redirects=True)
assert b"Invalid meal type" in resp.data
def test_respond_invalid_status(self, auth_client):
resp = auth_client.post("/respond", data={
"meal_type": "lunch",
"status": "maybe",
}, follow_redirects=True)
assert b"Invalid status" in resp.data
class TestHistoryRoute:
def test_history_requires_login(self, client):
resp = client.get("/history", follow_redirects=True)
assert b"Log In" in resp.data
def test_history_shows_past_date(self, auth_client):
past = date.today().replace(year=date.today().year - 1)
resp = auth_client.get(f"/history?date={past.isoformat()}")
assert resp.status_code == 200
assert b"History" in resp.data
class TestDeleteAccount:
def test_delete_own_account(self, auth_client):
"""Bob deletes his own account."""
resp = auth_client.post("/delete-account", data={
"password": "bobpass",
}, follow_redirects=True)
assert resp.status_code == 200
assert b"has been deleted" in resp.data
# Bob should be logged out
resp2 = auth_client.get("/dashboard", follow_redirects=True)
assert b"Log In" in resp2.data
def test_delete_own_account_wrong_password(self, auth_client):
"""Delete fails with wrong password."""
resp = auth_client.post("/delete-account", data={
"password": "wrongpass",
}, follow_redirects=True)
assert b"Incorrect password" in resp.data
# Still logged in
resp2 = auth_client.get("/dashboard")
assert resp2.status_code == 200
class TestAdminRemoveUser:
def test_admin_removes_user(self, admin_client):
"""Admin removes a regular user."""
# admin_client has admin as sole user — need another user to remove
admin_client.get("/logout")
# Register another user in admin's household
from models import get_household_by_name
hh = get_household_by_name("Admin Household")
admin_client.post("/register", data={
"username": "extra",
"password": "extrapass",
"confirm": "extrapass",
"household_action": "join",
"household_id": str(hh["id"]),
})
# admin_client logs back in as admin
admin_client.get("/logout")
admin_client.post("/login", data={
"username": "admin", "password": "adminpass",
})
# Remove the extra user
from models import get_user_by_username
extra = get_user_by_username("extra")
resp = admin_client.post(f"/admin/remove-user/{extra['id']}", follow_redirects=True)
assert b"has been removed" in resp.data
# Extra user should be gone
admin_client.get("/dashboard")
assert get_user_by_username("extra") is None
def test_non_admin_cannot_remove(self, auth_client):
"""Regular user (bob) cannot remove others."""
resp = auth_client.post("/admin/remove-user/1", follow_redirects=True)
assert b"Only admins" in resp.data
def test_admin_cannot_remove_admin(self, admin_client):
"""Admin cannot remove themselves via remove-user."""
resp = admin_client.post("/admin/remove-user/1", follow_redirects=True)
assert b"Cannot remove the admin" in resp.data
class TestAdminDeleteHousehold:
def test_admin_deletes_household(self, admin_client):
"""Admin deletes entire household."""
resp = admin_client.post("/admin/delete-household", data={
"password": "adminpass",
}, follow_redirects=True)
assert resp.status_code == 200
assert b"have been deleted" in resp.data
def test_admin_delete_household_wrong_password(self, admin_client):
resp = admin_client.post("/admin/delete-household", data={
"password": "wrong",
}, follow_redirects=True)
assert b"Incorrect password" in resp.data
def test_non_admin_cannot_delete_household(self, auth_client):
resp = auth_client.post("/admin/delete-household", data={
"password": "bobpass",
}, follow_redirects=True)
assert b"Only admins" in resp.data
class TestDashboardOtherUsersGrayedOut:
def test_other_users_buttons_are_disabled(self, auth_client):
"""Bob sees alice's status as badges, not clickable buttons."""
# Bob responds first so we can see his buttons
auth_client.post("/respond", data={
"meal_type": "lunch", "status": "yes",
}, follow_redirects=True)
resp = auth_client.get("/dashboard")
# Alice's status should show as a badge (not a form button)
assert b"badge-yes" in resp.data or b"badge-not_answered" in resp.data