130 lines
5.8 KiB
PHP
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);
|
|
}
|
|
}
|