Files
FabledSteward/tests/integration/test_account_time_zone.py
T
bvandeusenandClaude Opus 5.5 ea523b8c37
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 1m10s
CI / unit (push) Successful in 1m31s
CI / publish (push) Successful in 1m26s
feat(account): change your own password on the Account page (milestone 497)
Step #5428 "Account page: change your own password" of milestone 497
"Passwords: change your own, and reset it by email". A Password section
in the page's one form, for local accounts: current, new, again. Blank
keeps the password; a new one signs out every other browser
(sessions_since) and keeps this one. A wrong current password counts
toward the login throttle. Audited as account.password_changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:14:45 -04:00

83 lines
3.9 KiB
Python

"""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 <strong>New York</strong> (E" in page # EDT or EST
assert '<option value="America/New_York" selected>New York</option>' 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"