BadFileLookup to replace wfIsBadImage
[lhc/web/wiklou.git] / tests / phpunit / unit / includes / BadFileLookupTest.php
1 <?php
2
3 use MediaWiki\BadFileLookup;
4
5 /**
6 * @coversDefaultClass MediaWiki\BadFileLookup
7 */
8 class BadFileLookupTest extends MediaWikiUnitTestCase {
9 /** Shared with GlobalWithDBTest */
10 const BLACKLIST = <<<WIKITEXT
11 Comment line, no effect [[File:Good.jpg]]
12 * Indented list is also a comment [[File:Good.jpg]]
13 * [[File:Bad.jpg]] except [[Nasty page]]
14 *[[Image:Bad2.jpg]] also works
15 * So does [[Bad3.jpg]]
16 * [[User:Bad4.jpg]] works although it is silly
17 * [[File:Redirect to good.jpg]] doesn't do anything if RepoGroup is working, because we only look at
18 the final name, but will work if RepoGroup returns null
19 * List line with no link
20 * [[Malformed title<>]] doesn't break anything, the line is ignored [[File:Good.jpg]]
21 * [[File:Bad5.jpg]] before [[malformed title<>]] doesn't ignore the line
22 WIKITEXT;
23
24 /** Shared with GlobalWithDBTest */
25 public static function badImageHook( $name, &$bad ) {
26 switch ( $name ) {
27 case 'Hook_bad.jpg':
28 case 'Redirect_to_hook_good.jpg':
29 $bad = true;
30 return false;
31
32 case 'Hook_good.jpg':
33 case 'Redirect_to_hook_bad.jpg':
34 $bad = false;
35 return false;
36 }
37
38 return true;
39 }
40
41 private function getMockRepoGroup() {
42 $mock = $this->createMock( RepoGroup::class );
43 $mock->expects( $this->once() )->method( 'findFile' )
44 ->will( $this->returnCallback( function ( $name ) {
45 $mockFile = $this->createMock( File::class );
46 $mockFile->expects( $this->once() )->method( 'getTitle' )
47 ->will( $this->returnCallback( function () use ( $name ) {
48 switch ( $name ) {
49 case 'Redirect to bad.jpg':
50 return new TitleValue( NS_FILE, 'Bad.jpg' );
51 case 'Redirect_to_good.jpg':
52 return new TitleValue( NS_FILE, 'Good.jpg' );
53 case 'Redirect to hook bad.jpg':
54 return new TitleValue( NS_FILE, 'Hook_bad.jpg' );
55 case 'Redirect to hook good.jpg':
56 return new TitleValue( NS_FILE, 'Hook_good.jpg' );
57 default:
58 return new TitleValue( NS_FILE, $name );
59 }
60 } ) );
61 $mockFile->expects( $this->never() )->method( $this->anythingBut( 'getTitle' ) );
62 return $mockFile;
63 } ) );
64 $mock->expects( $this->never() )->method( $this->anythingBut( 'findFile' ) );
65
66 return $mock;
67 }
68
69 /**
70 * Just returns null for every findFile().
71 */
72 private function getMockRepoGroupNull() {
73 $mock = $this->createMock( RepoGroup::class );
74 $mock->expects( $this->once() )->method( 'findFile' )->willReturn( null );
75 $mock->expects( $this->never() )->method( $this->anythingBut( 'findFile' ) );
76
77 return $mock;
78 }
79
80 private function getMockTitleParser() {
81 $mock = $this->createMock( TitleParser::class );
82 $mock->method( 'parseTitle' )->will( $this->returnCallback( function ( $text ) {
83 if ( strpos( $text, '<' ) !== false ) {
84 throw $this->createMock( MalformedTitleException::class );
85 }
86 if ( strpos( $text, ':' ) === false ) {
87 return new TitleValue( NS_MAIN, $text );
88 }
89 list( $ns, $text ) = explode( ':', $text );
90 switch ( $ns ) {
91 case 'Image':
92 case 'File':
93 $ns = NS_FILE;
94 break;
95
96 case 'User':
97 $ns = NS_USER;
98 break;
99 }
100 return new TitleValue( $ns, $text );
101 } ) );
102 $mock->expects( $this->never() )->method( $this->anythingBut( 'parseTitle' ) );
103
104 return $mock;
105 }
106
107 public function setUp() {
108 parent::setUp();
109
110 $this->setTemporaryHook( 'BadImage', __CLASS__ . '::badImageHook' );
111 }
112
113 /**
114 * @dataProvider provideIsBadFile
115 * @covers ::__construct
116 * @covers ::isBadFile
117 */
118 public function testIsBadFile( $name, $title, $expected ) {
119 $bfl = new BadFileLookup(
120 function () {
121 return self::BLACKLIST;
122 },
123 new EmptyBagOStuff,
124 $this->getMockRepoGroup(),
125 $this->getMockTitleParser()
126 );
127
128 $this->assertSame( $expected, $bfl->isBadFile( $name, $title ) );
129 }
130
131 /**
132 * @dataProvider provideIsBadFile
133 * @covers ::__construct
134 * @covers ::isBadFile
135 */
136 public function testIsBadFile_nullRepoGroup( $name, $title, $expected ) {
137 $bfl = new BadFileLookup(
138 function () {
139 return self::BLACKLIST;
140 },
141 new EmptyBagOStuff,
142 $this->getMockRepoGroupNull(),
143 $this->getMockTitleParser()
144 );
145
146 // Hack -- these expectations are reversed if the repo group returns null. In that case 1)
147 // we don't honor redirects, and 2) we don't replace spaces by underscores (which makes the
148 // hook not see 'Hook bad.jpg').
149 if ( in_array( $name, [
150 'Redirect to bad.jpg',
151 'Redirect_to_good.jpg',
152 'Hook bad.jpg',
153 'Redirect to hook bad.jpg',
154 ] ) ) {
155 $expected = !$expected;
156 }
157
158 $this->assertSame( $expected, $bfl->isBadFile( $name, $title ) );
159 }
160
161 /** Shared with GlobalWithDBTest */
162 public static function provideIsBadFile() {
163 return [
164 'No context page' => [ 'Bad.jpg', null, true ],
165 'Context page not whitelisted' =>
166 [ 'Bad.jpg', new TitleValue( NS_MAIN, 'A page' ), true ],
167 'Good image' => [ 'Good.jpg', null, false ],
168 'Whitelisted context page' =>
169 [ 'Bad.jpg', new TitleValue( NS_MAIN, 'Nasty page' ), false ],
170 'Bad image with Image:' => [ 'Image:Bad.jpg', null, false ],
171 'Bad image with File:' => [ 'File:Bad.jpg', null, false ],
172 'Bad image with Image: in blacklist' => [ 'Bad2.jpg', null, true ],
173 'Bad image without prefix in blacklist' => [ 'Bad3.jpg', null, true ],
174 'Bad image with different namespace in blacklist' => [ 'Bad4.jpg', null, true ],
175 'Redirect to bad image' => [ 'Redirect to bad.jpg', null, true ],
176 'Redirect to good image' => [ 'Redirect_to_good.jpg', null, false ],
177 'Hook says bad (with space)' => [ 'Hook bad.jpg', null, true ],
178 'Hook says bad (with underscore)' => [ 'Hook_bad.jpg', null, true ],
179 'Hook says good' => [ 'Hook good.jpg', null, false ],
180 'Redirect to hook bad image' => [ 'Redirect to hook bad.jpg', null, true ],
181 'Redirect to hook good image' => [ 'Redirect to hook good.jpg', null, false ],
182 'Malformed title doesn\'t break the line' => [ 'Bad5.jpg', null, true ],
183 ];
184 }
185 }