From defe6860fc16d992064e9fad927369565af615cf Mon Sep 17 00:00:00 2001 From: nns Date: Tue, 26 May 2026 11:03:48 +0500 Subject: [PATCH] =?UTF-8?q?docs(e2e):=20=D0=B7=D0=B5=D0=BB=D1=91=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BE=D1=82=D1=87=D1=91=D1=82=D1=8B=20auth/cata?= =?UTF-8?q?log/stock=20edge-=D0=BF=D1=80=D0=BE=D0=B3=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit auth-edge 10/10, catalog-edge 12/12, stock-invariant-deep 10/10. Co-Authored-By: Claude Opus 4.7 --- .../auth-edge-2026-05-26T06-01-45-719Z.md | 105 ++++++++++++++++ .../catalog-edge-2026-05-26T06-02-05-506Z.md | 119 ++++++++++++++++++ ...invariant-deep-2026-05-26T06-02-16-375Z.md | 115 +++++++++++++++++ 3 files changed, 339 insertions(+) create mode 100644 tests/e2e/reports/auth-edge-2026-05-26T06-01-45-719Z.md create mode 100644 tests/e2e/reports/catalog-edge-2026-05-26T06-02-05-506Z.md create mode 100644 tests/e2e/reports/stock-invariant-deep-2026-05-26T06-02-16-375Z.md diff --git a/tests/e2e/reports/auth-edge-2026-05-26T06-01-45-719Z.md b/tests/e2e/reports/auth-edge-2026-05-26T06-01-45-719Z.md new file mode 100644 index 0000000..f05b404 --- /dev/null +++ b/tests/e2e/reports/auth-edge-2026-05-26T06-01-45-719Z.md @@ -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 + +Нет. diff --git a/tests/e2e/reports/catalog-edge-2026-05-26T06-02-05-506Z.md b/tests/e2e/reports/catalog-edge-2026-05-26T06-02-05-506Z.md new file mode 100644 index 0000000..6cd15be --- /dev/null +++ b/tests/e2e/reports/catalog-edge-2026-05-26T06-02-05-506Z.md @@ -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 — два товара могут иметь одинаковый артикул, путаница в учёте. diff --git a/tests/e2e/reports/stock-invariant-deep-2026-05-26T06-02-16-375Z.md b/tests/e2e/reports/stock-invariant-deep-2026-05-26T06-02-16-375Z.md new file mode 100644 index 0000000..47e972a --- /dev/null +++ b/tests/e2e/reports/stock-invariant-deep-2026-05-26T06-02-16-375Z.md @@ -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 + +Нет.