docs(e2e): зелёные отчёты auth/catalog/stock edge-прогонов

auth-edge 10/10, catalog-edge 12/12, stock-invariant-deep 10/10.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
nns 2026-05-26 11:03:48 +05:00
parent 9f0f071193
commit defe6860fc
3 changed files with 339 additions and 0 deletions

View file

@ -0,0 +1,105 @@
# E2E report: auth-edge
Запущен: 2026-05-26T06:01:37.536Z
Длительность: 5.3с
**Итог:** 10 ✓ / 0 ✗ / 0 ⚠ / 0 ◯ (всего 10)
## ✓ Step step01_bootstrap_admin: SuperAdmin создаёт орг + админа, получаем access+refresh
Длительность: 2224мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Initial login → 200 | ✓ status=200 |
| api | Получили access + refresh | ✓ |
## ✓ Step step02_refresh_token_works: Refresh: старый access обменивается на новые access+refresh
Длительность: 394мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST /connect/token (refresh) → 200 | ✓ status=200 |
| api | Новый access ≠ старый | ✓ |
| api | Новый refresh ≠ старый (rotation) | ✓ |
| api | Новый access → /api/me 200 | ✓ status=200 |
## ✓ Step step03_refresh_token_rotates: После refresh — старый refresh-token больше не работает (rotation)
Длительность: 145мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Повторное использование старого refresh → 4xx | ✓ status=400 error=invalid_grant |
## ✓ Step step04_invalid_refresh_rejected: Невалидный refresh-token возвращает 400 invalid_grant
Длительность: 88мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Случайный refresh → 4xx | ✓ status=400 error=invalid_grant |
## ✓ Step step05_tampered_jwt_rejected: JWT с подделанным org_id (изменён без переподписи) отшивается 401
Длительность: 41мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Изменённый payload (подпись не сходится) → 401 | ✓ status=401 |
## ✓ Step step06_random_jwt_rejected: Случайный JWT-подобный токен из другого ключа отшивается 401
Длительность: 17мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Случайный HS256 «JWT» → 401 | ✓ status=401 |
## ✓ Step step07_deactivated_user_blocked: Деактивация User.IsActive=false: повторный login и refresh возвращают 400
Длительность: 503мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Login деактивированного → 4xx | ✓ status=400 |
| api | Refresh деактивированного → 4xx | ✓ status=400 error=invalid_grant |
## ✓ Step step08_archived_org_blocks_login: Архивная организация: login существующего админа возвращает 400 invalid_grant
Длительность: 489мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Архивация орг → 200/204 | ✓ status=204 |
| api | Login юзера архивной орги → 4xx | ✓ status=400 error=Организация удалена или архивирована. Обратитесь к владельцу. |
## ✓ Step step09_duplicate_signup_blocked: Повторный signup с тем же email живой орги отвергается 400
Длительность: 64мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Повторный signup на занятый email → 4xx | ✓ status=400 {"error":"Пользователь с таким email уже зарегистрирован."} |
## ✓ Step step10_orphan_signup_reactivates: Signup с email orphan-юзера (его org удалена) — реактивирует с новой org
Длительность: 1313мс
| Тип | Проверка | Результат |
|---|---|---|
| api | First signup → 200/201 | ✓ status=200 {"organizationId":"c629b9ea-ffe2-44ae-b091-f9fb61c27956","email":"orphan-1779775297536@example.kz"} |
| api | Re-signup orphan email → 200/201 (реактивация) | ✓ status=200 {"organizationId":"c536d5d9-f5fd-4d80-8e3e-91c2345ef2aa","email":"orphan-1779775297536@example.kz"} |
| api | Login после реактивации → 200 | ✓ status=200 |
## Summary
- Passed: 10
- Failed: 0
- Warnings: 0
- Skipped: 0
## Critical bugs
Нет.

View file

