feat: guest passage — slip through as a wanderer
POST /auth/guest mints a real user row (wanderer-<4 hex>, collision retry, unusable random password) and issues the normal session cookie, per-IP rate limited at 5/hour. EnterPage gains the guest action; the séance shows a dismissible claim-a-name note for wanderer- users. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
60
backend/tests/test_auth_guest.py
Normal file
60
backend/tests/test_auth_guest.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import pytest
|
||||
|
||||
import app.routes.auth as auth_module
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_guest_limiter():
|
||||
# The limiter is module-level state; a previous test's hits would bleed
|
||||
# into the next one's per-IP budget.
|
||||
auth_module.guest_limiter._hits.clear()
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guest_creates_wanderer_and_cookie_works_on_me(client):
|
||||
response = await client.post("/auth/guest")
|
||||
assert response.status_code == 201
|
||||
body = response.json()
|
||||
assert body["username"].startswith("wanderer-")
|
||||
assert "password" not in body
|
||||
assert "qm_session" in response.cookies
|
||||
|
||||
me_resp = await client.get("/auth/me")
|
||||
assert me_resp.status_code == 200
|
||||
assert me_resp.json()["username"] == body["username"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guest_cannot_login_with_any_password(client):
|
||||
response = await client.post("/auth/guest")
|
||||
username = response.json()["username"]
|
||||
login_resp = await client.post(
|
||||
"/auth/login", json={"username": username, "password": "anythingatall"}
|
||||
)
|
||||
assert login_resp.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guest_rate_limit_fires_per_ip(client):
|
||||
for _ in range(5):
|
||||
response = await client.post("/auth/guest")
|
||||
assert response.status_code == 201
|
||||
response = await client.post("/auth/guest")
|
||||
assert response.status_code == 429
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_guest_username_collision_retries(client, monkeypatch):
|
||||
taken_resp = await client.post("/auth/guest")
|
||||
taken = taken_resp.json()["username"].removeprefix("wanderer-")
|
||||
|
||||
# First attempt collides with the existing wanderer; the retry must land
|
||||
# on the fresh suffix instead of erroring out.
|
||||
suffixes = iter([taken, "f4ee"])
|
||||
monkeypatch.setattr(
|
||||
auth_module.secrets, "token_hex", lambda n: next(suffixes)
|
||||
)
|
||||
response = await client.post("/auth/guest")
|
||||
assert response.status_code == 201
|
||||
assert response.json()["username"] == "wanderer-f4ee"
|
||||
Reference in New Issue
Block a user