Quelltext des aktuellen Standes
<?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());
});
};