mediawiki-extensions-Nuke/tests/phpunit/integration/SpecialNukeTest.php

1026 lines
33 KiB
PHP
Raw Normal View History

<?php
namespace MediaWiki\Extension\Nuke\Test\Integration;
use ErrorPageError;
use MediaWiki\Context\RequestContext;
use MediaWiki\Extension\Nuke\SpecialNuke;
use MediaWiki\Permissions\UltimateAuthority;
use MediaWiki\Request\FauxRequest;
use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use PermissionsError;
use SpecialPageTestBase;
use UploadFromFile;
use UserBlockedError;
/**
* @group Database
* @covers \MediaWiki\Extension\Nuke\SpecialNuke
*/
class SpecialNukeTest extends SpecialPageTestBase {
use TempUserTestTrait;
protected function newSpecialPage(): SpecialNuke {
$services = $this->getServiceContainer();
return new SpecialNuke(
$services->getJobQueueGroup(),
$services->getDBLoadBalancerFactory(),
$services->getPermissionManager(),
$services->getRepoGroup(),
$services->getUserFactory(),
$services->getUserOptionsLookup(),
$services->getUserNamePrefixSearch(),
$services->getUserNameUtils(),
$services->getNamespaceInfo(),
$services->getContentLanguage(),
$services->getService( 'NukeIPLookup' )
);
}
/**
* Ensure that the prompt prevents a user blocked from deleting
* pages from accessing the form.
*
* @return void
*/
public function testBlocked() {
$user = $this->getTestSysop()->getUser();
$performer = new UltimateAuthority( $user );
// Self-blocks should still prevent the form from being shown
$this->getServiceContainer()
->getBlockUserFactory()
->newBlockUser( $user, $performer, 'infinity', 'SpecialNukeTest::testBlocked' )
->placeBlockUnsafe();
$this->expectException( UserBlockedError::class );
$this->executeSpecialPage( '', null, 'qqx', $performer );
$this->getServiceContainer()
->getUnblockUserFactory()
->newUnblockUser( $user, $performer, 'SpecialNukeTest::testBlocked' )
->unblockUnsafe();
}
public function testProtectedPage() {
$pages = [];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Page456', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Page789', 'Test', NS_MAIN )[ 'title' ];
$this->overrideConfigValues( [
"GroupPermissions" => [
"testgroup" => [
"nuke" => true,
"delete" => true
]
]
] );
$services = $this->getServiceContainer();
$page = $services->getWikiPageFactory()
->newFromTitle( $pages[2] );
$restrictions = [];
foreach ( $services->getRestrictionStore()->listApplicableRestrictionTypes( $page ) as $type ) {
$restrictions[$type] = "sysop";
}
$cascade = false;
$page->doUpdateRestrictions(
$restrictions,
[],
$cascade,
"test",
$this->getTestSysop()->getUser()
);
$testUser = $this->getTestUser( [ "testgroup" ] );
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'other',
'wpReason' => 'Reason',
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $testUser->getUser()->getEditToken(),
], true );
$this->expectException( PermissionsError::class );
$this->executeSpecialPage( '', $request, 'qqx', $testUser->getAuthority() );
foreach ( $pages as $checkedPage ) {
$this->assertTrue( $checkedPage->exists() );
}
}
public function testPrompt() {
$admin = $this->getTestSysop()->getUser();
$this->disableAutoCreateTempUser();
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', null, 'qqx', $performer );
$this->assertStringContainsString( '(nuke-summary)', $html );
$this->assertStringContainsString( '(nuke-tools)', $html );
}
/**
* Ensure that the prompt prevents a nuke user without the checkuser-temporary-account permission
* from performing CheckUser IP lookups
*
* @return void
*/
public function testPromptCheckUserNoPermission() {
$this->expectException( PermissionsError::class );
$this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' );
$this->enableAutoCreateTempUser();
$ip = '1.2.3.4';
$adminUser = $this->getTestSysop()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
$permissions = $permissionManager->getUserPermissions( $adminUser );
$permissions = array_diff( $permissions, [ 'checkuser-temporary-account' ] );
$permissionManager->overrideUserRightsForTesting( $adminUser,
$permissions
);
$performer = new UltimateAuthority( $adminUser );
$request = new FauxRequest( [
'target' => $ip,
'action' => 'submit',
], true );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
}
/**
* Ensure that the prompt prevents a nuke user who hasn't accepted the agreement
* from performing CheckUser IP lookups
*
* @return void
*/
public function testPromptCheckUserNoPreference() {
$this->expectException( ErrorPageError::class );
$this->expectExceptionMessage(
'To view temporary account contributions for an IP, please accept' .
' the agreement in [[Special:Preferences|your preferences]].'
);
$this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' );
$this->enableAutoCreateTempUser();
$ip = '1.2.3.4';
$this->overrideConfigValues( [
'GroupPermissions' => [
'testgroup' => [
'nuke' => true,
'checkuser-temporary-account' => true
]
]
] );
$adminUser = $this->getTestUser( [ 'testgroup' ] )->getUser();
$request = new FauxRequest( [
'target' => $ip,
'action' => 'submit',
], true );
$adminPerformer = new UltimateAuthority( $adminUser );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer );
}
/**
* Ensure that the prompt displays the correct messages when
* temp accounts and CheckUser are enabled
*
* @return void
*/
public function testPromptWithCheckUser() {
$this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' );
$admin = $this->getTestSysop()->getUser();
$this->enableAutoCreateTempUser();
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', null, 'qqx', $performer );
$this->assertStringContainsString( '(nuke-summary)', $html );
$this->assertStringContainsString( '(nuke-tools-tempaccount)', $html );
}
public function testPromptTarget() {
$testUser = $this->getTestUser();
$performer = $testUser->getAuthority();
$this->editPage( 'Target1', 'test', "", NS_MAIN, $performer );
$this->editPage( 'Target2', 'test', "", NS_MAIN, $performer );
$adminUser = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'target' => $testUser->getUser()->getName()
] );
$adminPerformer = new UltimateAuthority( $adminUser );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer );
$this->assertStringContainsString( 'Target1', $html );
$this->assertStringContainsString( 'Target2', $html );
}
/**
* Ensure that the prompt works with anon IP searches when
* temp accounts are disabled
*
* @return void
*/
public function testPromptTargetAnonUser() {
$this->disableAutoCreateTempUser( [ 'known' => false ] );
$ip = '127.0.0.1';
$testUser = $this->getServiceContainer()->getUserFactory()->newAnonymous( $ip );
$performer = new UltimateAuthority( $testUser );
$this->editPage( 'Target1', 'test', "", NS_MAIN, $performer );
$this->editPage( 'Target2', 'test', "", NS_MAIN, $performer );
$adminUser = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'target' => $testUser->getUser()->getName()
] );
$adminPerformer = new UltimateAuthority( $adminUser );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer );
$this->assertStringContainsString( '(nuke-list:', $html );
$this->assertStringContainsString( 'Target1', $html );
$this->assertStringContainsString( 'Target2', $html );
$this->assertEquals( 2, substr_count( $html, '(nuke-editby: 127.0.0.1)' ) );
}
/**
* Ensure that the prompt returns temp accounts from IP lookups when
* temp accounts and CheckUser are enabled
*
* @return void
*/
public function testPromptTargetCheckUser() {
$this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' );
$this->enableAutoCreateTempUser();
$ip = '1.2.3.4';
RequestContext::getMain()->getRequest()->setIP( $ip );
$testUser = $this->getServiceContainer()->getTempUserCreator()
->create( null, new FauxRequest() )->getUser();
$this->editPage( 'Target1', 'test', "", NS_MAIN, $testUser );
$this->editPage( 'Target2', 'test', "", NS_MAIN, $testUser );
$adminUser = $this->getTestSysop()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
$permissionManager->overrideUserRightsForTesting( $adminUser,
array_merge(
$permissionManager->getUserPermissions( $adminUser ),
[ 'checkuser-temporary-account-no-preference' ]
) );
$request = new FauxRequest( [
'target' => $ip,
'action' => 'submit',
], true );
$adminPerformer = new UltimateAuthority( $adminUser );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer );
$this->assertEquals( 2, substr_count( $html, '(nuke-editby: ~2024-1)' ) );
$this->assertStringContainsString( '(nuke-list-tempaccount:', $html );
$this->assertStringContainsString( 'Target1', $html );
$this->assertStringContainsString( 'Target2', $html );
}
/**
* Ensure that the prompt returns temp accounts and IP accounts from IP lookups when
* temp accounts and CheckUser are enabled and Anonymous IP accounts exist
*
* @return void
*/
public function testPromptTargetCheckUserMixed() {
$this->markTestSkippedIfExtensionNotLoaded( 'CheckUser' );
$this->disableAutoCreateTempUser( [ 'known' => false ] );
$ip = '1.2.3.4';
$testUser = $this->getServiceContainer()->getUserFactory()->newAnonymous( $ip );
$performer = new UltimateAuthority( $testUser );
// create a page as an anonymous IP user
$this->editPage( 'Target1', 'test', "", NS_MAIN, $performer );
$this->enableAutoCreateTempUser();
RequestContext::getMain()->getRequest()->setIP( $ip );
$testUser = $this->getServiceContainer()->getTempUserCreator()
->create( null, new FauxRequest() )->getUser();
$performer = new UltimateAuthority( $testUser );
// create a page as a temp user
$this->editPage( 'Target2', 'test', "", NS_MAIN, $performer );
$adminUser = $this->getTestSysop()->getUser();
$permissionManager = $this->getServiceContainer()->getPermissionManager();
$permissionManager->overrideUserRightsForTesting( $adminUser,
array_merge(
$permissionManager->getUserPermissions( $adminUser ),
[ 'checkuser-temporary-account-no-preference' ]
) );
$request = new FauxRequest( [
'target' => $ip,
'action' => 'submit',
], true );
$adminPerformer = new UltimateAuthority( $adminUser );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $adminPerformer );
$this->assertSame( 1, substr_count( $html, ' (nuke-editby: ~2024-1)' ) );
$this->assertSame( 1, substr_count( $html, ' (nuke-editby: 1.2.3.4)' ) );
// They should all show up together
$this->assertStringContainsString( '(nuke-list-tempaccount:', $html );
$this->assertStringContainsString( 'Target1', $html );
$this->assertStringContainsString( 'Target2', $html );
}
public function testListNoPagesGlobal() {
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'ThisPageShouldNotExist-' . rand()
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( '(nuke-nopages-global)', $html );
$this->assertStringNotContainsString( '(nuke-nopages)', $html );
}
public function testListNoPagesUser() {
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'target' => 'ThisPageShouldNotExist-' . rand()
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringNotContainsString( '(nuke-nopages-global)', $html );
$this->assertStringContainsString( 'nuke-nopages', $html );
}
public function testListNamespace() {
$this->editPage( 'NukeUserPageTarget', 'test', '', NS_USER );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'NukeUserPageTarget',
'namespace' => NS_USER
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$expectedTitle = Title::makeTitle( NS_USER, 'NukeUserPageTarget' )
->getPrefixedText();
$this->assertStringContainsString( $expectedTitle, $html );
}
public function testListTalk() {
$this->editPage( 'NukeTalkPageTarget', 'test', '', NS_TALK );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'NukeTalkPageTarget'
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$expectedTitle = Title::makeTitle( NS_TALK, 'NukeTalkPageTarget' )
->getPrefixedText();
$this->assertStringContainsString( $expectedTitle, $html );
}
public function testListCapitalizedNamespace() {
$this->overrideConfigValues( [
'CapitalLinks' => false,
'CapitalLinkOverrides' => []
] );
$this->editPage( 'uncapsTarget', 'test' );
$this->editPage( 'UncapsTarget', 'test' );
$admin = $this->getTestSysop()->getUser();
$expectedTitle = Title::makeTitle( NS_MAIN, 'uncapsTarget' )
->getPrefixedText();
$shouldMatch = [
"%ncapsTarget",
"u%",
"uncapsTarget"
];
$shouldNotMatch = [
"UncapsTarget"
];
foreach ( $shouldMatch as $match ) {
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'uncapsTarget'
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( $expectedTitle, $html, "match: $match" );
foreach ( $shouldNotMatch as $noMatch ) {
$this->assertStringNotContainsString( $noMatch, $html );
}
}
}
public function testListCapitalizedNamespaceOverrides() {
$overriddenNamespaces = [
NS_PROJECT => true,
NS_MEDIAWIKI => true,
NS_USER => true,
];
$this->overrideConfigValues( [
'CapitalLinks' => false,
'CapitalLinkOverrides' => $overriddenNamespaces
] );
$overriddenNamespaces[ NS_MAIN ] = false;
foreach ( $overriddenNamespaces as $ns => $override ) {
$this->editPage( "UncapsTarget" . $ns, 'test', '', $ns );
// If capital links for this ns is `true`, then the existing page should be edited.
$this->editPage( "uncapsTarget" . $ns, 'test2', '', $ns );
}
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'u%'
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( "uncapsTarget" . NS_MAIN, $html );
// Check that the overridden namespaces are included in the search
$this->assertStringContainsString( "UncapsTarget" . NS_PROJECT, $html );
$this->assertStringContainsString( "UncapsTarget" . NS_MEDIAWIKI, $html );
$this->assertStringContainsString( "UncapsTarget" . NS_USER, $html );
$this->assertStringNotContainsString( "UncapsTarget" . NS_MAIN, $html );
// Check that the non-overridden namespaces are not included in the search
$this->assertStringNotContainsString( "uncapsTarget" . NS_PROJECT, $html );
$this->assertStringNotContainsString( "uncapsTarget" . NS_MEDIAWIKI, $html );
$this->assertStringNotContainsString( "uncapsTarget" . NS_USER, $html );
}
/**
* Check if Nuke still works with languages with complicated capitalization
* or no capitalization.
*
* Since the first letter of each title should be capitalized, searching for the
* lowercase version should still yield the uppercase version, or the same character
* if casing does not exist for the language.
*
* @return void
*/
public function testListCapitalized() {
/** @noinspection SpellCheckingInspection */
$testedLanguages = [
"ee" => [
"Ülemiste",
"ülemiste"
],
"zh" => "你好",
"ja" => "にほんご",
];
foreach ( $testedLanguages as $lang => $testData ) {
[ $created, $wanted ] = is_array( $testData ) ?
$testData : [ $testData, $testData ];
$this->overrideConfigValue( 'LanguageCode', $lang );
$this->editPage( $created, 'test' );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => $wanted
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( $created, $html );
}
}
public function testListLimit() {
$this->editPage( 'Page1', 'test' );
$this->editPage( 'Page2', 'test' );
$this->editPage( 'Page3', 'test' );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'Page%',
'limit' => 2
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertEquals( 2, substr_count( $html, '<li>' ) );
}
public function testListLimitWithHooks() {
$this->editPage( 'Page1', 'test' );
$this->editPage( 'Page2', 'test' );
$this->editPage( 'Page3', 'test' );
$this->setTemporaryHook( "NukeGetNewPages", static function ( $_1, $_2, $_3, $_4, &$pages ) {
$pages[] = "Page3";
return true;
} );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'Page%',
'limit' => 2
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertEquals( 2, substr_count( $html, '<li>' ) );
}
public function testListMaxAge() {
// 1 day
$maxAge = 86400;
$this->overrideConfigValues( [
'NukeMaxAge' => $maxAge,
'RCMaxAge' => $maxAge
] );
// Will never show up. If it does, the max age isn't being applied at all.
$page1Status = $this->editPage( 'Page1', 'test' );
// Will show up conditionally (see below).
$page2Status = $this->editPage( 'Page2', 'test' );
// Will always show up.
$this->editPage( 'Page3', 'test' );
// Prepare database connection and update query builder
$dbw = $this->getServiceContainer()->getConnectionProvider()->getPrimaryDatabase();
$uqb = $dbw->newUpdateQueryBuilder()
->update( 'revision' )
->caller( __METHOD__ );
// Manually change the rev_timestamp of Page1's creation in the database.
( clone $uqb )
->set( [ 'rev_timestamp' => $dbw->timestamp( time() - ( $maxAge * 3 ) ) ] )
->where( [
'rev_id' => $page1Status->getNewRevision()->getId()
] )->execute();
// Manually change the rev_timestamp of Page2's creation in the database.
( clone $uqb )
->set( [ 'rev_timestamp' => $dbw->timestamp( time() - $maxAge - 60 ) ] )
->where( [
'rev_id' => $page2Status->getNewRevision()->getId()
] )->execute();
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => 'Page%',
'limit' => 2
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringNotContainsString( 'Page1', $html );
$this->assertStringNotContainsString( 'Page2', $html );
$this->assertStringContainsString( 'Page3', $html );
// Now check if resetting $wgNukeMaxAge will show Page2.
// This should follow $wgRCMaxAge, which will include the page.
$this->overrideConfigValues( [
'RCMaxAge' => $maxAge * 2,
'NukeMaxAge' => 0
] );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringNotContainsString( 'Page1', $html );
$this->assertStringContainsString( 'Page2', $html );
$this->assertStringContainsString( 'Page3', $html );
// Now check if expanding just $wgNukeMaxAge will show Page2.
$this->overrideConfigValues( [
'RCMaxAge' => $maxAge,
'NukeMaxAge' => $maxAge * 2
] );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringNotContainsString( 'Page1', $html );
$this->assertStringContainsString( 'Page2', $html );
$this->assertStringContainsString( 'Page3', $html );
}
public function testExecutePattern() {
// Test that matching wildcards works, and that escaping wildcards works as documented
// at https://www.mediawiki.org/wiki/Help:Extension:Nuke
$this->editPage( '%PositiveNukeTest123', 'test' );
$this->editPage( 'NegativeNukeTest123', 'test' );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'pattern' => '\\%PositiveNukeTest%',
'wpFormIdentifier' => 'massdelete',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( 'PositiveNukeTest123', $html );
$this->assertStringNotContainsString( 'NegativeNukeTest123', $html );
}
public function testListFiles() {
$testFileName = $this->uploadTestFile()['title']->getPrefixedText();
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'namespace' => NS_FILE
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
// The title should be in the list
$this->assertStringContainsString( $testFileName, $html );
// There should also be an image preview
$this->assertStringContainsString( "<img src", $html );
}
public function testUserPages() {
$user = $this->getTestUser()->getUser();
$this->insertPage( 'Page123', 'Test', NS_MAIN, $user );
$this->insertPage( 'Paging456', 'Test', NS_MAIN, $user );
$this->insertPage( 'Should not show', 'No show' );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'target' => $user->getName(),
'wpFormIdentifier' => 'massdelete',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( 'Page123', $html );
$this->assertStringContainsString( 'Paging456', $html );
$this->assertStringNotContainsString( 'Should not show', $html );
}
public function testNamespaces() {
$this->insertPage( 'Page123', 'Test', NS_MAIN );
$this->insertPage( 'Paging456', 'Test', NS_MAIN );
$this->insertPage( 'Should not show', 'No show', NS_TALK );
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'submit',
'namespace' => NS_MAIN,
'wpFormIdentifier' => 'massdelete',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertStringContainsString( 'Page123', $html );
$this->assertStringContainsString( 'Paging456', $html );
$this->assertStringNotContainsString( 'Should not show', $html );
}
public function testDelete() {
$pages = [];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Paging456', 'Test', NS_MAIN )[ 'title' ];
$admin = $this->getTestSysop()->getUser();
$fauxReason = "Reason " . wfRandomString();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Vandalism',
'wpReason' => $fauxReason,
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$deleteCount = count( $pages );
$this->assertStringContainsString( "(nuke-delete-summary: $deleteCount)", $html );
$this->assertStringContainsString( '(nuke-deletion-queued: Page123)', $html );
$this->assertStringContainsString( '(nuke-deletion-queued: Paging456)', $html );
// Pre-check to confirm that the page hasn't been deleted yet
// "mw-tag-marker-nuke" is the CSS class for the "Nuke" tag's <span> on Special:Log.
$this->assertStringNotContainsString( 'mw-tag-marker-nuke', $this->getDeleteLogHtml() );
// Ensure all delete jobs are run
$this->runJobs();
$deleteLogHtml = $this->getDeleteLogHtml();
$this->assertStringContainsString( 'Vandalism', $deleteLogHtml );
$this->assertStringContainsString( 'mw-tag-marker-nuke', $deleteLogHtml );
$this->assertStringContainsString( $fauxReason, $deleteLogHtml );
}
public function testDeleteTarget() {
$pages = [];
$testUser = $this->getTestUser( "user" )->getUser();
$testUserName = $testUser->getName();
$pages[] = $this->uploadTestFile( $testUser )[ 'title' ];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN, $testUser )[ 'title' ];
$pages[] = $this->insertPage( 'Paging456', 'Test', NS_MAIN, $testUser )[ 'title' ];
$admin = $this->getTestSysop()->getUser();
$fauxReason = "Reason " . wfRandomString();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Vandalism',
'wpReason' => $fauxReason,
'target' => $testUserName,
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$deleteCount = count( $pages );
$this->assertStringContainsString(
"(nuke-delete-summary-user: $deleteCount, $testUserName)",
$html
);
}
public function testDeleteTargetAnon() {
$pages = [];
$testUser = $this->getTestUser( "user" )->getUser();
$testUserName = $testUser->getName();
$pages[] = $this->uploadTestFile( $testUser )[ 'title' ];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN, $testUser )[ 'title' ];
$pages[] = $this->insertPage( 'Paging456', 'Test', NS_MAIN, $testUser )[ 'title' ];
$admin = $this->getTestSysop()->getUser();
$fauxReason = "Reason " . wfRandomString();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Vandalism',
'wpReason' => $fauxReason,
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$deleteCount = count( $pages );
$this->assertStringContainsString(
"(nuke-delete-summary: $deleteCount)",
$html
);
}
public function testDeleteSkipped() {
$pages = [];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Paging 456', 'Test', NS_MAIN )[ 'title' ];
$skippedPage = $this->insertPage( 'Page 789', 'Test', NS_MAIN )['title'];
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Vandalism',
'originalPageList' => implode( '|', $pages ),
'pages' => [ $skippedPage->getPrefixedDBkey() ],
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$skippedCount = count( $pages );
$this->assertStringContainsString( '(nuke-delete-summary: 1)', $html );
$this->assertStringContainsString( "(nuke-skipped-summary: $skippedCount)", $html );
// Ensure all delete jobs are run
$this->runJobs();
// Make sure that those pages are not in the deletion log.
$deleteLogHtml = $this->getDeleteLogHtml();
foreach ( $pages as $title ) {
$this->assertStringNotContainsString( $title->getPrefixedText(), $deleteLogHtml );
}
$this->assertStringContainsString( $skippedPage->getPrefixedText(), $deleteLogHtml );
}
public function testDeleteDropdownReason() {
$pages = [];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Paging456', 'Test', NS_MAIN )[ 'title' ];
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Vandalism',
'wpReason' => "",
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
$this->executeSpecialPage( '', $request, 'qqx', $performer );
// Ensure all jobs are run
$this->runJobs();
// Check logs
$this->assertStringContainsString( "Vandalism", $this->getDeleteLogHtml() );
}
public function testDeleteCustomReason() {
$pages = [];
$pages[] = $this->insertPage( 'Page123', 'Test', NS_MAIN )[ 'title' ];
$pages[] = $this->insertPage( 'Paging456', 'Test', NS_MAIN )[ 'title' ];
$admin = $this->getTestSysop()->getUser();
$fauxReason = "Reason " . wfRandomString();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'other',
'wpReason' => $fauxReason,
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
$this->executeSpecialPage( '', $request, 'qqx', $performer );
// Ensure all jobs are run
$this->runJobs();
// Check logs
$this->assertStringContainsString( $fauxReason, $this->getDeleteLogHtml() );
}
public function testDeleteFiles() {
$testFileName = $this->uploadTestFile()['title']->getPrefixedText();
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Reason',
'wpReason' => 'Reason',
'pages' => [ $testFileName ],
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getEditToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
// Files are deleted instantly
$this->assertStringContainsString( "nuke-deleted", $html );
$this->assertStringContainsString( $testFileName, $html );
}
public function testDeleteHook() {
$wasCalled = false;
$pages = [];
$pages[] = $this->insertPage( 'Bad article', 'test', NS_MAIN )['title'];
$pages[] = $this->insertPage( 'DO NOT DELETE', 'test', NS_MAIN )['title'];
$pages[] = $this->insertPage( 'Bad article 2', 'test', NS_MAIN )['title'];
$this->setTemporaryHook(
"NukeDeletePage",
static function ( Title $title, $reason, &$deletionResult ) use ( &$wasCalled ) {
$wasCalled = true;
$deletionResult = $title->getPrefixedText() !== "DO NOT DELETE";
return $title->getPrefixedText() === "Bad article 2";
}
);
$admin = $this->getTestSysop()->getUser();
$request = new FauxRequest( [
'action' => 'delete',
'wpDeleteReasonList' => 'Reason',
'wpReason' => 'Reason',
'pages' => $pages,
'wpFormIdentifier' => 'nukelist',
'wpEditToken' => $admin->getRequest()->getSession()->getToken(),
], true );
$performer = new UltimateAuthority( $admin );
[ $html ] = $this->executeSpecialPage( '', $request, 'qqx', $performer );
$this->assertTrue( $wasCalled );
$this->assertStringContainsString( '(nuke-deleted: Bad article)', $html );
$this->assertStringContainsString( '(nuke-not-deleted: DO NOT DELETE)', $html );
$this->assertStringContainsString( '(nuke-deletion-queued: Bad article 2)', $html );
}
public function testSearchSubpages() {
$user = $this->getTestUser()->getUser();
$username = $user->getName();
$truncatedUsername = substr( $username, 0, 2 );
$searchResults1 = $this->newSpecialPage()
->prefixSearchSubpages( $truncatedUsername, 10, 0 );
$this->assertArrayContains( [ $username ], $searchResults1 );
$searchResults2 = $this->newSpecialPage()
->prefixSearchSubpages( "", 10, 0 );
$this->assertCount( 0, $searchResults2 );
}
/**
* Upload a test file.
*
* @param ?User|null $user
* @return array Title object and page id
*/
private function uploadTestFile( ?User $user = null ): array {
$exampleFilePath = realpath( __DIR__ . "/../../assets/Example.png" );
$tempFilePath = $this->getNewTempFile();
copy( $exampleFilePath, $tempFilePath );
$title = Title::makeTitle( NS_FILE, "Example " . rand() . ".png" );
$request = new FauxRequest( [], true );
$request->setUpload( 'wpUploadFile', [
'name' => $title->getText(),
'type' => 'image/png',
'tmp_name' => $tempFilePath,
'size' => filesize( $tempFilePath ),
'error' => UPLOAD_ERR_OK
] );
$upload = UploadFromFile::createFromRequest( $request );
$uploadStatus = $upload->performUpload(
"test",
false,
false,
$user ?? $this->getTestUser( "user" )->getUser()
);
$this->assertTrue( $uploadStatus->isOK() );
$this->getServiceContainer()->getJobRunner()->run( [] );
return [
'title' => $title,
'id' => $title->getId()
];
}
private function getDeleteLogHtml(): string {
$services = $this->getServiceContainer();
// TODO: Make this use qqx so tests can be checked against system message keys.
$specialLog = $services->getSpecialPageFactory()->getPage( 'Log' );
$specialLog->execute( "delete" );
return $specialLog->getOutput()->getHTML();
}
}