Тести на доступи — це єдине, що реально страхує вас від “випадково відкрили зайве” після змін у коді. У проєктах з ролями, approval-статусами і модулями типу “контроль публікацій” ризик завжди один: хтось додав новий маршрут, змінив фільтр, підвантажив зв’язок або “спростив” update, і в результаті користувач бачить/змінює те, що не повинен. Тому доступи тестують не як “один тест на все”, а як набір коротких сценаріїв: доступ до розділу, доступ до конкретного запису, доступ до дії, і захист від підміни полів.
Є два рівні тестів на доступи, які вам потрібні. Перший — інтеграційні (feature): реальний HTTP-запит на маршрут і перевірка статусу 200/302/403/404. Це перевіряє middleware, політики і контролери разом. Другий — unit/authorization-тести (опційно): прямі перевірки policy-методів (can/cannot) для конкретних комбінацій користувача й запису. У вашому випадку критичні саме feature-тести, бо у вас є middleware permissions, і головні збої трапляються на рівні маршруту/фільтрів.
Починайте з матриці ролей. Не треба писати всі комбінації одразу, але треба визначити мінімум 3–4 “актори”, які реально відрізняються правами. Наприклад: гість (не авторизований), автор (бачить/редагує своє), редактор (бачить ширше, може approve/publish), адміністратор (може все). Далі для кожного модуля визначаєте ключові дії: viewAny (список), view (перегляд), create, update, delete, і 1–2 кастомні (approve/publish). Саме під ці дії і пишуться тести.
Перший базовий блок — “вхід у модуль”. Це перевірка middleware permissions. Тут тест простий: актор без права отримує 403 (або редирект на логін, якщо гість), актор з правом отримує 200. Це гарантує, що ви не забули повісити middleware на новий маршрут або групу. Важливий момент: якщо у вас прийнято “ховати існування” і віддавати 404 замість 403 — тестуйте 404. Головне, щоб поведінка була стабільна.
Другий блок — “доступ до конкретного запису”. Це те, що найчастіше ламається через неправильні запити. Наприклад, автор має бачити тільки свої записи. Тест будується так: створюєте два записи — один належить автору, другий — іншому. Далі перевіряєте: для чужого запису edit/show має бути 403 або 404. Це ключовий тест, який ловить витоки через прямий підбір ID.
Третій блок — “доступ до дії”. Тут важливо не плутати “бачити форму” і “виконати дію”. Наприклад, кнопка може бути схована в UI, але користувач може відправити POST/PUT вручну. Тому завжди тестуйте саме endpoint: POST store, PUT update, DELETE destroy, POST approve тощо. Очікування: без прав — 403/404; з правами — 302 редирект і зміни в базі.
Четвертий блок — “захист від підміни полів”. Це найбільш підступна частина. У вас можуть бути поля, які не можна міняти звичайному редактору або автору: approval_status, subscription_required, created_by, billing_entity_id, фінансові суми, ознаки “оплачено”. Людина може підставити ці поля у форму або в запит. Навіть якщо policy дозволяє update тексту, сервіс повинен ігнорувати заборонені поля або відмовляти. Це тестується так: відправляєте update з дозволеним полем плюс забороненим, і перевіряєте, що дозволене оновилось, а заборонене — ні. Це реальний “анти-ескалаційний” тест, який рятує від найнеприємніших інцидентів.
П’ятий блок — “фільтри не розширюють доступ”. Якщо у вас є query string фільтри (user_id, status, date range), їх можна використати як інструмент витоку, якщо базове обмеження видимості накладено неправильно. Тест простий: актор з обмеженою видимістю викликає index з параметрами, які “просять” чужі записи, і перевіряє, що чужі записи не з’явились у відповіді. Це підтверджує, що ви спочатку обмежуєте по доступах, а потім застосовуєте фільтри.
Практичні статус-коди. 302 — це успішний POST/PUT/DELETE з редиректом на список. 200 — успішний перегляд. 403 — “заборонено”. 404 — “не знайдено”, коли ви ховаєте існування чужих записів, або коли вибірка йде через “доступний scope” і чужий запис просто не знаходиться. Важливо вибрати одну політику і тримати її консистентно, інакше тести будуть “плавати”, а система — поводитись непередбачувано.
Як організувати тести по файлах. Найзручніше робити по модулю: tests/Feature/Admin/NewsAccessTest.php, TagAccessTest.php, InvoiceAccessTest.php. Усередині — групи тестів по діях. Для читабельності використовуйте helper-методи: makeAuthor(), makeEditor(), grantPermissions(), createNewsOwnedBy($user). Це зменшує шум і робить тести підтримуваними.
Як зробити “актора з правами” у вашому контексті. Оскільки у вас permission middleware критичний, у тестах треба вміти видати користувачу потрібні права так само, як у проді: або через таблицю permissions/roles, або через ваші seeders. Найправильніше — мати маленький helper, який додає конкретний permission, наприклад news.view, news.update, news.approve. Якщо фабрик немає, користувачів створюєте руками. Головне — не вимикати middleware в тестах, бо тоді ви тестуєте не те, що працює в реальності.
Мінімальний “must-have” набір тестів на доступи для одного CRUD-модуля виглядає так. Гість не має доступу до index (редирект/403). Користувач без права не має доступу до index (403). Користувач з правом має доступ до index (200). Користувач не може редагувати чужий запис (403/404). Користувач з правом може створити запис (302 + запис у БД). Користувач без права не може видалити (403/404). І один тест на підмішування забороненого поля в update (поле не змінилось). Це 6–8 тестів, які дають максимальну віддачу за мінімум коду.
Висновок: тести на доступи — це ваш страховий механізм від найнеприємнішого класу багів: витоків і ескалації прав. Тестуйте доступ до модулів (middleware), доступ до конкретних записів (policy + вибірка), доступ до дій (POST/PUT/DELETE), і обов’язково підміну полів в update. Якщо ці тести є, ви можете сміливіше рефакторити, додавати фільтри і нові екрани, не боячись, що “десь відкрили зайве” або “хтось змінив статус без прав”.