"""Integration: the zone a user reads times in (milestone 490), against real Postgres. The browser's zone is saved for an account without one and never over one set by hand; the Account page shows and saves it, refuses a zone Steward does not know, and a change reaches the session on the next request. Requires STEWARD_DATABASE_URL.""" from __future__ import annotations import asyncio import os import time import uuid import pytest pytestmark = pytest.mark.integration @pytest.fixture def app(): if not os.environ.get("STEWARD_DATABASE_URL"): pytest.skip("needs Postgres") from steward.app import create_app return create_app(testing=False) def test_the_browser_zone_is_adopted_once_and_the_account_page_changes_it(app): from steward.models.users import User, UserRole async def _go(): tag = uuid.uuid4().hex[:8] uid = str(uuid.uuid4()) async with app.db_sessionmaker() as s: async with s.begin(): s.add(User(id=uid, username=f"tz-{tag}", email=f"tz-{tag}@test", password_hash="x", role=UserRole.viewer, is_active=True)) client = app.test_client() async with client.session_transaction() as sess: sess.update(user_id=uid, user_role="viewer", username=f"tz-{tag}", login_at=time.time(), _csrf="test-token") csrf = {"X-CSRF-Token": "test-token"} async def zone(): async with app.db_sessionmaker() as s: return (await s.get(User, uid)).time_zone out = {} # No zone yet: every page carries the script that reports the browser's. out["before"] = await (await client.get("/account/")).get_data(as_text=True) out["bogus"] = (await client.post("/account/time-zone/adopt", json={"time_zone": "Mars/Olympus"}, headers=csrf)).status_code out["adopt"] = await (await client.post("/account/time-zone/adopt", json={"time_zone": "America/New_York"}, headers=csrf)).get_json() out["after_adopt"] = await zone() # A second browser elsewhere does not replace it. out["again"] = await (await client.post("/account/time-zone/adopt", json={"time_zone": "Europe/Berlin"}, headers=csrf)).get_json() out["page"] = await (await client.get("/account/")).get_data(as_text=True) resp = await client.post("/account/", form={"time_zone": "Europe/London"}, headers=csrf) out["save"] = (resp.status_code, resp.headers.get("Location")) out["after_save"] = await zone() resp = await client.post("/account/", form={"time_zone": "Nowhere"}, headers=csrf) out["refused"] = (resp.status_code, await resp.get_data(as_text=True)) out["after_refused"] = await zone() async with client.session_transaction() as sess: out["session_tz"] = sess.get("tz") return out out = asyncio.run(_go()) assert "/account/time-zone/adopt" in out["before"] assert out["bogus"] == 400 assert out["adopt"] == {"adopted": True} and out["after_adopt"] == "America/New_York" assert out["again"] == {"adopted": False} page = out["page"] assert "times show in New York (E" in page # EDT or EST assert '' in page assert "/account/time-zone/adopt" not in page # nothing left to adopt assert out["save"] == (302, "/account/?done=saved") and out["after_save"] == "Europe/London" status, body = out["refused"] assert status == 400 and "Nowhere is not a time zone Steward knows." in body assert out["after_refused"] == "Europe/London" and out["session_tz"] == "Europe/London"