3 use MediaWiki\CommentStore\CommentStoreComment
;
4 use MediaWiki\Content\ContentHandler
;
5 use MediaWiki\Page\PageIdentityValue
;
6 use MediaWiki\Page\UndeletePage
;
7 use MediaWiki\Revision\SlotRecord
;
8 use MediaWiki\Storage\PageUpdatedEvent
;
9 use MediaWiki\Tests\Language\LanguageEventIngressSpyTrait
;
10 use MediaWiki\Tests\recentchanges\ChangeTrackingEventIngressSpyTrait
;
11 use MediaWiki\Tests\Search\SearchEventIngressSpyTrait
;
12 use MediaWiki\Tests\User\TempUser\TempUserTestTrait
;
13 use MediaWiki\Title\Title
;
14 use MediaWiki\User\UserIdentityValue
;
15 use PHPUnit\Framework\Assert
;
16 use Wikimedia\IPUtils
;
20 * @coversDefaultClass \MediaWiki\Page\UndeletePage
22 class UndeletePageTest
extends MediaWikiIntegrationTestCase
{
24 use TempUserTestTrait
;
25 use ChangeTrackingEventIngressSpyTrait
;
26 use SearchEventIngressSpyTrait
;
27 use LanguageEventIngressSpyTrait
;
35 * A logged out user who edited the page before it was archived.
40 protected function setUp(): void
{
43 $this->ipEditor
= '2001:DB8:0:0:0:0:0:1';
44 $this->setupPage( 'UndeletePageTest_thePage', NS_MAIN
, ' ' );
45 $this->setupPage( 'UndeletePageTest_thePage', NS_TALK
, ' ' );
49 * @param string $titleText
51 * @param string $content
53 private function setupPage( string $titleText, int $ns, string $content ): void
{
54 $this->disableAutoCreateTempUser();
55 $title = Title
::makeTitle( $ns, $titleText );
56 $page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title );
57 $performer = static::getTestUser()->getUser();
58 $content = ContentHandler
::makeContent( $content, $page->getTitle(), CONTENT_MODEL_WIKITEXT
);
59 $updater = $page->newPageUpdater( UserIdentityValue
::newAnonymous( $this->ipEditor
) )
60 ->setContent( SlotRecord
::MAIN
, $content );
62 $revisionRecord = $updater->saveRevision( CommentStoreComment
::newUnsavedComment( "testing" ) );
63 if ( !$updater->wasSuccessful() ) {
64 $this->fail( $updater->getStatus()->getWikiText() );
67 $this->pages
[] = [ 'page' => $page, 'revId' => $revisionRecord->getId() ];
68 $this->deletePage( $page, '', $performer );
72 * @covers ::undeleteUnsafe
73 * @covers ::undeleteRevisions
74 * @covers \MediaWiki\Revision\RevisionStoreFactory::getRevisionStoreForUndelete
75 * @covers \MediaWiki\User\ActorStoreFactory::getActorStoreForUndelete
77 public function testUndeleteRevisions() {
78 // TODO: MCR: Test undeletion with multiple slots. Check that slots remain untouched.
79 $revisionStore = $this->getServiceContainer()->getRevisionStore();
81 // First make sure old revisions are archived
82 $dbr = $this->getDb();
84 foreach ( [ 0, 1 ] as $key ) {
85 $row = $revisionStore->newArchiveSelectQueryBuilder( $dbr )
87 ->where( [ 'ar_rev_id' => $this->pages
[$key]['revId'] ] )
88 ->caller( __METHOD__
)->fetchRow();
89 $this->assertEquals( $this->ipEditor
, $row->ar_user_text
);
91 // Should not be in revision
92 $row = $dbr->newSelectQueryBuilder()
95 ->where( [ 'rev_id' => $this->pages
[$key]['revId'] ] )
97 $this->assertFalse( $row );
99 // Should not be in ip_changes
100 $row = $dbr->newSelectQueryBuilder()
102 ->from( 'ip_changes' )
103 ->where( [ 'ipc_rev_id' => $this->pages
[$key]['revId'] ] )
105 $this->assertFalse( $row );
108 // Enable autocreation of temporary users to test that undeletion of revisions performed by IP addresses works
109 // when temporary accounts are enabled.
110 $this->enableAutoCreateTempUser();
112 $undeletePage = $this->getServiceContainer()->getUndeletePageFactory()->newUndeletePage(
113 $this->pages
[0]['page'],
114 $this->getTestSysop()->getUser()
117 $status = $undeletePage->setUndeleteAssociatedTalk( true )->undeleteUnsafe( '' );
118 $this->assertEquals( 2, $status->value
[UndeletePage
::REVISIONS_RESTORED
] );
120 // check subject page and talk page are both back in the revision table
121 foreach ( [ 0, 1 ] as $key ) {
122 $row = $revisionStore->newSelectQueryBuilder( $dbr )
123 ->where( [ 'rev_id' => $this->pages
[$key]['revId'] ] )
124 ->caller( __METHOD__
)->fetchRow();
126 $this->assertNotFalse( $row, 'row exists in revision table' );
127 $this->assertEquals( $this->ipEditor
, $row->rev_user_text
);
129 // Should be back in ip_changes
130 $row = $dbr->newSelectQueryBuilder()
131 ->select( [ 'ipc_hex' ] )
132 ->from( 'ip_changes' )
133 ->where( [ 'ipc_rev_id' => $this->pages
[$key]['revId'] ] )
135 $this->assertNotFalse( $row, 'row exists in ip_changes table' );
136 $this->assertEquals( IPUtils
::toHex( $this->ipEditor
), $row->ipc_hex
);
141 * Regression test for T381225
142 * @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
144 public function testEventEmission() {
147 $page = PageIdentityValue
::localIdentity( 0, NS_MEDIAWIKI
, __METHOD__
);
149 $this->setupPage( $page->getDBkey(), $page->getNamespace(), 'Lorem Ipsum' );
154 $this->getServiceContainer()->getDomainEventSource()->registerListener(
156 static function ( PageUpdatedEvent
$event ) use ( &$calls ) {
158 $event->hasFlag( PageUpdatedEvent
::FLAG_RESTORED
),
159 PageUpdatedEvent
::FLAG_RESTORED
163 $event->hasFlag( PageUpdatedEvent
::FLAG_AUTOMATED
),
164 PageUpdatedEvent
::FLAG_AUTOMATED
167 $event->hasFlag( PageUpdatedEvent
::FLAG_SILENT
),
168 PageUpdatedEvent
::FLAG_SILENT
171 Assert
::assertTrue( $event->isContentChange(), 'isContentChange' );
173 // TODO: assert more properties
179 // Now undelete the page
180 $undeletePage = $this->getServiceContainer()->getUndeletePageFactory()->newUndeletePage(
182 $this->getTestSysop()->getUser()
185 $undeletePage->undeleteUnsafe( 'just a test' );
187 $this->runDeferredUpdates();
188 $this->assertSame( 1, $calls );
192 * Regression test for T381225
193 * @covers \MediaWiki\Page\UndeletePage::undeleteUnsafe
195 public function testEventPropagation() {
196 $page = PageIdentityValue
::localIdentity( 0, NS_MEDIAWIKI
, __METHOD__
);
198 $this->setupPage( $page->getDBkey(), $page->getNamespace(), 'Lorem Ipsum' );
201 $this->installChangeTrackingEventIngressSpyForUndeletion();
202 $this->installSearchEventIngressSpyForUndeletion();
203 $this->installLanguageEventIngressSpyForUndeletion();
205 // Now undelete the page
206 $undeletePage = $this->getServiceContainer()->getUndeletePageFactory()->newUndeletePage(
208 $this->getTestSysop()->getUser()
211 $undeletePage->undeleteUnsafe( 'just a test' );
213 // NOTE: assertions are applied by the spies installed earlier.
214 $this->runDeferredUpdates();