$tenantId, 'status' => Campaign::STATUS_DRAFT, 'ad_text' => 'Объявление', 'ad_link' => 'https://liderra.ru', 'media_path' => null, 'ord_category' => 'Размещение рекламы', 'budget_cap_rub' => '1000.00', 'audience_kind' => $kind, 'audience_params' => $params, 'created_by' => null, ]); } it('собирает кандидатов из сделок (окно, мягкое удаление, мастер-лид)', function () { $tenant = Tenant::factory()->create(); Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '79990000001', 'phone_operator' => 'МТС', 'received_at' => now()->subDays(2), ]); // вне окна Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '79990000002', 'phone_operator' => 'МТС', 'received_at' => now()->subDays(30), ]); // мягко удалён $deleted = Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '79990000003', 'phone_operator' => 'МТС', 'received_at' => now()->subDays(1), ]); $deleted->delete(); // дубликат другого лида Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '79990000004', 'phone_operator' => 'МТС', 'duplicate_of_id' => 999999, 'received_at' => now()->subDays(1), ]); $campaign = makeTgCampaign($tenant->id, Campaign::AUDIENCE_DEALS, ['days' => 5]); $audience = app(TelegramAudienceService::class)->build($campaign); expect($audience->phones)->toBe(['79990000001']) ->and($audience->candidatesCount)->toBe(1); }); it('собирает кандидатов из своей базы (base)', function () { $tenant = Tenant::factory()->create(); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000011', 'name' => 'Иван', 'operator' => 'Билайн']); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000012', 'name' => null, 'operator' => null]); $campaign = makeTgCampaign($tenant->id, Campaign::AUDIENCE_BASE); $audience = app(TelegramAudienceService::class)->build($campaign); expect($audience->phones)->toEqualCanonicalizing(['79990000011', '79990000012']) ->and($audience->candidatesCount)->toBe(2); }); it('собирает кандидатов из своего списка с учётом срока (list)', function () { $tenant = Tenant::factory()->create(); $campaign = makeTgCampaign($tenant->id, Campaign::AUDIENCE_LIST); CampaignPhone::create(['tenant_id' => $tenant->id, 'campaign_id' => $campaign->id, 'phone' => '79990000021', 'expires_at' => null]); CampaignPhone::create(['tenant_id' => $tenant->id, 'campaign_id' => $campaign->id, 'phone' => '79990000022', 'expires_at' => now()->addDays(3)]); // истёкший — не должен попасть CampaignPhone::create(['tenant_id' => $tenant->id, 'campaign_id' => $campaign->id, 'phone' => '79990000023', 'expires_at' => now()->subDay()]); $audience = app(TelegramAudienceService::class)->build($campaign); expect($audience->phones)->toEqualCanonicalizing(['79990000021', '79990000022']) ->and($audience->candidatesCount)->toBe(2); }); it('нормализует форматы и схлопывает дубли до голых 11 цифр', function () { $tenant = Tenant::factory()->create(); Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '89991112233', 'phone_operator' => null, 'received_at' => now()->subDays(1), ]); Deal::factory()->create([ 'tenant_id' => $tenant->id, 'phone' => '+79991112233', 'phone_operator' => 'МегаФон', 'received_at' => now()->subDays(2), ]); $campaign = makeTgCampaign($tenant->id, Campaign::AUDIENCE_DEALS, ['days' => 5]); $audience = app(TelegramAudienceService::class)->build($campaign); expect($audience->phones)->toBe(['79991112233']) ->and($audience->candidatesCount)->toBe(1); }); it('применяет стоп-лист (optout)', function () { $tenant = Tenant::factory()->create(); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000041', 'name' => null, 'operator' => null]); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000042', 'name' => null, 'operator' => null]); Optout::create(['tenant_id' => $tenant->id, 'phone' => '79990000042']); $campaign = makeTgCampaign($tenant->id, Campaign::AUDIENCE_BASE); $audience = app(TelegramAudienceService::class)->build($campaign); expect($audience->phones)->toBe(['79990000041']) ->and($audience->candidatesCount)->toBe(1); }); it('стоп-лист в любом формате вычищает нормализованного кандидата', function () { $tenant = Tenant::factory()->create(); // кандидаты — голый 11-значный вид (как хранит своя база) Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000041', 'name' => null, 'operator' => null]); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000042', 'name' => null, 'operator' => null]); Contact::create(['tenant_id' => $tenant->id, 'phone' => '79990000043', 'name' => null, 'operator' => null]); // отписки в «грязных» форматах (в рамках реального ограничения схемы phone varchar(11): // строку "+7 999 000-00-41" туда физически не записать — берём формы, которые влезают) Optout::create(['tenant_id' => $tenant->id, 'phone' => '89990000041']); // 8-префикс, 11 цифр Optout::create(['tenant_id' => $tenant->id, 'phone' => '9990000042']); // без кода страны, 10 цифр $audience = app(TelegramAudienceService::class)->build( makeTgCampaign($tenant->id, Campaign::AUDIENCE_BASE) ); expect($audience->phones)->not->toContain('79990000041') ->and($audience->phones)->not->toContain('79990000042') ->and($audience->phones)->toContain('79990000043'); }); it('никогда не смешивает номера разных тенантов', function () { $tenantA = Tenant::factory()->create(); $tenantB = Tenant::factory()->create(); Deal::factory()->create([ 'tenant_id' => $tenantA->id, 'phone' => '79990000031', 'phone_operator' => 'МТС', 'received_at' => now()->subDays(1), ]); Deal::factory()->create([ 'tenant_id' => $tenantB->id, 'phone' => '79990000032', 'phone_operator' => 'МТС', 'received_at' => now()->subDays(1), ]); Contact::create(['tenant_id' => $tenantB->id, 'phone' => '79990000033', 'name' => null, 'operator' => null]); $deals = app(TelegramAudienceService::class)->build(makeTgCampaign($tenantA->id, Campaign::AUDIENCE_DEALS, ['days' => 5])); $base = app(TelegramAudienceService::class)->build(makeTgCampaign($tenantA->id, Campaign::AUDIENCE_BASE)); expect($deals->phones)->toBe(['79990000031']) ->and($base->phones)->toBe([]); });