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/ai.naniguide.com/tests/Unit/SendingServers/Webhooks/BlastengineParseWebhookTest.php
<?php

use App\Model\BounceLog;
use App\Model\SendingServer;
use App\SendingServers\Drivers\Vendors\Blastengine\BlastengineApiDriver;
use App\SendingServers\Webhooks\BounceReceived;
use App\SendingServers\Webhooks\BounceType;
use App\SendingServers\Webhooks\IgnorableWebhookEvent;
use Illuminate\Http\Request;
use Tests\TestCase;

uses(TestCase::class);

/**
 * Wave D.4 parity: Blastengine delegates to BounceLog::recordHardBounce()
 * which has fallback lookup behaviors not in the standard listener path.
 *
 * Driver yields BounceReceived with `useLegacyRecordHardBounce: true` so
 * the listener calls the static helper directly — preserves byte-for-byte
 * parity (customer_id, fallback message_id lookup).
 */

function makeBlastengineDriver(): BlastengineApiDriver
{
    $server = new SendingServer(['type' => 'blastengine-api', 'name' => 'fixture']);
    $server->uid = 'fixture-uid';
    return new BlastengineApiDriver($server);
}

function beReq(array $body): Request
{
    return Request::create('/webhook/blastengine-api/fixture-uid', 'POST', [], [], [], [
        'CONTENT_TYPE' => 'application/json',
    ], json_encode($body));
}

test('blastengine HARDERROR → BounceReceived with useLegacyRecordHardBounce flag', function () {
    $driver = makeBlastengineDriver();
    $events = iterator_to_array($driver->parseWebhook(beReq([
        'events' => [
            ['event' => ['type' => 'HARDERROR', 'detail' => ['delivery_id' => 42]]],
        ],
    ])));

    expect($events)->toHaveCount(1)
        ->and($events[0])->toBeInstanceOf(BounceReceived::class)
        ->and($events[0]->runtimeMessageId)->toBe('42')
        ->and($events[0]->type)->toBe(BounceType::HARD)
        ->and($events[0]->bounceTypeRaw)->toBe(BounceLog::HARD)
        ->and($events[0]->shouldBlacklist)->toBeTrue()
        ->and($events[0]->useLegacyRecordHardBounce)->toBeTrue();    // PARITY: delegates to recordHardBounce
});

test('blastengine DROP → BounceReceived (same as HARDERROR)', function () {
    $driver = makeBlastengineDriver();
    $events = iterator_to_array($driver->parseWebhook(beReq([
        'events' => [
            ['event' => ['type' => 'DROP', 'detail' => ['delivery_id' => 7]]],
        ],
    ])));

    expect($events[0])->toBeInstanceOf(BounceReceived::class)
        ->and($events[0]->useLegacyRecordHardBounce)->toBeTrue();
});

test('blastengine SOFTERROR → IgnorableWebhookEvent (parity skip)', function () {
    $driver = makeBlastengineDriver();
    $events = iterator_to_array($driver->parseWebhook(beReq([
        'events' => [
            ['event' => ['type' => 'SOFTERROR', 'detail' => ['delivery_id' => 99]]],
        ],
    ])));

    expect($events[0])->toBeInstanceOf(IgnorableWebhookEvent::class);
});

test('blastengine missing delivery_id → silent skip', function () {
    $driver = makeBlastengineDriver();
    $events = iterator_to_array($driver->parseWebhook(beReq([
        'events' => [
            ['event' => ['type' => 'HARDERROR', 'detail' => []]],
        ],
    ])));

    expect($events)->toBeEmpty();
});

test('blastengine rawForDb is full body (preserve recordHardBounce parity)', function () {
    $driver = makeBlastengineDriver();
    $body = [
        'events' => [
            ['event' => ['type' => 'HARDERROR', 'detail' => ['delivery_id' => 42]]],
        ],
    ];
    $events = iterator_to_array($driver->parseWebhook(beReq($body)));

    // PARITY: legacy passed `$raw = file_get_contents('php://input')` to
    // recordHardBounce, NOT json_encode of just one event.
    expect($events[0]->rawForDb)->toBe(json_encode($body));
});