Files
portal/app/tests/Feature/Advertising/AdvertisingCampaignDeleteTest.php
T
Дмитрий cd77d32fc3 feat(реклама): удаление черновика кампании (T15) и загрузка своего списка номеров (T17)
DELETE /api/advertising/campaigns/{id} — удаляет кампанию только в статусе
draft своего тенанта (409 для запущенных/на паузе/др., 404 для чужого
тенанта); дочерние объявления и телефоны уходят каскадом на уровне схемы.

POST /api/advertising/campaigns/{id}/phones — принимает текстовое поле
или csv/txt-файл, номера построчно/через запятую нормализует через
PhoneNormalizer, невалидные отбрасывает, дубли схлопывает, пишет через
insertOrIgnore (unique tenant_id+campaign_id+phone), отвечает
{recognized, skipped}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-25 21:30:36 +03:00

105 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\AdCampaign;
use App\Models\AdCampaignAd;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Support\Facades\DB;
/**
* T15 — удаление черновика кампании: DELETE /api/advertising/campaigns/{id}.
*
* Мирроим auth/tenant setup из AdvertisingCampaignEndpointTest.php — без RefreshDatabase,
* каждый тест создаёт свой Tenant::factory()->create().
*/
beforeEach(function () {
$this->tenant = Tenant::factory()->create();
$this->user = User::factory()->create(['tenant_id' => $this->tenant->id]);
$this->actingAs($this->user);
});
it('deletes a draft campaign of own tenant', function () {
$campaign = AdCampaign::create([
'tenant_id' => $this->tenant->id,
'name' => 'Черновик на удаление',
'audience_days' => 10,
'weekly_budget_rub' => '1000.00',
]);
$response = $this->deleteJson("/api/advertising/campaigns/{$campaign->id}");
$response->assertStatus(204);
$this->assertDatabaseMissing('ad_campaigns', ['id' => $campaign->id]);
});
it('refuses to delete a running campaign', function () {
$campaign = AdCampaign::create([
'tenant_id' => $this->tenant->id,
'name' => 'Запущенная кампания',
'status' => AdCampaign::STATUS_RUNNING,
'audience_days' => 10,
'weekly_budget_rub' => '1000.00',
]);
$response = $this->deleteJson("/api/advertising/campaigns/{$campaign->id}");
$response->assertStatus(409);
$this->assertDatabaseHas('ad_campaigns', ['id' => $campaign->id]);
});
it('returns 404 for another tenants campaign and does not delete it', function () {
$tenantB = Tenant::factory()->create();
$campaignB = AdCampaign::create([
'tenant_id' => $tenantB->id,
'name' => 'Чужой черновик',
'audience_days' => 10,
'weekly_budget_rub' => '1000.00',
]);
$response = $this->deleteJson("/api/advertising/campaigns/{$campaignB->id}");
$response->assertStatus(404);
$this->assertDatabaseHas('ad_campaigns', ['id' => $campaignB->id]);
});
it('returns 404 for a non-existent campaign', function () {
$response = $this->deleteJson('/api/advertising/campaigns/999999999');
$response->assertStatus(404);
});
it('cascades delete to related ads and phones', function () {
$campaign = AdCampaign::create([
'tenant_id' => $this->tenant->id,
'name' => 'Черновик с детьми',
'audience_days' => 10,
'weekly_budget_rub' => '1000.00',
]);
$ad = AdCampaignAd::create([
'tenant_id' => $this->tenant->id,
'campaign_id' => $campaign->id,
'title' => 'Заголовок',
'text' => 'Текст объявления',
'href' => 'https://liderra.ru',
'moderation_status' => 'draft',
]);
DB::table('ad_campaign_phones')->insert([
'tenant_id' => $campaign->tenant_id,
'campaign_id' => $campaign->id,
'phone' => '79000000001',
'expires_at' => null,
'created_at' => now(),
'updated_at' => now(),
]);
$response = $this->deleteJson("/api/advertising/campaigns/{$campaign->id}");
$response->assertStatus(204);
$this->assertDatabaseMissing('ad_campaign_ads', ['id' => $ad->id]);
$this->assertDatabaseMissing('ad_campaign_phones', ['campaign_id' => $campaign->id]);
});