Remove WireGuard integration from production deployment to simplify infrastructure: - Remove docker-compose-direct-access.yml (VPN-bound services) - Remove VPN-only middlewares from Grafana, Prometheus, Portainer - Remove WireGuard middleware definitions from Traefik - Remove WireGuard IPs (10.8.0.0/24) from Traefik forwarded headers All monitoring services now publicly accessible via subdomains: - grafana.michaelschiemer.de (with Grafana native auth) - prometheus.michaelschiemer.de (with Basic Auth) - portainer.michaelschiemer.de (with Portainer native auth) All services use Let's Encrypt SSL certificates via Traefik.
244 lines
8.3 KiB
PHP
244 lines
8.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\QrCode\QrCodeGenerator;
|
|
use App\Framework\QrCode\QrCodeRenderer;
|
|
use App\Framework\QrCode\ValueObjects\EncodingMode;
|
|
use App\Framework\QrCode\ValueObjects\ErrorCorrectionLevel;
|
|
use App\Framework\QrCode\ValueObjects\QrCodeConfig;
|
|
use App\Framework\QrCode\ValueObjects\QrCodeVersion;
|
|
|
|
test('can generate QR code with default config', function () {
|
|
$data = 'Hello World';
|
|
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
expect($matrix)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
|
|
->and($matrix->getSize())->toBeGreaterThan(0)
|
|
->and($matrix->getVersion()->getVersionNumber())->toBe(1);
|
|
});
|
|
|
|
test('can generate QR code with auto-size config', function () {
|
|
$shortData = 'Test';
|
|
$longData = str_repeat('A', 30);
|
|
|
|
$matrix1 = QrCodeGenerator::generate($shortData);
|
|
$matrix2 = QrCodeGenerator::generate($longData);
|
|
|
|
expect($matrix1->getVersion()->getVersionNumber())->toBe(1)
|
|
->and($matrix2->getVersion()->getVersionNumber())->toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
test('can generate QR code with explicit version', function () {
|
|
$data = 'Test';
|
|
$config = QrCodeConfig::withVersion(2);
|
|
|
|
$matrix = QrCodeGenerator::generate($data, $config);
|
|
|
|
expect($matrix->getVersion()->getVersionNumber())->toBe(2)
|
|
->and($matrix->getSize())->toBe(25); // Version 2 = 25x25
|
|
});
|
|
|
|
test('generates proper matrix size for different versions', function () {
|
|
$data = 'Test';
|
|
|
|
$matrix1 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(1));
|
|
$matrix2 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(2));
|
|
$matrix3 = QrCodeGenerator::generate($data, QrCodeConfig::withVersion(3));
|
|
|
|
expect($matrix1->getSize())->toBe(21) // Version 1: 21x21
|
|
->and($matrix2->getSize())->toBe(25) // Version 2: 25x25
|
|
->and($matrix3->getSize())->toBe(29); // Version 3: 29x29
|
|
});
|
|
|
|
test('matrix contains finder patterns', function () {
|
|
$data = 'Test';
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
// Check top-left finder pattern (corner module should be dark)
|
|
expect($matrix->getModuleAt(0, 0)->isDark())->toBeTrue()
|
|
->and($matrix->getModuleAt(6, 6)->isDark())->toBeTrue();
|
|
});
|
|
|
|
test('matrix contains timing patterns', function () {
|
|
$data = 'Test';
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
// Row 6 should have alternating modules (timing pattern)
|
|
expect($matrix->getModuleAt(6, 8)->isDark())->toBeTrue()
|
|
->and($matrix->getModuleAt(6, 9)->isLight())->toBeTrue()
|
|
->and($matrix->getModuleAt(6, 10)->isDark())->toBeTrue();
|
|
});
|
|
|
|
test('throws exception when data too long for version', function () {
|
|
$data = str_repeat('A', 100); // Too long for version 1-3
|
|
|
|
QrCodeGenerator::generate($data);
|
|
})->throws(\App\Framework\Exception\FrameworkException::class);
|
|
|
|
test('matrix has correct dark module count', function () {
|
|
$data = 'Test';
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
$darkCount = $matrix->countDarkModules();
|
|
|
|
// Matrix should have some dark modules (at least finder patterns)
|
|
expect($darkCount)->toBeGreaterThan(50) // Finder patterns alone = ~63 modules
|
|
->and($darkCount)->toBeLessThan($matrix->getSize() * $matrix->getSize());
|
|
});
|
|
|
|
test('can generate ASCII representation', function () {
|
|
$data = 'A';
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
$ascii = $matrix->toAsciiArt();
|
|
|
|
expect($ascii)->toBeString()
|
|
->and($ascii)->toContain('█') // Should contain dark modules
|
|
->and($ascii)->toContain('░'); // Should contain light modules
|
|
});
|
|
|
|
test('can generate binary representation', function () {
|
|
$data = 'A';
|
|
$matrix = QrCodeGenerator::generate($data);
|
|
|
|
$binary = $matrix->toBinaryString();
|
|
|
|
expect($binary)->toBeString()
|
|
->and($binary)->toContain('1') // Should contain 1s (dark)
|
|
->and($binary)->toContain('0'); // Should contain 0s (light)
|
|
});
|
|
|
|
test('generates deterministic output for same input', function () {
|
|
$data = 'Test123';
|
|
|
|
$matrix1 = QrCodeGenerator::generate($data);
|
|
$matrix2 = QrCodeGenerator::generate($data);
|
|
|
|
expect($matrix1->toBinaryString())->toBe($matrix2->toBinaryString());
|
|
});
|
|
|
|
test('supports different data types', function () {
|
|
// Numeric-like data (but still byte mode in Phase 1)
|
|
$numeric = '1234567890';
|
|
$matrix1 = QrCodeGenerator::generate($numeric);
|
|
|
|
// Alphanumeric-like data
|
|
$alphanumeric = 'HELLO WORLD 123';
|
|
$matrix2 = QrCodeGenerator::generate($alphanumeric);
|
|
|
|
// Special characters
|
|
$special = 'test@example.com';
|
|
$matrix3 = QrCodeGenerator::generate($special);
|
|
|
|
// UTF-8
|
|
$utf8 = 'Hëllö Wörld';
|
|
$matrix4 = QrCodeGenerator::generate($utf8);
|
|
|
|
expect($matrix1)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
|
|
->and($matrix2)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
|
|
->and($matrix3)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class)
|
|
->and($matrix4)->toBeInstanceOf(\App\Framework\QrCode\ValueObjects\QrCodeMatrix::class);
|
|
});
|
|
|
|
// Instance method tests
|
|
test('can generate SVG using instance method', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$data = 'Hello World';
|
|
|
|
$svg = $generator->generateSvg($data);
|
|
|
|
expect($svg)->toBeString()
|
|
->and($svg)->toContain('<svg')
|
|
->and($svg)->toContain('</svg>');
|
|
});
|
|
|
|
test('can generate data URI using instance method', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$data = 'Hello World';
|
|
|
|
$dataUri = $generator->generateDataUri($data);
|
|
|
|
expect($dataUri)->toBeString()
|
|
->and($dataUri)->toStartWith('data:image/svg+xml;base64,');
|
|
});
|
|
|
|
test('can analyze data and get recommendations', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$data = 'Hello World';
|
|
|
|
$analysis = $generator->analyzeData($data);
|
|
|
|
expect($analysis)->toBeArray()
|
|
->and($analysis)->toHaveKey('dataLength')
|
|
->and($analysis)->toHaveKey('dataType')
|
|
->and($analysis)->toHaveKey('recommendedVersion')
|
|
->and($analysis)->toHaveKey('recommendedErrorLevel')
|
|
->and($analysis)->toHaveKey('encodingMode')
|
|
->and($analysis)->toHaveKey('matrixSize')
|
|
->and($analysis)->toHaveKey('capacity')
|
|
->and($analysis)->toHaveKey('efficiency')
|
|
->and($analysis['dataLength'])->toBe(strlen($data))
|
|
->and($analysis['recommendedVersion'])->toBeGreaterThan(0);
|
|
});
|
|
|
|
test('analyzeData detects URL type', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$url = 'https://example.com/test';
|
|
|
|
$analysis = $generator->analyzeData($url);
|
|
|
|
expect($analysis['dataType'])->toBe('url');
|
|
});
|
|
|
|
test('analyzeData detects TOTP type', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$totpUri = 'otpauth://totp/TestApp:user@example.com?secret=JBSWY3DPEHPK3PXP';
|
|
|
|
$analysis = $generator->analyzeData($totpUri);
|
|
|
|
expect($analysis['dataType'])->toBe('totp');
|
|
});
|
|
|
|
test('can generate TOTP QR code', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$totpUri = 'otpauth://totp/TestApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=TestApp';
|
|
|
|
$svg = $generator->generateTotpQrCode($totpUri);
|
|
|
|
expect($svg)->toBeString()
|
|
->and($svg)->toContain('<svg')
|
|
->and($svg)->toContain('</svg>');
|
|
});
|
|
|
|
test('generateSvg with explicit version', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$data = 'Test';
|
|
$version = QrCodeVersion::fromNumber(2);
|
|
|
|
$svg = $generator->generateSvg($data, ErrorCorrectionLevel::M, $version);
|
|
|
|
expect($svg)->toBeString()
|
|
->and($svg)->toContain('<svg');
|
|
});
|
|
|
|
test('generateDataUri with explicit version', function () {
|
|
$renderer = new QrCodeRenderer();
|
|
$generator = new QrCodeGenerator($renderer);
|
|
$data = 'Test';
|
|
$version = QrCodeVersion::fromNumber(3);
|
|
|
|
$dataUri = $generator->generateDataUri($data, ErrorCorrectionLevel::M, $version);
|
|
|
|
expect($dataUri)->toBeString()
|
|
->and($dataUri)->toStartWith('data:image/svg+xml;base64,');
|
|
});
|