173 lines
6.3 KiB
PHP
173 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Events\AdvertisingStopped;
|
|
use App\Jobs\SyncCampaignModerationJob;
|
|
use App\Models\AdCampaign;
|
|
use App\Models\AdCampaignAd;
|
|
use App\Models\Tenant;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
// Джоб намеренно обходит running/pending-кампании ВСЕХ тенантов — без изоляции
|
|
// между кейсами кампания из одного теста осталась бы видна следующему.
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('модерация: REJECTED объявление помечает кампанию rejected (passthrough)', function () {
|
|
config([
|
|
'services.yandex_direct.enabled' => true,
|
|
'services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com',
|
|
'services.yandex_direct.token' => 'DIRTOKEN',
|
|
]);
|
|
Http::fake([
|
|
'*/json/v5/ads' => Http::response(['result' => ['Ads' => [
|
|
['Id' => 555, 'Status' => 'REJECTED', 'State' => 'OFF', 'StatusClarification' => 'Нет гарантий'],
|
|
]]], 200),
|
|
]);
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
$campaign = AdCampaign::create([
|
|
'tenant_id' => $tenant->id,
|
|
'name' => 'Кампания на модерации',
|
|
'status' => AdCampaign::STATUS_PENDING_MODERATION,
|
|
'yandex_campaign_id' => 222,
|
|
'weekly_budget_rub' => '500.00',
|
|
'audience_days' => 10,
|
|
'use_uploaded_list' => true,
|
|
]);
|
|
$ad = AdCampaignAd::create([
|
|
'tenant_id' => $tenant->id,
|
|
'campaign_id' => $campaign->id,
|
|
'title' => 'Заголовок',
|
|
'text' => 'Текст объявления',
|
|
'href' => 'https://example.test',
|
|
'yandex_ad_id' => 555,
|
|
'moderation_status' => 'MODERATION',
|
|
]);
|
|
|
|
app(SyncCampaignModerationJob::class)->handle();
|
|
|
|
$ad->refresh();
|
|
expect($ad->moderation_status)->toBe('REJECTED');
|
|
expect($ad->moderation_reason)->toBe('Нет гарантий');
|
|
|
|
$campaign->refresh();
|
|
expect($campaign->status)->toBe(AdCampaign::STATUS_REJECTED);
|
|
});
|
|
|
|
it('модерация: все ACCEPTED переводит кампанию в running (passthrough)', function () {
|
|
config([
|
|
'services.yandex_direct.enabled' => true,
|
|
'services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com',
|
|
'services.yandex_direct.token' => 'DIRTOKEN',
|
|
]);
|
|
Http::fake([
|
|
'*/json/v5/ads' => Http::response(['result' => ['Ads' => [
|
|
['Id' => 555, 'Status' => 'ACCEPTED', 'State' => 'ON', 'StatusClarification' => null],
|
|
]]], 200),
|
|
]);
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
$campaign = AdCampaign::create([
|
|
'tenant_id' => $tenant->id,
|
|
'name' => 'Кампания на модерации 2',
|
|
'status' => AdCampaign::STATUS_PENDING_MODERATION,
|
|
'yandex_campaign_id' => 223,
|
|
'weekly_budget_rub' => '500.00',
|
|
'audience_days' => 10,
|
|
'use_uploaded_list' => true,
|
|
]);
|
|
$ad = AdCampaignAd::create([
|
|
'tenant_id' => $tenant->id,
|
|
'campaign_id' => $campaign->id,
|
|
'title' => 'Заголовок',
|
|
'text' => 'Текст объявления',
|
|
'href' => 'https://example.test',
|
|
'yandex_ad_id' => 555,
|
|
'moderation_status' => 'MODERATION',
|
|
]);
|
|
|
|
app(SyncCampaignModerationJob::class)->handle();
|
|
|
|
$ad->refresh();
|
|
expect($ad->moderation_status)->toBe('ACCEPTED');
|
|
|
|
$campaign->refresh();
|
|
expect($campaign->status)->toBe(AdCampaign::STATUS_RUNNING);
|
|
});
|
|
|
|
it('AdvertisingStopped ставит кампанию тенанта на паузу через реальный слушатель', function () {
|
|
config([
|
|
'services.yandex_direct.enabled' => true,
|
|
'services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com',
|
|
'services.yandex_direct.token' => 'DIRTOKEN',
|
|
]);
|
|
Http::fake([
|
|
'*/json/v5/campaigns' => Http::response(['result' => []], 200),
|
|
]);
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
$campaign = AdCampaign::create([
|
|
'tenant_id' => $tenant->id,
|
|
'name' => 'Кампания в работе',
|
|
'status' => AdCampaign::STATUS_RUNNING,
|
|
'yandex_campaign_id' => 224,
|
|
'weekly_budget_rub' => '500.00',
|
|
'audience_days' => 10,
|
|
'use_uploaded_list' => true,
|
|
]);
|
|
|
|
// Без Event::fake — доказываем, что слушатель реально ЗАРЕГИСТРИРОВАН и сработал.
|
|
event(new AdvertisingStopped($tenant->id));
|
|
|
|
$campaign->refresh();
|
|
expect($campaign->status)->toBe(AdCampaign::STATUS_STOPPED_NO_FUNDS);
|
|
|
|
Http::assertSent(function ($request) {
|
|
return str_contains($request->url(), '/json/v5/campaigns')
|
|
&& ($request->data()['method'] ?? null) === 'suspend';
|
|
});
|
|
});
|
|
|
|
it('AdvertisingStopped не трогает кампанию чужого тенанта (явный tenant-фильтр)', function () {
|
|
config([
|
|
'services.yandex_direct.enabled' => true,
|
|
'services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com',
|
|
'services.yandex_direct.token' => 'DIRTOKEN',
|
|
]);
|
|
Http::fake([
|
|
'*/json/v5/campaigns' => Http::response(['result' => []], 200),
|
|
]);
|
|
|
|
$tenantA = Tenant::factory()->create();
|
|
$tenantB = Tenant::factory()->create();
|
|
|
|
$campaignA = AdCampaign::create([
|
|
'tenant_id' => $tenantA->id,
|
|
'name' => 'Кампания тенанта A',
|
|
'status' => AdCampaign::STATUS_RUNNING,
|
|
'yandex_campaign_id' => 225,
|
|
'weekly_budget_rub' => '500.00',
|
|
'audience_days' => 10,
|
|
'use_uploaded_list' => true,
|
|
]);
|
|
$campaignB = AdCampaign::create([
|
|
'tenant_id' => $tenantB->id,
|
|
'name' => 'Кампания тенанта B',
|
|
'status' => AdCampaign::STATUS_RUNNING,
|
|
'yandex_campaign_id' => 226,
|
|
'weekly_budget_rub' => '500.00',
|
|
'audience_days' => 10,
|
|
'use_uploaded_list' => true,
|
|
]);
|
|
|
|
event(new AdvertisingStopped($tenantA->id));
|
|
|
|
$campaignA->refresh();
|
|
$campaignB->refresh();
|
|
|
|
expect($campaignA->status)->toBe(AdCampaign::STATUS_STOPPED_NO_FUNDS);
|
|
expect($campaignB->status)->toBe(AdCampaign::STATUS_RUNNING);
|
|
});
|