OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

TicketInlineImageTest.php

4 KB
<?php

declare(strict_types=1);

use OpenNIT\Tickets\ImapFetcher;

/**
 * Deckt ab, welche Inline-Bilder einer Mail als Anhang am Ticket landen.
 *
 * Ein Bild, das im Text steht, ist dort sichtbar und braucht keinen Chip. Für
 * jedes andere Inline-Bild ist der Anhang der einzige Weg ins Ticket – ohne ihn
 * fehlt es ersatzlos, weil auch der Text es nicht trägt.
 */
return static function (): void {

    /** Fetcher ohne Verbindung; geprüft werden nur die reinen Textschritte. */
    $fetcher = static fn (): ImapFetcher => new ImapFetcher(['username' => 'tickets@example.com']);

    /** Ruft eine private Methode auf. */
    $ruf = static function (ImapFetcher $f, string $name, array $args) {
        $m = new ReflectionMethod(ImapFetcher::class, $name);
        $m->setAccessible(true);
        return $m->invokeArgs($f, $args);
    };

    /** MIME-Part, wie ihn die IMAP-Erweiterung liefert. */
    $part = static function (string $contentId): object {
        $p = new stdClass();
        $p->id = $contentId;
        return $p;
    };

    test('Ein eingebettetes Bild wird nicht zusätzlich Anhang', function () use ($fetcher, $ruf, $part) {
        $f    = $fetcher();
        $html = '<p>Hallo</p><img src="cid:logo1" alt="">';
        $bild = ['logo1' => ['mime' => 'image/png', 'data' => 'ROHDATEN']];

        $ergebnis = $ruf($f, 'inlineCidImages', [$html, $bild]);

        assertTrue(str_contains($ergebnis, 'data:image/png;base64,'), 'Das Bild steht nicht im Text');
        assertTrue($ruf($f, 'stehtImText', [$part('<logo1>')]), 'Das Bild gilt nicht als eingebettet');
    });

    test('Ein zu großes Bild wird zum Anhang', function () use ($fetcher, $ruf, $part) {
        // Über der Einbettungsgrenze sammelt getInlineImages() das Bild nicht
        // ein; im Text steht es damit nicht.
        $f    = $fetcher();
        $html = '<p>Screenshot:</p><img src="cid:gross1" alt="">';

        $ruf($f, 'inlineCidImages', [$html, []]);

        assertFalse($ruf($f, 'stehtImText', [$part('<gross1>')]), 'Das Bild gilt fälschlich als eingebettet');
    });

    test('Ohne Content-ID wird das Bild zum Anhang', function () use ($fetcher, $ruf, $part) {
        $f = $fetcher();
        assertFalse($ruf($f, 'stehtImText', [$part('')]));
    });

    test('Eine im Text nicht benutzte Content-ID zählt nicht als eingebettet', function () use ($fetcher, $ruf, $part) {
        // Der Body verweist nur auf das erste Bild; das zweite bleibt übrig.
        $f     = $fetcher();
        $html  = '<img src="cid:eins" alt="">';
        $bilder = [
            'eins' => ['mime' => 'image/png', 'data' => 'A'],
            'zwei' => ['mime' => 'image/png', 'data' => 'B'],
        ];

        $ruf($f, 'inlineCidImages', [$html, $bilder]);

        assertTrue($ruf($f, 'stehtImText', [$part('<eins>')]));
        assertFalse($ruf($f, 'stehtImText', [$part('<zwei>')]));
    });

    test('Die Merkliste gilt nur für die gerade gelesene Mail', function () use ($fetcher, $ruf, $part) {
        $f = $fetcher();
        $ruf($f, 'inlineCidImages', ['<img src="cid:logo1">', ['logo1' => ['mime' => 'image/png', 'data' => 'A']]]);
        assertTrue($ruf($f, 'stehtImText', [$part('<logo1>')]));

        $eigenschaft = new ReflectionProperty(ImapFetcher::class, 'eingebetteteCids');
        $eigenschaft->setAccessible(true);
        $eigenschaft->setValue($f, []);

        assertFalse($ruf($f, 'stehtImText', [$part('<logo1>')]), 'Die Merkliste wirkt über die Mail hinaus');
    });

    test('Die Grenzen der Einbettung stehen fest', function () {
        // Sie entscheiden, ab wann ein Bild als Anhang statt im Text erscheint.
        $src = (string)file_get_contents(dirname(__DIR__) . '/src/Tickets/ImapFetcher.php');

        assertTrue(str_contains($src, 'strlen($data) > 500 * 1024'), 'Die Größengrenze fehlt');
        assertTrue(str_contains($src, 'count($out) >= 5'), 'Die Stückzahlgrenze fehlt');
        assertTrue(
            str_contains($src, "if (\$disposition === 'inline' && \$this->stehtImText(\$part)) {"),
            'Inline-Bilder werden wieder pauschal verworfen'
        );
    });
};