Files
portal/app/tests/Unit/Sms/SmscSmsProviderTest.php
T
Дмитрий 593030271b feat(смс): имя отправителя выбирается по оператору получателя, а не одно на всех
Имя согласовывает ОПЕРАТОР, и у каждого оно своё. Клиентская рассылка несла одно
имя всем каналам, а умолчанием ему служило `services.sms.mts.naming` — то есть имя,
согласованное с МТС, подставлялось Теле2 и подставилось бы Мегафону и Билайну.
Живой отказ Теле2 `invalid_source_address` 04.08.2026 — ровно этот механизм. На бою
у клиентов заведено НОЛЬ своих имён, значит это касалось каждой отправки.

Появился ClientSmsSenderResolver: спрашивает имя у той сети, чей номер. Пустая
строка на выходе — не «имени нет», а «своего имени для этой сети нет, канал
подпишется собственным согласованным именем из настроек».

Переведены на него оба места отправки: рассылка (SendClientSmsCampaignJob) и
авто-СМС по сделке (SendAutoSmsForDealJob).

Универсальный СМС-центр передавал имя КАК ЕСТЬ — теперь тоже падает на своё
(SMSC_NAMING). Раньше пустое имя туда не приходило никогда, с этой правкой стало
бы приходить: ушли бы СМС без отправителя.

Заплатка внутри канала Теле2 (приведение написания к своему регистру) остаётся, но
больше не несущая: она чинила следствие в одном канале, а причина была общая.

Экранное имя (ResolvesClientSmsSenderName) НЕ трогали — это запись намерения, а не
то, чем подпишется сеть; в его шапке теперь написано почему, чтобы не «починили»
обратно.

🪤 Урок по дороге: авто-СМС ловит любую ошибку и молча выходит. Справочник имён был
передан не в ту функцию — вместо падения получилась ТИШИНА, не уходило ничего.
Поймали три чужих теста, до того зелёных.

Проверено: 4614 тестов, упал один чужой (ExampleTest — в этой папке не собран
фронтенд). Статанализ 0. Стиль правленых файлов чист.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 05:28:35 +03:00

94 lines
3.2 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\Services\Sms\Providers\SmscSmsProvider;
use App\Services\Sms\SmsOutgoing;
use App\Services\Sms\SmsSendException;
use Illuminate\Support\Facades\Http;
function smsc_provider(): SmscSmsProvider
{
return new SmscSmsProvider('login', 'secret', ['*'], ['*' => 300]);
}
function smsc_message(): SmsOutgoing
{
return new SmsOutgoing(
phone: '79990000001',
body: 'Здравствуйте!',
senderName: 'liderra.ru',
operator: 'МегаФон',
segments: 1,
);
}
it('обслуживает всех как универсал', function () {
expect(smsc_provider()->servesOperators())->toBe(['*'])
->and(smsc_provider()->key())->toBe('smsc');
});
/**
* С 05.08.2026 имя отправителя выбирается ПО ОПЕРАТОРУ получателя, и пустая
* строка означает «своего имени у клиента для этой сети нет — подпишись своим».
* Универсал же передавал имя КАК ЕСТЬ и ушёл бы с пустым отправителем.
*/
it('пустое имя заменяет своим согласованным', function () {
Http::fake(['smsc.ru/*' => Http::response(['id' => 777, 'cnt' => 1], 200)]);
$provider = new SmscSmsProvider('login', 'secret', ['*'], ['*' => 300], 'liderra.ru');
$provider->send(new SmsOutgoing(
phone: '79990000001',
body: 'Здравствуйте!',
senderName: '',
operator: 'МегаФон',
segments: 1,
));
Http::assertSent(fn ($request) => $request['sender'] === 'liderra.ru');
});
it('заданное имя клиента своим не подменяет', function () {
Http::fake(['smsc.ru/*' => Http::response(['id' => 778, 'cnt' => 1], 200)]);
$provider = new SmscSmsProvider('login', 'secret', ['*'], ['*' => 300], 'liderra.ru');
$provider->send(new SmsOutgoing(
phone: '79990000001',
body: 'Здравствуйте!',
senderName: 'mybrand.ru',
operator: 'МегаФон',
segments: 1,
));
Http::assertSent(fn ($request) => $request['sender'] === 'mybrand.ru');
});
it('успешная отправка возвращает id сообщения и цену по сегментам', function () {
Http::fake(['smsc.ru/*' => Http::response(['id' => 555, 'cnt' => 1], 200)]);
$result = smsc_provider()->send(smsc_message());
expect($result->providerMessageId)->toBe('555')
->and($result->costKopecks)->toBe(300);
});
it('отказ smsc.ru бросает terminal-исключение', function () {
Http::fake(['smsc.ru/*' => Http::response(['error' => 'invalid number', 'error_code' => 7], 200)]);
expect(fn () => smsc_provider()->send(smsc_message()))
->toThrow(SmsSendException::class);
});
it('сетевой сбой — не terminal (можно повторить)', function () {
Http::fake(['smsc.ru/*' => Http::response('', 500)]);
try {
smsc_provider()->send(smsc_message());
$this->fail('ожидалось SmsSendException');
} catch (SmsSendException $e) {
expect($e->terminal)->toBeFalse();
}
});