Fix use of GenderCache in ApiPageSet::processTitlesArray
[lhc/web/wiklou.git] / tests / phpunit / includes / filerepo / LocalRepoTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 /**
6 * @coversDefaultClass LocalRepo
7 * @group Database
8 */
9 class LocalRepoTest extends MediaWikiIntegrationTestCase {
10 /**
11 * @param array $extraInfo To pass to LocalRepo constructor
12 */
13 private function newRepo( array $extraInfo = [] ) {
14 return new LocalRepo( $extraInfo + [
15 'name' => 'local',
16 'backend' => 'local-backend',
17 ] );
18 }
19
20 /**
21 * @param array $extraInfo To pass to constructor
22 * @param bool $expected
23 * @dataProvider provideHasSha1Storage
24 * @covers ::__construct
25 */
26 public function testHasSha1Storage( array $extraInfo, $expected ) {
27 $this->assertSame( $expected, $this->newRepo( $extraInfo )->hasSha1Storage() );
28 }
29
30 public static function provideHasSha1Storage() {
31 return [
32 [ [], false ],
33 [ [ 'storageLayout' => 'sha256' ], false ],
34 [ [ 'storageLayout' => 'sha1' ], true ],
35 ];
36 }
37
38 /**
39 * @param string $prefix 'img' or 'oi'
40 * @param string $expectedClass 'LocalFile' or 'OldLocalFile'
41 * @dataProvider provideNewFileFromRow
42 * @covers ::newFileFromRow
43 */
44 public function testNewFileFromRow( $prefix, $expectedClass ) {
45 $this->editPage( 'File:Test_file', 'Some description' );
46
47 $row = (object)[
48 "{$prefix}_name" => 'Test_file',
49 // We cheat and include this for img_ too, it will be ignored
50 "{$prefix}_archive_name" => 'Archive_name',
51 "{$prefix}_user" => '1',
52 "{$prefix}_timestamp" => '12345678910111',
53 "{$prefix}_metadata" => '',
54 "{$prefix}_sha1" => sha1( '' ),
55 "{$prefix}_size" => '0',
56 "{$prefix}_height" => '0',
57 "{$prefix}_width" => '0',
58 "{$prefix}_bits" => '0',
59 "{$prefix}_description_text" => '',
60 "{$prefix}_description_data" => null,
61 ];
62 $file = $this->newRepo()->newFileFromRow( $row );
63 $this->assertInstanceOf( $expectedClass, $file );
64 $this->assertSame( 'Test_file', $file->getName() );
65 $this->assertSame( 1, $file->getUser( 'id' ) );
66 }
67
68 public static function provideNewFileFromRow() {
69 return [
70 'img' => [ 'img', LocalFile::class ],
71 'oi' => [ 'oi', OldLocalFile::class ],
72 ];
73 }
74
75 /**
76 * @covers ::__construct
77 * @covers ::newFileFromRow
78 */
79 public function testNewFileFromRow_invalid() {
80 $this->setExpectedException( 'MWException', 'LocalRepo::newFileFromRow: invalid row' );
81
82 $row = (object)[
83 "img_user" => '1',
84 "img_timestamp" => '12345678910111',
85 "img_metadata" => '',
86 "img_sha1" => sha1( '' ),
87 "img_size" => '0',
88 "img_height" => '0',
89 "img_width" => '0',
90 "img_bits" => '0',
91 ];
92 $file = $this->newRepo()->newFileFromRow( $row );
93 }
94
95 /**
96 * @covers ::__construct
97 * @covers ::newFromArchiveName
98 */
99 public function testNewFromArchiveName() {
100 $this->editPage( 'File:Test_file', 'Some description' );
101
102 $file = $this->newRepo()->newFromArchiveName( 'Test_file', 'b' );
103 $this->assertInstanceOf( OldLocalFile::class, $file );
104 $this->assertSame( 'Test_file', $file->getName() );
105 }
106
107 // TODO cleanupDeletedBatch, deletedFileHasKey, hiddenFileHasKey
108
109 /**
110 * @covers ::__construct
111 * @covers ::cleanupDeletedBatch
112 */
113 public function testCleanupDeletedBatch_sha1Storage() {
114 $this->assertEquals( Status::newGood(),
115 $this->newRepo( [ 'storageLayout' => 'sha1' ] )->cleanupDeletedBatch( [] ) );
116 }
117
118 /**
119 * @param string $input
120 * @param string $expected
121 * @dataProvider provideGetHashFromKey
122 * @covers ::getHashFromKey
123 */
124 public function testGetHashFromKey( $input, $expected ) {
125 $this->assertSame( $expected, LocalRepo::getHashFromKey( $input ) );
126 }
127
128 public static function provideGetHashFromKey() {
129 return [
130 [ '', false ],
131 [ '.', false ],
132 [ 'a.', 'a' ],
133 [ '.b', 'b' ],
134 [ '..c', 'c' ],
135 [ 'd.x', 'd' ],
136 [ '.e.x', 'e' ],
137 [ '..f.x', 'f' ],
138 [ 'g..x', 'g' ],
139 ];
140 }
141
142 /**
143 * @covers ::__construct
144 * @covers ::checkRedirect
145 */
146 public function testCheckRedirect_nonRedirect() {
147 $this->editPage( 'File:Not a redirect', 'Not a redirect' );
148 $this->assertFalse(
149 $this->newRepo()->checkRedirect( Title::makeTitle( NS_FILE, 'Not a redirect' ) ) );
150 }
151
152 /**
153 * @covers ::__construct
154 * @covers ::checkRedirect
155 * @covers ::getSharedCacheKey
156 */
157 public function testCheckRedirect_redirect() {
158 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
159 $this->assertEquals( 'File:Target',
160 $this->newRepo()->checkRedirect( Title::makeTitle( NS_FILE, 'Redirect' ) )
161 ->getPrefixedText() );
162 }
163
164 /**
165 * @covers ::__construct
166 * @covers ::checkRedirect
167 * @covers ::getSharedCacheKey
168 * @covers ::getLocalCacheKey
169 */
170 public function testCheckRedirect_redirect_noWANCache() {
171 $this->markTestIncomplete( 'WANObjectCache::makeKey is final' );
172
173 $mockWan = $this->getMockBuilder( WANObjectCache::class )
174 ->setConstructorArgs( [ [ 'cache' => new EmptyBagOStuff ] ] )
175 ->setMethods( [ 'makeKey' ] )
176 ->getMock();
177 $mockWan->expects( $this->exactly( 2 ) )->method( 'makeKey' )->withConsecutive(
178 [ 'image_redirect', md5( 'Redirect' ) ],
179 [ 'filerepo', 'local', 'image_redirect', md5( 'Redirect' ) ]
180 )->will( $this->onConsecutiveCalls( false, 'somekey' ) );
181
182 $repo = $this->newRepo( [ 'wanCache' => $mockWan ] );
183
184 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
185 $this->assertEquals( 'File:Target',
186 $repo->checkRedirect( Title::makeTitle( NS_FILE, 'Redirect' ) )->getPrefixedText() );
187 }
188
189 /**
190 * @covers ::__construct
191 * @covers ::checkRedirect
192 */
193 public function testCheckRedirect_invalidFile() {
194 $this->setExpectedException( MWException::class, '`Notafile` is not a valid file title.' );
195 $this->newRepo()->checkRedirect( Title::makeTitle( NS_MAIN, 'Notafile' ) );
196 }
197
198 /**
199 * @covers ::__construct
200 * @covers ::findBySha1
201 */
202 public function testFindBySha1() {
203 $this->markTestIncomplete( "Haven't figured out how to upload files yet" );
204
205 $repo = $this->newRepo();
206
207 $tmpFileFactory = MediaWikiServices::getInstance()->getTempFSFileFactory();
208 foreach ( [ 'File1', 'File2', 'File3' ] as $name ) {
209 $fsFile = $tmpFileFactory->newTempFSFile( '' );
210 file_put_contents( $fsFile->getPath(), "$name contents" );
211 $localFile = $repo->newFile( $name );
212 $localFile->upload( $fsFile, 'Uploaded', "$name desc" );
213 }
214 }
215
216 /**
217 * @covers ::__construct
218 * @covers ::getSharedCacheKey
219 * @covers ::checkRedirect
220 * @covers ::invalidateImageRedirect
221 */
222 public function testInvalidateImageRedirect() {
223 global $wgTestMe;
224 $wgTestMe = true;
225 $repo = $this->newRepo(
226 [ 'wanCache' => new WANObjectCache( [ 'cache' => new HashBagOStuff ] ) ] );
227
228 $title = Title::makeTitle( NS_FILE, 'Redirect' );
229
230 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
231
232 $this->assertSame( 'File:Target',
233 $repo->checkRedirect( $title )->getPrefixedText() );
234
235 $this->editPage( 'File:Redirect', 'No longer a redirect' );
236
237 $this->assertSame( 'File:Target',
238 $repo->checkRedirect( $title )->getPrefixedText() );
239
240 $repo->invalidateImageRedirect( $title );
241 $repo->getMasterDB()->commit();
242
243 $this->markTestIncomplete(
244 "Can't figure out how to get image redirect validation to take effect" );
245
246 $this->assertSame( false, $repo->checkRedirect( $title ) );
247 }
248
249 /**
250 * @covers ::getInfo
251 */
252 public function testGetInfo() {
253 $this->setMwGlobals( [
254 'wgFavicon' => '//example.com/favicon.ico',
255 'wgSitename' => 'Test my site',
256 ] );
257
258 $repo = $this->newRepo( [ 'favicon' => 'Hey, this option is ignored in LocalRepo!' ] );
259
260 $this->assertSame( [
261 'name' => 'local',
262 'displayname' => 'Test my site',
263 'rootUrl' => false,
264 'local' => true,
265 'url' => false,
266 'thumbUrl' => false,
267 'initialCapital' => true,
268 // XXX This assumes protocol-relative will get expanded to http instead of https
269 'favicon' => 'http://example.com/favicon.ico',
270 ], $repo->getInfo() );
271 }
272
273 // XXX The following getInfo tests are really testing FileRepo, not LocalRepo, but we want to
274 // make sure they're true for LocalRepo too. How should we do this? A trait?
275
276 /**
277 * @covers ::getInfo
278 */
279 public function testGetInfo_name() {
280 $this->assertSame( 'some-name',
281 $this->newRepo( [ 'name' => 'some-name' ] )->getInfo()['name'] );
282 }
283
284 /**
285 * @covers ::getInfo
286 */
287 public function testGetInfo_displayName() {
288 $this->assertSame( wfMessage( 'shared-repo' )->text(),
289 $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['displayname'] );
290 }
291
292 /**
293 * @covers ::getInfo
294 */
295 public function testGetInfo_displayNameCustomMsg() {
296 $this->editPage( 'MediaWiki:Shared-repo-name-not-local', 'Name to display please' );
297 // Allow the message to take effect
298 MediaWikiServices::getInstance()->getMessageCache()->enable();
299
300 $this->assertSame( 'Name to display please',
301 $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['displayname'] );
302 }
303
304 /**
305 * @covers ::getInfo
306 */
307 public function testGetInfo_rootUrl() {
308 $this->assertSame( 'https://my.url',
309 $this->newRepo( [ 'url' => 'https://my.url' ] )->getInfo()['rootUrl'] );
310 }
311
312 /**
313 * @covers ::getInfo
314 */
315 public function testGetInfo_rootUrlCustomized() {
316 $this->assertSame(
317 'https://my.url/some/sub/dir',
318 $this->newRepo( [
319 'url' => 'https://my.url',
320 'zones' => [ 'public' => [ 'url' => 'https://my.url/some/sub/dir' ] ],
321 ] )->getInfo()['rootUrl']
322 );
323 }
324
325 /**
326 * @covers ::getInfo
327 */
328 public function testGetInfo_local() {
329 $this->assertFalse( $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['local'] );
330 }
331
332 /**
333 * @param string $setting
334 * @dataProvider provideGetInfo_optionalSettings
335 * @covers ::getInfo
336 */
337 public function testGetInfo_optionalSettings( $setting ) {
338 $this->assertSame( 'dummy test value',
339 $this->newRepo( [ $setting => 'dummy test value' ] )->getInfo()[$setting] );
340 }
341
342 public static function provideGetInfo_optionalSettings() {
343 return [
344 [ 'url' ],
345 [ 'thumbUrl' ],
346 [ 'initialCapital' ],
347 [ 'descBaseUrl' ],
348 [ 'scriptDirUrl' ],
349 [ 'articleUrl' ],
350 [ 'fetchDescription' ],
351 [ 'descriptionCacheExpiry' ],
352 ];
353 }
354
355 /**
356 * @param string $method
357 * @param mixed ...$args
358 * @dataProvider provideSkipWriteOperationIfSha1
359 * @covers ::store
360 * @covers ::storeBatch
361 * @covers ::cleanupBatch
362 * @covers ::publish
363 * @covers ::publishBatch
364 * @covers ::delete
365 * @covers ::deleteBatch
366 * @covers ::skipWriteOperationIfSha1
367 */
368 public function testSkipWriteOperationIfSha1( $method, ...$args ) {
369 $repo = $this->newRepo( [ 'storageLayout' => 'sha1' ] );
370 $this->assertEquals( Status::newGood(), $repo->$method( ...$args ) );
371 }
372
373 public static function provideSkipWriteOperationIfSha1() {
374 return [
375 [ 'store', '', '', '' ],
376 [ 'storeBatch', [ '' ] ],
377 [ 'cleanupBatch', [ '' ] ],
378 [ 'publish', '', '', '' ],
379 [ 'publishBatch', [ '' ] ],
380 [ 'delete', '', '' ],
381 [ 'deleteBatch', [ '' ] ],
382 ];
383 }
384 }