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/Unit/Sending/Dispatch/CampaignDeliveryDispatcherTest.php
<?php

/**
 * CampaignDeliveryDispatcherTest — verifies the per-page job builder constructs
 * one SendMessage per subscriber, propagates stopOnError + variant, and feeds
 * each built job to the caller-supplied closure.
 *
 * Mocks Campaign + Subscriber + RouletteWheel + Subscription via Mockery so the
 * dispatcher logic is testable without DB factories. The query chain
 * `subscribersToSend()->whereIn(...)->get()` is stubbed to return an Eloquent
 * Collection of fake Subscribers.
 */

use App\Jobs\SendMessage;
use App\Library\RouletteWheel;
use App\Library\Sending\Dispatch\CampaignDeliveryDispatcher;
use App\Model\AbTestVariant;
use App\Model\Campaign;
use App\Model\Customer;
use App\Model\Subscriber;
use App\Model\Subscription;
use Illuminate\Database\Eloquent\Collection;
use Tests\TestCase;

uses(TestCase::class);

function makeDispatcherCampaign(array $subscribers, bool $stopOnError = false, ?Subscription $subscription = null): Campaign
{
    $logger = Mockery::mock();
    $logger->shouldReceive('info')->byDefault();

    $serverPool = Mockery::mock(RouletteWheel::class);

    $customer = Mockery::mock(Customer::class);
    $customer->shouldReceive('getCurrentActiveSubscription')->andReturn($subscription)->byDefault();

    $whereInBuilder = Mockery::mock();
    $whereInBuilder->shouldReceive('get')->andReturn(new Collection($subscribers));

    $subscribersBuilder = Mockery::mock();
    $subscribersBuilder->shouldReceive('whereIn')->with('subscribers.id', Mockery::any())->andReturn($whereInBuilder);

    $campaign = Mockery::mock(Campaign::class);
    $campaign->shouldReceive('subscribersToSend')->andReturn($subscribersBuilder);
    $campaign->shouldReceive('getServersPool')->andReturn($serverPool);
    $campaign->shouldReceive('logger')->andReturn($logger);
    $campaign->shouldReceive('stopOnError')->andReturn($stopOnError);
    $campaign->shouldReceive('getAttribute')->with('customer')->andReturn($customer);

    return $campaign;
}

function makeFakeSubscriber(int $id): Subscriber
{
    $subscriber = Mockery::mock(Subscriber::class);
    $subscriber->shouldReceive('getAttribute')->with('id')->andReturn($id);

    return $subscriber;
}

it('builds one SendMessage per subscriber and feeds each to the closure', function () {
    $subs = [makeFakeSubscriber(1), makeFakeSubscriber(2), makeFakeSubscriber(3)];
    $campaign = makeDispatcherCampaign($subs);

    $built = [];
    (new CampaignDeliveryDispatcher())->buildDeliveryJobs(
        $campaign,
        page: 1,
        subscriberIds: [1, 2, 3],
        variant: null,
        onJob: function (SendMessage $job) use (&$built) {
            $built[] = $job;
        },
    );

    expect($built)->toHaveCount(3);
    foreach ($built as $job) {
        expect($job)->toBeInstanceOf(SendMessage::class);
    }
});

it('passes stopOnError flag from the campaign onto each job', function () {
    $campaign = makeDispatcherCampaign([makeFakeSubscriber(1)], stopOnError: true);

    $captured = null;
    (new CampaignDeliveryDispatcher())->buildDeliveryJobs(
        $campaign,
        1,
        [1],
        null,
        function (SendMessage $job) use (&$captured) {
            $captured = $job;
        },
    );

    // Inspect the protected $stopOnError via Reflection — it's set via setStopOnError(true).
    $ref = new ReflectionClass(SendMessage::class);
    $prop = $ref->getProperty('stopOnError');
    $prop->setAccessible(true);
    expect($prop->getValue($captured))->toBeTrue();
});

it('passes the variant through to the SendMessage constructor', function () {
    $variant = Mockery::mock(AbTestVariant::class);
    $campaign = makeDispatcherCampaign([makeFakeSubscriber(1)]);

    $captured = null;
    (new CampaignDeliveryDispatcher())->buildDeliveryJobs(
        $campaign,
        1,
        [1],
        $variant,
        function (SendMessage $job) use (&$captured) {
            $captured = $job;
        },
    );

    $ref = new ReflectionClass(SendMessage::class);
    $prop = $ref->getProperty('variant');
    $prop->setAccessible(true);
    expect($prop->getValue($captured))->toBe($variant);
});

it('logs "no contacts" when the page resolves to zero subscribers', function () {
    $logger = Mockery::mock();
    $logger->shouldReceive('info')->withArgs(function ($msg) {
        return str_contains($msg, 'no contacts in this page');
    })->once();

    $whereInBuilder = Mockery::mock();
    $whereInBuilder->shouldReceive('get')->andReturn(new Collection([]));

    $subscribersBuilder = Mockery::mock();
    $subscribersBuilder->shouldReceive('whereIn')->andReturn($whereInBuilder);

    $campaign = Mockery::mock(Campaign::class);
    $campaign->shouldReceive('subscribersToSend')->andReturn($subscribersBuilder);
    $campaign->shouldReceive('getServersPool')->andReturn(Mockery::mock(RouletteWheel::class));
    $campaign->shouldReceive('logger')->andReturn($logger);
    $campaign->shouldReceive('stopOnError')->andReturn(false);

    $called = 0;
    (new CampaignDeliveryDispatcher())->buildDeliveryJobs(
        $campaign,
        7,
        [99, 100],
        null,
        function () use (&$called) { $called++; },
    );

    expect($called)->toBe(0);
});

it('logs the first/last subscriber id range for non-empty pages', function () {
    $subs = [makeFakeSubscriber(101), makeFakeSubscriber(102), makeFakeSubscriber(103)];

    $logger = Mockery::mock();
    $logger->shouldReceive('info')->withArgs(function ($msg) {
        return str_contains($msg, 'Page 5')
            && str_contains($msg, 'from guy 101')
            && str_contains($msg, 'to 103');
    })->once();

    $whereInBuilder = Mockery::mock();
    $whereInBuilder->shouldReceive('get')->andReturn(new Collection($subs));

    $subscribersBuilder = Mockery::mock();
    $subscribersBuilder->shouldReceive('whereIn')->andReturn($whereInBuilder);

    $customer = Mockery::mock(Customer::class);
    $customer->shouldReceive('getCurrentActiveSubscription')->andReturn(null);

    $campaign = Mockery::mock(Campaign::class);
    $campaign->shouldReceive('subscribersToSend')->andReturn($subscribersBuilder);
    $campaign->shouldReceive('getServersPool')->andReturn(Mockery::mock(RouletteWheel::class));
    $campaign->shouldReceive('logger')->andReturn($logger);
    $campaign->shouldReceive('stopOnError')->andReturn(false);
    $campaign->shouldReceive('getAttribute')->with('customer')->andReturn($customer);

    (new CampaignDeliveryDispatcher())->buildDeliveryJobs(
        $campaign,
        5,
        [101, 102, 103],
        null,
        fn () => null,
    );
});