35f46926a9
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
"""Tests for PUT /api/users/{id}/password (admin only) - issue #25(c).
|
|
|
|
Kept in its own file rather than test_users.py: that file covers the rest of
|
|
the users router, worked on in a different lot in the same session.
|
|
"""
|
|
from httpx import AsyncClient
|
|
|
|
from src.backend.models.orm.user import User
|
|
from src.backend.tests.conftest import auth_headers
|
|
|
|
|
|
class TestChangeUserPassword:
|
|
async def test_admin_changes_password_invalidates_api_key(
|
|
self, client: AsyncClient, admin_user: User, maker_user: User
|
|
):
|
|
"""Changing a user's password invalidates their existing api_key,
|
|
|
|
the same as setup.py's change-user-password (setup.py:634).
|
|
"""
|
|
old_key = maker_user.plain_key
|
|
old_username = maker_user.username
|
|
assert old_key is not None
|
|
|
|
resp = await client.put(
|
|
f"/api/users/{maker_user.id}/password",
|
|
headers=auth_headers(admin_user),
|
|
json={"password": "NewSecurePass1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert "changed" in resp.json()["message"].lower()
|
|
|
|
# The old key no longer authenticates. Read it before the request
|
|
# above: the client and app share one session (see conftest), so
|
|
# `maker_user.plain_key` in this process reflects the mutation too.
|
|
resp2 = await client.get("/api/auth/me", headers={"X-API-Key": old_key})
|
|
assert resp2.status_code == 401
|
|
|
|
# The new password works and issues a fresh key.
|
|
resp3 = await client.post(
|
|
"/api/auth/login",
|
|
json={"username": old_username, "password": "NewSecurePass1"},
|
|
)
|
|
assert resp3.status_code == 200
|
|
assert resp3.json()["api_key"] != old_key
|
|
|
|
async def test_non_admin_forbidden(
|
|
self, client: AsyncClient, maker_user: User
|
|
):
|
|
resp = await client.put(
|
|
f"/api/users/{maker_user.id}/password",
|
|
headers=auth_headers(maker_user),
|
|
json={"password": "WhateverPass1"},
|
|
)
|
|
assert resp.status_code == 403
|
|
|
|
async def test_user_not_found(
|
|
self, client: AsyncClient, admin_user: User
|
|
):
|
|
resp = await client.put(
|
|
"/api/users/999999/password",
|
|
headers=auth_headers(admin_user),
|
|
json={"password": "WhateverPass1"},
|
|
)
|
|
assert resp.status_code == 404
|
|
|
|
async def test_password_too_short_rejected(
|
|
self, client: AsyncClient, admin_user: User, maker_user: User
|
|
):
|
|
resp = await client.put(
|
|
f"/api/users/{maker_user.id}/password",
|
|
headers=auth_headers(admin_user),
|
|
json={"password": "short"},
|
|
)
|
|
assert resp.status_code == 422
|