Files
portal/app/tests/Feature/Http/Webhook/SupplierWebhookValidationFormatTest.php
T
Дмитрий a49916b7fc
Accessibility (Pa11y live) / a11y (push) Has been cancelled
test: дозакрытие последних 5 — advisory-lock наблюдение, cap-3, webhook фаза-3, supplier-client URL
Набор полностью зелёный (55 to 0; 1713 pass + 4 skip). Всё тест-сторона:
- AuditChainRaceConditionTest: advisory-lock в audit_chain_hash РЕАЛЬНО присутствует
  (миграция 2026_05_30 применяется) — падало наблюдение: bind-параметр в SQL-сдвиге
  (? >> 32) не сдвигал → classid не совпадал. Декомпозицию ключа считаем в PHP.
  NB: db/schema.sql хранит функцию БЕЗ блокировки (минорный дрейф канона; прод через
  миграцию защищён) — стоит перегенерить schema.sql отдельно.
- SupplierConnectionTest WARN#2: matchEligibleProjects ограничен cap=LeadDistributor::CAP=3;
  ждать 3 из 6 видимых тенантов (кросс-tenant видимость под BYPASSRLS; при RLS было бы 0).
- SupplierWebhookTest + ValidationFormatTest: фаза 3 намеренно приняла проект без
  B-префикса как DIRECT (не теряем заявки) — тесты под новый контракт (202 / 422 по vid).
- SupplierPortalClientTest: fake-паттерн под старый URL /admin/rt-projects-load; клиент
  зовёт /admin/visit/rt-projects-load — обновлён паттерн.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 08:46:43 +03:00

68 lines
2.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Models\SystemSetting;
use Illuminate\Foundation\Testing\DatabaseTransactions;
uses(DatabaseTransactions::class);
beforeEach(function () {
SystemSetting::query()
->where('key', 'supplier_webhook_secret')
->update(['value' => 'test-secret-32chars-aaaaaaaaaaaaaa']);
SystemSetting::query()
->where('key', 'supplier_ip_allowlist')
->update(['value' => '[]']);
});
it('returns 422 JSON when supplier posts invalid payload WITHOUT Accept: application/json header', function () {
// Воспроизводит реальное поведение crm.bp-gr.ru: POST без Accept-JSON.
// До фикса (302→422) Laravel редиректил на / с Set-Cookie, поставщик
// терял тело запроса. После фикса всегда JSON.
$response = $this->call(
'POST',
'/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa',
[], // params
[], // cookies
[], // files
['HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded'], // server: НЕТ Accept JSON
http_build_query([
// Фаза 3 сняла валидацию формата project (non-B → DIRECT), поэтому
// для проверки JSON-рендера 422 используем всё ещё обязательное поле:
// пропускаем vid → гарантированный 422.
'project' => 'B1_test.ru',
'phone' => '79991234567',
'time' => time(),
])
);
$response->assertStatus(422);
expect($response->headers->get('Content-Type'))->toContain('application/json');
$response->assertJsonStructure(['message', 'errors' => ['vid']]);
});
it('still works correctly for postJson clients (regression)', function () {
$response = $this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
// vid пропущен намеренно → 422 (project-формат больше не валидируется, фаза 3).
'project' => 'B1_test.ru',
'phone' => '79991234567',
'time' => time(),
]);
$response->assertStatus(422)->assertJsonValidationErrors('vid');
});
it('non-webhook routes still use default render (no JSON forced)', function () {
// Регрессионный тест: дефолтный render остальных routes не сломан
// (например /login — должен возвращать redirect, а не JSON).
$response = $this->call(
'POST',
'/login',
['email' => 'bad', 'password' => ''],
[], [], [],
);
// Любой не-200 кроме 422-JSON допустим — главное чтобы наш fix не перехватил
expect($response->headers->get('Content-Type'))->not->toContain('application/json');
});