Files
portal/app/tests/Feature/Sales/AdAudienceIntakeTest.php
T
Дмитрий 9bc587c2de fix(sales): реальный телефон директора убран из тестов и планов; клиент Яндекс.Аудиторий возвращён в main
Коммит d2c2ec43 от 19.07 оторвался от main (dangling, ни в одной ветке):
чистка ПДн и YandexAudienceClient в основную ветку так и не попали.
Номера заменены на фиктивные 7999000000X, клиент и его тесты внесены заново.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 10:24:21 +03:00

107 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\SalesAdAudiencePhone;
use App\Models\SalesAdAudienceState;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\Concerns\SharesSupplierPdo;
/**
* Приём номеров в рекламную аудиторию сервис-каналом «Поиск → Портал».
*
* Токен канала — config('sales.integration_token') (middleware SalesIntegrationToken).
* В тестовом окружении env пуст, поэтому задаём его здесь, как в SalesIntegrationApiTest.
*
* SharesSupplierPdo обязателен: обе модели прибиты к соединению pgsql_supplier
* (отдельный PDO), и без шаринга DatabaseTransactions откатывал бы только
* соединение по умолчанию — записи текли бы из теста в тест.
*/
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
beforeEach(function () {
config(['sales.integration_token' => 'TESTTOKEN']);
});
test('без токена канал закрыт', function () {
$this->postJson('/api/sales/integration/ad-audience', ['phones' => []])
->assertUnauthorized();
});
test('принимает номера и ставит срок по настройке', function () {
SalesAdAudienceState::current()->update(['days' => 10]);
$this->postJson('/api/sales/integration/ad-audience', ['phones' => [[
'phone' => '79990000002', 'firm_inn' => '5505219940',
'firm_name' => 'Демидовы', 'city' => 'Омск', 'rubric' => 'chastnye-stomatologii',
]]], ['X-Sales-Token' => 'TESTTOKEN'])
->assertOk()->assertJson(['added' => 1, 'renewed' => 0]);
$row = SalesAdAudiencePhone::query()->where('phone', '79990000002')->firstOrFail();
expect($row->firm_name)->toBe('Демидовы')
->and($row->city)->toBe('Омск')
->and($row->firm_inn)->toBe('5505219940')
->and($row->removed_at)->toBeNull()
->and($row->synced_at)->toBeNull();
// Срок = момент приёма + 10 дней (минута допуска на время прогона теста).
expect($row->expires_at->timestamp)
->toBeGreaterThan(now()->addDays(10)->subMinute()->timestamp)
->toBeLessThan(now()->addDays(10)->addMinute()->timestamp);
});
test('повторная отправка продлевает срок, а не плодит дубль', function () {
$this->postJson('/api/sales/integration/ad-audience',
['phones' => [['phone' => '79990000002', 'firm_name' => 'Демидовы']]],
['X-Sales-Token' => 'TESTTOKEN'])->assertOk();
$first = SalesAdAudiencePhone::query()->where('phone', '79990000002')->firstOrFail()->expires_at;
$this->travel(2)->days();
$this->postJson('/api/sales/integration/ad-audience',
['phones' => [['phone' => '79990000002', 'firm_name' => 'Демидовы']]],
['X-Sales-Token' => 'TESTTOKEN'])
->assertOk()->assertJson(['added' => 0, 'renewed' => 1]);
expect(SalesAdAudiencePhone::query()->where('phone', '79990000002')->count())->toBe(1);
$again = SalesAdAudiencePhone::query()->where('phone', '79990000002')->firstOrFail();
expect($again->expires_at->timestamp)->toBeGreaterThan($first->timestamp);
});
test('вернувшийся номер снимает пометку удаления и просит пересинк', function () {
$this->postJson('/api/sales/integration/ad-audience',
['phones' => [['phone' => '79990000002']]],
['X-Sales-Token' => 'TESTTOKEN'])->assertOk();
SalesAdAudiencePhone::query()->where('phone', '79990000002')
->update(['removed_at' => now(), 'synced_at' => now()]);
$this->postJson('/api/sales/integration/ad-audience',
['phones' => [['phone' => '79990000002']]],
['X-Sales-Token' => 'TESTTOKEN'])
->assertOk()->assertJson(['renewed' => 1]);
$row = SalesAdAudiencePhone::query()->where('phone', '79990000002')->firstOrFail();
expect($row->removed_at)->toBeNull()->and($row->synced_at)->toBeNull();
});
test('отвергает номер не в формате Яндекса', function () {
foreach (['+79990000002', '89990000009', '7906197416', '799900000020'] as $bad) {
$this->postJson('/api/sales/integration/ad-audience',
['phones' => [['phone' => $bad]]],
['X-Sales-Token' => 'TESTTOKEN'])->assertStatus(422);
}
expect(SalesAdAudiencePhone::query()->count())->toBe(0);
});
test('пустой список номеров отвергается', function () {
$this->postJson('/api/sales/integration/ad-audience', [], ['X-Sales-Token' => 'TESTTOKEN'])
->assertStatus(422);
$this->postJson('/api/sales/integration/ad-audience', ['phones' => []], ['X-Sales-Token' => 'TESTTOKEN'])
->assertStatus(422);
});