diff options
Diffstat (limited to 'tests/app/Factories/ImageFactoryTest.php')
| -rw-r--r-- | tests/app/Factories/ImageFactoryTest.php | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/tests/app/Factories/ImageFactoryTest.php b/tests/app/Factories/ImageFactoryTest.php index 3f77d78c1c..bfe43cfffd 100644 --- a/tests/app/Factories/ImageFactoryTest.php +++ b/tests/app/Factories/ImageFactoryTest.php @@ -21,8 +21,13 @@ namespace Fisharebest\Webtrees\Factories; use Fisharebest\Webtrees\Services\PhpService; use Fisharebest\Webtrees\TestCase; +use League\Flysystem\Filesystem; +use League\Flysystem\FilesystemOperator; +use League\Flysystem\Local\LocalFilesystemAdapter; use PHPUnit\Framework\Attributes\CoversClass; +use function dirname; + #[CoversClass(ImageFactory::class)] class ImageFactoryTest extends TestCase { @@ -38,4 +43,132 @@ class ImageFactoryTest extends TestCase $response->getHeaderLine('content-security-policy'), ); } + + public function testFileResponseAddsDownloadHeaderForSafeSvg(): void + { + $php_service = $this->createStub(PhpService::class); + $image_factory = new ImageFactory($php_service); + $filesystem = $this->mediaFilesystem(); + + $php_service + ->method('extensionLoaded') + ->willReturnCallback(static fn (string $extension): bool => $extension === 'dom'); + + $response = $image_factory->fileResponse( + filesystem: $filesystem, + path: 'safe.svg', + download: true, + ); + + self::assertSame('image/svg+xml', $response->getHeaderLine('content-type')); + self::assertSame('default-src none', $response->getHeaderLine('content-security-policy')); + self::assertSame('attachment; filename="safe.svg"', $response->getHeaderLine('content-disposition')); + } + + public function testFileResponseBlocksSvgWithActiveContent(): void + { + $php_service = $this->createStub(PhpService::class); + $image_factory = new ImageFactory($php_service); + $filesystem = $this->mediaFilesystem(); + + $php_service + ->method('extensionLoaded') + ->willReturnCallback(static fn (string $extension): bool => $extension === 'dom'); + + $response = $image_factory->fileResponse( + filesystem: $filesystem, + path: 'unsafe.svg', + download: false, + ); + + self::assertSame('image/svg+xml', $response->getHeaderLine('content-type')); + self::assertSame('SVG image blocked due to XSS.', $response->getHeaderLine('x-image-exception')); + } + + public function testFileResponseBlocksSvgWithoutDomExtension(): void + { + $php_service = $this->createStub(PhpService::class); + $image_factory = new ImageFactory($php_service); + $filesystem = $this->mediaFilesystem(); + + $php_service + ->method('extensionLoaded') + ->willReturnCallback(static fn (string $extension): bool => false); + + $response = $image_factory->fileResponse( + filesystem: $filesystem, + path: 'safe.svg', + download: false, + ); + + self::assertSame('image/svg+xml', $response->getHeaderLine('content-type')); + self::assertSame( + 'Need the PHP dom extension to verify SVG files.', + $response->getHeaderLine('x-image-exception'), + ); + } + + public function testFileResponseReturnsNotFoundForMissingFile(): void + { + $image_factory = new ImageFactory(new PhpService()); + $filesystem = $this->mediaFilesystem(); + + $response = $image_factory->fileResponse( + filesystem: $filesystem, + path: 'missing.svg', + download: false, + ); + + self::assertSame('image/svg+xml', $response->getHeaderLine('content-type')); + self::assertStringContainsString( + 'UnableToReadFile', + $response->getHeaderLine('x-thumbnail-exception'), + ); + } + + public function testThumbnailResponseReturnsImageForJpeg(): void + { + $image_factory = new ImageFactory(new PhpService()); + $filesystem = $this->mediaFilesystem(); + + $response = $image_factory->thumbnailResponse( + filesystem: $filesystem, + path: 'Elizabeth_II.jpg', + width: 40, + height: 40, + fit: 'contain', + ); + + self::assertSame('image/jpeg', $response->getHeaderLine('content-type')); + self::assertSame('default-src none', $response->getHeaderLine('content-security-policy')); + self::assertSame('', $response->getHeaderLine('content-disposition')); + self::assertNotSame('', $response->getBody()->getContents()); + } + + public function testThumbnailResponseReturnsNotFoundForMissingFile(): void + { + $image_factory = new ImageFactory(new PhpService()); + $filesystem = $this->mediaFilesystem(); + + $response = $image_factory->thumbnailResponse( + filesystem: $filesystem, + path: 'missing.jpg', + width: 40, + height: 40, + fit: 'contain', + ); + + self::assertSame('image/svg+xml', $response->getHeaderLine('content-type')); + self::assertStringContainsString( + 'UnableTo', + $response->getHeaderLine('x-thumbnail-exception'), + ); + } + + private function mediaFilesystem(): FilesystemOperator + { + $root = dirname(__DIR__, 2) . '/data/media'; + + return new Filesystem(new LocalFilesystemAdapter($root)); + } } |
