117 lines
5.3 KiB
PHP
117 lines
5.3 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use Steuerkram\Config;
|
||
use Steuerkram\Models\Setting;
|
||
|
||
/**
|
||
* Test 70: Logo-Upload – Formular-Struktur, warnungsfreies Speichern und
|
||
* Ablage in data/logo außerhalb des Web-Roots.
|
||
*/
|
||
class SettingsLogoUploadTest extends TestCase
|
||
{
|
||
protected string $testName = 'Test 70: Logo-Upload – keine Warnings, Formulare nicht verschachtelt, Ablage in data/logo';
|
||
|
||
public function run(): void
|
||
{
|
||
// 1) Setting::update() darf mit einem Datensatz ohne die optionalen Keys
|
||
// (z.B. mail_subject_template, seit "E-Mail-Texte pro Kunde" nicht mehr im
|
||
// Einstellungsformular) keine Warning erzeugen – sonst geht Output raus,
|
||
// bevor View::redirect() den Location-Header setzen kann.
|
||
$warnings = [];
|
||
set_error_handler(function (int $no, string $msg) use (&$warnings): bool {
|
||
$warnings[] = $msg;
|
||
return true;
|
||
}, E_ALL);
|
||
|
||
try {
|
||
Setting::update([
|
||
'firma' => 'Testfirma',
|
||
'strasse' => 'Teststr. 1',
|
||
'plz' => '12345',
|
||
'ort' => 'Testort',
|
||
]);
|
||
} finally {
|
||
restore_error_handler();
|
||
}
|
||
|
||
$this->assert(
|
||
$warnings === [],
|
||
'Setting::update() ohne optionale Keys erzeugt keine Warnings (erhalten: ' . implode(' | ', $warnings) . ')'
|
||
);
|
||
|
||
$stored = Setting::get();
|
||
$this->assertEquals('Rechnung %RNR%', $stored['mail_subject_template'], 'Fehlender Betreff fällt auf den Default zurück');
|
||
$this->assertEquals('Testfirma', $stored['firma'], 'Firma wurde gespeichert');
|
||
|
||
// 2) Logo liegt in data/logo, die DB speichert nur den Dateinamen.
|
||
// Bewusst ein eigener Testdateiname – deleteLogo() löscht die Datei
|
||
// physisch, ein echtes logo.png darf davon nie getroffen werden.
|
||
$testFile = 'logo-test-' . getmypid() . '.png';
|
||
$testPath = $this->registerTempPath(Config::get('logo_dir') . '/' . $testFile);
|
||
|
||
Setting::setLogo($testFile);
|
||
$this->assertEquals($testFile, Setting::get()['logo_path'], 'setLogo() speichert nur den Dateinamen');
|
||
|
||
$abs = Setting::logoAbsolutePath();
|
||
$this->assertEquals($testPath, $abs, 'logoAbsolutePath() zeigt in das Logo-Datenverzeichnis');
|
||
$this->assert(
|
||
!str_contains((string)$abs, '/public/'),
|
||
'Logo liegt außerhalb des Web-Roots'
|
||
);
|
||
|
||
// Altbestände mit webroot-relativem Präfix bleiben auffindbar, falls
|
||
// Migration 089 noch nicht gelaufen ist
|
||
Setting::setLogo('static/uploads/logo/' . $testFile);
|
||
$this->assertEquals(
|
||
$testPath,
|
||
Setting::logoAbsolutePath(),
|
||
'Legacy-Pfad wird auf das Logo-Verzeichnis abgebildet'
|
||
);
|
||
|
||
// deleteLogo() entfernt Datei und DB-Eintrag
|
||
if (!is_dir((string)Config::get('logo_dir'))) {
|
||
mkdir((string)Config::get('logo_dir'), 0755, true);
|
||
}
|
||
file_put_contents($testPath, 'dummy');
|
||
Setting::setLogo($testFile);
|
||
Setting::deleteLogo();
|
||
|
||
$this->assert(!file_exists($testPath), 'deleteLogo() entfernt die Datei aus data/logo');
|
||
$this->assert(empty(Setting::get()['logo_path']), 'deleteLogo() entfernt den Pfad');
|
||
$this->assert(Setting::logoAbsolutePath() === null, 'Ohne Logo liefert logoAbsolutePath() null');
|
||
|
||
// 3) Belege und Logo liegen getrennt und beide außerhalb von public/
|
||
$uploadDir = (string)Config::get('upload_dir');
|
||
$logoDir = (string)Config::get('logo_dir');
|
||
$this->assert(!str_contains($uploadDir, '/public/'), 'Belegverzeichnis liegt außerhalb des Web-Roots');
|
||
$this->assert($uploadDir !== $logoDir, 'Belege und Logo haben getrennte Verzeichnisse');
|
||
$this->assert(str_ends_with($uploadDir, '/data/belege'), 'Belege liegen in data/belege');
|
||
$this->assert(str_ends_with($logoDir, '/data/logo'), 'Logo liegt in data/logo');
|
||
|
||
// 3) Das Logo-Upload-Formular darf nicht in #settings-form verschachtelt sein –
|
||
// sonst verwirft der Browser das innere <form> und der Hochladen-Button
|
||
// postet an /settings statt an /settings/logo.
|
||
$view = file_get_contents(dirname(__DIR__) . '/src/Views/settings/form.php');
|
||
$this->assertNotEmpty($view, 'settings/form.php ist lesbar');
|
||
|
||
$settingsFormStart = strpos($view, '<form id="settings-form"');
|
||
$settingsFormEnd = strpos($view, '</form>', (int)$settingsFormStart);
|
||
$this->assert($settingsFormStart !== false && $settingsFormEnd !== false, '#settings-form ist im View vorhanden');
|
||
|
||
$inner = substr($view, (int)$settingsFormStart + 1, (int)$settingsFormEnd - (int)$settingsFormStart);
|
||
$this->assert(
|
||
!str_contains($inner, '<form '),
|
||
'Innerhalb von #settings-form steht kein weiteres <form>-Element'
|
||
);
|
||
$this->assert(
|
||
str_contains($view, '<form id="logo-upload-form"') && str_contains($view, 'action="/settings/logo"'),
|
||
'Eigenständiges #logo-upload-form mit action="/settings/logo" vorhanden'
|
||
);
|
||
$this->assert(
|
||
str_contains($view, 'name="logo" accept=".png,.jpg,.jpeg" form="logo-upload-form"'),
|
||
'Datei-Input ist per form-Attribut an #logo-upload-form gebunden'
|
||
);
|
||
}
|
||
}
|