# Тестовое покрытие проекта 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. Покрыть оставшиеся модели и контроллеры