Files
portal/app/tests/Unit/Advertising/YandexDirectMediaClientTest.php
T

213 lines
7.8 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Services\Advertising\YandexDirectClient;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
uses(TestCase::class);
it('adds a cpm banner media campaign with cp maximum impressions strategy', function () {
Http::fake([
'*/json/v5/campaigns' => Http::response(['result' => ['AddResults' => [['Id' => 777]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$id = $client->addCpmBannerCampaign(
'Лидерра медийная #1',
'2026-07-26',
'2026-08-26',
50_000_000,
1_000_000_000,
3,
7,
);
expect($id)->toBe(777);
Http::assertSent(function ($req) {
$body = $req->data();
$campaign = $body['params']['Campaigns'][0];
$strategy = $campaign['CpmBannerCampaign']['BiddingStrategy'];
$cpMax = $strategy['Network']['CpMaximumImpressions'];
$freq = $campaign['CpmBannerCampaign']['FrequencyCap'];
return $body['method'] === 'add'
&& $campaign['Name'] === 'Лидерра медийная #1'
&& $campaign['StartDate'] === '2026-07-26'
&& $campaign['EndDate'] === '2026-08-26'
&& $strategy['Search']['BiddingStrategyType'] === 'SERVING_OFF'
&& $strategy['Network']['BiddingStrategyType'] === 'CP_MAXIMUM_IMPRESSIONS'
&& $cpMax['AverageCpm'] === 50_000_000
&& $cpMax['SpendLimit'] === 1_000_000_000
&& $cpMax['StartDate'] === '2026-07-26'
&& $cpMax['EndDate'] === '2026-08-26'
&& $cpMax['AutoContinue'] === 'NO'
&& $freq['Impressions'] === 3
&& $freq['PeriodDays'] === 7;
});
});
it('sends auto continue yes when requested for the cpm banner campaign', function () {
Http::fake([
'*/json/v5/campaigns' => Http::response(['result' => ['AddResults' => [['Id' => 778]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$client->addCpmBannerCampaign(
'Лидерра медийная #2',
'2026-07-26',
'2026-08-26',
50_000_000,
1_000_000_000,
3,
7,
'YES',
);
Http::assertSent(function ($req) {
$body = $req->data();
return $body['params']['Campaigns'][0]['CpmBannerCampaign']['BiddingStrategy']['Network']['CpMaximumImpressions']['AutoContinue'] === 'YES';
});
});
it('adds an empty cpm banner ad group with an empty keywords object', function () {
Http::fake([
'*/json/v5/adgroups' => Http::response(['result' => ['AddResults' => [['Id' => 555]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$id = $client->addCpmBannerAdGroup(777, 'Группа медийная', [1, 213]);
expect($id)->toBe(555);
Http::assertSent(function ($req) {
$body = $req->data();
$group = $body['params']['AdGroups'][0];
$matches = $body['method'] === 'add'
&& $group['Name'] === 'Группа медийная'
&& $group['CampaignId'] === 777
&& $group['RegionIds'] === [1, 213];
// Пустой JSON-объект {}, а не массив []
$decoded = json_decode($req->body());
$matches = $matches && $decoded->params->AdGroups[0]->CpmBannerKeywordsAdGroup instanceof stdClass;
return $matches && str_contains($req->body(), '"CpmBannerKeywordsAdGroup":{}');
});
});
it('adds a media audience target without a context bid', function () {
Http::fake([
'*/json/v5/audiencetargets' => Http::response(['result' => ['AddResults' => [['Id' => 999]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$id = $client->addMediaAudienceTarget(555, 58034825);
expect($id)->toBe(999);
Http::assertSent(function ($req) {
$body = $req->data();
$target = $body['params']['AudienceTargets'][0];
return $body['method'] === 'add'
&& $target['AdGroupId'] === 555
&& $target['RetargetingListId'] === 58034825
&& ! array_key_exists('ContextBid', $target);
});
});
it('adds a cpm banner ad by creative id', function () {
Http::fake([
'*/json/v5/ads' => Http::response(['result' => ['AddResults' => [['Id' => 111]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$id = $client->addCpmBannerAd(555, 4242, 'https://liderra.ru/promo');
expect($id)->toBe(111);
Http::assertSent(function ($req) {
$body = $req->data();
$ad = $body['params']['Ads'][0];
return $body['method'] === 'add'
&& $ad['AdGroupId'] === 555
&& $ad['CpmBannerAdBuilderAd']['Creative']['CreativeId'] === 4242
&& $ad['CpmBannerAdBuilderAd']['Href'] === 'https://liderra.ru/promo';
});
});
it('parses a creative preview into an array with is_adaptive true when YES', function () {
Http::fake([
'*/json/v5/creatives' => Http::response(['result' => ['Creatives' => [[
'Id' => 4242,
'Type' => 'HTML5_CREATIVE',
'PreviewUrl' => 'https://yastatic.net/preview/4242',
'ThumbnailUrl' => 'https://yastatic.net/thumb/4242',
'IsAdaptive' => 'YES',
'Width' => 300,
'Height' => 250,
]]]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$preview = $client->getCreativePreview(4242);
expect($preview)->toBe([
'id' => 4242,
'type' => 'HTML5_CREATIVE',
'preview_url' => 'https://yastatic.net/preview/4242',
'thumbnail_url' => 'https://yastatic.net/thumb/4242',
'is_adaptive' => true,
'width' => 300,
'height' => 250,
]);
Http::assertSent(function ($req) {
$body = $req->data();
return $body['method'] === 'get'
&& $body['params']['SelectionCriteria']['Ids'] === [4242]
&& $body['params']['FieldNames'] === ['Id', 'Type', 'PreviewUrl', 'ThumbnailUrl', 'IsAdaptive', 'Width', 'Height'];
});
});
it('returns null from creative preview when the creative is not found', function () {
Http::fake([
'*/json/v5/creatives' => Http::response(['result' => ['Creatives' => []]], 200),
]);
$client = new YandexDirectClient('https://api-sandbox.direct.yandex.com', 'TESTTOKEN');
$preview = $client->getCreativePreview(9999);
expect($preview)->toBeNull();
});
it('lists image creative ids with their sizes and ignores video creatives', function () {
Http::fake([
'*/json/v5/creatives' => Http::response(['result' => ['Creatives' => [
['Id' => 1163441165, 'Type' => 'VIDEO_EXTENSION_CREATIVE', 'Width' => 1080, 'Height' => 1080],
['Id' => 1163586673, 'Type' => 'HTML5_CREATIVE', 'Width' => 300, 'Height' => 250],
['Id' => 1163586680, 'Type' => 'HTML5_CREATIVE', 'Width' => 728, 'Height' => 90],
]]]),
]);
$client = new YandexDirectClient('https://api.direct.yandex.com', 'T');
// Картиночные креативы Яндекс хранит типом HTML5_CREATIVE даже для загруженных JPG —
// проверено живым запросом 27.07.2026. Видео-креативы в набор попадать не должны.
expect($client->listImageCreativeIds())->toBe([
1163586673 => [300, 250],
1163586680 => [728, 90],
]);
});
it('returns an empty snapshot when the account has no creatives', function () {
Http::fake(['*/json/v5/creatives' => Http::response(['result' => []])]);
$client = new YandexDirectClient('https://api.direct.yandex.com', 'T');
expect($client->listImageCreativeIds())->toBe([]);
});