2026-09-26 13:25:33 +03:00
"""Управление пользователями: один сквозной сценарий (создание → права → отключение → пароли → удаление)."""
import uuid
import httpx
from tests.conftest import BASE
def _client ( username : str , password : str ):
"""Клиент с токеном пользователя; None — вход не удался."""
c = httpx . Client ( base_url = BASE , timeout = 30 )
r = c . post ( "/auth/login" , json = { "username" : username , "password" : password })
if r . status_code != 200 :
c . close ()
return None
c . headers [ "Authorization" ] = "Bearer " + r . json ()[ "access_token" ]
return c
2026-09-27 11:26:57 +03:00
def test_users_management ( client , org ):
2026-09-26 13:25:33 +03:00
name , uid , other = f "qa- { uuid . uuid4 () . hex [: 8 ] } " , None , None
try :
2026-09-27 11:26:57 +03:00
created = client . post ( "/users" , json = { "username" : name , "password" : "start-pass-123" , "role" : "viewer" , "organization_id" : org [ "id" ]}) # изменение 033
2026-09-26 13:25:33 +03:00
assert created . status_code == 201 , created . text
uid = created . json ()[ "id" ]
assert created . json ()[ "role" ] == "viewer" and created . json ()[ "is_active" ] is True
2026-09-27 11:26:57 +03:00
assert created . json ()[ "organization_id" ] == org [ "id" ] # изменение 033
2026-09-26 13:25:33 +03:00
2026-09-27 11:26:57 +03:00
assert client . post ( "/users" , json = { "username" : name . upper (), "password" : "start-pass-123" , "organization_id" : org [ "id" ]}) . status_code == 409 # логин занят без учёта регистра
2026-09-26 13:25:33 +03:00
assert client . post ( "/users" , json = { "username" : "system" , "password" : "start-pass-123" }) . status_code == 422 # служебный логин журнала
assert client . post ( "/users" , json = { "username" : "ab" , "password" : "start-pass-123" }) . status_code == 422 # короткий логин
assert client . post ( "/users" , json = { "username" : "short-pw" , "password" : "123" }) . status_code == 422 # короткий пароль
2026-09-27 11:26:57 +03:00
assert client . post ( "/users" , json = { "username" : f "qa- { uuid . uuid4 () . hex [: 8 ] } " , "password" : "start-pass-123" , "role" : "admin" }) . status_code == 422 # админ без организации (изменение 033, находка №2)
2026-09-26 13:25:33 +03:00
other = _client ( name , "start-pass-123" )
assert other is not None
assert other . get ( "/auth/me" ) . json ()[ "role" ] == "viewer"
2026-09-27 11:26:57 +03:00
assert other . get ( "/users" ) . status_code == 403 # список пользователей только для суперадминистратора (изменение 032)
2026-09-26 13:25:33 +03:00
assert other . post ( "/organizations" , json = { "name" : "qa" , "inn" : "1234567890" }) . status_code == 403 # роль «просмотр» — только чтение
# отключение действует немедленно, включая ранее выданный токен
assert client . patch ( f "/users/ { uid } " , json = { "is_active" : False }) . json ()[ "is_active" ] is False
assert other . get ( "/auth/me" ) . status_code == 401
assert _client ( name , "start-pass-123" ) is None
# возврат доступа и сброс пароля администратором
client . patch ( f "/users/ { uid } " , json = { "is_active" : True , "password" : "reset-pass-123" })
assert _client ( name , "start-pass-123" ) is None
other = _client ( name , "reset-pass-123" )
assert other is not None
# смена своего пароля: нужен текущий, новый не должен совпадать с ним
assert other . post ( "/users/me/password" , json = { "current_password" : "wrong" , "new_password" : "third-pass-123" }) . status_code == 403
assert other . post ( "/users/me/password" , json = { "current_password" : "reset-pass-123" , "new_password" : "reset-pass-123" }) . status_code == 422
2026-09-26 21:33:50 +03:00
changed = other . post ( "/users/me/password" , json = { "current_password" : "reset-pass-123" , "new_password" : "third-pass-123" })
assert changed . status_code == 200
2026-09-26 13:25:33 +03:00
assert _client ( name , "third-pass-123" ) is not None
2026-09-26 21:33:50 +03:00
assert other . get ( "/auth/me" ) . status_code == 401 # старый токен отозван сменой пароля (изменение 023)
other . headers [ "Authorization" ] = "Bearer " + changed . json ()[ "access_token" ]
assert other . get ( "/auth/me" ) . status_code == 200 # токен из ответа смены пароля рабочий
2026-09-26 13:25:33 +03:00
# свою учётную запись удалить или отключить нельзя
me = client . get ( "/auth/me" ) . json ()
assert client . delete ( f "/users/ { me [ 'id' ] } " ) . status_code == 409
assert client . patch ( f "/users/ { me [ 'id' ] } " , json = { "is_active" : False }) . status_code == 409
assert client . patch ( f "/users/ { uid } " , json = { "role" : "admin" }) . json ()[ "role" ] == "admin"
2026-09-27 11:26:57 +03:00
# реконсиляция «роль — организация» по итоговому состоянию (изменение 033, находка №3)
promoted = client . patch ( f "/users/ { uid } " , json = { "role" : "superadmin" })
assert promoted . status_code == 200 and promoted . json ()[ "organization_id" ] is None # повышение снимает организацию само
assert client . patch ( f "/users/ { uid } " , json = { "role" : "viewer" }) . status_code == 422 # понижение без организации
demoted = client . patch ( f "/users/ { uid } " , json = { "role" : "viewer" , "organization_id" : org [ "id" ]})
assert demoted . status_code == 200 and demoted . json ()[ "role" ] == "viewer"
2026-09-26 13:25:33 +03:00
assert client . delete ( f "/users/ { uid } " ) . status_code == 204
uid = None
assert client . get ( "/audit" , params = { "event_type" : "user.created" }) . json ()[ "total" ] >= 1
assert client . get ( "/audit" , params = { "event_type" : "user.password_reset" }) . json ()[ "total" ] >= 1
finally :
if other is not None :
other . close ()
if uid is not None :
client . delete ( f "/users/ { uid } " )