127 lines
4.5 KiB
PHP
127 lines
4.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use App\Models\ClientSmsMessage;
|
||
|
|
use App\Models\ClientSmsOptout;
|
||
|
|
use App\Models\Tenant;
|
||
|
|
use App\Services\ClientSms\ClientSmsRecipientSelector;
|
||
|
|
use App\Services\Sms\OperatorNormalizer;
|
||
|
|
use App\Services\Sms\Providers\MtsSmsProvider;
|
||
|
|
use App\Services\Sms\SmsRouter;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Отбор получателей клиентской СМС-рассылки (Task 5). Получатели уже несут
|
||
|
|
* оператора (его определил ClientSmsAudienceBuilder) — здесь только стоп-лист,
|
||
|
|
* дубли внутри рассылки и маршрут. Никакого отсева по каналу отправителя —
|
||
|
|
* Фаза 1 всегда шлёт от имени liderra.ru.
|
||
|
|
*
|
||
|
|
* Роутер собран руками (не app()-синглтон): в песочнице singleton — заглушка,
|
||
|
|
* которая маршрутизирует всё подряд, и тест ничего бы не проверил.
|
||
|
|
*/
|
||
|
|
uses(RefreshDatabase::class);
|
||
|
|
|
||
|
|
function client_sms_selector(): ClientSmsRecipientSelector
|
||
|
|
{
|
||
|
|
$router = new SmsRouter([
|
||
|
|
new MtsSmsProvider(token: 'test', serves: ['mts'], priceKopecks: ['*' => 0]),
|
||
|
|
]);
|
||
|
|
|
||
|
|
return new ClientSmsRecipientSelector($router, new OperatorNormalizer);
|
||
|
|
}
|
||
|
|
|
||
|
|
it('стоп-лист побеждает даже подходящий маршрут', function () {
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
|
||
|
|
ClientSmsOptout::create([
|
||
|
|
'tenant_id' => $tenant->id,
|
||
|
|
'phone' => '79990000001',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$plan = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000001', 'operator' => 'mts']],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($plan->sendable)->toBeEmpty()
|
||
|
|
->and($plan->skipped)->toBe(['79990000001' => ClientSmsMessage::SKIP_OPTOUT]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('номер МТС уходит в отправку через МТС-провайдера', function () {
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
|
||
|
|
$plan = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000002', 'operator' => 'mts']],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($plan->skipped)->toBeEmpty()
|
||
|
|
->and($plan->sendable)->toHaveCount(1)
|
||
|
|
->and($plan->sendable[0]['phone'])->toBe('79990000002')
|
||
|
|
->and($plan->sendable[0]['operator'])->toBe('mts')
|
||
|
|
->and($plan->sendable[0]['provider'])->not->toBeNull()
|
||
|
|
->and($plan->sendable[0]['provider']->key())->toBe('mts');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('оператор без маршрута помечается skipped_no_route', function () {
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
|
||
|
|
$plan = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000003', 'operator' => 'beeline']],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($plan->sendable)->toBeEmpty()
|
||
|
|
->and($plan->skipped)->toBe(['79990000003' => ClientSmsMessage::SKIP_NO_ROUTE]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('неизвестный/пустой оператор тоже помечается skipped_no_route', function () {
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
|
||
|
|
$planNull = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000005', 'operator' => null]],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
$planGarbage = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000006', 'operator' => 'garbage']],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($planNull->skipped)->toBe(['79990000005' => ClientSmsMessage::SKIP_NO_ROUTE])
|
||
|
|
->and($planGarbage->skipped)->toBe(['79990000006' => ClientSmsMessage::SKIP_NO_ROUTE]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('дубль внутри одной рассылки считается один раз', function () {
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
|
||
|
|
$plan = client_sms_selector()->build(
|
||
|
|
[
|
||
|
|
['phone' => '79990000004', 'operator' => 'mts'],
|
||
|
|
['phone' => '79990000004', 'operator' => 'mts'],
|
||
|
|
],
|
||
|
|
$tenant->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($plan->sendable)->toHaveCount(1)
|
||
|
|
->and($plan->skipped)->toBe(['79990000004' => ClientSmsMessage::SKIP_DUPLICATE]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('стоп-лист чужого тенанта не глушит тот же номер у своего', function () {
|
||
|
|
$tenantA = Tenant::factory()->create();
|
||
|
|
$tenantB = Tenant::factory()->create();
|
||
|
|
|
||
|
|
ClientSmsOptout::create([
|
||
|
|
'tenant_id' => $tenantB->id,
|
||
|
|
'phone' => '79990000007',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$plan = client_sms_selector()->build(
|
||
|
|
[['phone' => '79990000007', 'operator' => 'mts']],
|
||
|
|
$tenantA->id,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect($plan->skipped)->toBeEmpty()
|
||
|
|
->and($plan->sendable)->toHaveCount(1);
|
||
|
|
});
|