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