Files
urlshort-qr-gen/tests/SlugTest.php
T
florian.wilde 8a7939bee2 test: Testsuite fuer Kernlogik, QR-Codes, Domains und Sicherheit
232 Tests, unter anderem:
- Slug-Erzeugung, Kollisionsbehandlung und Validierung
- URL-Validierung inklusive IDN, Prozentkodierung und abgelehnter
  Protokolle; Punycode gegengeprueft gegen idn_to_ascii
- QR-Erzeugung in allen Groessen, mit Logo und Level-H-Automatik
- Weiterleitung gegen eine echte SQLite-Datenbank samt Domain-
  Aufloesung ueber den Host-Header
- Update einer bestehenden Installation auf mehrere Domains
- Angriffsseitig: gefaelschte Proxy-Header, Host-Header-Injektion,
  SVG mit Skripten, XXE und externen Referenzen
2026-08-03 18:11:40 +02:00

144 lines
4.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests;
use App\Support\Slug;
use PHPUnit\Framework\TestCase;
final class SlugTest extends TestCase
{
public function testRandomSlugHasRequestedLengthAndSafeAlphabet(): void
{
for ($i = 0; $i < 50; $i++) {
$slug = Slug::random(7);
self::assertSame(7, strlen($slug));
self::assertMatchesRegularExpression('/^[a-z0-9]+$/', $slug);
// 0 und 1 sind ausgeschlossen (Verwechslung mit o und l).
self::assertDoesNotMatchRegularExpression('/[01]/', $slug);
self::assertTrue(Slug::isValid($slug));
}
}
public function testRandomSlugLengthIsConfigurable(): void
{
self::assertSame(6, strlen(Slug::random(6)));
self::assertSame(8, strlen(Slug::random(8)));
}
public function testRandomSlugsAreEssentiallyUnique(): void
{
$slugs = [];
for ($i = 0; $i < 500; $i++) {
$slugs[Slug::random(7)] = true;
}
// 33^7 Kombinationen: Kollisionen sind bei 500 Ziehungen praktisch ausgeschlossen.
self::assertGreaterThanOrEqual(499, count($slugs));
}
public function testRandomUniqueSkipsTakenSlugs(): void
{
$taken = [];
$calls = 0;
$exists = static function (string $candidate) use (&$taken, &$calls): bool {
$calls++;
// Die ersten drei Vorschlaege gelten als vergeben.
if ($calls <= 3) {
$taken[] = $candidate;
return true;
}
return false;
};
$slug = Slug::randomUnique($exists);
self::assertNotContains($slug, $taken);
self::assertTrue(Slug::isValid($slug));
self::assertSame(4, $calls);
}
public function testRandomUniqueGrowsLengthOnRepeatedCollisions(): void
{
$attempt = 0;
$slug = Slug::randomUnique(
static function () use (&$attempt): bool {
$attempt++;
return $attempt <= 4; // vier Kollisionen erzwingen
},
7,
);
self::assertSame(8, strlen($slug));
}
public function testRandomRejectsUnreasonableLengths(): void
{
$this->expectException(\InvalidArgumentException::class);
Slug::random(3);
}
/**
* @return array<string,array{0:string,1:bool}>
*/
public static function slugValidationProvider(): array
{
return [
'einfach' => ['messe2026', true],
'mit Bindestrich' => ['messe-2026-halle-3', true],
'nur Ziffern' => ['12345', true],
'einzelnes Zeichen' => ['a', true],
'leer' => ['', false],
'Grossbuchstaben' => ['Messe2026', false],
'Umlaut' => ['grüße', false],
'Leerzeichen' => ['messe 2026', false],
'Unterstrich' => ['messe_2026', false],
'Punkt' => ['messe.2026', false],
'Slash' => ['messe/2026', false],
'fuehrender Bindestrich' => ['-messe', false],
'abschliessender Bindestrich' => ['messe-', false],
'doppelter Bindestrich' => ['messe--2026', false],
'reserviert admin' => ['admin', false],
'reserviert login' => ['login', false],
'zu lang' => [str_repeat('a', 65), false],
'maximale Laenge' => [str_repeat('a', 64), true],
];
}
/**
* @dataProvider slugValidationProvider
*/
public function testValidation(string $slug, bool $expected): void
{
self::assertSame($expected, Slug::isValid($slug));
self::assertSame($expected, Slug::validationError($slug) === null);
}
public function testNormalizeCreatesUsableSlugs(): void
{
self::assertSame('messe-2026', Slug::normalize('Messe 2026'));
self::assertSame('gruesse-aus-muenchen', Slug::normalize('Grüße aus München'));
self::assertSame('a-b', Slug::normalize('--a---b--'));
self::assertSame('strasse', Slug::normalize('Straße'));
self::assertSame('', Slug::normalize(' '));
}
public function testNormalizedInputIsAlwaysValidOrEmpty(): void
{
foreach (['Messe 2026', 'Grüße!', 'a/b/c', '###', 'Test_Fall'] as $input) {
$normalized = Slug::normalize($input);
if ($normalized !== '') {
self::assertTrue(Slug::isValid($normalized), $input . ' -> ' . $normalized);
}
}
}
}