gplx/mediawiki/tests/phpunit/includes/filerepo/Thumbnail404EntryPointTest.php

312 lines
7.8 KiB
PHP

<?php
use MediaWiki\Context\RequestContext;
use MediaWiki\FileRepo\Thumbnail404EntryPoint;
use MediaWiki\MainConfigNames;
use MediaWiki\Request\FauxRequest;
use MediaWiki\Tests\FileRepo\TestRepoTrait;
use MediaWiki\Tests\MockEnvironment;
use MediaWiki\Title\Title;
/**
* @covers \MediaWiki\FileRepo\Thumbnail404EntryPoint
* @group Database
*/
class Thumbnail404EntryPointTest extends MediaWikiIntegrationTestCase {
use TestRepoTrait;
use MockHttpTrait;
private const PNG_MAGIC = "\x89\x50\x4e\x47";
private const JPEG_MAGIC = "\xff\xd8\xff\xe0";
private const IMAGES_DIR = __DIR__ . '/../../data/media';
/**
* will be called only once per test class
*/
public function addDBDataOnce() {
// Set a named user account for the request context as the default,
// so that these tests do not fail with temp accounts enabled
RequestContext::getMain()->setUser( $this->getTestUser()->getUser() );
// Create mock repo with test files
$this->initTestRepoGroup();
$this->importFileToTestRepo( self::IMAGES_DIR . '/greyscale-png.png', 'Test.png' );
$this->importFileToTestRepo( self::IMAGES_DIR . '/Animated_PNG_example_bouncing_beach_ball.png' );
$this->importFileToTestRepo( self::IMAGES_DIR . '/test.jpg', 'Icon.jpg' );
// Create a second version of Test.png
$this->importFileToTestRepo( self::IMAGES_DIR . '/greyscale-na-png.png', 'Test.png' );
// Create a redirect
$title = Title::makeTitle( NS_FILE, 'Redirect_to_Test.png' );
$this->editPage( $title, '#REDIRECT [[File:Test.png]]' );
}
public static function tearDownAfterClass(): void {
self::destroyTestRepo();
parent::tearDownAfterClass();
}
public function setUp(): void {
parent::setUp();
$this->installTestRepoGroup();
}
/**
* @param FauxRequest|string|null $request
*
* @return MockEnvironment
*/
private function makeEnvironment( $request ): MockEnvironment {
if ( !$request ) {
$request = new FauxRequest();
}
if ( is_string( $request ) ) {
$req = new FauxRequest( [] );
$req->setRequestURL( $request );
$request = $req;
}
return new MockEnvironment( $request );
}
/**
* @param MockEnvironment|null $environment
* @param FauxRequest|RequestContext|string|array|null $request
*
* @return Thumbnail404EntryPoint
*/
private function getEntryPoint(
?MockEnvironment $environment = null,
$request = null
) {
if ( !$request && $environment ) {
$request = $environment->getFauxRequest();
}
if ( $request instanceof RequestContext ) {
$context = $request;
$request = $context->getRequest();
} else {
$context = new RequestContext();
$context->setRequest( $request );
$context->setUser( $this->getTestUser()->getUser() );
}
if ( !$environment ) {
$environment = $this->makeEnvironment( $request );
}
$entryPoint = new Thumbnail404EntryPoint(
$context,
$environment,
$this->getServiceContainer()
);
$entryPoint->enableOutputCapture();
return $entryPoint;
}
public static function provideNotFound() {
yield 'non-existing image' => [
'/w/images/thumb/a/aa/Xyzzy.png/13px-Xyzzy.png',
404
];
yield 'malformed name' => [
'/w/images/thumb/x/xx/XyzzyXyzzy',
400
];
}
/**
* @dataProvider provideNotFound
*/
public function testNotFound( $req, $expectedStatus ) {
$env = $this->makeEnvironment( $req );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( $expectedStatus );
$env->assertHeaderValue(
'text/html; charset=utf-8',
'Content-Type'
);
$this->assertStringContainsString(
'<title>Error generating thumbnail</title>',
$output
);
}
public function testStreamFile() {
$file = $this->getTestRepo()->newFile( 'Test.png' );
$rel = $file->getRel();
$name = $file->getName();
$env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-$name" );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 200 );
$this->assertThumbnail(
[ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
$output
);
return [ 'data' => $output, 'width' => 13 ];
}
public function testStreamFileWithThumbPath() {
$this->overrideConfigValue( MainConfigNames::ThumbPath, '/thumbnails/' );
$file = $this->getTestRepo()->newFile( 'Test.png' );
$rel = $file->getRel();
$env = $this->makeEnvironment( "/thumbnails/$rel/13px-Test.png" );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 200 );
$this->assertThumbnail(
[ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
$output
);
}
public function testStreamFileWithLongName() {
$this->overrideConfigValue( MainConfigNames::VaryOnXFP, true );
// Note that abbrvThreshold is 16 per MockRepTrait
$file = $this->getTestRepo()->newFile( 'Animated_PNG_example_bouncing_beach_ball.png' );
$rel = $file->getRel();
$name = $file->getName();
// use abbreviated name
$env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-thumbnail.png" );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 200, $output );
$env->assertHeaderValue( null, 'Vary' );
// use long name
$env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-$name" );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 301, $output );
$env->assertHeaderValue( 'X-Forwarded-Proto', 'Vary' );
$this->assertStringEndsWith(
"/w/images/thumb/$rel/13px-thumbnail.png",
$env->getFauxResponse()->getHeader( 'Location' )
);
}
/**
* @depends testStreamFile
*/
public function testStreamOldFile( array $latestThumbnailInfo ) {
$file = $this->getTestRepo()->newFile( 'Test.png' );
$history = $file->getHistory();
$oldFile = $history[0];
$uri = '/w/images/thumb/' . $oldFile->getArchiveRel()
. '/' . $oldFile->getArchiveName() . '/13px-Test.png';
$env = $this->makeEnvironment( $uri );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 200 );
$this->assertNotSame(
$latestThumbnailInfo['data'],
$output,
'Thumbnail for the old version should not be the same as the ' .
'thumbnail for the latest version'
);
$this->assertThumbnail(
[ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
$output
);
}
public function testStreamTempFile() {
$user = $this->getTestUser()->getUser();
$stash = new UploadStash( $this->getTestRepo(), $user );
$file = $stash->stashFile( self::IMAGES_DIR . '/adobergb.jpg' );
$uri = '/w/images/thumb/temp/' . $file->getRel()
. '/13px-' . $file->getName();
$env = $this->makeEnvironment( $uri );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$output = $entryPoint->getCapturedOutput();
$env->assertStatusCode( 200 );
$this->assertThumbnail(
[ 'magic' => self::JPEG_MAGIC, 'width' => 13, ],
$output
);
}
public function testBadPath() {
$file = $this->getTestRepo()->newFile( 'Test.png' );
$rel = $file->getRel();
$uri = "/w/images/thumb/$rel/148px-XYZZY";
$env = $this->makeEnvironment( $uri );
$entryPoint = $this->getEntryPoint( $env );
$entryPoint->run();
$entryPoint->getCapturedOutput();
$env->assertStatusCode( 404 );
}
/**
* @param array $props
* @param string $output binary data
*/
private function assertThumbnail( array $props, string $output ): void {
if ( isset( $props['magic'] ) ) {
$this->assertStringStartsWith(
$props['magic'],
$output,
'Magic number should match'
);
}
if ( isset( $props['width'] ) && function_exists( 'getimagesizefromstring' ) ) {
[ $width, ] = getimagesizefromstring( $output );
$this->assertSame(
$props['width'],
$width
);
}
}
}