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 () { $tenant = Tenant::factory()->create(); SmsGlobalOptout::create(['phone' => '79990000010', 'reason' => 'жалоба оператору']); $plan = client_sms_selector()->build( [['phone' => '79990000010', 'operator' => 'mts']], $tenant->id, ); expect($plan->sendable)->toBeEmpty() ->and($plan->skipped)->toBe(['79990000010' => ClientSmsMessage::SKIP_GLOBAL_OPTOUT]); }); it('общий стоп-лист закрывает номер у ВСЕХ тенантов сразу', function () { $tenantA = Tenant::factory()->create(); $tenantB = Tenant::factory()->create(); SmsGlobalOptout::create(['phone' => '79990000011']); foreach ([$tenantA->id, $tenantB->id] as $tenantId) { $plan = client_sms_selector()->build( [['phone' => '79990000011', 'operator' => 'mts']], $tenantId, ); expect($plan->sendable)->toBeEmpty() ->and($plan->skipped)->toBe(['79990000011' => ClientSmsMessage::SKIP_GLOBAL_OPTOUT]); } }); it('общий стоп-лист проверяется РАНЬШЕ тенантского — причина видна именно общая', function () { $tenant = Tenant::factory()->create(); SmsGlobalOptout::create(['phone' => '79990000012']); ClientSmsOptout::create(['tenant_id' => $tenant->id, 'phone' => '79990000012']); $plan = client_sms_selector()->build( [['phone' => '79990000012', 'operator' => 'mts']], $tenant->id, ); expect($plan->skipped)->toBe(['79990000012' => ClientSmsMessage::SKIP_GLOBAL_OPTOUT]); }); 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); });