OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

UpdateChannelTest.php

7 KB
<?php

declare(strict_types=1);

use OpenNIT\Content\UpdateManager;

/**
 * Deckt die Auswahl des Update-Kanals ab: welche Kanäle eine Instanz anbietet,
 * wie ein gespeicherter Name aufgelöst wird und dass ein optionaler Kanal ohne
 * Freischaltung in der Umgebung nirgends erreichbar ist.
 */
return static function (): void {

    $wurzel = dirname(__DIR__);

    /** Setzt die Freischaltung für die Dauer eines Falls und räumt danach auf. */
    $mitSchalter = static function (?string $wert, callable $fall): void {
        $name   = 'OPENNIT_UPDATE_CHANNELS_EXTRA';
        $vorher = $_ENV[$name] ?? null;

        if ($wert === null) {
            unset($_ENV[$name], $_SERVER[$name]);
        } else {
            $_ENV[$name] = $wert;
        }

        try {
            $fall();
        } finally {
            unset($_ENV[$name], $_SERVER[$name]);
            if ($vorher !== null) {
                $_ENV[$name] = $vorher;
            }
        }
    };

    // ----------------------------------------------------------------
    // Standardzustand: nur die beiden bekannten Kanäle
    // ----------------------------------------------------------------

    test('Ohne Freischaltung stehen genau Stable und Development zur Wahl', function () use ($mitSchalter) {
        $mitSchalter(null, function () {
            assertSame(['stable', 'development'], array_keys(UpdateManager::availableChannels()));
        });
    });

    test('Ein nicht freigeschalteter Kanalname fällt auf Stable zurück', function () use ($mitSchalter) {
        // Auch ein von Hand in die runtime.php geschriebener Name darf die
        // Instanz nicht auf einen gesperrten Proxy zeigen lassen.
        $mitSchalter(null, function () {
            assertSame('stable', UpdateManager::resolveChannel('experimental'));
            assertSame('stable', UpdateManager::resolveChannel('gibt-es-nicht'));
            assertSame('stable', UpdateManager::resolveChannel(null));
            assertSame('development', UpdateManager::resolveChannel('development'));
        });
    });

    // ----------------------------------------------------------------
    // Freigeschalteter Zustand
    // ----------------------------------------------------------------

    test('Mit Freischaltung kommt der optionale Kanal hinzu', function () use ($mitSchalter) {
        $mitSchalter('1', function () {
            $kanaele = UpdateManager::availableChannels();

            assertTrue(array_key_exists('experimental', $kanaele));
            assertSame('experimental', UpdateManager::resolveChannel('experimental'));
            // Die bekannten Kanäle bleiben unverändert erreichbar.
            assertSame(UpdateManager::CHANNELS['stable'], $kanaele['stable']);
            assertSame(UpdateManager::CHANNELS['development'], $kanaele['development']);
        });
    });

    test('Der Schalter versteht die üblichen Schreibweisen', function () use ($mitSchalter) {
        foreach (['1', 'true', 'on', 'yes'] as $an) {
            $mitSchalter($an, function () use ($an) {
                assertSame('experimental', UpdateManager::resolveChannel('experimental'), "Wert: {$an}");
            });
        }
        foreach (['0', 'false', 'off', '', 'vielleicht'] as $aus) {
            $mitSchalter($aus, function () use ($aus) {
                assertSame('stable', UpdateManager::resolveChannel('experimental'), "Wert: {$aus}");
            });
        }
    });

    // ----------------------------------------------------------------
    // Verdrahtung: alle Stellen nutzen dieselbe Auflösung
    // ----------------------------------------------------------------

    test('Der Updater prüft die Auswahl gegen die verfügbaren Kanäle', function () use ($wurzel) {
        // Die gespeicherte Auswahl darf nicht gegen die feste Liste geprüft
        // werden – sonst wäre der optionale Kanal auch ohne Freischaltung
        // speicherbar oder mit Freischaltung nicht speicherbar.
        $src = (string)file_get_contents($wurzel . '/src/Controllers/Admin/UpdateController.php');

        assertFalse(str_contains($src, 'UpdateManager::CHANNELS'), 'Feste Kanalliste im Controller');
        assertTrue(str_contains($src, 'UpdateManager::resolveChannel'), 'Auflösung im Controller');
        assertTrue(str_contains($src, 'UpdateManager::availableChannels()'), 'Kanalliste im Controller');
    });

    test('Die Kanal-Kachel erscheint nur bei freigeschaltetem Kanal', function () use ($wurzel) {
        $tpl = (string)file_get_contents($wurzel . '/templates/admin/update/index.php');

        // Jeder Auftritt des optionalen Kanals im Formular liegt hinter der Prüfung.
        $bloecke = preg_split('/<\?php if \(in_array\(\'experimental\', \$updateChannels, true\)\): \?>/', $tpl);
        assertSame(3, count($bloecke), 'Erwartet: Kachel und Warnung je hinter einer Prüfung');
        assertFalse(str_contains($bloecke[0], 'experimental'), 'Optionaler Kanal ausserhalb der Pruefung');
    });

    test('Die Kanal-Kachel benennt jeden Kanal beim Namen', function () use ($wurzel) {
        // Eine Zweiweg-Unterscheidung wuerde jeden Kanal ausser Development als
        // "Stable" und "empfohlen" ausweisen – gerade beim risikoreichsten also
        // das Gegenteil dessen, was gilt.
        $tpl = (string)file_get_contents($wurzel . '/templates/admin/update/index.php');

        $von   = strpos($tpl, '$kanalKachel = [');
        assertTrue($von !== false, 'Keine Zuordnung je Kanal');
        $block = substr($tpl, (int)$von, 600);

        foreach (['stable', 'development', 'experimental'] as $kanal) {
            assertTrue(str_contains($block, "'{$kanal}'"), "Kanal fehlt in der Zuordnung: {$kanal}");
        }
        // Unbekannte Namen folgen der Auflösung im Controller.
        assertTrue(str_contains($block, "][\$updateChannel] ?? ['Stable'"), 'Kein Ausweichwert');

        // Die Kachel liest nur noch aus der Zuordnung.
        $kachel = substr($tpl, (int)strpos($tpl, 'status-tile-label">Update-Kanal'), 400);
        assertFalse(str_contains($kachel, "=== 'development'"), 'Zweiweg-Unterscheidung in der Kachel');
    });

    test('Der optionale Kanal ist in der Auslieferung nicht beschrieben', function () use ($wurzel) {
        // Er soll nur finden, wer ihn kennt – also kein Eintrag in den
        // Vorlagen, dem Handbuch oder dem Changelog.
        foreach (['/.env.example', '/CHANGELOG.md', '/README.md'] as $datei) {
            $pfad = $wurzel . $datei;
            if (!is_file($pfad)) {
                continue;
            }
            $inhalt = (string)file_get_contents($pfad);
            assertFalse(stripos($inhalt, 'OPENNIT_UPDATE_CHANNELS_EXTRA') !== false, "Schalter in {$datei}");
            assertFalse(stripos($inhalt, 'opennit-experimental') !== false, "Kanal in {$datei}");
        }
    });
};