docs: add test cases wiki page
219
Test-Cases.md
Normal file
219
Test-Cases.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Test Cases
|
||||
|
||||
Все тесты запускаются внутри Docker-контейнера:
|
||||
|
||||
```
|
||||
docker compose exec web python -m pytest
|
||||
```
|
||||
|
||||
Общее покрытие: **~73%** (65+ тест-кейсов).
|
||||
|
||||
---
|
||||
|
||||
## Юнит-тесты
|
||||
|
||||
### `_is_weight` — определение весового товара (`test_tasks_vk_sync.py`)
|
||||
|
||||
| ID | Входные данные | Ожидаемый результат |
|
||||
|----|----------------|---------------------|
|
||||
| U-01 | `"г"` | `True` |
|
||||
| U-02 | `"г."` | `True` |
|
||||
| U-03 | `"гр"` | `True` |
|
||||
| U-04 | `"гр."` | `True` |
|
||||
| U-05 | `"грамм"` | `True` |
|
||||
| U-06 | `"граммов"` | `True` |
|
||||
| U-07 | `" Г "` (пробелы, верхний регистр) | `True` |
|
||||
| U-08 | `"кг"` | `False` |
|
||||
| U-09 | `"шт"` | `False` |
|
||||
| U-10 | `"л"` | `False` |
|
||||
| U-11 | `None` | `False` |
|
||||
| U-12 | `""` | `False` |
|
||||
|
||||
### `_calc_price` — расчёт цены в копейках (`test_tasks_vk_sync.py`)
|
||||
|
||||
| ID | Входные данные | VK_WEIGHT_PRICE_MULTIPLIER | Ожидаемый результат |
|
||||
|----|----------------|---------------------------|---------------------|
|
||||
| U-13 | `Decimal("150")`, `"шт"` | 10 | `15000` (150 руб × 100) |
|
||||
| U-14 | `Decimal("50")`, `"г"` | 10 | `50000` (50 × 10 × 100) |
|
||||
| U-15 | `None`, `"шт"` | — | `0` |
|
||||
| U-16 | `Decimal("0")`, `"шт"` | — | `0` |
|
||||
|
||||
### `_name_for_vk` — очистка названия (`test_tasks_vk_sync.py`)
|
||||
|
||||
| ID | Входные данные | Ожидаемый результат |
|
||||
|----|----------------|---------------------|
|
||||
| U-17 | `"Чай; зелёный; Китай"` | `"Чай, зелёный, Китай"` |
|
||||
| U-18 | `"Пуэр (выдержанный)"` | `"Пуэр (выдержанный)"` |
|
||||
|
||||
### `_build_description` — формирование описания (`test_tasks_vk_sync.py`)
|
||||
|
||||
| ID | Сценарий | Проверяется |
|
||||
|----|----------|-------------|
|
||||
| U-19 | Весовой товар, `"г"`, нет описания | Содержит `"10г"` и название |
|
||||
| U-20 | Штучный товар, есть описание из Эвотора | Содержит текст описания |
|
||||
| U-21 | Штучный товар, нет описания | Содержит название |
|
||||
|
||||
### `_delete_orphans` — удаление устаревших товаров VK (`test_tasks_vk_sync.py`)
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| U-22 | VK-товар `"222"` не в `owned_ids` → удаляется | `results["deleted"] == 1`, `db.delete` вызван |
|
||||
| U-23 | VK API возвращает `{"error": {...}}` | `results["errors"] == 1`, `db.delete` не вызван |
|
||||
| U-24 | `owned_ids` пуст → все VK-товары — орфаны | `results["deleted"] == 1` |
|
||||
|
||||
### `_fetch_stores` — загрузка магазинов из Эвотора (`test_tasks_catalog.py`)
|
||||
|
||||
| ID | Формат ответа API | Ожидаемый результат |
|
||||
|----|-------------------|---------------------|
|
||||
| U-25 | Список `[{"id": "s1", ...}]` | Список магазинов |
|
||||
| U-26 | Словарь `{"items": [...], "total": 1}` | Распаковывает `items` |
|
||||
|
||||
### `_fetch_groups` — загрузка групп (`test_tasks_catalog.py`)
|
||||
|
||||
| ID | HTTP-статус | Ожидаемый результат |
|
||||
|----|-------------|---------------------|
|
||||
| U-27 | `200` | Список групп |
|
||||
| U-28 | `402` | `None` |
|
||||
| U-29 | `403` | `None` |
|
||||
|
||||
### `_fetch_products` — загрузка товаров (`test_tasks_catalog.py`)
|
||||
|
||||
| ID | HTTP-статус | Ожидаемый результат |
|
||||
|----|-------------|---------------------|
|
||||
| U-30 | `200` | Список товаров |
|
||||
| U-31 | `402` | `None` |
|
||||
| U-32 | `403` | `None` |
|
||||
|
||||
---
|
||||
|
||||
## Интеграционные тесты — задачи каталога (`test_tasks_catalog.py`)
|
||||
|
||||
Используют SQLite in-memory БД через фикстуру `override_db`.
|
||||
|
||||
| ID | Сценарий | Проверяется |
|
||||
|----|----------|-------------|
|
||||
| I-01 | `_sync_user` с новым магазином | `CachedStore` создан в БД |
|
||||
| I-02 | `_sync_user` с товаром | `CachedProduct` создан, поля `group_evotor_id`, `allow_to_sell` верны |
|
||||
| I-03 | `_fetch_stores` бросает исключение | Никаких записей в БД, задача завершается без ошибки |
|
||||
|
||||
---
|
||||
|
||||
## Интеграционные тесты — маршруты каталога (`test_routes_catalog.py`)
|
||||
|
||||
| ID | Маршрут | Сценарий | Ожидаемый результат |
|
||||
|----|---------|----------|---------------------|
|
||||
| I-04 | `GET /catalog/stores` | Без сессии | Редирект `303 → /login` |
|
||||
| I-05 | `GET /catalog/stores` | Нет магазинов | `200`, текст «не загружены» |
|
||||
| I-06 | `GET /catalog/stores` | 2 магазина | `200`, оба названия в ответе |
|
||||
| I-07 | `GET /catalog/stores` | Магазин другого пользователя | Не отображается |
|
||||
| I-08 | `GET /catalog/stores/{id}/groups` | Группа с 2 товарами | `200`, название группы, число `"2"` |
|
||||
| I-09 | `GET /catalog/stores/{id}/groups` | Пустая группа | `200`, число `"0"` |
|
||||
| I-10 | `GET /catalog/stores/{id}/groups` | Несуществующий магазин | Редирект `303` |
|
||||
| I-11 | `GET /catalog/stores/{id}/products` | 2 товара | `200`, оба названия |
|
||||
| I-12 | `GET /catalog/stores/{id}/products?group=g1` | Фильтр по группе | Только товары группы g1 |
|
||||
| I-13 | `GET /catalog/stores/{id}/products` | Товар с группой | Название группы в ответе |
|
||||
| I-14 | `GET /catalog/stores/{id}/products` | Товар без группы | `200`, товар отображается |
|
||||
| I-15 | `POST /catalog/stores/{id}/toggle` | Первое отключение из 3 магазинов | `SyncFilter` include: только s2, s3 (не s1) |
|
||||
| I-16 | `POST /catalog/stores/{id}/toggle` | Повторный toggle включает | s1 снова в списке include |
|
||||
| I-17 | `POST /catalog/stores/{id}/toggle` | Без сессии | Редирект `303 → /login` |
|
||||
| I-18 | `POST /catalog/stores/{id}/groups/{gid}/toggle` | Первое отключение группы g1 | include: только g2 (не g1) |
|
||||
| I-19 | `POST /catalog/stores/{id}/groups/{gid}/toggle` | Повторный toggle включает g1 | g1 снова в include |
|
||||
|
||||
---
|
||||
|
||||
## Интеграционные тесты — маршруты подключений (`test_routes_connections.py`)
|
||||
|
||||
### Авторизация
|
||||
|
||||
| ID | Маршрут | Ожидаемый результат |
|
||||
|----|---------|---------------------|
|
||||
| I-20 | `GET /connections` | Без сессии → `303 → /login` |
|
||||
|
||||
### GET /connections
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-21 | Нет подключений | `200`, «Эвотор», «ВКонтакте», «Не подключено» |
|
||||
| I-22 | Есть Эвотор-подключение | `200`, «Подключено», первые 8 символов токена |
|
||||
|
||||
### POST /connections/evotor
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-23 | Новый токен | `303` с `success=1`, запись создана, `api_token` сгенерирован |
|
||||
| I-24 | Обновление токена | Токен обновлён в БД |
|
||||
| I-25 | Пустой токен | `200`, текст «обязателен» |
|
||||
|
||||
### POST /connections/evotor/disconnect
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-26 | Отключение | `303`, запись удалена из БД |
|
||||
|
||||
### POST /connections/vk (ручной токен)
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-27 | Валидный токен + group_id | `303` с `success=1`, запись создана |
|
||||
| I-28 | Пустой токен | `200`, текст «обязателен» |
|
||||
|
||||
### POST /connections/vk/disconnect
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-29 | Отключение | `303`, запись удалена |
|
||||
|
||||
### GET /vk-auth — OAuth-редирект
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-30 | VK_CLIENT_ID настроен | `302` на `oauth.vk.com/authorize` с `client_id` и `response_type=token` |
|
||||
| I-31 | VK_CLIENT_ID пуст | `303` с `error=vk_not_configured` |
|
||||
|
||||
### GET /vk-callback
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-32 | Аутентифицирован | `200`, HTML с `access_token` и `fetch` |
|
||||
|
||||
### POST /vk-callback/save
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-33 | Неверный state | `200`, `{"ok": false}`, сообщение содержит «state» |
|
||||
| I-34 | Пустой токен | `200`, `{"ok": false}` |
|
||||
| I-35 | Без аутентификации | `401` |
|
||||
|
||||
### POST /connections/evotor/test
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-36 | Нет подключения | `200`, `{"ok": false}`, «не настроено» |
|
||||
| I-37 | Успешный тест (2 магазина) | `200`, `{"ok": true}`, сообщение содержит `"2"` |
|
||||
| I-38 | Неверный токен (401) | `200`, `{"ok": false}`, сообщение содержит `"401"` |
|
||||
|
||||
### POST /connections/vk/test
|
||||
|
||||
| ID | Сценарий | Ожидаемый результат |
|
||||
|----|----------|---------------------|
|
||||
| I-39 | Нет group_id | `200`, `{"ok": false}`, «сообщества» |
|
||||
| I-40 | Успешный тест | `200`, `{"ok": true}`, название сообщества + «включён» |
|
||||
| I-41 | VK API error 5 | `200`, `{"ok": false}`, содержит `"5"` |
|
||||
|
||||
---
|
||||
|
||||
## Запуск тестов
|
||||
|
||||
```bash
|
||||
# Все тесты
|
||||
docker compose exec web python -m pytest
|
||||
|
||||
# Только юнит-тесты
|
||||
docker compose exec web python -m pytest tests/test_tasks_vk_sync.py tests/test_tasks_catalog.py -v
|
||||
|
||||
# Интеграционные тесты маршрутов
|
||||
docker compose exec web python -m pytest tests/test_routes_connections.py tests/test_routes_catalog.py -v
|
||||
|
||||
# С покрытием
|
||||
docker compose exec web python -m pytest --cov=web --cov-report=term-missing
|
||||
```
|
||||
Reference in New Issue
Block a user