Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Controllers\AssetController;
/**
* Deckt die Zuordnung eines unbekannten Geräts zu einem vorhandenen Asset ab.
*
* Der Weg besteht aus drei Teilen, die zusammenpassen müssen: ein Suchfeld, ein
* Endpunkt dahinter und eine Auswahl, die als asset_id im Formular landet.
* Fehlt ein Teil, bleibt das Feld ohne sichtbare Wirkung.
*/
return static function (): void {
$template = static fn (): string => (string)file_get_contents(
dirname(__DIR__) . '/templates/admin/assets/unmatched-devices.php'
);
// ----------------------------------------------------------------
// Endpunkt
// ----------------------------------------------------------------
test('Der Suchendpunkt existiert und ist erreichbar registriert', function () {
assertTrue(
method_exists(AssetController::class, 'unmatchedDeviceAssetSearch'),
'Die Aktion fehlt'
);
$app = (string)file_get_contents(dirname(__DIR__) . '/src/Core/Application.php');
assertTrue(
str_contains($app, "'/admin/inventory/unmatched-devices/asset-search'"),
'Die Route fehlt'
);
// Vor der {id}-Route, damit "asset-search" nicht als ID gelesen wird.
assertTrue(
strpos($app, "'/admin/inventory/unmatched-devices/asset-search'")
< strpos($app, "'/admin/inventory/unmatched-devices/{id}/assign'"),
'Die Suchroute steht hinter der {id}-Route'
);
});
test('Die Suche folgt dem Recht, das auch die Zuordnung verlangt', function () {
// Ein Treffer, den man anschließend nicht beschreiben darf, wäre irreführend.
$src = (string)file_get_contents(dirname(__DIR__) . '/src/Controllers/AssetController.php');
$von = strpos($src, 'public function unmatchedDeviceAssetSearch');
assertTrue($von !== false);
$block = substr($src, $von, 2500);
assertTrue(str_contains($block, "authorize('manage_assets')"), 'Keine Rechteprüfung');
assertTrue(str_contains($block, "accessibleLocations('manage_assets')"), 'Kein Standort-Filter');
assertTrue(str_contains($block, "status <> 'decommissioned'"), 'Ausgemusterte Assets sind nicht ausgeschlossen');
});
// ----------------------------------------------------------------
// Oberfläche
// ----------------------------------------------------------------
test('Das Suchfeld ist mit dem Endpunkt verbunden', function () use ($template) {
$tpl = $template();
assertTrue(str_contains($tpl, "getElementById('assetSearch').addEventListener('input'"), 'Kein Listener am Suchfeld');
assertTrue(str_contains($tpl, '/admin/inventory/unmatched-devices/asset-search?q='), 'Kein Aufruf des Endpunkts');
assertTrue(str_contains($tpl, 'encodeURIComponent'), 'Der Suchbegriff wird nicht kodiert');
});
test('Ein Treffer wird als auswählbares asset_id angeboten', function () use ($template) {
// Der Absende-Handler erwartet genau dieses Feld.
$tpl = $template();
assertTrue(str_contains($tpl, 'name="asset_id"'), 'Die Treffer bieten kein auswählbares Feld');
assertTrue(str_contains($tpl, "querySelector('input[name=\"asset_id\"]:checked')"), 'Die Auswahl wird nicht gelesen');
});
test('Fremde Angaben werden vor der Ausgabe entschärft', function () use ($template) {
// Asset-Namen stammen unter anderem aus Sync-Quellen.
$tpl = $template();
assertTrue(str_contains($tpl, 'function escapeHtml('), 'Keine Maskierung vorhanden');
assertTrue(str_contains($tpl, 'escapeHtml(a.inventory_number'), 'Treffer werden unmaskiert eingesetzt');
});
test('Die Suche startet erst ab zwei Zeichen und entprellt', function () use ($template) {
$tpl = $template();
assertTrue(str_contains($tpl, 'begriff.length < 2'), 'Keine Mindestlänge');
assertTrue(str_contains($tpl, 'clearTimeout(assetSearchTimer)'), 'Keine Entprellung');
});
};