Files
portal/app/tests/Feature/Http/Webhook/SupplierWebhookLoggingTest.php
T

110 lines
3.5 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\SystemSetting;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\RateLimiter;
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' => '[]']);
// Clear rate limiter between tests
RateLimiter::clear('supplier-webhook:127.0.0.1');
});
it('logs status=received when lead is accepted (202)', function () {
Bus::fake();
$this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
'vid' => 900001,
'project' => 'B1_log-test.ru',
'phone' => '79991234567',
'time' => time(),
])->assertStatus(202);
$log = DB::table('webhook_log')
->where('status', 'received')
->where('source', 'supplier')
->latest('created_at')
->first();
expect($log)->not->toBeNull();
expect($log->status)->toBe('received');
expect($log->source)->toBe('supplier');
expect($log->tenant_id)->toBeNull();
});
it('logs status=rejected_secret when secret is wrong (404)', function () {
$this->postJson('/api/webhook/supplier/wrong-secret-here', [
'vid' => 900002,
'project' => 'B1_log-test.ru',
'phone' => '79991234567',
'time' => time(),
])->assertStatus(404);
$log = DB::table('webhook_log')
->where('status', 'rejected_secret')
->where('source', 'supplier')
->latest('created_at')
->first();
expect($log)->not->toBeNull();
expect($log->status)->toBe('rejected_secret');
expect($log->tenant_id)->toBeNull();
});
it('logs status=rejected_ip when IP is not in allowlist (404)', function () {
SystemSetting::query()->where('key', 'supplier_ip_allowlist')
->update(['value' => '["1.2.3.4"]']);
$this->withServerVariables(['REMOTE_ADDR' => '5.6.7.8'])
->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
'vid' => 900003,
'project' => 'B1_log-test.ru',
'phone' => '79991234567',
'time' => time(),
])->assertStatus(404);
$log = DB::table('webhook_log')
->where('status', 'rejected_ip')
->where('source', 'supplier')
->latest('created_at')
->first();
expect($log)->not->toBeNull();
expect($log->status)->toBe('rejected_ip');
expect($log->tenant_id)->toBeNull();
});
it('logs status=rate_limited when per-IP rate limit exceeded (429)', function () {
Bus::fake();
// Saturate the rate limiter
$key = 'supplier-webhook:127.0.0.1';
$limit = 600; // RATE_LIMIT_PER_MINUTE constant
for ($i = 0; $i < $limit; $i++) {
RateLimiter::hit($key, 60);
}
$this->postJson('/api/webhook/supplier/test-secret-32chars-aaaaaaaaaaaaaa', [
'vid' => 900004,
'project' => 'B1_log-test.ru',
'phone' => '79991234567',
'time' => time(),
])->assertStatus(429);
$log = DB::table('webhook_log')
->where('status', 'rate_limited')
->where('source', 'supplier')
->latest('created_at')
->first();
expect($log)->not->toBeNull();
expect($log->status)->toBe('rate_limited');
expect($log->tenant_id)->toBeNull();
});