# Тестовое покрытие проекта Stroyprofit CRM
## Текущее состояние
### Инфраструктура тестирования
- **Фреймворк:** PHPUnit 11
- **Доп. зависимости:** Mockery (подключён, но не используется), Faker (используется в фабриках)
- **БД в тестах:** реальная MySQL (SQLite закомментирован в `phpunit.xml`)
- **Сброс БД:** `RefreshDatabase` + `$seed = true` в каждом тест-классе
- **Моки:** не применяются — все зависимости реальные (интеграционный стиль)
### Статистика
| Категория | Всего файлов | Покрыто | Покрытие |
|------------------|-------------|---------|----------|
| Тест-файлов | 29 | — | — |
| Тест-методов | ~302 | — | — |
| Модели | 34 | 5 | ~15% |
| Сервисы | 28 | 13 | ~46% |
| Jobs | 21 | 0 | 0% |
| Контроллеры | 30 | 4 | ~13% |
| Form Requests | 22 | 0 | 0% |
| Helpers | 5 | 3 | 60% |
| Observers | 1 | 1 | 100% |
---
## Что покрыто тестами
### Unit — Helpers
- `CountHelper` — числительные (единицы/2-4/5+)
- `DateHelper` — конвертация дат (Excel, ISO, человеческий формат)
- `Price` — форматирование цен с символом ₽
### Unit — Models
- `Order` — константы статусов, атрибуты, связи, soft delete
- `Reclamation` — константы статусов, связи, activeReservations, openShortages
- `InventoryMovement` — константы типов, акцессоры, связи, скоупы, касты
- `Shortage` — статусы, акцессоры, связи, скоупы, методы addReserved/recalculate
- `SparePartOrder` — статусы, reserved_qty, free_qty, canReserve, скоупы
- `SparePart` — цены в копейках, складские остатки, резервы, minimum stock
### Unit — Services
- `FileService` — сохранение файлов (Unicode, спецсимволы, дубликаты)
- `GenerateDocumentsService` — генерация ZIP-архивов, монтаж/сдача/рекламация (частично через markTestSkipped)
- `ImportOrdersService` — импорт заказов из Excel
- `ShortageService` — FIFO, processPartOrderReceipt, calculateOrderQuantity, getCriticalShortages
- `SparePartInventoryService` — getStockInfo, getInventorySummary, getBelowMinStock
- `SparePartIssueService` — issueReservation, issueForReclamation, directIssue, correctInventory
- `SparePartReservationService` — reserve, cancelForReclamation, adjustReservation
- `Export/ExportAreasService` — экспорт районов в XLSX
- `Export/ExportDistrictsService` — экспорт округов в XLSX
- `Import/ImportAreasService` — импорт районов (создание, обновление, восстановление)
- `Import/ImportDistrictsService` — импорт округов
- `Import/ImportMafOrdersService` — импорт MAF-заказов
- `Import/ImportSparePartOrdersService` — импорт заказов запчастей
### Unit — Observers
- `SparePartOrderObserver` — автопокрытие shortages при создании/обновлении заказа
### Feature
- `AdminAreaController` — CRUD + import/export (16 тестов)
- `AdminDistrictController` — CRUD + import/export (15 тестов)
- `OrderController` — CRUD, MAF-операции, фото, документы, генерация, экспорт (22 теста)
- `ReclamationController` — CRUD, файлы, запчасти, детали, генерация (22 теста)
---
## Доработки
### Приоритет 1 — Критически важно
#### 1.1 Перевести БД на SQLite in-memory для тестов
**Файл:** `phpunit.xml`
Раскомментировать строки:
```xml
```
Текущая конфигурация требует запущенную MySQL, что делает запуск тестов в CI и на чистой машине невозможным без дополнительной настройки. Нужно убедиться, что все миграции совместимы с SQLite (убрать специфичные для MySQL типы: `json` → `text`, `enum` → `string`, fulltext-индексы).
#### 1.2 Покрыть Jobs тестами (0% покрытие)
Все 21 джоб не имеют тестов. Минимально необходимо покрыть:
| Job | Что тестировать |
|-----|----------------|
| `ExportOrdersJob` | диспетчеризация, создание файла, статус Import |
| `ExportMafJob` | аналогично |
| `ExportReclamationsJob` | аналогично |
| `ExportScheduleJob` | аналогично |
| `GenerateFilesPack` | создание ZIP, корректность содержимого |
| `GenerateInstallationPack` | аналогично |
| `GenerateHandoverPack` | аналогично |
| `ImportJob` | обработка файла, ошибки валидации |
| `NotifyManagerNewOrderJob` | отправка уведомления (мок FCM) |
| `NotifyManagerChangeStatusJob` | аналогично |
**Рекомендация:** использовать `Queue::fake()` + `Bus::fake()` для проверки диспетчеризации без реального выполнения.
#### 1.3 Подключить Mockery и использовать моки
Сейчас все тесты сервисов — интеграционные (реальная БД). Это медленно и хрупко. Нужно добавить unit-тесты с моками для изоляции зависимостей:
```php
// Пример: мокировать репозиторий в SparePartReservationService
$sparePart = Mockery::mock(SparePart::class);
$sparePart->shouldReceive('lockForUpdate')->andReturnSelf();
```
---
### Приоритет 2 — Важно
#### 2.1 Покрыть контроллеры Feature-тестами
Не покрытые контроллеры с высоким бизнес-приоритетом:
| Контроллер | Что тестировать |
|-----------|----------------|
| `MafOrderController` | CRUD, статусы, документы |
| `SparePartController` | CRUD, цены, остатки |
| `SparePartOrderController` | создание, статусы, отгрузки |
| `SparePartReservationController` | резервирование, отмена |
| `SparePartInventoryController` | инвентаризация |
| `ContractController` | CRUD, загрузка файлов |
| `ScheduleController` | создание расписания из заказа/рекламации |
| `ImportController` | загрузка файла, диспетчеризация джоба |
| `ReportController` | генерация отчётов, фильтрация |
| `ProductController` / `ProductSKUController` | CRUD каталога |
| `UserController` | создание, редактирование пользователей |
Минимальный набор тестов для каждого контроллера:
- Неаутентифицированный запрос → редирект на логин
- Успешный запрос → корректный ответ (200/redirect)
- Невалидные данные → 422 / ошибки валидации
- Авторизация (роли) — если применима
#### 2.2 Покрыть Form Requests тестами
22 Request-класса не имеют изолированных тестов. Добавить тесты валидации:
```php
// tests/Unit/Requests/StoreOrderRequestTest.php
public function test_required_fields(): void
{
$request = new StoreOrderRequest();
$validator = Validator::make([], $request->rules());
$this->assertTrue($validator->fails());
$this->assertArrayHasKey('client_name', $validator->errors()->toArray());
}
```
Приоритетные Request-классы:
- `Order/StoreOrderRequest` — основная форма заказа
- `StoreMafOrderRequest` — форма MAF
- `StoreReclamationRequest` / `StoreReclamationDetailsRequest`
- `StoreSparePartOrderRequest` / `ShipSparePartOrderRequest`
- `StoreContractRequest`
#### 2.3 Покрыть непокрытые сервисы
| Сервис | Что тестировать |
|--------|----------------|
| `ExportOrdersService` | формирование XLSX, корректность данных |
| `ExportMafService` | аналогично |
| `ExportReclamationsService` | аналогично |
| `ExportScheduleService` | аналогично |
| `ExportSparePartsService` | аналогично |
| `ImportReclamationsService` | импорт, маппинг, ошибки |
| `ImportMafsService` | аналогично |
| `ImportCatalogService` | создание/обновление товаров |
| `Import/ImportSparePartsService` | аналогично |
| `Import/ImportYearDataService` | аналогично |
| `PdfConverterClient` | HTTP-запрос к PDF-сервису (мок HTTP-клиента) |
| `Export/ExportYearDataService` | аналогично |
---
### Приоритет 3 — Желательно
#### 3.1 Покрыть непокрытые модели
Модели без тестов (нет отдельных тест-файлов):
- `MafOrder` — статусы, связи, атрибуты
- `Product` / `ProductSKU` — связи, SKU-логика
- `Contract` — связи, загрузка файлов
- `Schedule` — связи с Order/Reclamation, статусы
- `Reservation` — связи, статусы, количество
- `SparePartOrderShipment` — списание резервов
- `Dictionary/Area` / `Dictionary/District` — soft delete, связи
- Database Views (`OrderView`, `MafOrdersView` и т.д.) — корректность агрегации
#### 3.2 Покрыть непокрытые Helpers
- `ExcelHelper` — форматирование ячеек, типы данных
- `roles.php` — проверка наличия/наименования ролей
#### 3.3 Добавить тесты для Auth-контроллеров
Покрыть базовые сценарии аутентификации:
- Успешный логин
- Логин с неверным паролем
- Выход из системы
- Сброс пароля (если используется)
#### 3.4 Добавить тесты политик авторизации (Policies/Gates)
Если в проекте есть ролевая модель (файл `roles.php`, `Role` модель) — добавить тесты:
- Доступ к admin-контроллерам только для admin-роли
- Запрет доступа для пользователей без нужной роли
#### 3.5 Настроить покрытие кода (Coverage)
Добавить генерацию HTML-отчёта покрытия:
```xml
```
Требуется Xdebug или PCOV. Добавить в `Makefile`:
```bash
make test-coverage # php artisan test --coverage
```
---
## Технические проблемы
### Тесты зависят от наличия Excel-шаблонов
`GenerateDocumentsServiceTest` использует `markTestSkipped()` если шаблоны отсутствуют в `/templates/`. Нужно:
- Добавить тестовые шаблоны в `tests/fixtures/templates/`
- Или сделать шаблоны доступными в CI через переменную окружения
### Фикстура `tests/fixtures/test_orders_import.xlsx`
`ImportOrdersServiceTest` требует реальный xlsx-файл. Убедиться что файл находится в git и содержит актуальные данные.
### Нет изоляции тестов сервисов
Все Unit-тесты сервисов реально обращаются к БД — это интеграционные тесты. Нужно:
- Переименовать директорию `tests/Unit/Services/` → `tests/Integration/Services/`
- Или создать настоящие Unit-тесты с моками зависимостей
---
## Рекомендуемый порядок выполнения доработок
1. Настроить SQLite in-memory в `phpunit.xml` (быстрый прогресс без кода)
2. Покрыть `MafOrderController` Feature-тестами (высокий бизнес-приоритет)
3. Покрыть `SparePartController` + `SparePartOrderController` Feature-тестами
4. Добавить тесты для `ExportOrdersService` и других экспорт-сервисов
5. Покрыть Form Requests тестами (быстро, высокая ценность)
6. Добавить тесты для Jobs с `Queue::fake()`
7. Настроить Coverage-отчёт
8. Добавить тесты Auth-контроллеров
9. Покрыть оставшиеся модели и контроллеры