OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

BulkAndMaintenanceTest.php

8 KB
<?php

declare(strict_types=1);

use OpenNIT\Core\Database;
use OpenNIT\Services\MaintenanceWindow;

/**
 * Deckt zwei kleinere Änderungen ab: die Massenbearbeitung im Inventar und
 * das Stummschalten des Monitorings während geplanter Wartungen.
 */
return static function (): void {

    $wurzel = dirname(__DIR__);
    $src    = static fn (string $datei): string => (string)file_get_contents($wurzel . $datei);

    // ----------------------------------------------------------------
    // Massenbearbeitung
    // ----------------------------------------------------------------

    test('Nur ausgefüllte Felder werden geändert', function () use ($src) {
        // Ein leer gelassenes Feld darf nicht sämtliche Geräte leeren.
        $code = $src('/src/Controllers/AssetController.php');
        $von  = strpos($code, 'public function bulkUpdate');
        $block = substr($code, (int)$von, 2600);

        assertTrue(str_contains($block, '$standortSetzen'), 'Standort nicht bedingt');
        assertTrue(str_contains($block, '$zuweisungSetzen'), 'Zuweisung nicht bedingt');
        assertTrue(str_contains($block, 'kein Feld zum Ändern'), 'Leere Eingabe nicht abgefangen');
    });

    test('Die Massenbearbeitung achtet auf die Standort-Rechte', function () use ($src) {
        // Weder darf ein Gerät aus fremdem Standort geändert, noch eines in
        // einen fremden Ziel-Standort verschoben werden.
        $code = $src('/src/Controllers/AssetController.php');
        $von  = strpos($code, 'public function bulkUpdate');
        $block = substr($code, (int)$von, 3200);

        assertTrue(str_contains($block, "canForLocation('manage_assets', \$neuerStandort)"), 'Ziel-Standort ungeprüft');
        assertTrue(str_contains($block, "assetLocationOk(\$id, 'manage_assets')"), 'Quelle ungeprüft');
    });

    test('Eine vom MDM geführte Zuweisung bleibt auch hier unberührt', function () use ($src) {
        $code = $src('/src/Controllers/AssetController.php');
        $von  = strpos($code, 'public function bulkUpdate');
        $block = substr($code, (int)$von, 3600);

        assertTrue(str_contains($block, 'mdmFuehrtZuweisung($asset)'), 'MDM-Schutz fehlt in der Massenbearbeitung');
    });

    test('Jede Änderung landet einzeln im Lebenslauf des Geräts', function () use ($src) {
        // Ein Sammel-Eintrag ohne Gerätebezug wäre am Gerät unsichtbar.
        $code = $src('/src/Controllers/AssetController.php');
        $von  = strpos($code, 'public function bulkUpdate');
        $bis   = strpos($code, 'private function standortName', (int)$von);
        $block = substr($code, (int)$von, (int)$bis - (int)$von);

        assertTrue(str_contains($block, "'asset_updated'"), 'Falsche Aktion');
        assertTrue(str_contains($block, "'Massenbearbeitung'"), 'Herkunft nicht benannt');
        // Beschriftete Werte, damit die Zeitleiste sie lesbar darstellt.
        assertTrue(str_contains($block, "\$alt['Status']"), 'Werte nicht beschriftet');
    });

    test('Die Liste bietet die Massenbearbeitung an', function () use ($src) {
        $tpl = $src('/templates/assets/list.php');

        assertTrue(str_contains($tpl, 'openBulkEdit()'), 'Keine Schaltfläche');
        assertTrue(str_contains($tpl, '/inventory/bulk-update'), 'Kein Ziel');
        assertTrue(str_contains($tpl, 'nicht ändern'), 'Kein Weglass-Eintrag in der Auswahl');

        $routen = $src('/src/Core/Application.php');
        assertTrue(str_contains($routen, "'/inventory/bulk-update'"), 'Route fehlt');
    });

    // ----------------------------------------------------------------
    // Wartungsfenster
    // ----------------------------------------------------------------

    /** Datenbank-Doppel, das je Tabelle eine Antwort liefert. */
    $db = static function (array $antworten): Database {
        return new class ($antworten) extends Database {
            public function __construct(private array $antworten)
            {
            }

            public function fetchOne(string $sql, array $params = []): ?array
            {
                foreach ($this->antworten as $tabelle => $wert) {
                    if (str_contains($sql, $tabelle)) {
                        if ($wert === 'fehlt') {
                            throw new \RuntimeException('Tabelle nicht vorhanden');
                        }
                        return $wert;
                    }
                }
                return null;
            }

            public function fetchColumn(string $sql, array $params = [], int $column = 0): mixed
            {
                return $this->antworten['setting'] ?? null;
            }
        };
    };

    test('Eine laufende Wartung der Statusseite wird erkannt', function () use ($db) {
        $treffer = MaintenanceWindow::aktive($db(['status_maintenances' => ['title' => 'Speicher-Umzug']]));

        assertSame('statusseite', $treffer['quelle']);
        assertSame('Speicher-Umzug', $treffer['titel']);
    });

    test('Ohne Statusseite greift der Wartungskalender', function () use ($db) {
        $treffer = MaintenanceWindow::aktive($db([
            'status_maintenances' => 'fehlt',
            'maintenance_events'  => ['title' => 'Patchday'],
        ]));

        assertSame('wartungskalender', $treffer['quelle']);
        assertSame('Patchday', $treffer['titel']);
    });

    test('Ohne Wartung und ohne Tabellen bleibt es still', function () use ($db) {
        assertSame(null, MaintenanceWindow::aktive($db([])));
        assertSame(null, MaintenanceWindow::aktive($db([
            'status_maintenances' => 'fehlt',
            'maintenance_events'  => 'fehlt',
        ])), 'Fehlende Tabellen dürfen nicht durchschlagen');
    });

    test('Ohne eingeschalteten Schalter wird nichts unterdrückt', function () use ($db) {
        // Auch mitten in einer Wartung: erst der Schalter macht still.
        $mitWartung = ['status_maintenances' => ['title' => 'Umzug'], 'setting' => '0'];
        assertSame(null, MaintenanceWindow::unterdruecktJetzt($db($mitWartung)));

        $mitWartung['setting'] = '1';
        assertSame('Umzug', MaintenanceWindow::unterdruecktJetzt($db($mitWartung))['titel']);
    });

    test('Die Meldung wird trotzdem vollständig aufgezeichnet', function () use ($src) {
        // Stumm heißt nicht verworfen – sonst fehlte der Vorgang später.
        $code = $src('/src/Controllers/MonitoringController.php');
        $einfuegen = strpos($code, 'INSERT INTO monitoring_events');
        $stumm     = strpos($code, 'MaintenanceWindow::unterdruecktJetzt');

        assertTrue($einfuegen !== false && $stumm !== false, 'Eine Hälfte fehlt');
        assertTrue($stumm > $einfuegen, 'Die Unterdrückung greift vor dem Aufzeichnen');
        assertTrue(str_contains($code, 'notify_suppressed = 1'), 'Stummer Alarm nicht gekennzeichnet');
    });

    test('Die Kennzeichnung übersteht eine Instanz ohne die neue Spalte', function () use ($src) {
        $code = $src('/src/Controllers/MonitoringController.php');
        $von  = strpos($code, 'MaintenanceWindow::unterdruecktJetzt');
        $block = substr($code, (int)$von, 700);

        assertTrue(str_contains($block, 'try {'), 'Kein Schutz für ältere Instanzen');
    });

    test('Der Schalter ist in den Einstellungen erreichbar', function () use ($src) {
        $tpl = $src('/templates/admin/monitoring/settings.php');

        assertTrue(str_contains($tpl, 'mute_during_maintenance'), 'Kein Schalter');
        // Die Tagesgenauigkeit des Kalenders wird benannt, statt sie zu verschweigen.
        assertTrue(str_contains($tpl, 'decken deshalb den gesamten Tag ab'), 'Einschränkung nicht erklärt');
    });
};