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/DriverCapabilityMatrixTest.php
<?php

use App\Model\SendingServer;
use App\SendingServers\Capabilities\AllowsCrossSendingDomain;
use App\SendingServers\Capabilities\AllowsLocalDomainVerify;
use App\SendingServers\Capabilities\AllowsLocalEmailVerify;
use App\SendingServers\Capabilities\ReceivesWebhooks;
use App\SendingServers\Capabilities\SendsCustomVerificationEmail;
use App\SendingServers\Capabilities\SignsDkimOnServer;
use App\SendingServers\Capabilities\SupportsCustomReturnPath;
use App\SendingServers\Capabilities\SupportsIdentitySync;
use App\SendingServers\Capabilities\SupportsRemoteDomainVerify;
use App\SendingServers\DriverRegistry;
use App\SendingServers\Drivers\SendingDriver;
use Tests\TestCase;

uses(TestCase::class);

/**
 * Capability matrix — every built-in driver must implement exactly the set of
 * `App\SendingServers\Capabilities\*` interfaces matching the legacy boolean
 * flag table. Regression catch: if a driver forgets to implement a capability
 * that legacy code exposed, call sites guarded by `instanceof <Capability>`
 * silently skip vendor logic — bugs only surface in production.
 *
 * Each row asserts the FULL set; missing OR extra capability fails the test.
 */

/**
 * Build a driver instance from a fresh in-memory SendingServer (no DB needed).
 */
function makeDriverForType(string $type): SendingDriver
{
    $server = new SendingServer(['type' => $type, 'name' => 'fixture']);
    $server->uid = 'fixture-uid';
    return DriverRegistry::resolve($server);
}

/**
 * Per-vendor expected capability list. Source of truth: legacy boolean
 * methods on each STI subclass before the migration. See doc §F.4 + §2.3.
 */
dataset('driverCapabilities', [
    'sendgrid-api' => [
        'sendgrid-api',
        [ReceivesWebhooks::class, SupportsIdentitySync::class, SupportsRemoteDomainVerify::class],
        // NOT: AllowsLocalDomainVerify, AllowsLocalEmailVerify, AllowsCrossSendingDomain, SignsDkimOnServer
    ],
    'sendgrid-smtp' => [
        'sendgrid-smtp',
        [SupportsIdentitySync::class, SupportsRemoteDomainVerify::class],
    ],
    'mailgun-api' => [
        'mailgun-api',
        [ReceivesWebhooks::class, SupportsIdentitySync::class],
    ],
    'mailgun-smtp' => [
        'mailgun-smtp',
        [SupportsIdentitySync::class],
    ],
    'amazon-api' => [
        'amazon-api',
        [
            ReceivesWebhooks::class,
            SupportsIdentitySync::class,
            SupportsRemoteDomainVerify::class,
            SendsCustomVerificationEmail::class,
            SignsDkimOnServer::class,
        ],
    ],
    'amazon-smtp' => [
        'amazon-smtp',
        [
            ReceivesWebhooks::class,
            SupportsIdentitySync::class,
            SupportsRemoteDomainVerify::class,
            SendsCustomVerificationEmail::class,
            SignsDkimOnServer::class,
        ],
    ],
    'elasticemail-api' => [
        'elasticemail-api',
        [ReceivesWebhooks::class, SupportsIdentitySync::class, SupportsRemoteDomainVerify::class],
    ],
    'elasticemail-smtp' => [
        'elasticemail-smtp',
        [SupportsIdentitySync::class, SupportsRemoteDomainVerify::class],
    ],
    'sparkpost-api' => [
        'sparkpost-api',
        [ReceivesWebhooks::class, SupportsIdentitySync::class],
    ],
    'sparkpost-smtp' => [
        'sparkpost-smtp',
        [SupportsIdentitySync::class],
    ],
    'blastengine-api' => [
        'blastengine-api',
        [ReceivesWebhooks::class, AllowsLocalDomainVerify::class, AllowsCrossSendingDomain::class],
    ],
    'blastengine-smtp' => [
        'blastengine-smtp',
        [AllowsLocalDomainVerify::class, AllowsCrossSendingDomain::class],
    ],
    'smtp' => [
        'smtp',
        [
            AllowsLocalDomainVerify::class,
            AllowsLocalEmailVerify::class,
            AllowsCrossSendingDomain::class,
            SupportsCustomReturnPath::class,
        ],
    ],
    'sendmail' => [
        'sendmail',
        [
            AllowsLocalDomainVerify::class,
            AllowsLocalEmailVerify::class,
            AllowsCrossSendingDomain::class,
            SupportsCustomReturnPath::class,
        ],
    ],
    'gmail-oauth' => [
        'gmail-oauth',
        [AllowsLocalDomainVerify::class, AllowsLocalEmailVerify::class, AllowsCrossSendingDomain::class],
    ],
]);

