'test-token']); SalesAdAudienceState::current()->update(['enabled' => true, 'yandex_segment_id' => 58029600]); }); it('номер без хозяина в рекламу не попадает', function () { // Сирота из схемы до v8.77: фирмы у него нет, значит ночной пересчёт его // никогда не тронет. Попади он в сегмент — крутился бы вечно. Http::fake([ 'api-audience.yandex.ru/*' => Http::response(['segment' => ['id' => 58029600]], 200), ]); $firm = SalesAdAudienceFirm::create(['firm_name' => 'Тестовая стоматология']); SalesAdAudiencePhone::create(['phone' => '79990000001', 'firm_id' => $firm->id, 'expires_at' => now()->addDays(3), 'state' => 'active']); SalesAdAudiencePhone::create(['phone' => '79990000009', 'firm_id' => null, 'expires_at' => now()->addDays(3), 'state' => 'active']); SyncAdAudienceJob::dispatchSync(); Http::assertSent(function (Request $request) { $body = (string) $request->body(); return str_contains($body, '79990000001') && ! str_contains($body, '79990000009'); }); }); it('пока рубильник выключен, в Яндекс ничего не уходит', function () { Http::fake(); SalesAdAudienceState::current()->update(['enabled' => false]); SyncAdAudienceJob::dispatchSync(); Http::assertNothingSent(); }); it('заливает только активные номера', function () { Http::fake([ 'api-audience.yandex.ru/*' => Http::response(['segment' => ['id' => 58029600]], 200), ]); $firm = SalesAdAudienceFirm::create(['firm_name' => 'Тестовая стоматология']); SalesAdAudiencePhone::create(['phone' => '79990000001', 'firm_id' => $firm->id, 'expires_at' => now()->addDays(3), 'state' => 'active']); SalesAdAudiencePhone::create(['phone' => '79990000002', 'firm_id' => $firm->id, 'expires_at' => now()->addDays(3), 'state' => 'paused']); SalesAdAudiencePhone::create(['phone' => '79990000003', 'firm_id' => $firm->id, 'expires_at' => now()->addDays(3), 'state' => 'stopped']); SyncAdAudienceJob::dispatchSync(); Http::assertSent(function (Request $request) { $body = (string) $request->body(); return str_contains($request->url(), 'modify_data') && str_contains($body, '79990000001') && ! str_contains($body, '79990000002') && ! str_contains($body, '79990000003'); }); expect(SalesAdAudienceState::current()->last_synced_at)->not->toBeNull(); }); it('ошибку Яндекса показывает начальнику, а не прячет', function () { Http::fake([ 'api-audience.yandex.ru/*' => Http::response(['error' => 'quota exceeded'], 429), ]); $firm = SalesAdAudienceFirm::create(['firm_name' => 'Тестовая стоматология']); SalesAdAudiencePhone::create(['phone' => '79990000001', 'firm_id' => $firm->id, 'expires_at' => now()->addDays(3), 'state' => 'active']); SyncAdAudienceJob::dispatchSync(); expect(SalesAdAudienceState::current()->last_error)->toContain('quota'); }); it('фирму, помеченную только для ВК, в Яндекс не льёт', function () { // 🔴 Смысл фичи: начальник увёл фирму в ВК — из яндексового сегмента она // должна уйти сама при ближайшей заливке, без ручной чистки. config(['services.yandex_audience.token' => 'test-token']); SalesAdAudienceState::current()->update(['enabled' => true, 'yandex_segment_id' => 58029600]); $yandexFirm = SalesAdAudienceFirm::create([ 'firm_name' => 'Греем в Яндексе', 'warmup_started_at' => now(), 'channels' => 'yandex', ]); SalesAdAudiencePhone::create([ 'phone' => '79990000001', 'firm_id' => $yandexFirm->id, 'expires_at' => now()->addDays(30), 'state' => 'active', ]); $vkFirm = SalesAdAudienceFirm::create([ 'firm_name' => 'Греем в ВК', 'warmup_started_at' => now(), 'channels' => 'vk', ]); SalesAdAudiencePhone::create([ 'phone' => '79990000002', 'firm_id' => $vkFirm->id, 'expires_at' => now()->addDays(30), 'state' => 'active', ]); Http::fake(['api-audience.yandex.ru/*' => Http::response(['result' => true], 200)]); SyncAdAudienceJob::dispatchSync(); Http::assertSent(function (Request $r) { $body = collect($r->data())->firstWhere('name', 'file'); $csv = is_array($body) && is_string($body['contents'] ?? null) ? $body['contents'] : ''; expect($csv)->toContain('79990000001') ->and($csv)->not->toContain('79990000002'); return true; }); });