HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/be.naniguide.com/tests/Feature/AdsR5_1WizardUiTest.php
<?php

/**
 * Ads Phase R5.1 — 6-step campaign wizard UI.
 *
 * R5 added the tenant-scoped FormRequest fields (ad_platform_uids[],
 * ad_creative_uid, ad_audience_uid).  R5.1 surfaces them as wizard steps
 * and wires the service to persist them.
 *
 * Verifies:
 *   1. Migration applied — ad_audience_id column on ad_campaigns.
 *   2. AdCampaign casts + relations include ad_audience_id + audience().
 *   3. AdCampaignController::create passes platforms + creatives +
 *      audiences (tenant-scoped, active platforms only).
 *   4. AdCampaignService::create resolves ad_creative_uid → ad_creative_id.
 *   5. AdCampaignService::create resolves ad_audience_uid → ad_audience_id.
 *   6. AdCampaignService::create attaches ad_platform_uids[] via the
 *      belongsToMany pivot, scoped to current customer + active status.
 *   7. AdCampaignService::create with empty/missing wizard fields keeps
 *      the legacy create flow working (backward compat).
 *   8. Wizard view markup carries all 6 steps + R5.1 lang keys.
 */

use App\Model\AdAudience;
use App\Model\AdCampaign;
use App\Model\AdCreative;
use App\Model\AdPlatform;
use App\Model\Customer;
use App\Services\Ads\AdCampaignService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Schema;

uses(Tests\TestCase::class);
uses(DatabaseTransactions::class);

beforeEach(function () {
    $this->customer = Customer::whereHas('user')->orderBy('id')->first();
    if (! $this->customer) {
        $this->markTestSkipped('No seeded customer with user — run DemoSeeder first');
    }
    $this->user = $this->customer->user;
    $this->customerId = $this->customer->local()->id;
});

function r51Platform(int $customerId, string $platformKey = 'meta', string $name = 'R5.1 Platform', string $status = AdPlatform::STATUS_ACTIVE): AdPlatform
{
    $platform = new AdPlatform([
        'uid' => 'r51plat_' . uniqid(),
        'customer_id' => $customerId,
        'platform' => $platformKey,
        'name' => $name,
        'access_token' => 'r51tok_' . uniqid(),
        'platform_user_id' => 'p_' . uniqid(),
        'platform_user_name' => 'r51user',
    ]);
    $platform->status = $status;
    $platform->health_status = AdPlatform::HEALTH_HEALTHY;
    $platform->save();
    return $platform;
}

// ---------------------------------------------------------------------------
// 1-2. Migration + model
// ---------------------------------------------------------------------------

test('R5.1 migration adds ad_audience_id column to ad_campaigns', function () {
    expect(Schema::hasColumn('ad_campaigns', 'ad_audience_id'))->toBeTrue();
});

test('AdCampaign fillable carries ad_audience_id + audience() relation', function () {
    $campaign = new AdCampaign();
    expect($campaign->getFillable())->toContain('ad_audience_id');
    expect(method_exists($campaign, 'audience'))->toBeTrue();
});

// ---------------------------------------------------------------------------
// 3. Controller passes wizard data
// ---------------------------------------------------------------------------

test('create() passes platforms (active) + creatives + audiences to view', function () {
    r51Platform($this->customerId, 'meta', 'R5.1 Active Meta', AdPlatform::STATUS_ACTIVE);
    r51Platform($this->customerId, 'google', 'R5.1 Stale Google', AdPlatform::STATUS_REVOKED);

    AdCreative::create([
        'uid' => 'r51cr_' . uniqid(),
        'customer_id' => $this->customerId,
        'name' => 'R5.1 Test Creative',
        'type' => 'image',
        'status' => 'active',
    ]);

    AdAudience::create([
        'uid' => 'r51aud_' . uniqid(),
        'customer_id' => $this->customerId,
        'name' => 'R5.1 Test Audience',
        'type' => 'custom',
        'status' => 'active',
    ]);

    $response = $this->actingAs($this->user)->get(route('refactor.ads.campaigns.create'));

    $response->assertOk();
    $response->assertSee('R5.1 Active Meta');
    $response->assertDontSee('R5.1 Stale Google');
    $response->assertSee('R5.1 Test Creative');
    $response->assertSee('R5.1 Test Audience');
});

