@@ -80,6 +80,9 @@ protected function tearDown(): void {
8080 if ( property_exists ( '\MediaWiki\MediaWikiServices ' , 'testUse418 ' ) ) {
8181 \MediaWiki \MediaWikiServices::$ testUse418 = false ;
8282 }
83+ if ( property_exists ( '\MediaWiki\MediaWikiServices ' , 'testProtectedActions ' ) ) {
84+ \MediaWiki \MediaWikiServices::$ testProtectedActions = null ;
85+ }
8386 // Only reset if the method exists (in our test stubs)
8487 if ( method_exists ( '\MediaWiki\MediaWikiServices ' , 'resetForTesting ' ) ) {
8588 \MediaWiki \MediaWikiServices::resetForTesting ();
@@ -167,6 +170,240 @@ public function testNonRevisionTypeAlwaysAllowed() {
167170 $ this ->assertTrue ( $ result );
168171 }
169172
173+ /**
174+ * @covers ::onMediaWikiPerformAction
175+ */
176+ public function testHistoryActionBlocksAnonymous () {
177+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
178+
179+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
180+ $ request ->method ( 'getVal ' )->willReturnCallback ( static function ( $ key , $ default = null ) {
181+ if ( $ key === 'action ' ) {
182+ return 'history ' ;
183+ }
184+ return $ default ;
185+ } );
186+
187+ $ user = $ this ->createMock ( self ::$ userClassName );
188+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
189+
190+ $ article = $ this ->createMock ( self ::$ articleClassName );
191+ $ title = $ this ->createMock ( self ::$ titleClassName );
192+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
193+
194+ $ runner = $ this ->getMockBuilder ( Hooks::class )
195+ ->onlyMethods ( [ 'denyAccess ' ] )
196+ ->getMock ();
197+ $ runner ->expects ( $ this ->once () )->method ( 'denyAccess ' );
198+
199+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
200+ $ this ->assertFalse ( $ result );
201+ }
202+
203+ /**
204+ * @covers ::onMediaWikiPerformAction
205+ */
206+ public function testHistoryActionAllowsLoggedIn () {
207+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
208+
209+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
210+ $ request ->method ( 'getVal ' )->willReturnCallback ( static function ( $ key , $ default = null ) {
211+ if ( $ key === 'action ' ) {
212+ return 'history ' ;
213+ }
214+ return $ default ;
215+ } );
216+
217+ $ user = $ this ->createMock ( self ::$ userClassName );
218+ $ user ->method ( 'isRegistered ' )->willReturn ( true );
219+
220+ $ article = $ this ->createMock ( self ::$ articleClassName );
221+ $ title = $ this ->createMock ( self ::$ titleClassName );
222+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
223+
224+ $ runner = $ this ->getMockBuilder ( Hooks::class )
225+ ->onlyMethods ( [ 'denyAccess ' ] )
226+ ->getMock ();
227+ $ runner ->expects ( $ this ->never () )->method ( 'denyAccess ' );
228+
229+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
230+ $ this ->assertTrue ( $ result );
231+ }
232+
233+ /**
234+ * @covers ::onMediaWikiPerformAction
235+ */
236+ public function testEmptyProtectedActionsAllowsHistory () {
237+ // Skip this test in MediaWiki environment - it requires service container
238+ if ( !property_exists ( '\MediaWiki\MediaWikiServices ' , 'testProtectedActions ' ) ) {
239+ $ this ->markTestSkipped (
240+ 'Test requires stub MediaWikiServices with testProtectedActions. Skipped in MediaWiki environment. '
241+ );
242+ }
243+
244+ // Set empty array to allow all actions
245+ \MediaWiki \MediaWikiServices::$ testProtectedActions = [];
246+
247+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
248+
249+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
250+ $ request ->method ( 'getVal ' )->willReturnCallback ( static function ( $ key , $ default = null ) {
251+ if ( $ key === 'action ' ) {
252+ return 'history ' ;
253+ }
254+ return $ default ;
255+ } );
256+
257+ $ user = $ this ->createMock ( self ::$ userClassName );
258+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
259+
260+ $ article = $ this ->createMock ( self ::$ articleClassName );
261+ $ title = $ this ->createMock ( self ::$ titleClassName );
262+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
263+
264+ $ runner = $ this ->getMockBuilder ( Hooks::class )
265+ ->onlyMethods ( [ 'denyAccess ' ] )
266+ ->getMock ();
267+ $ runner ->expects ( $ this ->never () )->method ( 'denyAccess ' );
268+
269+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
270+ $ this ->assertTrue ( $ result );
271+ }
272+
273+ /**
274+ * @covers ::onMediaWikiPerformAction
275+ */
276+ public function testCustomProtectedActionBlocks () {
277+ // Skip this test in MediaWiki environment - it requires service container
278+ if ( !property_exists ( '\MediaWiki\MediaWikiServices ' , 'testProtectedActions ' ) ) {
279+ $ this ->markTestSkipped (
280+ 'Test requires stub MediaWikiServices with testProtectedActions. Skipped in MediaWiki environment. '
281+ );
282+ }
283+
284+ // Set custom protected actions
285+ \MediaWiki \MediaWikiServices::$ testProtectedActions = [ 'edit ' , 'delete ' ];
286+
287+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
288+
289+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
290+ $ request ->method ( 'getVal ' )->willReturnCallback ( static function ( $ key , $ default = null ) {
291+ if ( $ key === 'action ' ) {
292+ return 'edit ' ;
293+ }
294+ return $ default ;
295+ } );
296+
297+ $ user = $ this ->createMock ( self ::$ userClassName );
298+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
299+
300+ $ article = $ this ->createMock ( self ::$ articleClassName );
301+ $ title = $ this ->createMock ( self ::$ titleClassName );
302+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
303+
304+ $ runner = $ this ->getMockBuilder ( Hooks::class )
305+ ->onlyMethods ( [ 'denyAccess ' ] )
306+ ->getMock ();
307+ $ runner ->expects ( $ this ->once () )->method ( 'denyAccess ' );
308+
309+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
310+ $ this ->assertFalse ( $ result );
311+ }
312+
313+ /**
314+ * @covers ::onMediaWikiPerformAction
315+ */
316+ public function testCustomProtectedActionAllowsOtherActions () {
317+ // Skip this test in MediaWiki environment - it requires service container
318+ if ( !property_exists ( '\MediaWiki\MediaWikiServices ' , 'testProtectedActions ' ) ) {
319+ $ this ->markTestSkipped (
320+ 'Test requires stub MediaWikiServices with testProtectedActions. Skipped in MediaWiki environment. '
321+ );
322+ }
323+
324+ // Set custom protected actions that don't include 'history'
325+ \MediaWiki \MediaWikiServices::$ testProtectedActions = [ 'edit ' , 'delete ' ];
326+
327+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
328+
329+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
330+ $ request ->method ( 'getVal ' )->willReturnCallback ( static function ( $ key , $ default = null ) {
331+ if ( $ key === 'action ' ) {
332+ return 'history ' ;
333+ }
334+ return $ default ;
335+ } );
336+
337+ $ user = $ this ->createMock ( self ::$ userClassName );
338+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
339+
340+ $ article = $ this ->createMock ( self ::$ articleClassName );
341+ $ title = $ this ->createMock ( self ::$ titleClassName );
342+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
343+
344+ $ runner = $ this ->getMockBuilder ( Hooks::class )
345+ ->onlyMethods ( [ 'denyAccess ' ] )
346+ ->getMock ();
347+ $ runner ->expects ( $ this ->never () )->method ( 'denyAccess ' );
348+
349+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
350+ $ this ->assertTrue ( $ result );
351+ }
352+
353+ /**
354+ * @covers ::onMediaWikiPerformAction
355+ */
356+ public function testDiffParameterBlocksAnonymous () {
357+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
358+
359+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
360+ $ request ->method ( 'getVal ' )->willReturnMap ( [
361+ [ 'diff ' , null , '1234 ' ],
362+ ] );
363+
364+ $ user = $ this ->createMock ( self ::$ userClassName );
365+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
366+
367+ $ article = $ this ->createMock ( self ::$ articleClassName );
368+ $ title = $ this ->createMock ( self ::$ titleClassName );
369+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
370+
371+ $ runner = $ this ->getMockBuilder ( Hooks::class )
372+ ->onlyMethods ( [ 'denyAccess ' ] )
373+ ->getMock ();
374+ $ runner ->expects ( $ this ->once () )->method ( 'denyAccess ' );
375+
376+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
377+ $ this ->assertFalse ( $ result );
378+ }
379+
380+ /**
381+ * @covers ::onMediaWikiPerformAction
382+ */
383+ public function testOldidParameterBlocksAnonymous () {
384+ $ output = $ this ->createMock ( self ::$ outputPageClassName );
385+
386+ $ request = $ this ->createMock ( self ::$ webRequestClassName );
387+ $ request ->method ( 'getVal ' )->willReturnMap ( [
388+ [ 'oldid ' , null , '5678 ' ],
389+ ] );
390+
391+ $ user = $ this ->createMock ( self ::$ userClassName );
392+ $ user ->method ( 'isRegistered ' )->willReturn ( false );
393+
394+ $ article = $ this ->createMock ( self ::$ articleClassName );
395+ $ title = $ this ->createMock ( self ::$ titleClassName );
396+ $ wiki = $ this ->createMock ( self ::$ actionEntryPointClassName );
397+
398+ $ runner = $ this ->getMockBuilder ( Hooks::class )
399+ ->onlyMethods ( [ 'denyAccess ' ] )
400+ ->getMock ();
401+ $ runner ->expects ( $ this ->once () )->method ( 'denyAccess ' );
402+
403+ $ result = $ runner ->onMediaWikiPerformAction ( $ output , $ article , $ title , $ user , $ request , $ wiki );
404+ $ this ->assertFalse ( $ result );
405+ }
406+
170407 /**
171408 * @covers ::onSpecialPageBeforeExecute
172409 * @dataProvider provideBlockedSpecialPages
0 commit comments