122 lines
5.5 KiB
PHP
122 lines
5.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use App\Models\AdCampaign;
|
||
|
|
use App\Models\AdCampaignBanner;
|
||
|
|
use App\Models\Tenant;
|
||
|
|
use App\Services\Advertising\CampaignReviveService;
|
||
|
|
use Illuminate\Support\Facades\Http;
|
||
|
|
|
||
|
|
/** Отклонённая кампания, уже заведённая в Яндексе, с одним отклонённым баннером. */
|
||
|
|
function rejectedCampaign(): array
|
||
|
|
{
|
||
|
|
$tenant = Tenant::factory()->create();
|
||
|
|
$campaign = AdCampaign::create([
|
||
|
|
'tenant_id' => $tenant->id, 'name' => 'C', 'audience_days' => 10, 'use_uploaded_list' => false,
|
||
|
|
'status' => AdCampaign::STATUS_REJECTED,
|
||
|
|
'yandex_campaign_id' => 555, 'yandex_ad_group_id' => 666, 'yandex_segment_id' => 777,
|
||
|
|
'moderation_reason' => 'Изображение не подошло',
|
||
|
|
]);
|
||
|
|
$banner = AdCampaignBanner::create([
|
||
|
|
'tenant_id' => $tenant->id, 'campaign_id' => $campaign->id,
|
||
|
|
'width' => 300, 'height' => 250, 'bytes' => 1000, 'included' => true,
|
||
|
|
'path' => 'ad-banners/a/300x250.jpg',
|
||
|
|
'yandex_creative_id' => 100, 'yandex_ad_id' => 200,
|
||
|
|
'moderation_status' => AdCampaignBanner::MOD_REJECTED,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return [$tenant, $campaign, $banner];
|
||
|
|
}
|
||
|
|
|
||
|
|
it('оживление возвращает кампанию в черновик и чистит номера отклонённых объявлений', function () {
|
||
|
|
config(['services.yandex_direct.enabled' => true]);
|
||
|
|
Http::fake(['*' => Http::response(['result' => ['DeleteResults' => [['Id' => 200]]]])]);
|
||
|
|
|
||
|
|
[, $campaign, $banner] = rejectedCampaign();
|
||
|
|
|
||
|
|
app(CampaignReviveService::class)->revive($campaign);
|
||
|
|
|
||
|
|
expect($campaign->refresh()->status)->toBe(AdCampaign::STATUS_DRAFT)
|
||
|
|
->and($banner->refresh()->yandex_creative_id)->toBeNull()
|
||
|
|
->and($banner->refresh()->yandex_ad_id)->toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Номера кампании, группы и сегмента — то, на чём держится возобновляемый запуск.
|
||
|
|
* Стереть их — значит завести в Яндексе второй комплект всего и заплатить дважды.
|
||
|
|
*/
|
||
|
|
it('номера кампании, группы и сегмента при оживлении уцелевают', function () {
|
||
|
|
config(['services.yandex_direct.enabled' => true]);
|
||
|
|
Http::fake(['*' => Http::response(['result' => ['DeleteResults' => [['Id' => 200]]]])]);
|
||
|
|
|
||
|
|
[, $campaign] = rejectedCampaign();
|
||
|
|
|
||
|
|
app(CampaignReviveService::class)->revive($campaign);
|
||
|
|
|
||
|
|
$fresh = $campaign->refresh();
|
||
|
|
expect((int) $fresh->yandex_campaign_id)->toBe(555)
|
||
|
|
->and((int) $fresh->yandex_ad_group_id)->toBe(666)
|
||
|
|
->and((int) $fresh->yandex_segment_id)->toBe(777);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('оживить можно только отклонённую кампанию', function () {
|
||
|
|
[, $campaign] = rejectedCampaign();
|
||
|
|
$campaign->update(['status' => AdCampaign::STATUS_RUNNING]);
|
||
|
|
|
||
|
|
expect(fn () => app(CampaignReviveService::class)->revive($campaign))
|
||
|
|
->toThrow(RuntimeException::class);
|
||
|
|
});
|
||
|
|
|
||
|
|
/** Яндекс не удалил объявление — это не повод не дать клиенту исправиться. */
|
||
|
|
it('неудача удаления в Яндексе не мешает оживлению', function () {
|
||
|
|
config(['services.yandex_direct.enabled' => true]);
|
||
|
|
Http::fake(['*' => Http::response(['error' => ['error_string' => 'нет такого']], 200)]);
|
||
|
|
|
||
|
|
[, $campaign, $banner] = rejectedCampaign();
|
||
|
|
|
||
|
|
app(CampaignReviveService::class)->revive($campaign);
|
||
|
|
|
||
|
|
expect($campaign->refresh()->status)->toBe(AdCampaign::STATUS_DRAFT)
|
||
|
|
->and($banner->refresh()->yandex_ad_id)->toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('при выключенном рубильнике в Яндекс не ходим вовсе', function () {
|
||
|
|
config(['services.yandex_direct.enabled' => false]);
|
||
|
|
Http::fake();
|
||
|
|
|
||
|
|
[, $campaign] = rejectedCampaign();
|
||
|
|
|
||
|
|
app(CampaignReviveService::class)->revive($campaign);
|
||
|
|
|
||
|
|
Http::assertNothingSent();
|
||
|
|
expect($campaign->refresh()->status)->toBe(AdCampaign::STATUS_DRAFT);
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Принятый баннер трогать нельзя: его объявление живо в кабинете и на нём держится
|
||
|
|
* возобновляемый запуск. Обнулить ему номера — значит при следующем «Запустить»
|
||
|
|
* завести в Яндексе второе такое же объявление.
|
||
|
|
*/
|
||
|
|
it('принятый баннер оживление не трогает', function () {
|
||
|
|
config(['services.yandex_direct.enabled' => true]);
|
||
|
|
Http::fake(['*' => Http::response(['result' => ['DeleteResults' => [['Id' => 200]]]])]);
|
||
|
|
|
||
|
|
[$tenant, $campaign] = rejectedCampaign();
|
||
|
|
$accepted = AdCampaignBanner::create([
|
||
|
|
'tenant_id' => $tenant->id, 'campaign_id' => $campaign->id,
|
||
|
|
'width' => 728, 'height' => 90, 'bytes' => 1000, 'included' => true,
|
||
|
|
'path' => 'ad-banners/a/728x90.jpg',
|
||
|
|
'yandex_creative_id' => 300, 'yandex_ad_id' => 400,
|
||
|
|
'moderation_status' => AdCampaignBanner::MOD_ACCEPTED,
|
||
|
|
]);
|
||
|
|
|
||
|
|
app(CampaignReviveService::class)->revive($campaign);
|
||
|
|
|
||
|
|
expect((int) $accepted->refresh()->yandex_creative_id)->toBe(300)
|
||
|
|
->and((int) $accepted->refresh()->yandex_ad_id)->toBe(400);
|
||
|
|
|
||
|
|
// И в Яндекс на удаление ушёл только номер отклонённого объявления.
|
||
|
|
Http::assertSent(fn ($req) => $req->data()['params']['SelectionCriteria']['Ids'] === [200]);
|
||
|
|
});
|