Merge "mediawiki.api: Remove console warning for legacy token type"
[mediawiki.git] / tests / phpunit / includes / filerepo / Thumbnail404EntryPointTest.php
blob14c7bc79694ca428127ffbab4bb59fe0fb1cab8a
1 <?php
3 use MediaWiki\Context\RequestContext;
4 use MediaWiki\FileRepo\Thumbnail404EntryPoint;
5 use MediaWiki\MainConfigNames;
6 use MediaWiki\Request\FauxRequest;
7 use MediaWiki\Tests\FileRepo\TestRepoTrait;
8 use MediaWiki\Tests\MockEnvironment;
9 use MediaWiki\Title\Title;
11 /**
12 * @covers \MediaWiki\FileRepo\Thumbnail404EntryPoint
13 * @group Database
15 class Thumbnail404EntryPointTest extends MediaWikiIntegrationTestCase {
17 use TestRepoTrait;
18 use MockHttpTrait;
20 private const PNG_MAGIC = "\x89\x50\x4e\x47";
21 private const JPEG_MAGIC = "\xff\xd8\xff\xe0";
23 private const IMAGES_DIR = __DIR__ . '/../../data/media';
25 /**
26 * will be called only once per test class
28 public function addDBDataOnce() {
29 // Set a named user account for the request context as the default,
30 // so that these tests do not fail with temp accounts enabled
31 RequestContext::getMain()->setUser( $this->getTestUser()->getUser() );
32 // Create mock repo with test files
33 $this->initTestRepoGroup();
35 $this->importFileToTestRepo( self::IMAGES_DIR . '/greyscale-png.png', 'Test.png' );
36 $this->importFileToTestRepo( self::IMAGES_DIR . '/Animated_PNG_example_bouncing_beach_ball.png' );
37 $this->importFileToTestRepo( self::IMAGES_DIR . '/test.jpg', 'Icon.jpg' );
39 // Create a second version of Test.png
40 $this->importFileToTestRepo( self::IMAGES_DIR . '/greyscale-dot-na-png.png', 'Test.png' );
42 // Create a redirect
43 $title = Title::makeTitle( NS_FILE, 'Redirect_to_Test.png' );
44 $this->editPage( $title, '#REDIRECT [[File:Test.png]]' );
47 public static function tearDownAfterClass(): void {
48 self::destroyTestRepo();
49 parent::tearDownAfterClass();
52 public function setUp(): void {
53 parent::setUp();
55 $this->installTestRepoGroup();
58 /**
59 * @param FauxRequest|string|null $request
61 * @return MockEnvironment
63 private function makeEnvironment( $request ): MockEnvironment {
64 if ( !$request ) {
65 $request = new FauxRequest();
68 if ( is_string( $request ) ) {
69 $req = new FauxRequest( [] );
70 $req->setRequestURL( $request );
71 $request = $req;
74 return new MockEnvironment( $request );
77 /**
78 * @param MockEnvironment|null $environment
79 * @param FauxRequest|RequestContext|string|array|null $request
81 * @return Thumbnail404EntryPoint
83 private function getEntryPoint(
84 ?MockEnvironment $environment = null,
85 $request = null
86 ) {
87 if ( !$request && $environment ) {
88 $request = $environment->getFauxRequest();
91 if ( $request instanceof RequestContext ) {
92 $context = $request;
93 $request = $context->getRequest();
94 } else {
95 $context = new RequestContext();
96 $context->setRequest( $request );
97 $context->setUser( $this->getTestUser()->getUser() );
100 if ( !$environment ) {
101 $environment = $this->makeEnvironment( $request );
104 $entryPoint = new Thumbnail404EntryPoint(
105 $context,
106 $environment,
107 $this->getServiceContainer()
110 $entryPoint->enableOutputCapture();
111 return $entryPoint;
114 public static function provideNotFound() {
115 yield 'non-existing image' => [
116 '/w/images/thumb/a/aa/Xyzzy.png/13px-Xyzzy.png',
119 yield 'malformed name' => [
120 '/w/images/thumb/x/xx/XyzzyXyzzy',
126 * @dataProvider provideNotFound
128 public function testNotFound( $req, $expectedStatus ) {
129 $env = $this->makeEnvironment( $req );
130 $entryPoint = $this->getEntryPoint( $env );
132 $entryPoint->run();
133 $output = $entryPoint->getCapturedOutput();
135 $env->assertStatusCode( $expectedStatus );
136 $env->assertHeaderValue(
137 'text/html; charset=utf-8',
138 'Content-Type'
141 $this->assertStringContainsString(
142 '<title>Error generating thumbnail</title>',
143 $output
147 public function testStreamFile() {
148 $file = $this->getTestRepo()->newFile( 'Test.png' );
149 $rel = $file->getRel();
150 $name = $file->getName();
152 $env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-$name" );
153 $entryPoint = $this->getEntryPoint( $env );
155 $entryPoint->run();
156 $output = $entryPoint->getCapturedOutput();
158 $env->assertStatusCode( 200 );
160 $this->assertThumbnail(
161 [ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
162 $output
165 return [ 'data' => $output, 'width' => 13 ];
168 public function testStreamFileWithThumbPath() {
169 $this->overrideConfigValue( MainConfigNames::ThumbPath, '/thumbnails/' );
171 $file = $this->getTestRepo()->newFile( 'Test.png' );
172 $rel = $file->getRel();
174 $env = $this->makeEnvironment( "/thumbnails/$rel/13px-Test.png" );
175 $entryPoint = $this->getEntryPoint( $env );
177 $entryPoint->run();
178 $output = $entryPoint->getCapturedOutput();
180 $env->assertStatusCode( 200 );
182 $this->assertThumbnail(
183 [ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
184 $output
188 public function testStreamFileWithLongName() {
189 $this->overrideConfigValue( MainConfigNames::VaryOnXFP, true );
191 // Note that abbrvThreshold is 16 per MockRepTrait
192 $file = $this->getTestRepo()->newFile( 'Animated_PNG_example_bouncing_beach_ball.png' );
193 $rel = $file->getRel();
194 $name = $file->getName();
196 // use abbreviated name
197 $env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-thumbnail.png" );
198 $entryPoint = $this->getEntryPoint( $env );
200 $entryPoint->run();
201 $output = $entryPoint->getCapturedOutput();
203 $env->assertStatusCode( 200, $output );
204 $env->assertHeaderValue( null, 'Vary' );
206 // use long name
207 $env = $this->makeEnvironment( "/w/images/thumb/$rel/13px-$name" );
208 $entryPoint = $this->getEntryPoint( $env );
210 $entryPoint->run();
211 $output = $entryPoint->getCapturedOutput();
213 $env->assertStatusCode( 301, $output );
214 $env->assertHeaderValue( 'X-Forwarded-Proto', 'Vary' );
216 $this->assertStringEndsWith(
217 "/w/images/thumb/$rel/13px-thumbnail.png",
218 $env->getFauxResponse()->getHeader( 'Location' )
223 * @depends testStreamFile
225 public function testStreamOldFile( array $latestThumbnailInfo ) {
226 $file = $this->getTestRepo()->newFile( 'Test.png' );
227 $history = $file->getHistory();
228 $oldFile = $history[0];
230 $this->assertNotSame(
231 $file->getSize(),
232 $oldFile->getSize(),
233 'Old and latest file version should not have the same size'
236 $curThumbPath = $file->getThumbPath( '13px-Test.png' );
237 $oldThumbPath = $oldFile->getThumbPath( '13px-Test.png' );
238 $this->assertFalse(
239 $file->getRepo()->getBackend()->fileExists( [ 'src' => $oldThumbPath ] ),
240 'Thumbnail for old file version does not exist'
243 $uri = '/w/images/thumb/' . $oldFile->getArchiveRel()
244 . '/' . $oldFile->getArchiveName() . '/13px-Test.png';
246 $env = $this->makeEnvironment( $uri );
247 $entryPoint = $this->getEntryPoint( $env );
249 $entryPoint->run();
250 $output = $entryPoint->getCapturedOutput();
251 $env->assertStatusCode( 200 );
253 $this->assertNotSame(
254 $file->getRepo()->getBackend()->getFileSha1Base36( [ 'src' => $oldThumbPath ] ),
255 $file->getRepo()->getBackend()->getFileSha1Base36( [ 'src' => $curThumbPath ] ),
256 "Thumbnails at $oldThumbPath and $curThumbPath should have different hashes"
259 $this->assertNotSame(
260 $latestThumbnailInfo['data'],
261 $output,
262 'Thumbnail for the old version should not be the same as the ' .
263 'thumbnail for the latest version'
266 $this->assertThumbnail(
267 [ 'magic' => self::PNG_MAGIC, 'width' => 13, ],
268 $output
272 public function testStreamTempFile() {
273 $user = $this->getTestUser()->getUser();
274 $stash = new UploadStash( $this->getTestRepo(), $user );
275 $file = $stash->stashFile( self::IMAGES_DIR . '/adobergb.jpg' );
277 $uri = '/w/images/thumb/temp/' . $file->getRel()
278 . '/13px-' . $file->getName();
280 $env = $this->makeEnvironment( $uri );
281 $entryPoint = $this->getEntryPoint( $env );
283 $entryPoint->run();
284 $output = $entryPoint->getCapturedOutput();
286 $env->assertStatusCode( 200 );
287 $this->assertThumbnail(
288 [ 'magic' => self::JPEG_MAGIC, 'width' => 13, ],
289 $output
293 public function testBadPath() {
294 $file = $this->getTestRepo()->newFile( 'Test.png' );
295 $rel = $file->getRel();
297 $uri = "/w/images/thumb/$rel/148px-XYZZY";
299 $env = $this->makeEnvironment( $uri );
300 $entryPoint = $this->getEntryPoint( $env );
302 $entryPoint->run();
303 $entryPoint->getCapturedOutput();
305 $env->assertStatusCode( 404 );
309 * @param array $props
310 * @param string $output binary data
312 private function assertThumbnail( array $props, string $output ): void {
313 if ( isset( $props['magic'] ) ) {
314 $this->assertStringStartsWith(
315 $props['magic'],
316 $output,
317 'Magic number should match'
321 if ( isset( $props['width'] ) && function_exists( 'getimagesizefromstring' ) ) {
322 [ $width, ] = getimagesizefromstring( $output );
323 $this->assertSame(
324 $props['width'],
325 $width