test('driver implements expected capability set', function (string $type, array $expectedCaps) {
    $driver = makeDriverForType($type);

    // Every driver implements core SendingDriver.
    expect($driver)->toBeInstanceOf(SendingDriver::class);

    // Required capabilities present.
    foreach ($expectedCaps as $cap) {
        expect($driver)->toBeInstanceOf($cap);
    }

    // Capabilities NOT in the expected set must NOT be implemented (regression
    // catch: accidentally adding a capability would change call-site behavior).
    $allCaps = [
        ReceivesWebhooks::class,
        SupportsIdentitySync::class,
        SupportsRemoteDomainVerify::class,
        SupportsCustomReturnPath::class,
        SignsDkimOnServer::class,
        AllowsLocalDomainVerify::class,
        AllowsLocalEmailVerify::class,
        AllowsCrossSendingDomain::class,
        SendsCustomVerificationEmail::class,
    ];
    $unexpected = array_diff($allCaps, $expectedCaps);
    foreach ($unexpected as $cap) {
        expect($driver)->not->toBeInstanceOf($cap);
    }
})->with('driverCapabilities');

test('every registered driver type resolves to a SendingDriver', function () {
    $registered = array_keys(DriverRegistry::all());
    // 15 built-in + N plugin drivers — guard against shrinkage, allow growth.
    expect(count($registered))->toBeGreaterThanOrEqual(15);

    foreach ($registered as $type) {
        $server = new SendingServer(['type' => $type, 'name' => 'fixture']);
        $server->uid = 'fixture-' . $type;

        $driver = DriverRegistry::resolve($server);
        expect($driver)->toBeInstanceOf(SendingDriver::class);
        expect($driver->getServiceName())->toBeString()->not->toBeEmpty();
    }
});

test('driver getServiceName returns vendor-specific label', function () {
    expect(makeDriverForType('sendgrid-api')->getServiceName())->toBe('SendGrid');
    expect(makeDriverForType('sendgrid-smtp')->getServiceName())->toBe('SendGrid');
    expect(makeDriverForType('mailgun-api')->getServiceName())->toBe('Mailgun');
    expect(makeDriverForType('mailgun-smtp')->getServiceName())->toBe('Mailgun');
    expect(makeDriverForType('amazon-api')->getServiceName())->toBe('Amazon SES');
    expect(makeDriverForType('amazon-smtp')->getServiceName())->toBe('Amazon SES');
    expect(makeDriverForType('elasticemail-api')->getServiceName())->toBe('ElasticEmail');
    expect(makeDriverForType('elasticemail-smtp')->getServiceName())->toBe('ElasticEmail');
    expect(makeDriverForType('sparkpost-api')->getServiceName())->toBe('SparkPost');
    expect(makeDriverForType('sparkpost-smtp')->getServiceName())->toBe('SparkPost');
    expect(makeDriverForType('blastengine-api')->getServiceName())->toBe('Blastengine');
    expect(makeDriverForType('blastengine-smtp')->getServiceName())->toBe('Blastengine');
    expect(makeDriverForType('smtp')->getServiceName())->toBe('SMTP');
    expect(makeDriverForType('sendmail')->getServiceName())->toBe('Sendmail');
    expect(makeDriverForType('gmail-oauth')->getServiceName())->toBe('Gmail OAuth2');
});

test('webhookUrl per-instance routing builds /webhook/{type}/{uid}', function () {
    $server = new SendingServer(['type' => 'sendgrid-api', 'name' => 'fixture']);
    $server->uid = 'srv-abc-123';
    $driver = DriverRegistry::resolve($server);

    expect($driver)->toBeInstanceOf(ReceivesWebhooks::class);
    /** @var ReceivesWebhooks $driver */
    $url = $driver->webhookUrl();

    expect($url)->toContain('/webhook/sendgrid-api/srv-abc-123');
});

test('Sendmail driver test() returns failure when path missing', function () {
    $server = new SendingServer(['type' => 'sendmail', 'name' => 'fixture']);
    $server->setConfig('sendmail_path', '/this/path/does/not/exist/sendmail');

    $driver = DriverRegistry::resolve($server);
    $result = $driver->test();

    expect($result->ok)->toBeFalse();
    expect($result->error)->toContain('does not exist');
});

test('Sendmail driver test() returns failure when path empty', function () {
    $server = new SendingServer(['type' => 'sendmail', 'name' => 'fixture']);
    // No sendmail_path set.

    $driver = DriverRegistry::resolve($server);
    $result = $driver->test();

    expect($result->ok)->toBeFalse();
    expect($result->error)->toContain('not configured');
});

test('Blastengine driver test() always returns success (legacy parity)', function () {
    $driver = makeDriverForType('blastengine-api');
    expect($driver->test()->ok)->toBeTrue();
});

test('SendingServer::driver() memoizes the driver instance', function () {
    $server = new SendingServer(['type' => 'smtp', 'name' => 'fixture']);
    $server->uid = 'srv-mem-1';

    $a = $server->driver();
    $b = $server->driver();

    expect($a)->toBe($b);   // same object reference
});

test('SendingServer::driver() throws on unknown type', function () {
    $server = new SendingServer(['type' => 'nonexistent-vendor', 'name' => 'fixture']);
    $server->uid = 'srv-bad';

    expect(fn () => $server->driver())
        ->toThrow(\App\SendingServers\Exceptions\UnknownSendingServerType::class);
});