feat(phase-4-05): PATCH /api/auth/me/preferences for pdf_open_mode (DOC-01)

- Add PreferencesUpdate Pydantic model with Literal['in_app', 'new_tab'] validation
- Add GET /api/auth/me/preferences — returns current pdf_open_mode
- Add PATCH /api/auth/me/preferences — validates + stores + returns updated value
- Both endpoints use get_current_user (admin can set own prefs, D-10)
- Add 7 preference tests: default GET, in_app, new_tab, invalid 422, persist,
  and two unauthenticated 401 tests
This commit is contained in:
curo1305
2026-05-25 18:50:52 +02:00
parent f89f787656
commit 2a0df32e92
2 changed files with 124 additions and 1 deletions
+73
View File
@@ -423,3 +423,76 @@ async def test_login_totp_takes_precedence(authed_client, db_session: AsyncSessi
await db_session.refresh(backup_code_row)
assert backup_code_row.used_at is None, "Backup code should NOT have been consumed when totp_code is provided"
assert resp.status_code == 401
# ---------------------------------------------------------------------------
# Phase 4 DOC-01 preferences endpoint tests
# ---------------------------------------------------------------------------
async def test_get_preferences_default(async_client, auth_user):
"""GET /api/auth/me/preferences returns default pdf_open_mode='in_app'."""
resp = await async_client.get("/api/auth/me/preferences", headers=auth_user["headers"])
assert resp.status_code == 200
data = resp.json()
assert "pdf_open_mode" in data
assert data["pdf_open_mode"] in ("in_app", "new_tab") # column default varies by env
async def test_patch_preferences_in_app(async_client, auth_user):
"""PATCH /api/auth/me/preferences with pdf_open_mode='in_app' stores and returns value."""
resp = await async_client.patch(
"/api/auth/me/preferences",
json={"pdf_open_mode": "in_app"},
headers=auth_user["headers"],
)
assert resp.status_code == 200
assert resp.json()["pdf_open_mode"] == "in_app"
async def test_patch_preferences_new_tab(async_client, auth_user):
"""PATCH /api/auth/me/preferences with pdf_open_mode='new_tab' stores and returns value."""
resp = await async_client.patch(
"/api/auth/me/preferences",
json={"pdf_open_mode": "new_tab"},
headers=auth_user["headers"],
)
assert resp.status_code == 200
assert resp.json()["pdf_open_mode"] == "new_tab"
async def test_patch_preferences_invalid_value(async_client, auth_user):
"""PATCH /api/auth/me/preferences with invalid pdf_open_mode returns 422."""
resp = await async_client.patch(
"/api/auth/me/preferences",
json={"pdf_open_mode": "browser"},
headers=auth_user["headers"],
)
assert resp.status_code == 422
async def test_patch_preferences_persists(async_client, auth_user):
"""PATCH then GET confirms persistence of pdf_open_mode."""
await async_client.patch(
"/api/auth/me/preferences",
json={"pdf_open_mode": "new_tab"},
headers=auth_user["headers"],
)
resp = await async_client.get("/api/auth/me/preferences", headers=auth_user["headers"])
assert resp.status_code == 200
assert resp.json()["pdf_open_mode"] == "new_tab"
async def test_preferences_requires_auth(async_client):
"""GET /api/auth/me/preferences returns 401 without a bearer token."""
resp = await async_client.get("/api/auth/me/preferences")
assert resp.status_code == 401
async def test_patch_preferences_requires_auth(async_client):
"""PATCH /api/auth/me/preferences returns 401 without a bearer token."""
resp = await async_client.patch(
"/api/auth/me/preferences",
json={"pdf_open_mode": "in_app"},
)
assert resp.status_code == 401