3 use MediaWiki\MediaWikiServices
;
6 * @coversDefaultClass LocalRepo
9 class LocalRepoTest
extends MediaWikiIntegrationTestCase
{
11 * @param array $extraInfo To pass to LocalRepo constructor
13 private function newRepo( array $extraInfo = [] ) {
14 return new LocalRepo( $extraInfo +
[
16 'backend' => 'local-backend',
21 * @param array $extraInfo To pass to constructor
22 * @param bool $expected
23 * @dataProvider provideHasSha1Storage
24 * @covers ::__construct
26 public function testHasSha1Storage( array $extraInfo, $expected ) {
27 $this->assertSame( $expected, $this->newRepo( $extraInfo )->hasSha1Storage() );
30 public static function provideHasSha1Storage() {
33 [ [ 'storageLayout' => 'sha256' ], false ],
34 [ [ 'storageLayout' => 'sha1' ], true ],
39 * @param string $prefix 'img' or 'oi'
40 * @param string $expectedClass 'LocalFile' or 'OldLocalFile'
41 * @dataProvider provideNewFileFromRow
42 * @covers ::newFileFromRow
44 public function testNewFileFromRow( $prefix, $expectedClass ) {
45 $this->editPage( 'File:Test_file', 'Some description' );
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,
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' ) );
68 public static function provideNewFileFromRow() {
70 'img' => [ 'img', LocalFile
::class ],
71 'oi' => [ 'oi', OldLocalFile
::class ],
76 * @covers ::__construct
77 * @covers ::newFileFromRow
79 public function testNewFileFromRow_invalid() {
80 $this->setExpectedException( 'MWException', 'LocalRepo::newFileFromRow: invalid row' );
84 "img_timestamp" => '12345678910111',
86 "img_sha1" => sha1( '' ),
92 $file = $this->newRepo()->newFileFromRow( $row );
96 * @covers ::__construct
97 * @covers ::newFromArchiveName
99 public function testNewFromArchiveName() {
100 $this->editPage( 'File:Test_file', 'Some description' );
102 $file = $this->newRepo()->newFromArchiveName( 'Test_file', 'b' );
103 $this->assertInstanceOf( OldLocalFile
::class, $file );
104 $this->assertSame( 'Test_file', $file->getName() );
107 // TODO cleanupDeletedBatch, deletedFileHasKey, hiddenFileHasKey
110 * @covers ::__construct
111 * @covers ::cleanupDeletedBatch
113 public function testCleanupDeletedBatch_sha1Storage() {
114 $this->assertEquals( Status
::newGood(),
115 $this->newRepo( [ 'storageLayout' => 'sha1' ] )->cleanupDeletedBatch( [] ) );
119 * @param string $input
120 * @param string $expected
121 * @dataProvider provideGetHashFromKey
122 * @covers ::getHashFromKey
124 public function testGetHashFromKey( $input, $expected ) {
125 $this->assertSame( $expected, LocalRepo
::getHashFromKey( $input ) );
128 public static function provideGetHashFromKey() {
139 [ '01234567890123456789012345678901.x', '1234567890123456789012345678901' ],
144 * @covers ::__construct
145 * @covers ::checkRedirect
147 public function testCheckRedirect_nonRedirect() {
148 $this->editPage( 'File:Not a redirect', 'Not a redirect' );
150 $this->newRepo()->checkRedirect( Title
::makeTitle( NS_FILE
, 'Not a redirect' ) ) );
154 * @covers ::__construct
155 * @covers ::checkRedirect
156 * @covers ::getSharedCacheKey
158 public function testCheckRedirect_redirect() {
159 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
160 $this->assertEquals( 'File:Target',
161 $this->newRepo()->checkRedirect( Title
::makeTitle( NS_FILE
, 'Redirect' ) )
162 ->getPrefixedText() );
166 * @covers ::__construct
167 * @covers ::checkRedirect
168 * @covers ::getSharedCacheKey
169 * @covers ::getLocalCacheKey
171 public function testCheckRedirect_redirect_noWANCache() {
172 $this->markTestIncomplete( 'WANObjectCache::makeKey is final' );
174 $mockWan = $this->getMockBuilder( WANObjectCache
::class )
175 ->setConstructorArgs( [ [ 'cache' => new EmptyBagOStuff
] ] )
176 ->setMethods( [ 'makeKey' ] )
178 $mockWan->expects( $this->exactly( 2 ) )->method( 'makeKey' )->withConsecutive(
179 [ 'image_redirect', md5( 'Redirect' ) ],
180 [ 'filerepo', 'local', 'image_redirect', md5( 'Redirect' ) ]
181 )->will( $this->onConsecutiveCalls( false, 'somekey' ) );
183 $repo = $this->newRepo( [ 'wanCache' => $mockWan ] );
185 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
186 $this->assertEquals( 'File:Target',
187 $repo->checkRedirect( Title
::makeTitle( NS_FILE
, 'Redirect' ) )->getPrefixedText() );
191 * @covers ::__construct
192 * @covers ::checkRedirect
194 public function testCheckRedirect_invalidFile() {
195 $this->setExpectedException( MWException
::class, '`Notafile` is not a valid file title.' );
196 $this->newRepo()->checkRedirect( Title
::makeTitle( NS_MAIN
, 'Notafile' ) );
200 * @covers ::__construct
201 * @covers ::findBySha1
203 public function testFindBySha1() {
204 $this->markTestIncomplete( "Haven't figured out how to upload files yet" );
206 $repo = $this->newRepo();
208 $tmpFileFactory = MediaWikiServices
::getInstance()->getTempFSFileFactory();
209 foreach ( [ 'File1', 'File2', 'File3' ] as $name ) {
210 $fsFile = $tmpFileFactory->newTempFSFile( '' );
211 file_put_contents( $fsFile->getPath(), "$name contents" );
212 $localFile = $repo->newFile( $name );
213 $localFile->upload( $fsFile, 'Uploaded', "$name desc" );
218 * @covers ::__construct
219 * @covers ::getSharedCacheKey
220 * @covers ::checkRedirect
221 * @covers ::invalidateImageRedirect
223 public function testInvalidateImageRedirect() {
226 $repo = $this->newRepo(
227 [ 'wanCache' => new WANObjectCache( [ 'cache' => new HashBagOStuff
] ) ] );
229 $title = Title
::makeTitle( NS_FILE
, 'Redirect' );
231 $this->editPage( 'File:Redirect', '#REDIRECT [[File:Target]]' );
233 $this->assertSame( 'File:Target',
234 $repo->checkRedirect( $title )->getPrefixedText() );
236 $this->editPage( 'File:Redirect', 'No longer a redirect' );
238 $this->assertSame( 'File:Target',
239 $repo->checkRedirect( $title )->getPrefixedText() );
241 $repo->invalidateImageRedirect( $title );
242 $repo->getMasterDB()->commit();
244 $this->markTestIncomplete(
245 "Can't figure out how to get image redirect validation to take effect" );
247 $this->assertSame( false, $repo->checkRedirect( $title ) );
253 public function testGetInfo() {
254 $this->setMwGlobals( [
255 'wgFavicon' => '//example.com/favicon.ico',
256 'wgSitename' => 'Test my site',
259 $repo = $this->newRepo( [ 'favicon' => 'Hey, this option is ignored in LocalRepo!' ] );
263 'displayname' => 'Test my site',
268 'initialCapital' => true,
269 // XXX This assumes protocol-relative will get expanded to http instead of https
270 'favicon' => 'http://example.com/favicon.ico',
271 ], $repo->getInfo() );
274 // XXX The following getInfo tests are really testing FileRepo, not LocalRepo, but we want to
275 // make sure they're true for LocalRepo too. How should we do this? A trait?
280 public function testGetInfo_name() {
281 $this->assertSame( 'some-name',
282 $this->newRepo( [ 'name' => 'some-name' ] )->getInfo()['name'] );
288 public function testGetInfo_displayName() {
289 $this->assertSame( wfMessage( 'shared-repo' )->text(),
290 $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['displayname'] );
296 public function testGetInfo_displayNameCustomMsg() {
297 $this->editPage( 'MediaWiki:Shared-repo-name-not-local', 'Name to display please' );
298 // Allow the message to take effect
299 MediaWikiServices
::getInstance()->getMessageCache()->enable();
301 $this->assertSame( 'Name to display please',
302 $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['displayname'] );
308 public function testGetInfo_rootUrl() {
309 $this->assertSame( 'https://my.url',
310 $this->newRepo( [ 'url' => 'https://my.url' ] )->getInfo()['rootUrl'] );
316 public function testGetInfo_rootUrlCustomized() {
318 'https://my.url/some/sub/dir',
320 'url' => 'https://my.url',
321 'zones' => [ 'public' => [ 'url' => 'https://my.url/some/sub/dir' ] ],
322 ] )->getInfo()['rootUrl']
329 public function testGetInfo_local() {
330 $this->assertFalse( $this->newRepo( [ 'name' => 'not-local' ] )->getInfo()['local'] );
334 * @param string $setting
335 * @dataProvider provideGetInfo_optionalSettings
338 public function testGetInfo_optionalSettings( $setting ) {
339 $this->assertSame( 'dummy test value',
340 $this->newRepo( [ $setting => 'dummy test value' ] )->getInfo()[$setting] );
343 public static function provideGetInfo_optionalSettings() {
347 [ 'initialCapital' ],
351 [ 'fetchDescription' ],
352 [ 'descriptionCacheExpiry' ],
357 * @param string $method
358 * @param mixed ...$args
359 * @dataProvider provideSkipWriteOperationIfSha1
361 * @covers ::storeBatch
362 * @covers ::cleanupBatch
364 * @covers ::publishBatch
366 * @covers ::deleteBatch
367 * @covers ::skipWriteOperationIfSha1
369 public function testSkipWriteOperationIfSha1( $method, ...$args ) {
370 $repo = $this->newRepo( [ 'storageLayout' => 'sha1' ] );
371 $this->assertEquals( Status
::newGood(), $repo->$method( ...$args ) );
374 public static function provideSkipWriteOperationIfSha1() {
376 [ 'store', '', '', '' ],
377 [ 'storeBatch', [ '' ] ],
378 [ 'cleanupBatch', [ '' ] ],
379 [ 'publish', '', '', '' ],
380 [ 'publishBatch', [ '' ] ],
381 [ 'delete', '', '' ],
382 [ 'deleteBatch', [ '' ] ],