Merge "Drop cache interwiki"
[mediawiki.git] / tests / phpunit / includes / specials / SpecialBlockTest.php
blobd8405f3bfcae050273a71132efb9153f079a109b
1 <?php
3 use MediaWiki\Block\BlockRestrictionStore;
4 use MediaWiki\Block\DatabaseBlock;
5 use MediaWiki\Block\DatabaseBlockStore;
6 use MediaWiki\Block\Restriction\ActionRestriction;
7 use MediaWiki\Block\Restriction\NamespaceRestriction;
8 use MediaWiki\Block\Restriction\PageRestriction;
9 use MediaWiki\Content\ContentHandler;
10 use MediaWiki\Context\DerivativeContext;
11 use MediaWiki\Context\RequestContext;
12 use MediaWiki\MainConfigNames;
13 use MediaWiki\Request\FauxRequest;
14 use MediaWiki\Specials\SpecialBlock;
15 use MediaWiki\Status\Status;
16 use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
17 use MediaWiki\Title\Title;
18 use Wikimedia\Rdbms\IConnectionProvider;
19 use Wikimedia\TestingAccessWrapper;
21 /**
22 * @group Blocking
23 * @group Database
24 * @coversDefaultClass \MediaWiki\Specials\SpecialBlock
26 class SpecialBlockTest extends SpecialPageTestBase {
27 use MockAuthorityTrait;
29 /** @var DatabaseBlockStore */
30 private $blockStore;
32 /**
33 * @inheritDoc
35 protected function newSpecialPage() {
36 $services = $this->getServiceContainer();
37 return new SpecialBlock(
38 $services->getBlockUtils(),
39 $services->getBlockPermissionCheckerFactory(),
40 $services->getBlockUserFactory(),
41 $this->blockStore,
42 $services->getUserNameUtils(),
43 $services->getUserNamePrefixSearch(),
44 $services->getBlockActionInfo(),
45 $services->getTitleFormatter(),
46 $services->getNamespaceInfo()
50 protected function setUp(): void {
51 parent::setUp();
52 $this->blockStore = $this->getServiceContainer()->getDatabaseBlockStore();
55 /**
56 * @covers ::getFormFields
58 public function testGetFormFields() {
59 $this->overrideConfigValues( [
60 MainConfigNames::BlockAllowsUTEdit => true,
61 MainConfigNames::EnablePartialActionBlocks => true,
62 MainConfigNames::UseCodexSpecialBlock => false,
63 ] );
64 $page = $this->newSpecialPage();
65 $wrappedPage = TestingAccessWrapper::newFromObject( $page );
66 $fields = $wrappedPage->getFormFields();
67 $this->assertIsArray( $fields );
68 $this->assertArrayHasKey( 'Target', $fields );
69 $this->assertArrayHasKey( 'Expiry', $fields );
70 $this->assertArrayHasKey( 'Reason', $fields );
71 $this->assertArrayHasKey( 'CreateAccount', $fields );
72 $this->assertArrayHasKey( 'DisableUTEdit', $fields );
73 $this->assertArrayHasKey( 'AutoBlock', $fields );
74 $this->assertArrayHasKey( 'HardBlock', $fields );
75 $this->assertArrayHasKey( 'PreviousTarget', $fields );
76 $this->assertArrayHasKey( 'Confirm', $fields );
77 $this->assertArrayHasKey( 'EditingRestriction', $fields );
78 $this->assertArrayNotHasKey( 'options-messages', $fields['EditingRestriction'] );
79 $this->assertArrayNotHasKey( 'option-descriptions-messages', $fields['EditingRestriction'] );
80 $this->assertArrayHasKey( 'PageRestrictions', $fields );
81 $this->assertArrayHasKey( 'NamespaceRestrictions', $fields );
82 $this->assertArrayHasKey( 'ActionRestrictions', $fields );
85 /**
86 * @dataProvider provideGetFormFieldsCodex
87 * @covers ::getFormFields
88 * @covers ::execute
90 public function testCodexFormData( array $params, array $expected, bool $preErrors = false ): void {
91 $this->overrideConfigValues( [
92 MainConfigNames::BlockAllowsUTEdit => true,
93 MainConfigNames::EnablePartialActionBlocks => true,
94 MainConfigNames::UseCodexSpecialBlock => true,
95 ] );
96 $context = RequestContext::getMain();
97 $context->setRequest( new FauxRequest( array_merge( $params, [ 'uselang' => 'qqx' ] ) ) );
98 $context->setTitle( Title::newFromText( 'Block', NS_SPECIAL ) );
99 $context->setUser( $this->getTestSysop()->getUser() );
100 $page = $this->newSpecialPage();
101 $wrappedPage = TestingAccessWrapper::newFromObject( $page );
102 $wrappedPage->execute( null );
103 $actualJsConfigVars = $wrappedPage->getOutput()->getJsConfigVars();
104 $this->assertArrayContains( $expected, $actualJsConfigVars );
105 if ( $preErrors ) {
106 $this->assertArrayHasKey( 'blockPreErrors', $actualJsConfigVars );
107 } else {
108 $this->assertArrayNotHasKey( 'blockPreErrors', $actualJsConfigVars );
112 public static function provideGetFormFieldsCodex(): Generator {
113 yield 'wpExpiry 3 hours' => [
114 [ 'wpExpiry' => '3 hours' ],
115 [ 'blockExpiryPreset' => '3 hours' ],
117 yield 'wpExpiry indefinite' => [
118 [ 'wpExpiry' => 'indefinite' ],
119 [ 'blockExpiryPreset' => 'infinite' ],
121 yield 'wpExpiry YYYY-MM-DDTHH:mm:SS' => [
122 [ 'wpExpiry' => '2999-01-01T12:59:59' ],
123 [ 'blockExpiryPreset' => '2999-01-01T12:59' ],
125 yield 'wpExpiry YYYY-MM-DD HH:mm:SS' => [
126 [ 'wpExpiry' => '2999-01-01 12:59:59' ],
127 [ 'blockExpiryPreset' => '2999-01-01T12:59' ],
129 yield 'wpExpiry YYYYMMDDHHmmSS' => [
130 [ 'wpExpiry' => '29990101125959' ],
131 [ 'blockExpiryPreset' => '2999-01-01T12:59' ],
133 yield 'wpExpiry YYYY-MM-DDTHH:mm' => [
134 [ 'wpExpiry' => '2999-01-01T12:59' ],
135 [ 'blockExpiryPreset' => '2999-01-01T12:59' ],
137 yield 'wpTarget NonexistentUser' => [
138 [ 'wpTarget' => 'NonexistentUser' ],
139 [ 'blockTargetUser' => 'NonexistentUser' ],
140 true,
145 * @covers ::getFormFields
147 public function testGetFormFieldsActionRestrictionDisabled() {
148 $this->overrideConfigValue( MainConfigNames::EnablePartialActionBlocks, false );
149 $page = $this->newSpecialPage();
150 $wrappedPage = TestingAccessWrapper::newFromObject( $page );
151 $fields = $wrappedPage->getFormFields();
152 $this->assertArrayNotHasKey( 'ActionRestrictions', $fields );
156 * @covers ::maybeAlterFormDefaults
158 public function testMaybeAlterFormDefaults() {
159 $this->overrideConfigValue( MainConfigNames::BlockAllowsUTEdit, true );
161 $block = $this->insertBlock();
163 // Refresh the block from the database.
164 $block = $this->blockStore->newFromTarget( $block->getTargetUserIdentity() );
166 $page = $this->newSpecialPage();
168 $wrappedPage = TestingAccessWrapper::newFromObject( $page );
169 $wrappedPage->target = $block->getTargetUserIdentity();
170 $fields = $wrappedPage->getFormFields();
172 $this->assertSame( $block->getTargetName(), $fields['Target']['default'] );
173 $this->assertSame( $block->isHardblock(), $fields['HardBlock']['default'] );
174 $this->assertSame( $block->isCreateAccountBlocked(), $fields['CreateAccount']['default'] );
175 $this->assertSame( $block->isAutoblocking(), $fields['AutoBlock']['default'] );
176 $this->assertSame( !$block->isUsertalkEditAllowed(), $fields['DisableUTEdit']['default'] );
177 $this->assertSame( $block->getReasonComment()->text, $fields['Reason']['default'] );
178 $this->assertSame( 'infinite', $fields['Expiry']['default'] );
182 * @covers ::maybeAlterFormDefaults
184 public function testMaybeAlterFormDefaultsPartial() {
185 $this->overrideConfigValue( MainConfigNames::EnablePartialActionBlocks, true );
186 $badActor = $this->getTestUser()->getUser();
187 $sysop = $this->getTestSysop()->getUser();
188 $pageSaturn = $this->getExistingTestPage( 'Saturn' );
189 $pageMars = $this->getExistingTestPage( 'Mars' );
190 $actionId = 100;
192 $block = new DatabaseBlock( [
193 'address' => $badActor,
194 'by' => $sysop,
195 'expiry' => 'infinity',
196 'sitewide' => 0,
197 'enableAutoblock' => true,
198 ] );
200 $block->setRestrictions( [
201 new PageRestriction( 0, $pageSaturn->getId() ),
202 new PageRestriction( 0, $pageMars->getId() ),
203 new NamespaceRestriction( 0, NS_TALK ),
204 // Deleted page.
205 new PageRestriction( 0, 999999 ),
206 new ActionRestriction( 0, $actionId ),
207 ] );
209 $this->blockStore->insertBlock( $block );
211 // Refresh the block from the database.
212 $block = $this->blockStore->newFromTarget( $block->getTargetUserIdentity() );
214 $page = $this->newSpecialPage();
216 $wrappedPage = TestingAccessWrapper::newFromObject( $page );
217 $wrappedPage->target = $block->getTargetUserIdentity();
218 $fields = $wrappedPage->getFormFields();
220 $titles = [
221 $pageMars->getTitle()->getPrefixedText(),
222 $pageSaturn->getTitle()->getPrefixedText(),
225 $this->assertSame( $block->getTargetName(), $fields['Target']['default'] );
226 $this->assertSame( 'partial', $fields['EditingRestriction']['default'] );
227 $this->assertSame( implode( "\n", $titles ), $fields['PageRestrictions']['default'] );
228 $this->assertSame( [ $actionId ], $fields['ActionRestrictions']['default'] );
232 * @covers ::processForm
234 public function testProcessForm() {
235 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
236 $badActor = $this->getTestUser()->getUserIdentity();
237 $context = RequestContext::getMain();
238 $context->setUser( $this->getTestSysop()->getUser() );
240 $page = $this->newSpecialPage();
241 $reason = 'test';
242 $expiry = 'infinity';
243 $data = [
244 'Target' => (string)$badActor,
245 'Expiry' => 'infinity',
246 'Reason' => [
247 $reason,
249 'Confirm' => '1',
250 'CreateAccount' => '0',
251 'DisableUTEdit' => '0',
252 'DisableEmail' => '0',
253 'HardBlock' => '0',
254 'AutoBlock' => '1',
255 'HideUser' => '0',
256 'Watch' => '0',
258 $result = $page->processForm( $data, $context );
260 $this->assertTrue( $result );
262 $block = $this->blockStore->newFromTarget( $badActor );
263 $this->assertSame( $reason, $block->getReasonComment()->text );
264 $this->assertSame( $expiry, $block->getExpiry() );
268 * @covers ::processForm
270 public function testProcessFormExisting() {
271 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
272 $badActor = $this->getTestUser()->getUser();
273 $sysop = $this->getTestSysop()->getUser();
274 $context = RequestContext::getMain();
275 $context->setUser( $sysop );
277 // Create a block that will be updated.
278 $block = new DatabaseBlock( [
279 'address' => $badActor,
280 'by' => $sysop,
281 'expiry' => 'infinity',
282 'sitewide' => 0,
283 'enableAutoblock' => false,
284 ] );
285 $this->blockStore->insertBlock( $block );
287 $page = $this->newSpecialPage();
288 $reason = 'test';
289 $expiry = 'infinity';
290 $data = [
291 'Target' => (string)$badActor,
292 'Expiry' => 'infinity',
293 'Reason' => [
294 $reason,
296 'Confirm' => '1',
297 'CreateAccount' => '0',
298 'DisableUTEdit' => '0',
299 'DisableEmail' => '0',
300 'HardBlock' => '0',
301 'AutoBlock' => '1',
302 'HideUser' => '0',
303 'Watch' => '0',
305 $result = $page->processForm( $data, $context );
307 $this->assertTrue( $result );
309 $block = $this->blockStore->newFromTarget( $badActor );
310 $this->assertSame( $reason, $block->getReasonComment()->text );
311 $this->assertSame( $expiry, $block->getExpiry() );
312 $this->assertTrue( $block->isAutoblocking() );
316 * @covers ::processForm
318 public function testProcessFormRestrictions() {
319 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
320 $this->overrideConfigValue( MainConfigNames::EnablePartialActionBlocks, true );
322 $badActor = $this->getTestUser()->getUser();
323 $context = RequestContext::getMain();
324 $context->setUser( $this->getTestSysop()->getUser() );
326 $pageSaturn = $this->getExistingTestPage( 'Saturn' );
327 $pageMars = $this->getExistingTestPage( 'Mars' );
328 $actionId = 100;
330 $titles = [
331 $pageSaturn->getTitle()->getText(),
332 $pageMars->getTitle()->getText(),
335 $page = $this->newSpecialPage();
336 $reason = 'test';
337 $expiry = 'infinity';
338 $data = [
339 'Target' => (string)$badActor,
340 'Expiry' => 'infinity',
341 'Reason' => [
342 $reason,
344 'Confirm' => '1',
345 'CreateAccount' => '0',
346 'DisableUTEdit' => '0',
347 'DisableEmail' => '0',
348 'HardBlock' => '0',
349 'AutoBlock' => '1',
350 'HideUser' => '0',
351 'Watch' => '0',
352 'EditingRestriction' => 'partial',
353 'PageRestrictions' => implode( "\n", $titles ),
354 'NamespaceRestrictions' => '',
355 'ActionRestrictions' => [ $actionId ],
357 $result = $page->processForm( $data, $context );
359 $this->assertTrue( $result );
361 $block = $this->blockStore->newFromTarget( $badActor );
362 $this->assertSame( $reason, $block->getReasonComment()->text );
363 $this->assertSame( $expiry, $block->getExpiry() );
364 $this->assertCount( 3, $block->getRestrictions() );
365 $this->assertTrue( $this->getBlockRestrictionStore()->equals( $block->getRestrictions(), [
366 new PageRestriction( $block->getId(), $pageMars->getId() ),
367 new PageRestriction( $block->getId(), $pageSaturn->getId() ),
368 new ActionRestriction( $block->getId(), $actionId ),
369 ] ) );
373 * @covers ::processForm
375 public function testProcessFormRestrictionsChange() {
376 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
377 $badActor = $this->getTestUser()->getUser();
378 $context = RequestContext::getMain();
379 $context->setUser( $this->getTestSysop()->getUser() );
381 $pageSaturn = $this->getExistingTestPage( 'Saturn' );
382 $pageMars = $this->getExistingTestPage( 'Mars' );
384 $titles = [
385 $pageSaturn->getTitle()->getText(),
386 $pageMars->getTitle()->getText(),
389 // Create a partial block.
390 $page = $this->newSpecialPage();
391 $reason = 'test';
392 $expiry = 'infinity';
393 $data = [
394 'Target' => (string)$badActor,
395 'Expiry' => 'infinity',
396 'Reason' => [
397 $reason,
399 'Confirm' => '1',
400 'CreateAccount' => '1',
401 'DisableUTEdit' => '0',
402 'DisableEmail' => '0',
403 'HardBlock' => '0',
404 'AutoBlock' => '1',
405 'HideUser' => '0',
406 'Watch' => '0',
407 'EditingRestriction' => 'partial',
408 'PageRestrictions' => implode( "\n", $titles ),
409 'NamespaceRestrictions' => '',
411 $result = $page->processForm( $data, $context );
413 $this->assertTrue( $result );
415 $block = $this->blockStore->newFromTarget( $badActor );
416 $this->assertSame( $reason, $block->getReasonComment()->text );
417 $this->assertSame( $expiry, $block->getExpiry() );
418 $this->assertFalse( $block->isSitewide() );
419 $this->assertTrue( $block->isCreateAccountBlocked() );
420 $this->assertCount( 2, $block->getRestrictions() );
421 $this->assertTrue( $this->getBlockRestrictionStore()->equals( $block->getRestrictions(), [
422 new PageRestriction( $block->getId(), $pageMars->getId() ),
423 new PageRestriction( $block->getId(), $pageSaturn->getId() ),
424 ] ) );
426 // Remove a page from the partial block.
427 $data['PageRestrictions'] = $pageMars->getTitle()->getText();
428 $result = $page->processForm( $data, $context );
430 $this->assertTrue( $result );
432 $block = $this->blockStore->newFromTarget( $badActor );
433 $this->assertSame( $reason, $block->getReasonComment()->text );
434 $this->assertSame( $expiry, $block->getExpiry() );
435 $this->assertFalse( $block->isSitewide() );
436 $this->assertTrue( $block->isCreateAccountBlocked() );
437 $this->assertCount( 1, $block->getRestrictions() );
438 $this->assertTrue( $this->getBlockRestrictionStore()->equals( $block->getRestrictions(), [
439 new PageRestriction( $block->getId(), $pageMars->getId() ),
440 ] ) );
442 // Remove the last page from the block.
443 $data['PageRestrictions'] = '';
444 $result = $page->processForm( $data, $context );
446 $this->assertTrue( $result );
448 $block = $this->blockStore->newFromTarget( $badActor );
449 $this->assertSame( $reason, $block->getReasonComment()->text );
450 $this->assertSame( $expiry, $block->getExpiry() );
451 $this->assertFalse( $block->isSitewide() );
452 $this->assertTrue( $block->isCreateAccountBlocked() );
453 $this->assertSame( [], $block->getRestrictions() );
455 // Change to sitewide.
456 $data['EditingRestriction'] = 'sitewide';
457 $result = $page->processForm( $data, $context );
459 $this->assertTrue( $result );
461 $block = $this->blockStore->newFromTarget( $badActor );
462 $this->assertSame( $reason, $block->getReasonComment()->text );
463 $this->assertSame( $expiry, $block->getExpiry() );
464 $this->assertTrue( $block->isSitewide() );
465 $this->assertSame( [], $block->getRestrictions() );
467 // Ensure that there are no restrictions where the blockId is 0.
468 $count = $this->getDb()->newSelectQueryBuilder()
469 ->select( '*' )
470 ->from( 'ipblocks_restrictions' )
471 ->where( [ 'ir_ipb_id' => 0 ] )
472 ->caller( __METHOD__ )->fetchRowCount();
473 $this->assertSame( 0, $count );
477 * @dataProvider provideProcessFormUserTalkEditFlag
478 * @covers ::processForm
480 public function testProcessFormUserTalkEditFlag( $options, $expected ) {
481 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
482 $this->overrideConfigValue( MainConfigNames::BlockAllowsUTEdit, $options['configAllowsUserTalkEdit'] );
484 $performer = $this->getTestSysop()->getUser();
485 $target = $this->getTestUser()->getUser();
487 $context = new DerivativeContext( RequestContext::getMain() );
488 $context->setUser( $performer );
490 $data = [
491 'Target' => $target,
492 'PreviousTarget' => $target,
493 'Expiry' => 'infinity',
494 'CreateAccount' => '1',
495 'DisableEmail' => '0',
496 'HardBlock' => '0',
497 'AutoBlock' => '0',
498 'Watch' => '0',
499 'Confirm' => '1',
500 'DisableUTEdit' => $options['optionBlocksUserTalkEdit'],
503 if ( !$options['userTalkNamespaceBlocked'] ) {
504 $data['EditingRestriction'] = 'partial';
505 $data['PageRestrictions'] = '';
506 $data['NamespaceRestrictions'] = '';
509 $result = $this->newSpecialPage()->processForm(
510 $data,
511 $context
514 if ( is_string( $expected ) ) {
515 $this->assertStatusError( $expected, $result );
516 } else {
517 $block = $this->blockStore->newFromTarget( $target );
518 $this->assertSame( $expected, $block->isUsertalkEditAllowed() );
523 * Test cases for whether own user talk edit is allowed, with different combinations of:
524 * - whether user talk namespace blocked
525 * - config BlockAllowsUTEdit true/false
526 * - block option specifying whether to block own user talk edit
527 * For more about the desired behaviour, see T252892.
529 * @return array
531 public static function provideProcessFormUserTalkEditFlag() {
532 return [
533 'Always allowed if user talk namespace not blocked' => [
535 'userTalkNamespaceBlocked' => false,
536 'configAllowsUserTalkEdit' => true,
537 'optionBlocksUserTalkEdit' => false,
539 true,
541 'Always allowed if user talk namespace not blocked (config is false)' => [
543 'userTalkNamespaceBlocked' => false,
544 'configAllowsUserTalkEdit' => false,
545 'optionBlocksUserTalkEdit' => false,
547 true,
549 'Error if user talk namespace not blocked, but option blocks user talk edit' => [
551 'userTalkNamespaceBlocked' => false,
552 'configAllowsUserTalkEdit' => true,
553 'optionBlocksUserTalkEdit' => true,
555 'ipb-prevent-user-talk-edit',
557 'Always blocked if user talk namespace blocked and wgBlockAllowsUTEdit is false' => [
559 'userTalkNamespaceBlocked' => true,
560 'configAllowsUserTalkEdit' => false,
561 'optionBlocksUserTalkEdit' => false,
563 false,
565 'Option used if user talk namespace blocked and config is true (blocked)' => [
567 'userTalkNamespaceBlocked' => true,
568 'configAllowsUserTalkEdit' => true,
569 'optionBlocksUserTalkEdit' => true,
571 false,
573 'Option used if user talk namespace blocked and config is true (not blocked)' => [
575 'userTalkNamespaceBlocked' => true,
576 'configAllowsUserTalkEdit' => true,
577 'optionBlocksUserTalkEdit' => false,
579 true,
585 * @dataProvider provideProcessFormErrors
586 * @covers ::processForm
588 public function testProcessFormErrors( $data, $expected, $options = [] ) {
589 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
590 $this->overrideConfigValue( MainConfigNames::BlockAllowsUTEdit, true );
592 $performer = $this->getTestSysop()->getUser();
593 $target = !empty( $options['blockingSelf'] ) ? $performer : '1.2.3.4';
594 $defaultData = [
595 'Target' => $target,
596 'PreviousTarget' => $target,
597 'Expiry' => 'infinity',
598 'CreateAccount' => '0',
599 'DisableUTEdit' => '0',
600 'DisableEmail' => '0',
601 'HardBlock' => '0',
602 'AutoBlock' => '0',
603 'Confirm' => '0',
604 'Watch' => '0',
607 $context = new DerivativeContext( RequestContext::getMain() );
608 $context->setUser( $performer );
610 $result = $this->newSpecialPage()->processForm(
611 array_merge( $defaultData, $data ),
612 $context
615 if ( $result instanceof Status ) {
616 $this->assertStatusMessage( $expected, $result );
617 } else {
618 $error = is_array( $result[0] ) ? $result[0][0] : $result[0];
619 $this->assertEquals( $expected, $error );
623 public static function provideProcessFormErrors() {
624 return [
625 'Invalid expiry' => [
627 'Expiry' => 'invalid',
629 'ipb_expiry_invalid',
631 'Expiry is in the past' => [
633 'Expiry' => 'yesterday',
635 'ipb_expiry_old',
637 'Bad ip address' => [
639 'Target' => '1.2.3.4/1234',
641 'badipaddress',
643 'Edit user talk page invalid with no restrictions' => [
645 'EditingRestriction' => 'partial',
646 'DisableUTEdit' => '1',
647 'PageRestrictions' => '',
648 'NamespaceRestrictions' => '',
650 'ipb-prevent-user-talk-edit',
652 'Edit user talk page invalid with namespace restriction !== NS_USER_TALK ' => [
654 'EditingRestriction' => 'partial',
655 'DisableUTEdit' => '1',
656 'PageRestrictions' => '',
657 'NamespaceRestrictions' => NS_USER,
659 'ipb-prevent-user-talk-edit',
661 'Blocking self and target changed' => [
663 'PreviousTarget' => 'other',
664 'Confirm' => '1',
666 'ipb-blockingself',
668 'blockingSelf' => true,
671 'Blocking self and no confirm' => [
673 'ipb-blockingself',
675 'blockingSelf' => true,
678 'Empty expiry' => [
680 'Expiry' => '',
682 'ipb_expiry_invalid',
684 'Expiry valid but longer than 50 chars' => [
686 'Expiry' => '30th September 9999 19:19:19.532453 Europe/Amsterdam',
688 'ipb_expiry_invalid',
694 * @dataProvider provideProcessFormErrorsReblock
695 * @covers ::processForm
697 public function testProcessFormErrorsReblock( $data, $permissions, $expected ) {
698 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
699 $this->overrideConfigValue( MainConfigNames::BlockAllowsUTEdit, true );
701 $performer = $this->getTestSysop()->getUser();
702 $this->overrideUserPermissions( $performer, $permissions );
703 $blockedUser = $this->getTestUser()->getUser();
705 $block = new DatabaseBlock( [
706 'address' => $blockedUser,
707 'by' => $performer,
708 'hideName' => true,
709 ] );
710 $this->blockStore->insertBlock( $block );
712 // Matches the existing block
713 $defaultData = [
714 'Target' => $blockedUser->getName(),
715 'PreviousTarget' => $blockedUser->getName(),
716 'Expiry' => 'infinity',
717 'DisableUTEdit' => '1',
718 'CreateAccount' => '0',
719 'DisableEmail' => '0',
720 'HardBlock' => '0',
721 'AutoBlock' => '0',
722 'HideUser' => '1',
723 'Confirm' => '1',
724 'Watch' => '0',
727 $context = new DerivativeContext( RequestContext::getMain() );
728 $context->setUser( $performer );
730 $result = $this->newSpecialPage()->processForm(
731 array_merge( $defaultData, $data ),
732 $context
735 if ( $result instanceof Status ) {
736 $this->assertStatusMessage( $expected, $result );
737 } else {
738 $error = is_array( $result[0] ) ? $result[0][0] : $result[0];
739 $this->assertEquals( $expected, $error );
743 public static function provideProcessFormErrorsReblock() {
744 return [
745 'Reblock user with Confirm false' => [
747 // Avoid error for hiding user with confirm false
748 'HideUser' => '0',
749 'Confirm' => '0',
751 [ 'block', 'hideuser' ],
752 'ipb_already_blocked',
754 'Reblock user with Reblock false' => [
755 [ 'Reblock' => '0' ],
756 [ 'block', 'hideuser' ],
757 'ipb_already_blocked',
759 'Reblock with confirm True but target has changed' => [
760 [ 'PreviousTarget' => '1.2.3.4' ],
761 [ 'block', 'hideuser' ],
762 'ipb_already_blocked',
764 'Reblock with same block' => [
765 [ 'HideUser' => '1' ],
766 [ 'block', 'hideuser' ],
767 'ipb_already_blocked',
769 'Reblock hidden user with wrong permissions' => [
770 [ 'HideUser' => '0' ],
771 [ 'block', 'hideuser' => false ],
772 'cant-see-hidden-user',
778 * @dataProvider provideProcessFormErrorsHideUser
779 * @covers ::processForm
781 public function testProcessFormErrorsHideUser( $data, $permissions, $expected ) {
782 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
783 $performer = $this->getTestSysop()->getUser();
784 $this->overrideUserPermissions( $performer, array_merge( $permissions, [ 'block' ] ) );
786 $defaultData = [
787 'Target' => $this->getTestUser()->getUser(),
788 'HideUser' => '1',
789 'Expiry' => 'infinity',
790 'Confirm' => '1',
791 'CreateAccount' => '0',
792 'DisableUTEdit' => '0',
793 'DisableEmail' => '0',
794 'HardBlock' => '0',
795 'AutoBlock' => '0',
796 'Watch' => '0',
799 $context = new DerivativeContext( RequestContext::getMain() );
800 $context->setUser( $performer );
802 $result = $this->newSpecialPage()->processForm(
803 array_merge( $defaultData, $data ),
804 $context
807 if ( $result instanceof Status ) {
808 $this->assertStatusMessage( $expected, $result );
809 } else {
810 $error = is_array( $result[0] ) ? $result[0][0] : $result[0];
811 $this->assertEquals( $expected, $error );
815 public static function provideProcessFormErrorsHideUser() {
816 return [
817 'HideUser with wrong permissions' => [
819 [ 'hideuser' => '0' ],
820 'badaccess-group0',
822 'Hideuser with partial block' => [
823 [ 'EditingRestriction' => 'partial' ],
824 [ 'hideuser' ],
825 'ipb_hide_partial',
827 'Hideuser with finite expiry' => [
828 [ 'Expiry' => '1 hour' ],
829 [ 'hideuser' ],
830 'ipb_expiry_temp',
832 'Hideuser with no confirm' => [
833 [ 'Confirm' => '0' ],
834 [ 'hideuser' ],
835 'ipb-confirmhideuser',
841 * @covers ::processForm
843 public function testProcessFormErrorsHideUserProlific() {
844 $this->hideDeprecated( SpecialBlock::class . '::processForm' );
845 $this->overrideConfigValue( MainConfigNames::HideUserContribLimit, 0 );
847 $performer = $this->mockRegisteredUltimateAuthority();
848 $userToBlock = $this->getTestUser()->getUser();
849 $pageSaturn = $this->getExistingTestPage( 'Saturn' );
850 $pageSaturn->doUserEditContent(
851 ContentHandler::makeContent( 'content', $pageSaturn->getTitle() ),
852 $userToBlock,
853 'summary'
856 $context = new DerivativeContext( RequestContext::getMain() );
857 $context->setAuthority( $performer );
859 $result = $this->newSpecialPage()->processForm(
861 'Target' => $userToBlock,
862 'CreateAccount' => '1',
863 'HideUser' => '1',
864 'Expiry' => 'infinity',
865 'Confirm' => '1',
866 'DisableUTEdit' => '0',
867 'DisableEmail' => '0',
868 'HardBlock' => '0',
869 'AutoBlock' => '0',
870 'Watch' => '0',
872 $context
875 if ( $result instanceof Status ) {
876 $this->assertStatusMessage( 'ipb_hide_invalid', $result );
877 } else {
878 $error = is_array( $result[0] ) ? $result[0][0] : $result[0];
879 $this->assertEquals( 'ipb_hide_invalid', $error );
884 * @dataProvider provideGetTargetAndType
885 * @covers ::getTargetAndTypeInternal
887 public function testGetTargetAndType( $par, $requestData, $expectedTarget ) {
888 $request = new FauxRequest( $requestData );
889 /** @var SpecialBlock $page */
890 $page = TestingAccessWrapper::newFromObject( $this->newSpecialPage() );
891 [ $target, $type ] = $page->getTargetAndTypeInternal( $par, $request );
892 $this->assertSame( $expectedTarget, $target );
895 public static function provideGetTargetAndType() {
896 $invalidTarget = '';
897 return [
898 'Choose \'wpTarget\' parameter first' => [
899 '2.2.2.0/24',
901 'wpTarget' => '1.1.1.0/24',
902 'ip' => '3.3.3.0/24',
903 'wpBlockAddress' => '4.4.4.0/24',
905 '1.1.1.0/24',
907 'Choose subpage parameter second' => [
908 '2.2.2.0/24',
910 'wpTarget' => $invalidTarget,
911 'ip' => '3.3.3.0/24',
912 'wpBlockAddress' => '4.4.4.0/24',
914 '2.2.2.0/24',
916 'Choose \'ip\' parameter third' => [
917 $invalidTarget,
919 'wpTarget' => $invalidTarget,
920 'ip' => '3.3.3.0/24',
921 'wpBlockAddress' => '4.4.4.0/24',
923 '3.3.3.0/24',
925 'Choose \'wpBlockAddress\' parameter fourth' => [
926 $invalidTarget,
928 'wpTarget' => $invalidTarget,
929 'ip' => $invalidTarget,
930 'wpBlockAddress' => '4.4.4.0/24',
932 '4.4.4.0/24',
934 'Subpage, no valid request data' => [
935 '2.2.2.0/24',
937 '2.2.2.0/24',
939 'No valid request data or subpage parameter' => [
940 null,
942 null,
947 protected function insertBlock() {
948 $badActor = $this->getTestUser()->getUser();
949 $sysop = $this->getTestSysop()->getUser();
951 $block = new DatabaseBlock( [
952 'address' => $badActor,
953 'by' => $sysop,
954 'expiry' => 'infinity',
955 'sitewide' => 1,
956 'enableAutoblock' => true,
957 ] );
959 $this->blockStore->insertBlock( $block );
961 return $block;
965 * Get a BlockRestrictionStore instance
967 * @return BlockRestrictionStore
969 private function getBlockRestrictionStore(): BlockRestrictionStore {
970 $dbProvider = $this->createMock( IConnectionProvider::class );
972 return new BlockRestrictionStore( $dbProvider );