From f100e5ef85c55caa79abc900838c968aa1a70880 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 7 Oct 2026 14:41:44 -0400 Subject: [PATCH] tests: the self-revoke routing check reads the URL map; its 400 moves to Postgres test_devices signed a fake account into a session and relied on login_required answering without the database. Since 3dd0b44 the session path reads the account's epoch, so the fake account hit an unreachable database and 500ed. The routing property (the static /devices/self rule beats /devices/) is now asserted on the URL map, and the view's 400 for a web session is an integration test with a real account. #5173. Co-Authored-By: Claude Opus 5.5 --- tests/test_devices.py | 18 ++++++++---------- tests/test_integration.py | 9 +++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/test_devices.py b/tests/test_devices.py index 1d2052c..158e5f9 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -33,16 +33,14 @@ async def test_revoke_self_requires_auth(app): assert resp.status_code == 401 -async def test_revoke_self_without_a_bearer_token_is_a_bad_request(app): - # Doubles as the routing check: a session-authenticated caller presents no - # device token, so the self-revoke view answers 400 BEFORE any DB access. A 404 - # here would mean "self" fell through to the id-keyed route as a malformed UUID - # — i.e. that the static rule stopped winning. - client = app.test_client() - async with client.session_transaction() as sess: - sess["user_id"] = "00000000-0000-0000-0000-000000000001" - resp = await client.delete("/api/auth/devices/self") - assert resp.status_code == 400 +def test_revoke_self_routes_to_the_self_revoke_view(app): + # The static rule must win over `/devices/`: if "self" fell through to + # the id-keyed route it would be read as a malformed UUID and answer 404. Checked + # on the URL map, because every route behind login_required now reads the + # account's session epoch (#5173). The view's 400 for a caller with no bearer + # token is in test_integration.py. + endpoint, _ = app.url_map.bind("localhost").match("/api/auth/devices/self", method="DELETE") + assert endpoint == "auth.revoke_own_device" async def test_device_login_validates_input(app): diff --git a/tests/test_integration.py b/tests/test_integration.py index 6609515..780c0bc 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1170,3 +1170,12 @@ async def test_a_short_password_leaves_the_reset_link_usable(app_client, db): assert short.status_code == 400 ok = await client.post("/api/auth/reset-password", json={"token": token, "password": "a-brand-new-password"}) assert ok.status_code == 200 + + +async def test_revoking_this_device_from_a_web_session_is_a_bad_request(app_client, db): + """A session-cookie caller holds no device token, so "revoke the one I'm using" + has nothing to name. Moved here from the unit lane when the session check began + reading the account (#5173).""" + await _signed_in(app_client, "web") + resp = await app_client.delete("/api/auth/devices/self") + assert resp.status_code == 400