fix(tradein/team): устойчивая сортировка списка + семантика unlimited в батч-квотах (#2554) #2566

Merged
lekss361 merged 2 commits from fix/tradein-team-list-order into main 2026-07-30 19:17:11 +00:00
2 changed files with 28 additions and 1 deletions
Showing only changes of commit df943ea1c7 - Show all commits

View file

@ -16,7 +16,14 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
# (`app.core.rbac._propagate_authenticated_user` кодирует latin-1 с # (`app.core.rbac._propagate_authenticated_user` кодирует latin-1 с
# errors="replace"), поэтому валидация формы обязательна на границе API, # errors="replace"), поэтому валидация формы обязательна на границе API,
# а не только на уровне БД. # а не только на уровне БД.
_USERNAME_RE = re.compile(r"^[A-Za-z0-9._-]{3,64}$") #
# `\Z`, НЕ `$` — deep-review seed #2564: в Python `$` матчит перед trailing
# newline (`re.match(r'...\$', 'admin\n')` → True), а Postgres `~` в CHECK
# tradein_users_username_ascii_ck (миграция 193) — False. С `$` строка
# "admin\n" проходила бы Pydantic-валидацию и падала уже в БД → 500 вместо
# честного 422. `\Z` — конец строки БЕЗ поблажки на trailing newline, совпадает
# с семантикой Postgres `~`.
_USERNAME_RE = re.compile(r"^[A-Za-z0-9._-]{3,64}\Z")
class QuotaStatusOut(BaseModel): class QuotaStatusOut(BaseModel):

View file

@ -519,6 +519,26 @@ def test_create_employee_non_ascii_username_422(client: TestClient, store: _Stor
assert resp.status_code == 422 assert resp.status_code == 422
@pytest.mark.parametrize("username", ["admin\n", "user1\n"])
def test_create_employee_trailing_newline_username_422_not_500(
client: TestClient, store: _Store, username: str
) -> None:
"""Deep-review seed #2564: Python `$` matches BEFORE a trailing newline
(`re.match(r'...\\$', 'admin\\n')` True), but Postgres `~` (CHECK
tradein_users_username_ascii_ck, migration 193) does NOT a username with a
trailing "\\n" used to pass Pydantic validation and crash in the DB (500)
instead of a clean 422. `_USERNAME_RE` now uses `\\Z`, matching Postgres `~`
semantics exactly."""
store.add_user("mgr_a", hash_password("Secret123!"), role="manager")
_login(client, "mgr_a", "Secret123!")
resp = client.post(
"/api/v1/team/employees",
json={"username": username, "password": "Secret123!"},
)
assert resp.status_code == 422, resp.text
def test_create_employee_duplicate_username_409(client: TestClient, store: _Store) -> None: def test_create_employee_duplicate_username_409(client: TestClient, store: _Store) -> None:
store.add_user("mgr_a", hash_password("Secret123!"), role="manager") store.add_user("mgr_a", hash_password("Secret123!"), role="manager")
store.add_user("emp_dup", hash_password("Secret123!"), role="employee") store.add_user("emp_dup", hash_password("Secret123!"), role="employee")