OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

MailTransportFactoryTest.php

9 KB
<?php

declare(strict_types=1);

use OpenNIT\Mail\MailTransportFactory;
use OpenNIT\Mail\Microsoft\GraphFetcher;
use OpenNIT\Mail\Microsoft\GraphMailer;
use OpenNIT\Mail\Microsoft\ImapOAuthFetcher;
use OpenNIT\Notifications\Mailer;
use OpenNIT\Security\CredentialCrypto;
use OpenNIT\Mail\Microsoft\TokenProvider;
use OpenNIT\Tickets\ImapFetcher;

return static function (): void {

    $appKey = 'test-app-key-0123456789abcdef'; // >= 16 Zeichen für CredentialCrypto

    // ---- Transportwahl Versand ----
    test('senderFromMailConfig: basic → Mailer', function () {
        $m = MailTransportFactory::senderFromMailConfig(['auth_method' => 'basic', 'host' => 'smtp.x']);
        assertSame(Mailer::class, get_class($m), 'basic muss SMTP-Mailer liefern');
    });

    test('senderFromMailConfig: graph → GraphMailer', function () {
        $m = MailTransportFactory::senderFromMailConfig([
            'auth_method' => 'graph',
            'oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 's', 'mailbox' => 'm@x'],
        ]);
        assertSame(GraphMailer::class, get_class($m));
    });

    test('senderFromMailConfig: xoauth2 → SMTP-Mailer (mit Token)', function () {
        $m = MailTransportFactory::senderFromMailConfig([
            'auth_method' => 'xoauth2',
            'oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 's', 'mailbox' => 'm@x'],
        ]);
        assertSame(Mailer::class, get_class($m), 'XOAUTH2 nutzt den SMTP-Mailer');
    });

    // ---- Transportwahl Empfang ----
    test('fetcherFromMailConfig: basic → ImapFetcher', function () {
        $f = MailTransportFactory::fetcherFromMailConfig(['auth_method' => 'basic', 'host' => 'imap.x']);
        assertSame(ImapFetcher::class, get_class($f));
    });

    test('fetcherFromMailConfig: graph → GraphFetcher', function () {
        $f = MailTransportFactory::fetcherFromMailConfig([
            'auth_method' => 'graph',
            'oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 's', 'mailbox' => 'm@x'],
        ]);
        assertSame(GraphFetcher::class, get_class($f));
    });

    test('fetcherFromMailConfig: xoauth2 → ImapOAuthFetcher', function () {
        $f = MailTransportFactory::fetcherFromMailConfig([
            'auth_method' => 'xoauth2',
            'oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 's', 'mailbox' => 'm@x'],
        ]);
        assertSame(ImapOAuthFetcher::class, get_class($f));
    });

    // ---- Leer-Guard (darf keine funktionierende Config "erfinden") ----
    test('ticketSenderConfig: ohne Konfiguration → leeres Array', function () use ($appKey) {
        assertSame([], MailTransportFactory::ticketSenderConfig([], $appKey));
    });

    test('globalSenderConfig: ohne Konfiguration → leeres Array', function () use ($appKey) {
        assertSame([], MailTransportFactory::globalSenderConfig([], $appKey));
    });

    // ---- Basic: Passwort wird entschlüsselt, Felder erhalten ----
    test('ticketSenderConfig: basic entschlüsselt Passwort & behält Host', function () use ($appKey) {
        $enc = CredentialCrypto::encrypt('geheim123', $appKey);
        $cfg = ['tickets_smtp' => ['host' => 'smtp.x', 'from_email' => 'a@x', 'password' => $enc]];
        $out = MailTransportFactory::ticketSenderConfig($cfg, $appKey);
        assertSame('basic', $out['auth_method'] ?? null);
        assertSame('smtp.x', $out['host'] ?? null);
        assertSame('geheim123', $out['password'] ?? null, 'Passwort muss entschlüsselt sein');
    });

    test('ticketSenderConfig: nur Haupt-SMTP als Fallback', function () use ($appKey) {
        $cfg = ['smtp' => ['host' => 'main.x', 'from_email' => 'sys@x']];
        $out = MailTransportFactory::ticketSenderConfig($cfg, $appKey);
        assertSame('main.x', $out['host'] ?? null, 'fällt auf Haupt-SMTP zurück');
    });

    // ---- OAuth: Secret entschlüsselt, from_email-Fallback auf Postfach ----
    test('ticketSenderConfig: graph entschlüsselt Secret & from_email-Fallback', function () use ($appKey) {
        $cfg = [
            'tickets_imap' => ['auth_method' => 'graph'],
            'tickets_smtp' => ['auth_method' => 'graph'],
            'tickets_oauth' => [
                'tenant_id' => 't', 'client_id' => 'c',
                'client_secret' => CredentialCrypto::encrypt('s3cret', $appKey),
                'mailbox' => 'tickets@x', 'flow' => 'client_credentials',
            ],
        ];
        $out = MailTransportFactory::ticketSenderConfig($cfg, $appKey);
        assertSame('graph', $out['auth_method'] ?? null);
        assertSame('s3cret', $out['oauth']['client_secret'] ?? null, 'Secret muss entschlüsselt sein');
        assertSame('tickets@x', $out['from_email'] ?? null, 'from_email fällt auf Postfach zurück');
    });

    // ---- Empfangs-Config + Post-Fetch ----
    test('ticketFetcherConfig: graph reicht Post-Fetch-Aktion durch', function () use ($appKey) {
        $cfg = [
            'tickets_imap' => ['auth_method' => 'graph', 'post_fetch_action' => 'trash', 'trash_mailbox' => 'Archiv'],
            'tickets_oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 'x', 'mailbox' => 'm@x'],
        ];
        $out = MailTransportFactory::ticketFetcherConfig($cfg, $appKey);
        assertSame('graph', $out['auth_method'] ?? null);
        assertSame('trash', $out['post_fetch_action'] ?? null);
        assertSame('Archiv', $out['trash_mailbox'] ?? null);
    });

    // ---- isFetchConfigured ----
    test('isFetchConfigured: basic braucht Host', function () {
        assertTrue(MailTransportFactory::isFetchConfigured(['tickets_imap' => ['host' => 'imap.x']]));
        assertFalse(MailTransportFactory::isFetchConfigured(['tickets_imap' => []]));
    });

    test('isFetchConfigured: graph braucht vollständige OAuth-Daten', function () {
        $complete = [
            'tickets_imap'  => ['auth_method' => 'graph'],
            'tickets_oauth' => ['tenant_id' => 't', 'client_id' => 'c', 'client_secret' => 's', 'mailbox' => 'm@x'],
        ];
        assertTrue(MailTransportFactory::isFetchConfigured($complete));

        $incomplete = [
            'tickets_imap'  => ['auth_method' => 'graph'],
            'tickets_oauth' => ['tenant_id' => 't', 'client_id' => 'c'], // Secret + Postfach fehlen
        ];
        assertFalse(MailTransportFactory::isFetchConfigured($incomplete));
    });

    // ---- Google Workspace Provider ----
    test('providerEndpoints: Google liefert Gmail-Hosts + Scope', function () {
        $ep = MailTransportFactory::providerEndpoints(MailTransportFactory::PROVIDER_GOOGLE);
        assertSame('imap.gmail.com', $ep['imap_host']);
        assertSame('smtp.gmail.com', $ep['smtp_host']);
        assertSame(TokenProvider::SCOPE_GOOGLE_MAIL, $ep['scope']);
    });

    test('providerEndpoints: Default (Microsoft) liefert Office365-Hosts', function () {
        $ep = MailTransportFactory::providerEndpoints(MailTransportFactory::PROVIDER_MICROSOFT);
        assertSame('outlook.office365.com', $ep['imap_host']);
        assertSame(TokenProvider::SCOPE_OUTLOOK, $ep['scope']);
    });

    test('fetcherFromMailConfig: google/xoauth2 → ImapOAuthFetcher', function () {
        $f = MailTransportFactory::fetcherFromMailConfig([
            'auth_method' => 'xoauth2',
            'oauth' => ['provider' => 'google', 'client_id' => 'c', 'client_secret' => 's',
                        'refresh_token' => 'r', 'mailbox' => 'me@firma.de'],
        ]);
        assertSame(ImapOAuthFetcher::class, get_class($f));
    });

    test('senderFromMailConfig: google/xoauth2 → SMTP-Mailer', function () {
        $m = MailTransportFactory::senderFromMailConfig([
            'auth_method' => 'xoauth2',
            'oauth' => ['provider' => 'google', 'client_id' => 'c', 'client_secret' => 's',
                        'refresh_token' => 'r', 'mailbox' => 'me@firma.de'],
        ]);
        assertSame(Mailer::class, get_class($m));
    });

    test('isFetchConfigured: google braucht Refresh-Token statt Tenant', function () {
        $complete = [
            'tickets_imap'  => ['auth_method' => 'xoauth2'],
            'tickets_oauth' => ['provider' => 'google', 'client_id' => 'c', 'client_secret' => 's',
                                'mailbox' => 'm@x', 'refresh_token' => 'r'],
        ];
        assertTrue(MailTransportFactory::isFetchConfigured($complete), 'mit Refresh-Token vollständig');

        $noRefresh = $complete;
        unset($noRefresh['tickets_oauth']['refresh_token']);
        assertFalse(MailTransportFactory::isFetchConfigured($noRefresh), 'ohne Refresh-Token unvollständig');
    });

    test('TokenProvider: google ist erst mit Refresh-Token konfiguriert', function () {
        $base = ['provider' => 'google', 'client_id' => 'c', 'client_secret' => 's'];
        assertFalse((new TokenProvider($base))->isConfigured(), 'ohne Refresh-Token nicht konfiguriert');
        assertTrue((new TokenProvider($base + ['refresh_token' => 'r']))->isConfigured());
    });

    test('TokenProvider: microsoft braucht weiterhin Tenant', function () {
        $noTenant = ['provider' => 'microsoft', 'client_id' => 'c', 'client_secret' => 's'];
        assertFalse((new TokenProvider($noTenant))->isConfigured());
        assertTrue((new TokenProvider($noTenant + ['tenant_id' => 't']))->isConfigured());
    });
};