7bd8124199
POST /api/auth/register now requires a real date_of_birth (ISO YYYY-MM-DD). The server computes age (auth.calculate_age) and rejects under-18s with 403; a future/nonsensical date is 400. The DOB is stored and surfaced only to that user (/api/auth/me). Still self-attested (a user can enter a false DOB) -- but a concrete, stored, legally-meaningful data point and the seam for a future ID-check pass. Encryption-at-rest for the column and the Tier-2 hook are not done; the limitation is stated plainly in docs/architecture/content_moderation.md 5.2. - auth.py: RegisterRequest.date_of_birth (date); MIN_AGE=18; register() computes + enforces; UserResponse.date_of_birth typed `date | None` (the old `str | None` would have 500'd /me once a DOB was actually stored). - api_client.gd / main_menu.gd: the Over18 checkbox is now a "Date of birth (YYYY-MM-DD)" field with a client-side sanity check. - tests: test_auth_age_gate.py rewritten around the endpoint + age math (adult/minor/exactly-18/future-dob/stored); ~25 over_18 references across 6 backend test files + 6 game smoke tests updated. Backend 74 passed; game smokes green (auth 6/6, menu_full_flow 12/12, e2e 4/4, etc.); fresh-clone boot PASS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
55 lines
2.1 KiB
Python
55 lines
2.1 KiB
Python
"""Tests for POST /api/worlds/{id}/join.
|
|
|
|
The happy path spawns a real Godot process, so it's covered by a manual /
|
|
checkpoint integration test, not here. These cover the envelope: auth,
|
|
visibility, and the 503 when orchestration is disabled (the default).
|
|
"""
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
|
|
|
|
def _auth(client: TestClient, name: str) -> tuple[dict, int]:
|
|
r = client.post("/api/auth/register", json={
|
|
"username": name, "email": f"{name}@e.com", "password": "secret123", "date_of_birth": "1990-01-01",
|
|
})
|
|
assert r.status_code == 201, r.text
|
|
headers = {"Authorization": f"Bearer {r.json()['access_token']}"}
|
|
me = client.get("/api/users/me", headers=headers)
|
|
return headers, me.json()["id"]
|
|
|
|
|
|
def _make_world(client: TestClient, headers: dict, visibility: str = "public") -> int:
|
|
r = client.post("/api/worlds/", headers=headers, json={"name": "W", "visibility": visibility})
|
|
assert r.status_code == 201, r.text
|
|
return r.json()["id"]
|
|
|
|
|
|
class TestJoinWorld:
|
|
def test_requires_auth(self):
|
|
client = TestClient(app)
|
|
assert client.post("/api/worlds/1/join").status_code == 401
|
|
|
|
def test_unknown_world_is_404(self):
|
|
client = TestClient(app)
|
|
headers, _ = _auth(client, "join_404")
|
|
assert client.post("/api/worlds/999999/join", headers=headers).status_code == 404
|
|
|
|
def test_private_world_of_another_user_is_404(self):
|
|
client = TestClient(app)
|
|
owner_headers, _ = _auth(client, "join_owner")
|
|
wid = _make_world(client, owner_headers, "private")
|
|
other_headers, _ = _auth(client, "join_other")
|
|
assert client.post(f"/api/worlds/{wid}/join", headers=other_headers).status_code == 404
|
|
|
|
def test_disabled_orchestration_returns_503(self):
|
|
# GAME_SERVERS_ENABLED defaults to False in tests.
|
|
client = TestClient(app)
|
|
headers, _ = _auth(client, "join_503")
|
|
wid = _make_world(client, headers, "public")
|
|
r = client.post(f"/api/worlds/{wid}/join", headers=headers)
|
|
assert r.status_code == 503
|
|
assert "not enabled" in r.json()["detail"]
|