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
Owner

Follow-up по deep-review PR #2563. Должен лечь ДО seed #2564 — иначе пагинация дашборда поедет.

  1. ORDER BY created_at DESC, id DESC в обоих list-запросах. Причина: created_at DEFAULT now() — время транзакции, а seed #2557 вставляет 13 юзеров одной транзакцией → идентичные метки → на PostgreSQL порядок между страницами не гарантирован (сотрудники пропадают/дублируются при листании). Тест фиксирует контракт тай-брейкера; оговорка автора: in-memory БД детерминирована (stable sort), поэтому тест проверяет корректность offset/limit-математики, а не саму PG-патологию — её ни SQLite, ни fake DB не воспроизводят.

  2. _batch_quota_status приведён в соответствие с account_quota.is_unlimited: unlimited из override читается только если username есть в roles.yaml. Выбрано менять код, а не докстринг, потому что реальный enforcement (check_and_raise/increment) использует ту же is_unlimited — иначе список показывал бы «без лимита», пока движок продолжает считать лимит. Тесты симметричные: DB-only сотрудник с unlimited=true → в списке false; kopylov (есть в roles.yaml) → override честится.

uv run pytest -q: 2818 passed (33 теста в test_team_api.py), 1 pre-existing fail. ruff чисто.

Follow-up по deep-review PR #2563. **Должен лечь ДО seed #2564** — иначе пагинация дашборда поедет. 1. **`ORDER BY created_at DESC, id DESC`** в обоих list-запросах. Причина: `created_at DEFAULT now()` — время транзакции, а seed #2557 вставляет 13 юзеров одной транзакцией → идентичные метки → на PostgreSQL порядок между страницами не гарантирован (сотрудники пропадают/дублируются при листании). Тест фиксирует контракт тай-брейкера; оговорка автора: in-memory БД детерминирована (stable sort), поэтому тест проверяет корректность offset/limit-математики, а не саму PG-патологию — её ни SQLite, ни fake DB не воспроизводят. 2. **`_batch_quota_status` приведён в соответствие с `account_quota.is_unlimited`**: `unlimited` из override читается только если username есть в `roles.yaml`. Выбрано менять код, а не докстринг, потому что реальный enforcement (`check_and_raise`/`increment`) использует ту же `is_unlimited` — иначе список показывал бы «без лимита», пока движок продолжает считать лимит. Тесты симметричные: DB-only сотрудник с `unlimited=true` → в списке `false`; `kopylov` (есть в roles.yaml) → override честится. `uv run pytest -q`: 2818 passed (33 теста в test_team_api.py), 1 pre-existing fail. ruff чисто.
lekss361 added 1 commit 2026-07-30 19:05:22 +00:00
fix(tradein/team): устойчивая сортировка списка + семантика unlimited в батч-квотах (#2554)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m0s
2e05a16a14
Deep-review PR #2563 follow-up (после merge+deploy):
1. ORDER BY created_at DESC, id DESC в _LIST_EMPLOYEES_BY_MANAGER_SQL /
   _LIST_EMPLOYEES_ALL_SQL. created_at DEFAULT now() — время транзакции, bulk-seed
   (#2557) вставляет много юзеров одной транзакцией -> идентичный timestamp у N+
   строк -> без тай-брейкера порядок между LIMIT/OFFSET страницами на PostgreSQL
   для строк-близнецов не гарантирован (сотрудники пропадали/дублировались бы
   при листании). id (BIGINT IDENTITY, монотонный) — детерминированный tie-break.
2. _batch_quota_status: unlimited теперь честно совпадает с
   account_quota.is_unlimited — override.unlimited=true честится ТОЛЬКО для
   username, присутствующего в roles.yaml (KeyError -> unlimited=False всегда,
   override даже не читается). Раньше батч всегда читал override независимо от
   roles.yaml -> список мог показать "unlimited" для квоты, которую реальный
   enforcement (check_and_raise/increment, тот же is_unlimited) не признаёт.
   Сегодня недостижимо (unlimited есть только у kopylov/praktika, оба в
   roles.yaml), но станет достижимым при расширении ролевки.
bot-backend added 1 commit 2026-07-30 19:11:18 +00:00
fix(tradein/team): \Z вместо $ в username-regex — 422 вместо 500 на trailing newline (#2554)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 13s
CI / changes (pull_request) Successful in 13s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m18s
df943ea1c7
Deep-review seed'а (#2564) нашёл смежный дефект в уже смерженном коде (#2563):
Python `$` матчит перед trailing newline (re.match(r'...\$', 'admin\n') -> True),
а Postgres `~` в CHECK tradein_users_username_ascii_ck (миграция 193) - False.
username="admin\n" проходил Pydantic-валидацию и падал уже в БД -> 500 вместо
честного 422. `\Z` - конец строки без поблажки на trailing newline, совпадает
с семантикой Postgres `~`.

Grep по app/schemas/ (pattern=/regex=/re.compile/re.match/re.fullmatch) -
других regex-валидаторов с `$` в схемах trade-in нет, team.py - единственный
файл с regex-based полем.
lekss361 merged commit 1364cd8e7a into main 2026-07-30 19:17:11 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2566
No description provided.