// ---------------------------------------------------------------------------
// 4-6. Service::create wires wizard fields to the model
// ---------------------------------------------------------------------------

test('Service::create resolves ad_creative_uid to ad_creative_id', function () {
    $creative = AdCreative::create([
        'uid' => 'r51cr_' . uniqid(),
        'customer_id' => $this->customerId,
        'name' => 'Wizard Creative',
        'type' => 'image',
        'status' => 'active',
    ]);

    $service = app(AdCampaignService::class);
    $campaign = $service->create($this->customer->local(), [
        'name' => 'R5.1 Test Campaign',
        'objective' => 'traffic',
        'ad_creative_uid' => $creative->uid,
    ]);

    expect($campaign->ad_creative_id)->toBe($creative->id);
});

test('Service::create resolves ad_audience_uid to ad_audience_id', function () {
    $audience = AdAudience::create([
        'uid' => 'r51aud_' . uniqid(),
        'customer_id' => $this->customerId,
        'name' => 'Wizard Audience',
        'type' => 'custom',
        'status' => 'active',
    ]);

    $service = app(AdCampaignService::class);
    $campaign = $service->create($this->customer->local(), [
        'name' => 'R5.1 Audience Campaign',
        'objective' => 'leads',
        'ad_audience_uid' => $audience->uid,
    ]);

    expect($campaign->ad_audience_id)->toBe($audience->id);
});

test('Service::create attaches ad_platform_uids via belongsToMany pivot', function () {
    $p1 = r51Platform($this->customerId, 'meta', 'R5.1 Pivot Meta');
    $p2 = r51Platform($this->customerId, 'google', 'R5.1 Pivot Google');

    $service = app(AdCampaignService::class);
    $campaign = $service->create($this->customer->local(), [
        'name' => 'R5.1 Multi-Platform',
        'objective' => 'sales',
        'ad_platform_uids' => [$p1->uid, $p2->uid],
    ]);

    $platformIds = $campaign->platforms()->pluck('ad_platforms.id')->all();
    expect($platformIds)->toContain($p1->id);
    expect($platformIds)->toContain($p2->id);
});

test('Service::create skips inactive platforms even if uid is in the array', function () {
    $active = r51Platform($this->customerId, 'meta', 'Active', AdPlatform::STATUS_ACTIVE);
    $inactive = r51Platform($this->customerId, 'google', 'Revoked', AdPlatform::STATUS_REVOKED);

    $service = app(AdCampaignService::class);
    $campaign = $service->create($this->customer->local(), [
        'name' => 'R5.1 Skip Inactive',
        'objective' => 'awareness',
        'ad_platform_uids' => [$active->uid, $inactive->uid],
    ]);

    $platformIds = $campaign->platforms()->pluck('ad_platforms.id')->all();
    expect($platformIds)->toContain($active->id);
    expect($platformIds)->not->toContain($inactive->id);
});

// ---------------------------------------------------------------------------
// 7. Backward compat — legacy create flow still works
// ---------------------------------------------------------------------------

test('Service::create with no wizard fields keeps the legacy 4-step flow working', function () {
    $service = app(AdCampaignService::class);
    $campaign = $service->create($this->customer->local(), [
        'name' => 'Legacy Campaign',
        'objective' => 'traffic',
        'daily_budget' => '100',
    ]);

    expect($campaign)->toBeInstanceOf(AdCampaign::class);
    expect($campaign->ad_creative_id)->toBeNull();
    expect($campaign->ad_audience_id)->toBeNull();
    expect($campaign->platforms()->count())->toBe(0);
});

// ---------------------------------------------------------------------------
// 8. Wizard view markup
// ---------------------------------------------------------------------------

test('Wizard view markup carries all 6 steps + new wizard step labels', function () {
    $response = $this->actingAs($this->user)->get(route('refactor.ads.campaigns.create'));

    $response->assertOk();
    // 6 step indicators
    $response->assertSee('data-step="1"', false);
    $response->assertSee('data-step="2"', false);
    $response->assertSee('data-step="3"', false);
    $response->assertSee('data-step="4"', false);
    $response->assertSee('data-step="5"', false);
    $response->assertSee('data-step="6"', false);
    // 6 panels
    $response->assertSee('data-panel="6"', false);
    // R5.1 lang keys rendered
    $response->assertSee('Platforms');
    $response->assertSee('Audience');
    $response->assertSee('Creative');
    $response->assertSee('Review');
});