create(). */ beforeEach(function () { $this->tenant = Tenant::factory()->create(); $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); $this->actingAs($this->user); }); it('returns 401 for pause without auth', function () { auth()->logout(); $this->postJson('/api/advertising/campaigns/1/pause')->assertStatus(401); }); it('returns 401 for resume without auth', function () { auth()->logout(); $this->postJson('/api/advertising/campaigns/1/resume')->assertStatus(401); }); it('pauses a running campaign without calling Direct when disabled', function () { config(['services.yandex_direct.enabled' => false]); $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Кампания на паузу', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_RUNNING, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/pause"); $response->assertOk()->assertJsonPath('status', 'paused'); $this->assertDatabaseHas('ad_campaigns', [ 'id' => $campaign->id, 'status' => AdCampaign::STATUS_PAUSED, ]); }); it('pauses a pending_moderation campaign and calls Direct suspend when enabled', function () { config(['services.yandex_direct.enabled' => true]); config(['services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com']); config(['services.yandex_direct.token' => 'DIRTOKEN']); Http::fake(['*/json/v5/campaigns' => Http::response(['result' => ['SuspendResults' => [['Id' => 555]]]])]); $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Кампания на модерации', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_PENDING_MODERATION, 'yandex_campaign_id' => 555, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/pause"); $response->assertOk()->assertJsonPath('status', 'paused'); Http::assertSent(function ($request) { return str_contains($request->url(), '/json/v5/campaigns') && ($request['method'] ?? null) === 'suspend' && ($request['params']['SelectionCriteria']['Ids'][0] ?? null) === 555; }); $this->assertDatabaseHas('ad_campaigns', [ 'id' => $campaign->id, 'status' => AdCampaign::STATUS_PAUSED, ]); }); it('rejects pausing a draft campaign with 409', function () { $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Черновик', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_DRAFT, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/pause"); $response->assertStatus(409) ->assertJsonPath('message', 'Кампанию нельзя поставить на паузу из текущего состояния.'); $campaign->refresh(); expect($campaign->status)->toBe(AdCampaign::STATUS_DRAFT); }); it('returns 404 pausing another tenant campaign', function () { $tenantB = Tenant::factory()->create(); $campaignB = AdCampaign::create([ 'tenant_id' => $tenantB->id, 'name' => 'Чужая кампания', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_RUNNING, ]); $this->postJson("/api/advertising/campaigns/{$campaignB->id}/pause")->assertStatus(404); }); it('resumes a paused campaign and calls Direct resume when enabled', function () { config(['services.yandex_direct.enabled' => true]); config(['services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com']); config(['services.yandex_direct.token' => 'DIRTOKEN']); Http::fake(['*/json/v5/campaigns' => Http::response(['result' => ['ResumeResults' => [['Id' => 777]]]])]); $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Кампания на паузе', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_PAUSED, 'yandex_campaign_id' => 777, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/resume"); $response->assertOk()->assertJsonPath('status', 'running'); Http::assertSent(function ($request) { return str_contains($request->url(), '/json/v5/campaigns') && ($request['method'] ?? null) === 'resume' && ($request['params']['SelectionCriteria']['Ids'][0] ?? null) === 777; }); $this->assertDatabaseHas('ad_campaigns', [ 'id' => $campaign->id, 'status' => AdCampaign::STATUS_RUNNING, ]); }); it('resumes a paused campaign without calling Direct when disabled', function () { config(['services.yandex_direct.enabled' => false]); $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Кампания без Директа', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_PAUSED, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/resume"); $response->assertOk()->assertJsonPath('status', 'running'); }); it('rejects resuming a running campaign with 409', function () { $campaign = AdCampaign::create([ 'tenant_id' => $this->tenant->id, 'name' => 'Уже идёт', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_RUNNING, ]); $response = $this->postJson("/api/advertising/campaigns/{$campaign->id}/resume"); $response->assertStatus(409) ->assertJsonPath('message', 'Возобновить можно только кампанию на паузе.'); $campaign->refresh(); expect($campaign->status)->toBe(AdCampaign::STATUS_RUNNING); }); it('returns 404 resuming another tenant campaign', function () { $tenantB = Tenant::factory()->create(); $campaignB = AdCampaign::create([ 'tenant_id' => $tenantB->id, 'name' => 'Чужая кампания на паузе', 'audience_days' => 10, 'weekly_budget_rub' => '1000.00', 'status' => AdCampaign::STATUS_PAUSED, ]); $this->postJson("/api/advertising/campaigns/{$campaignB->id}/resume")->assertStatus(404); });