diff --git a/app/app/Console/Commands/CheckSupplierWebhookSecretCommand.php b/app/app/Console/Commands/CheckSupplierWebhookSecretCommand.php new file mode 100644 index 00000000..6c671219 --- /dev/null +++ b/app/app/Console/Commands/CheckSupplierWebhookSecretCommand.php @@ -0,0 +1,59 @@ += 32 chars (требование verifySecret в SupplierWebhookController). + */ +class CheckSupplierWebhookSecretCommand extends Command +{ + protected $signature = 'supplier:check-webhook-secret'; + + protected $description = 'Deploy-time validator: проверка supplier_webhook_secret seed (Plan 2.6 fix #i)'; + + public function handle(): int + { + $row = DB::table('system_settings')->where('key', 'supplier_webhook_secret')->first(); + + if ($row === null) { + $this->error('FAIL: system_settings row для key=supplier_webhook_secret не найдена. Schema seed повреждён или БД не мигрирована.'); + + return self::FAILURE; + } + + $value = (string) $row->value; + + if ($value === '__SET_ON_DEPLOY__') { + $this->error('FAIL: supplier_webhook_secret = "__SET_ON_DEPLOY__" (placeholder из schema seed). Override через UPDATE system_settings перед deploy.'); + + return self::FAILURE; + } + + if (strlen($value) < 32) { + $this->error('FAIL: supplier_webhook_secret слишком короткий (length='.strlen($value).', нужно >=32 chars для совместимости с verifySecret в SupplierWebhookController).'); + + return self::FAILURE; + } + + $this->info('OK: supplier_webhook_secret valid (length='.strlen($value).' chars, не placeholder).'); + + return self::SUCCESS; + } +} diff --git a/app/phpstan-baseline.neon b/app/phpstan-baseline.neon index 20f7e7d1..86cae2ca 100644 --- a/app/phpstan-baseline.neon +++ b/app/phpstan-baseline.neon @@ -911,3 +911,9 @@ parameters: identifier: method.notFound count: 2 path: tests/Feature/Console/ResetDeliveredTodayCommandTest.php + + - + message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:artisan\(\)\.$#' + identifier: method.notFound + count: 4 + path: tests/Feature/Console/CheckSupplierWebhookSecretCommandTest.php diff --git a/app/tests/Feature/Console/CheckSupplierWebhookSecretCommandTest.php b/app/tests/Feature/Console/CheckSupplierWebhookSecretCommandTest.php new file mode 100644 index 00000000..b12b0dd4 --- /dev/null +++ b/app/tests/Feature/Console/CheckSupplierWebhookSecretCommandTest.php @@ -0,0 +1,44 @@ +updateOrInsert( + ['key' => 'supplier_webhook_secret'], + ['value' => '__SET_ON_DEPLOY__', 'type' => 'string', 'description' => 'test seed'] + ); + + $this->artisan('supplier:check-webhook-secret')->assertExitCode(1); +}); + +test('rejects too-short secret (< 32 chars)', function () { + DB::table('system_settings') + ->updateOrInsert( + ['key' => 'supplier_webhook_secret'], + ['value' => 'short-secret-only-20-chars', 'type' => 'string', 'description' => 'test'] + ); + + $this->artisan('supplier:check-webhook-secret')->assertExitCode(1); +}); + +test('rejects missing seed row', function () { + DB::table('system_settings')->where('key', 'supplier_webhook_secret')->delete(); + + $this->artisan('supplier:check-webhook-secret')->assertExitCode(1); +}); + +test('accepts valid secret (>=32 chars and not placeholder)', function () { + DB::table('system_settings') + ->updateOrInsert( + ['key' => 'supplier_webhook_secret'], + ['value' => str_repeat('a', 64), 'type' => 'string', 'description' => 'test seed'] + ); + + $this->artisan('supplier:check-webhook-secret')->assertExitCode(0); +});