89 lines
4.3 KiB
PHP
89 lines
4.3 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|||
|
|
use Illuminate\Support\Facades\DB;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Сторож прав рабочей роли на таблицы модуля.
|
|||
|
|
*
|
|||
|
|
* Зачем он появился (Task 8). В миграциях модуля права выдавались строкой
|
|||
|
|
* `GRANT … TO crm_app_user` без проверки, есть ли такая роль: на чистой базе это
|
|||
|
|
* валит `migrate` целиком. Проверку я добавил — но у неё есть своя опасность:
|
|||
|
|
* опечатка в имени роли внутри проверки НЕ даёт ошибки, права просто молча не
|
|||
|
|
* выдаются. А это ровно тот блокер выката, что стоил нам дня 27.07 (журнал В-36):
|
|||
|
|
* на бою первая же запись падает с «нет доступа», а тесты и локальная база этого
|
|||
|
|
* не видят — там суперпользователь.
|
|||
|
|
*
|
|||
|
|
* Поэтому здесь проверяется не текст миграции, а результат: реально ли у роли есть
|
|||
|
|
* право писать в каждую таблицу модуля и пользоваться её счётчиком.
|
|||
|
|
*
|
|||
|
|
* ⚠️ Честное ограничение: тест доказывает это только там, где роли заведены
|
|||
|
|
* (`db/00_create_roles.sql`). Где ролей нет — он пропускается и не доказывает ничего.
|
|||
|
|
*/
|
|||
|
|
uses(RefreshDatabase::class);
|
|||
|
|
|
|||
|
|
/** Таблица → права, которые модуль обещает рабочей роли. */
|
|||
|
|
const CLIENT_SMS_TABLE_GRANTS = [
|
|||
|
|
'client_sms_campaigns' => ['SELECT', 'INSERT', 'UPDATE'],
|
|||
|
|
'client_sms_campaign_phones' => ['SELECT', 'INSERT', 'DELETE'],
|
|||
|
|
'client_sms_messages' => ['SELECT', 'INSERT', 'UPDATE'],
|
|||
|
|
'client_sms_optouts' => ['SELECT', 'INSERT', 'DELETE'],
|
|||
|
|
'client_sms_contacts' => ['SELECT', 'INSERT', 'DELETE'],
|
|||
|
|
'client_sms_templates' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'],
|
|||
|
|
'client_sms_tariffs' => ['SELECT'],
|
|||
|
|
'client_sms_settings' => ['SELECT'],
|
|||
|
|
'client_sms_senders' => ['SELECT', 'INSERT', 'UPDATE'],
|
|||
|
|
'client_sms_auto_rule' => ['SELECT', 'INSERT', 'UPDATE'],
|
|||
|
|
'sms_global_optouts' => ['SELECT'],
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
function clientSmsRoleExists(string $role): bool
|
|||
|
|
{
|
|||
|
|
return DB::selectOne('SELECT 1 AS ok FROM pg_roles WHERE rolname = ?', [$role]) !== null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
it('у рабочей роли есть обещанные права на таблицы модуля', function () {
|
|||
|
|
if (! clientSmsRoleExists('crm_app_user')) {
|
|||
|
|
$this->markTestSkipped('Роли crm_app_user на этой базе нет — проверять нечего.');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (CLIENT_SMS_TABLE_GRANTS as $table => $privileges) {
|
|||
|
|
foreach ($privileges as $privilege) {
|
|||
|
|
$granted = DB::selectOne(
|
|||
|
|
'SELECT has_table_privilege(?, ?, ?) AS ok',
|
|||
|
|
['crm_app_user', $table, $privilege],
|
|||
|
|
)->ok;
|
|||
|
|
|
|||
|
|
expect($granted)->toBeTrue("Роли crm_app_user не выдано право {$privilege} на {$table}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Счётчики (sequence) — отдельная история: право на таблицу их НЕ покрывает, и
|
|||
|
|
* именно этого не хватало всем 14 миграциям модуля до фикса В-36.
|
|||
|
|
*/
|
|||
|
|
it('у рабочей роли есть право пользоваться счётчиками таблиц модуля', function () {
|
|||
|
|
if (! clientSmsRoleExists('crm_app_user')) {
|
|||
|
|
$this->markTestSkipped('Роли crm_app_user на этой базе нет — проверять нечего.');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$sequences = DB::select(<<<'SQL'
|
|||
|
|
SELECT sequencename FROM pg_sequences
|
|||
|
|
WHERE schemaname = 'public' AND sequencename LIKE 'client\_sms\_%'
|
|||
|
|
SQL);
|
|||
|
|
|
|||
|
|
expect($sequences)->not->toBeEmpty();
|
|||
|
|
|
|||
|
|
foreach ($sequences as $row) {
|
|||
|
|
$granted = DB::selectOne(
|
|||
|
|
'SELECT has_sequence_privilege(?, ?, ?) AS ok',
|
|||
|
|
['crm_app_user', $row->sequencename, 'USAGE'],
|
|||
|
|
)->ok;
|
|||
|
|
|
|||
|
|
expect($granted)->toBeTrue("Роли crm_app_user не выдано право на счётчик {$row->sequencename}");
|
|||
|
|
}
|
|||
|
|
});
|