Files
portal/app/tests/Feature/ClientSms/SenderOverviewTest.php
T
Дмитрий a159bf73cb feat,смс: портал научился сети Мотива — маршрут через Билайна на выключателе
Номер абонента Мотива не доходил даже до каналов: словарь операторов о Мотиве
не знал, и номер выбывал на первой заставе «кому мы вообще шлём».

Что сделано:
· словарь знает ОБА сырых написания — торговое «Мотив» от ДаДаты и юридическое
  ООО "ЕКАТЕРИНБУРГ-2000" из реестра Россвязи, где слова «Мотив» нет ни разу
  замер по DEF-9xx.csv: 29 диапазонов, среди 89 имён реестра столкновений нет;
· Мотив добавлен к четвёрке владельца — сетей стало пять;
· маршрут через Билайна стоит на выключателе SMS_BEELINE_SERVES_MOTIV,
  умолчание ВЫКЛЮЧЕНО: до одобрения имени отправка вернёт 20230, а этот отказ
  окончательный — повтора нет, сообщение умрёт;
· цена по сети, а не одна на канал: 770 копеек за Мотива. Экран сверки тоже
  научен считать по сети — иначе занижал бы расход на 1,84 ₽ с сообщения;
· проверка ввода настроек и галки в админке пропускают Мотива: без этого
  владелец не смог бы его отметить, а заполненный список отменяет код;
· на клиентском экране «Имя отправителя» появилась пятая строка — скрыть её
  значило бы солгать умолчанием.

Тесты: модуль 548/548, было 530; 1801 проверка. Фронтенд 120/120, Sales 483/483.
Контрольные прогоны: убранное юридическое написание красит ровно четыре
зависящих от него теста, перевёрнутое умолчание выключателя — ровно один.

Не выкачено. Включать после того, как Билайн согласует имя — ориентир 24.08.2026.
2026-08-10 20:27:55 +03:00

128 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\ClientSmsSender;
use App\Models\ClientSmsSettings;
use App\Models\Tenant;
use App\Services\ClientSms\AllowedSmsOperators;
use App\Services\ClientSms\ClientSmsSenderOverview;
use App\Services\ClientSms\ClientSmsSenderResolver;
use App\Services\Sms\OperatorNormalizer;
use App\Services\Sms\Providers\MtsSmsProvider;
use App\Services\Sms\Providers\T2SmsProvider;
use App\Services\Sms\SmsRouter;
use Illuminate\Foundation\Testing\RefreshDatabase;
/**
* Честная картина по операторам для экрана «Имя отправителя».
*
* Клиент должен видеть по КАЖДОЙ сети, что с его рассылкой будет: уйдёт под его
* именем, уйдёт от имени Лидерры или не уйдёт вовсе — и почему. До 05.08.2026
* экран показывал ОДНО имя на всех, и после отсева по имени
* (см. ClientSmsSenderResolver) это стало прямой полуправдой: номера молча
* исчезали бы из рассылки, а экран продолжал бы обещать отправку.
*
* Названия операторов словами живут на ЭКРАНЕ, а не здесь: сервер отдаёт ключи
* и причины, экран решает, как их назвать человеку.
*/
uses(RefreshDatabase::class);
function overviewFor(int $tenantId): array
{
// Роутер собран руками: в песочнице синглтон — заглушка, которая
// маршрутизирует всё подряд, и картина вышла бы неправдой.
$router = new SmsRouter([
new MtsSmsProvider(token: 'test', serves: ['mts'], priceKopecks: ['*' => 0]),
new T2SmsProvider('login', 'pass', ['tele2'], ['*' => 692]),
]);
$overview = new ClientSmsSenderOverview(
$router,
new AllowedSmsOperators(new OperatorNormalizer),
new ClientSmsSenderResolver,
);
return collect($overview->forTenant($tenantId))->keyBy('operator')->all();
}
function makeOverviewSender(?array $operators): Tenant
{
$tenant = Tenant::factory()->create();
ClientSmsSender::create([
'tenant_id' => $tenant->id,
'name' => 'mybrand.ru',
'name_type' => ClientSmsSender::TYPE_WEBSITE,
'status' => ClientSmsSender::STATUS_ACTIVE,
'operators' => $operators,
'monthly_fee_rub' => '0.00',
'consent_at' => now(),
]);
return $tenant;
}
it('показывает все сети владельца, а не только те, куда можем', function () {
$tenant = Tenant::factory()->create();
// Клиент должен видеть и недоступные сети — иначе он не понимает, куда
// делась половина его базы.
// 10.08.2026 сетей стало ПЯТЬ: добавился Мотив. Пока канал Билайна его не
// возит (выключатель SMS_BEELINE_SERVES_MOTIV опущен до одобрения имени),
// строка честно скажет «канала нет» — но она есть, и клиент не гадает.
expect(array_keys(overviewFor((int) $tenant->id)))
->toBe(['mts', 'beeline', 'megafon', 'tele2', 'motiv']);
});
it('своего имени нет — уйдёт от имени Лидерры', function () {
$tenant = Tenant::factory()->create();
$row = overviewFor((int) $tenant->id)['mts'];
expect($row['will_send'])->toBeTrue()
->and($row['reason'])->toBe('liderra_name')
->and($row['sender_name'])->toBeNull();
});
it('имя согласовано у этой сети — уйдёт под именем клиента', function () {
$tenant = makeOverviewSender(['mts', 'tele2']);
$row = overviewFor((int) $tenant->id)['tele2'];
expect($row['will_send'])->toBeTrue()
->and($row['reason'])->toBe('own_name')
->and($row['sender_name'])->toBe('mybrand.ru');
});
it('имя есть, но у этой сети не согласовано — НЕ уйдёт', function () {
$tenant = makeOverviewSender(['mts']);
$row = overviewFor((int) $tenant->id)['tele2'];
expect($row['will_send'])->toBeFalse()
->and($row['reason'])->toBe('name_not_approved');
});
it('канала к сети нет вовсе — НЕ уйдёт, и причина именно про канал', function () {
$tenant = Tenant::factory()->create();
foreach (['beeline', 'megafon'] as $operator) {
$row = overviewFor((int) $tenant->id)[$operator];
expect($row['will_send'])->toBeFalse()
->and($row['reason'])->toBe('no_channel');
}
});
it('оператор вычеркнут владельцем — причина про запрет, а не про канал', function () {
// Запрет важнее наличия канала: канал к МТС есть, но слать запрещено.
ClientSmsSettings::query()->update(['allowed_operators' => ['tele2']]);
$tenant = Tenant::factory()->create();
$row = overviewFor((int) $tenant->id)['mts'];
expect($row['will_send'])->toBeFalse()
->and($row['reason'])->toBe('operator_denied');
});