Files
portal/app/tests/Feature/ClientTg/AudienceServiceTest.php
T

170 lines
7.5 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\ClientTg\Campaign;
use App\Models\ClientTg\CampaignPhone;
use App\Models\ClientTg\Contact;
use App\Models\ClientTg\Optout;
use App\Models\Deal;
use App\Models\Tenant;
use App\Services\ClientTg\TelegramAudienceService;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
function makeTgCampaign(int $tenantId, string $kind, ?array $params = null): Campaign
{
return Campaign::create([
'tenant_id' => $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([]);
});