Files
2026-10-16 19:12:24 +00:00

130 lines
5.8 KiB
PHP

<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/includes/themes.php';
if (!class_exists('ZipArchive')) {
echo "Theme-Tests übersprungen: PHP-ZIP ist nicht installiert.\n";
exit(0);
}
$testDirectory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'deskhub-theme-test-' . bin2hex(random_bytes(6));
$archiveDirectory = $testDirectory . DIRECTORY_SEPARATOR . 'archives';
$themesDirectory = $testDirectory . DIRECTORY_SEPARATOR . 'themes';
mkdir($archiveDirectory, 0755, true);
try {
$validArchive = $archiveDirectory . DIRECTORY_SEPARATOR . 'valid.zip';
create_test_theme_archive($validArchive, [
'theme.css' => "/*\n * Author: Test Autor\n * Version: 2.3.1\n */\n:root { --primary: #123456; }",
'images/background.png' => 'not-a-real-image',
]);
$theme = install_custom_theme_archive($validArchive, 'Test Theme', $themesDirectory);
$themes = list_custom_themes($themesDirectory);
assert_true(count($themes) === 1, 'Das installierte Theme wird nicht aufgelistet.');
assert_true($themes[0]['id'] === $theme['id'], 'Die Theme-ID stimmt nicht überein.');
assert_true($themes[0]['author'] === 'Test Autor', 'Der Theme-Autor wurde nicht aus theme.css übernommen.');
assert_true($themes[0]['theme_version'] === '2.3.1', 'Die Theme-Version wurde nicht aus theme.css übernommen.');
assert_true(
is_file($themesDirectory . DIRECTORY_SEPARATOR . $theme['id'] . DIRECTORY_SEPARATOR . 'theme.css'),
'theme.css wurde nicht installiert.'
);
$namedUpload = $archiveDirectory . DIRECTORY_SEPARATOR . 'My-Theme_Name.zip';
copy($validArchive, $namedUpload);
$uploadedTheme = install_custom_theme_upload([
'name' => 'My-Theme_Name.zip',
'tmp_name' => $namedUpload,
'size' => filesize($namedUpload),
'error' => UPLOAD_ERR_OK,
], $themesDirectory);
assert_true($uploadedTheme['name'] === 'My Theme Name', 'Der Theme-Name wird nicht aus dem ZIP-Dateinamen übernommen.');
assert_true(!$uploadedTheme['replaced'], 'Der erste Upload wurde fälschlich als Aktualisierung markiert.');
$replacementArchive = $archiveDirectory . DIRECTORY_SEPARATOR . 'replacement.zip';
create_test_theme_archive($replacementArchive, [
'theme.css' => ':root { --primary: #abcdef; }',
]);
$replacementTheme = install_custom_theme_upload([
'name' => 'My-Theme_Name.zip',
'tmp_name' => $replacementArchive,
'size' => filesize($replacementArchive),
'error' => UPLOAD_ERR_OK,
], $themesDirectory);
assert_true($replacementTheme['replaced'], 'Der erneute Upload wurde nicht als Aktualisierung erkannt.');
assert_true($replacementTheme['id'] === $uploadedTheme['id'], 'Beim Aktualisieren wurde die Theme-ID geändert.');
assert_true(
count(list_custom_themes($themesDirectory)) === 2,
'Der erneute Upload hat einen doppelten Listeneintrag erzeugt.'
);
assert_true(
file_get_contents(
$themesDirectory . DIRECTORY_SEPARATOR . $replacementTheme['id'] . DIRECTORY_SEPARATOR . 'theme.css'
) === ':root { --primary: #abcdef; }',
'Die Theme-Dateien wurden beim erneuten Upload nicht ersetzt.'
);
$traversalArchive = $archiveDirectory . DIRECTORY_SEPARATOR . 'traversal.zip';
create_test_theme_archive($traversalArchive, [
'theme.css' => ':root {}',
'../escape.css' => 'body {}',
]);
assert_install_fails($traversalArchive, $themesDirectory, 'unsicheren Dateipfad');
assert_true(!file_exists($testDirectory . DIRECTORY_SEPARATOR . 'escape.css'), 'ZIP-Pfad hat das Zielverzeichnis verlassen.');
$executableArchive = $archiveDirectory . DIRECTORY_SEPARATOR . 'executable.zip';
create_test_theme_archive($executableArchive, [
'theme.css' => ':root {}',
'payload.php' => '<?php echo "unsafe";',
]);
assert_install_fails($executableArchive, $themesDirectory, 'Dateityp nicht erlaubt');
$missingStylesheetArchive = $archiveDirectory . DIRECTORY_SEPARATOR . 'missing-css.zip';
create_test_theme_archive($missingStylesheetArchive, ['images/background.png' => 'image']);
assert_install_fails($missingStylesheetArchive, $themesDirectory, 'fehlt die Datei theme.css');
assert_true(delete_custom_theme($replacementTheme['id'], $themesDirectory), 'Das aktualisierte Theme konnte nicht gelöscht werden.');
assert_true(delete_custom_theme($theme['id'], $themesDirectory), 'Das Theme konnte nicht gelöscht werden.');
assert_true(list_custom_themes($themesDirectory) === [], 'Das gelöschte Theme wird weiterhin aufgelistet.');
echo "Theme-Upload, Aktualisierung, automatische Namensgebung, Schutzprüfungen und Löschen funktionieren.\n";
} finally {
if (is_dir($testDirectory)) {
remove_custom_theme_directory($testDirectory);
}
}
function create_test_theme_archive(string $path, array $files): void
{
$zip = new ZipArchive();
if ($zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException("Test-ZIP konnte nicht erstellt werden: {$path}");
}
foreach ($files as $name => $contents) {
$zip->addFromString($name, $contents);
}
$zip->close();
}
function assert_install_fails(string $archive, string $themesDirectory, string $expectedMessage): void
{
try {
install_custom_theme_archive($archive, 'Ungültiges Theme', $themesDirectory);
} catch (RuntimeException $e) {
assert_true(
str_contains($e->getMessage(), $expectedMessage),
"Unerwartete Fehlermeldung: {$e->getMessage()}"
);
return;
}
throw new RuntimeException("Ungültiges Archiv wurde akzeptiert: {$archive}");
}
function assert_true(bool $condition, string $message): void
{
if (!$condition) {
throw new RuntimeException($message);
}
}