@ -0,0 +1,119 @@
# E2E report: catalog-edge
Запущен: 2026-05-26T06:02:00.235Z
Длительность: 3.7с
**Итог:** 12 ✓ / 0 ✗ / 0 ⚠ / 0 ◯ (всего 12)
## ✓ Step step01_bootstrap: Орг + admin + lookups
Длительность: 1393мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Bootstrap lookups получены | ✓ |
## ✓ Step step02_empty_product_name_rejected: POST product с пустым name → 400
Длительность: 120мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST product с пустым name → 400 | ✓ status=400 {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status": |
## ✓ Step step03_negative_price_rejected: POST product с отрицательной ценой amount=-100 → 400
Длительность: 11мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST product с amount=-100 → 400 | ✓ status=400 {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status": |
## ✓ Step step04_oversized_name_truncated_or_rejected: POST product с name > 500 символов → 400 (превышение maxLength)
Длительность: 8мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST product с name=600 chars → 4xx | ✓ status=400 {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors |
## ✓ Step step05_duplicate_product_article: POST второго product с тем же article → 4xx (если уникальный) или OK + проверка БД
Длительность: 1027мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST 1-й product с article OK | ✓ status=201 |
| api | POST 2-й product с тем же article (gap) | ✓ не запрещено сервером |
## ✓ Step step06_self_parent_group_rejected: POST product-group с parentId=собственный id (цикл) → 400
Длительность: 54мс
| Тип | Проверка | Результат |
|---|---|---|
| api | PUT product-group parentId=self → 4xx | ✓ status=400 {"error":"ParentId cannot be self"} |
## ✓ Step step07_delete_group_with_children: DELETE group у которой есть подгруппы → 409
Длительность: 80мс
| Тип | Проверка | Результат |
|---|---|---|
| api | DELETE group с детьми → 4xx | ✓ status=400 {"error":"Системную группу удалить нельзя."} |
## ✓ Step step08_delete_group_with_products: DELETE group в которой есть продукты → 409
Длительность: 11мс
| Тип | Проверка | Результат |
|---|---|---|
| api | DELETE group с продуктами → 4xx | ✓ status=400 {"error":"Системную группу удалить нельзя."} |
## ✓ Step step09_delete_unit_with_products: DELETE enable у unit, на которую ссылаются продукты → 409
Длительность: 34мс
| Тип | Проверка | Результат |
|---|---|---|
| api | DELETE enable у unit с продуктами → 4xx | ✓ status=409 {"error":"Единица используется в товарах. Перепривяжите товары на другую единицу прежде чем отключать.","products":["First","Second"]} |
## ✓ Step step10_delete_system_price_type: DELETE PriceType.IsSystem=true → 409
Длительность: 51мс
| Тип | Проверка | Результат |
|---|---|---|
| api | DELETE системной PriceType → 4xx | ✓ status=400 {"error":"Системная запись не может быть удалена."} |
## ✓ Step step11_second_retail_price_type: POST PriceType с IsRetail=true когда уже есть Retail → 409
Длительность: 64мс
| Тип | Проверка | Результат |
|---|---|---|
| api | POST второй IsRetail PriceType — 201 (флаг перенесён) или 4xx | ✓ status=201 |
| api | IsRetail=true ровно у одного PriceType (uniqueness) | ✓ count=1 |
## ✓ Step step12_delete_counterparty_with_supply: DELETE counterparty который использован в Supply → 409
Длительность: 800мс
| Тип | Проверка | Результат |
|---|---|---|
| api | DELETE counterparty с Supply → 4xx | ✓ status=409 {"error":"Нельзя удалить контрагента: он используется в документах или товарах.","usedAsSupplier":true,"usedAsCustomer": |
## Summary
- Passed: 12
- Failed: 0
- Warnings: 0
- Skipped: 0
## Critical bugs
Нет.
## Logic gaps
- Article у Product не уникален per-org — два товара могут иметь одинаковый артикул, путаница в учёте.

View file

@ -0,0 +1,115 @@
# E2E report: stock-invariant-deep
Запущен: 2026-05-26T06:02:08.247Z
Длительность: 6.5с
**Итог:** 10 ✓ / 0 ✗ / 0 ⚠ / 0 ◯ (всего 10)
## ✓ Step step01_bootstrap: Орг + admin + product (стартовый остаток 0)
Длительность: 1555мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Bootstrap product создан | ✓ 54af3580-d626-42e0-92b1-cc70bb3fb90d |
| db | Stock.Quantity == 0 | ✓ actual=0 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=0 sum=0 |
## ✓ Step step02_supply_a_qty_20: Supply A qty=20 → invariant stock=20, Σ movement=20
Длительность: 969мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Supply A qty=20 проведена | ✓ |
| db | Stock.Quantity == 20 | ✓ actual=20 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=20 sum=20 |
## ✓ Step step03_sale_a_qty_5: RetailSale A qty=5 → invariant stock=15, Σ movement=15
Длительность: 951мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Sale A qty=5 проведена | ✓ |
| db | Stock.Quantity == 15 | ✓ actual=15 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=15 sum=15 |
## ✓ Step step04_supply_b_qty_10: Supply B qty=10 → invariant stock=25, Σ movement=25
Длительность: 514мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Supply B qty=10 проведена | ✓ |
| db | Stock.Quantity == 25 | ✓ actual=25 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=25 sum=25 |
## ✓ Step step05_sale_b_qty_8: RetailSale B qty=8 → invariant stock=17, Σ movement=17
Длительность: 527мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Sale B qty=8 проведена | ✓ |
| db | Stock.Quantity == 17 | ✓ actual=17 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=17 sum=17 |
## ✓ Step step06_unpost_sale_a: Unpost RetailSale A → invariant stock=22, Σ movement=22
Длительность: 433мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Unpost Sale A → 200/204 | ✓ status=204 |
| db | Stock.Quantity == 22 | ✓ actual=22 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=22 sum=22 |
## ✓ Step step07_repost_sale_a: Re-post RetailSale A → invariant stock=17, Σ movement=17
Длительность: 424мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Re-post Sale A → 200/204 | ✓ status=204 |
| db | Stock.Quantity == 17 | ✓ actual=17 |
| db | Stock.Quantity == Σ StockMovement (invariant) | ✓ stock=17 sum=17 |
## ✓ Step step08_movement_count_correct: Всего StockMovement по продукту = 6 строк (2 supply + 2 sale + reverse sale + repost sale)
Длительность: 182мс
| Тип | Проверка | Результат |
|---|---|---|
| db | StockMovement содержит как минимум 4 строки | ✓ count=6 |
| db | StockMovement не более 8 строк (нет лишних дублей) | ✓ count=6 |
## ✓ Step step09_concurrent_sales_serialized: Два POST /post одновременно на один остаток — один 200, второй 409
Длительность: 600мс
| Тип | Проверка | Результат |
|---|---|---|
| api | Два Draft созданы | ✓ |
| api | Ровно один post 2xx, второй 4xx (5xx) | ✓ statuses=204,409 |
| db | Stock >= 0 (не минус из-за гонки) | ✓ stock=7 |
| db | Stock == Σ Movement после гонки | ✓ stock=7 sum=7 |
## ✓ Step step10_final_invariant: Финальный invariant после всех операций сохраняется
Длительность: 369мс
| Тип | Проверка | Результат |
|---|---|---|
| db | Финальный invariant Stock == Σ Movement | ✓ stock=7 sum=7 |
## Summary
- Passed: 10
- Failed: 0
- Warnings: 0
- Skipped: 0
## Critical bugs
Нет.