X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=tests%2Fphpunit%2Fincludes%2Fpage%2FWikiPageDbTestBase.php;h=b8480dd014d8d3bc0d57be044615f7690cace491;hb=2ef178072f6f46abde7bdcc2630389a8b2837557;hp=9de2bc91a86041dda917f2aeb1e40508e2f80b9e;hpb=12ca3d7eaa0287572867b45be9d7f3d943091480;p=lhc%2Fweb%2Fwiklou.git diff --git a/tests/phpunit/includes/page/WikiPageDbTestBase.php b/tests/phpunit/includes/page/WikiPageDbTestBase.php index 9de2bc91a8..b8480dd014 100644 --- a/tests/phpunit/includes/page/WikiPageDbTestBase.php +++ b/tests/phpunit/includes/page/WikiPageDbTestBase.php @@ -11,7 +11,9 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase { $this->tablesUsed, [ 'page', 'revision', + 'redirect', 'archive', + 'category', 'ip_changes', 'text', @@ -154,6 +156,7 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase { /** * @covers WikiPage::doDeleteArticle + * @covers WikiPage::doDeleteArticleReal */ public function testDoDeleteArticle() { $page = $this->createPage( @@ -695,62 +698,64 @@ more stuff } /** - * @todo FIXME: this is a better rollback test than the one below, but it - * keeps failing in jenkins for some reason. + * @covers WikiPage::doRollback + * @covers WikiPage::commitRollback */ - public function broken_testDoRollback() { + public function testDoRollback() { $admin = $this->getTestSysop()->getUser(); + $user1 = $this->getTestUser()->getUser(); + // Use the confirmed group for user2 to make sure the user is different + $user2 = $this->getTestUser( [ 'confirmed' ] )->getUser(); - $text = "one"; $page = $this->newPage( __METHOD__ ); - $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), + + // Make some edits + $text = "one"; + $status1 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "section one", EDIT_NEW, false, $admin ); - $user1 = $this->getTestUser()->getUser(); $text .= "\n\ntwo"; - $page = new WikiPage( $page->getTitle() ); - $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), + $status2 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section two", 0, false, $user1 ); - $user2 = $this->getTestUser()->getUser(); $text .= "\n\nthree"; - $page = new WikiPage( $page->getTitle() ); - $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), + $status3 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), "adding section three", 0, false, $user2 ); - # we are having issues with doRollback spuriously failing. Apparently - # the last revision somehow goes missing or not committed under some - # circumstances. So, make sure the last revision has the right user name. - $dbr = wfGetDB( DB_REPLICA ); - $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) ); - - $page = new WikiPage( $page->getTitle() ); - $rev3 = $page->getRevision(); - $this->assertEquals( '127.0.2.13', $rev3->getUserText() ); - - $rev2 = $rev3->getPrevious(); - $this->assertEquals( '127.0.1.11', $rev2->getUserText() ); - - $rev1 = $rev2->getPrevious(); - $this->assertEquals( 'Admin', $rev1->getUserText() ); - - # now, try the actual rollback - $token = $admin->getEditToken( - [ $page->getTitle()->getPrefixedText(), $user2->getName() ], - null - ); - $errors = $page->doRollback( + /** @var Revision $rev1 */ + /** @var Revision $rev2 */ + /** @var Revision $rev3 */ + $rev1 = $status1->getValue()['revision']; + $rev2 = $status2->getValue()['revision']; + $rev3 = $status3->getValue()['revision']; + + /** + * We are having issues with doRollback spuriously failing. Apparently + * the last revision somehow goes missing or not committed under some + * circumstances. So, make sure the revisions have the correct usernames. + */ + $this->assertEquals( 3, Revision::countByPageId( wfGetDB( DB_REPLICA ), $page->getId() ) ); + $this->assertEquals( $admin->getName(), $rev1->getUserText() ); + $this->assertEquals( $user1->getName(), $rev2->getUserText() ); + $this->assertEquals( $user2->getName(), $rev3->getUserText() ); + + // Now, try the actual rollback + $token = $admin->getEditToken( 'rollback' ); + $rollbackErrors = $page->doRollback( $user2->getName(), - "testing revert", + "testing rollback", $token, false, - $details, + $resultDetails, $admin ); - if ( $errors ) { - $this->fail( "Rollback failed:\n" . print_r( $errors, true ) - . ";\n" . print_r( $details, true ) ); + if ( $rollbackErrors ) { + $this->fail( + "Rollback failed:\n" . + print_r( $rollbackErrors, true ) . ";\n" . + print_r( $resultDetails, true ) + ); } $page = new WikiPage( $page->getTitle() ); @@ -760,10 +765,10 @@ more stuff } /** - * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason. * @covers WikiPage::doRollback + * @covers WikiPage::commitRollback */ - public function testDoRollback() { + public function testDoRollback_simple() { $admin = $this->getTestSysop()->getUser(); $text = "one"; @@ -812,6 +817,7 @@ more stuff /** * @covers WikiPage::doRollback + * @covers WikiPage::commitRollback */ public function testDoRollbackFailureSameContent() { $admin = $this->getTestSysop()->getUser(); @@ -1150,4 +1156,543 @@ more stuff ]; } + /** + * @covers WikiPage::updateCategoryCounts + */ + public function testUpdateCategoryCounts() { + $page = new WikiPage( Title::newFromText( __METHOD__ ) ); + + // Add an initial category + $page->updateCategoryCounts( [ 'A' ], [], 0 ); + + $this->assertEquals( 1, Category::newFromName( 'A' )->getPageCount() ); + $this->assertEquals( 0, Category::newFromName( 'B' )->getPageCount() ); + $this->assertEquals( 0, Category::newFromName( 'C' )->getPageCount() ); + + // Add a new category + $page->updateCategoryCounts( [ 'B' ], [], 0 ); + + $this->assertEquals( 1, Category::newFromName( 'A' )->getPageCount() ); + $this->assertEquals( 1, Category::newFromName( 'B' )->getPageCount() ); + $this->assertEquals( 0, Category::newFromName( 'C' )->getPageCount() ); + + // Add and remove a category + $page->updateCategoryCounts( [ 'C' ], [ 'A' ], 0 ); + + $this->assertEquals( 0, Category::newFromName( 'A' )->getPageCount() ); + $this->assertEquals( 1, Category::newFromName( 'B' )->getPageCount() ); + $this->assertEquals( 1, Category::newFromName( 'C' )->getPageCount() ); + } + + public function provideUpdateRedirectOn() { + yield [ '#REDIRECT [[Foo]]', true, null, true, true, 0 ]; + yield [ '#REDIRECT [[Foo]]', true, 'Foo', true, false, 1 ]; + yield [ 'SomeText', false, null, false, true, 0 ]; + yield [ 'SomeText', false, 'Foo', false, false, 1 ]; + } + + /** + * @dataProvider provideUpdateRedirectOn + * @covers WikiPage::updateRedirectOn + * + * @param string $initialText + * @param bool $initialRedirectState + * @param string|null $redirectTitle + * @param bool|null $lastRevIsRedirect + * @param bool $expectedSuccess + * @param int $expectedRowCount + */ + public function testUpdateRedirectOn( + $initialText, + $initialRedirectState, + $redirectTitle, + $lastRevIsRedirect, + $expectedSuccess, + $expectedRowCount + ) { + static $pageCounter = 0; + $pageCounter++; + + $page = $this->createPage( Title::newFromText( __METHOD__ . $pageCounter ), $initialText ); + $this->assertSame( $initialRedirectState, $page->isRedirect() ); + + $redirectTitle = is_string( $redirectTitle ) + ? Title::newFromText( $redirectTitle ) + : $redirectTitle; + + $success = $page->updateRedirectOn( $this->db, $redirectTitle, $lastRevIsRedirect ); + $this->assertSame( $expectedSuccess, $success, 'Success assertion' ); + /** + * updateRedirectOn explicitly updates the redirect table (and not the page table). + * Most of core checks the page table for redirect status, so we have to be ugly and + * assert a select from the table here. + */ + $this->assertRedirectTableCountForPageId( $page->getId(), $expectedRowCount ); + } + + private function assertRedirectTableCountForPageId( $pageId, $expected ) { + $this->assertSelect( + 'redirect', + 'COUNT(*)', + [ 'rd_from' => $pageId ], + [ [ strval( $expected ) ] ] + ); + } + + /** + * @covers WikiPage::insertRedirectEntry + */ + public function testInsertRedirectEntry_insertsRedirectEntry() { + $page = $this->createPage( Title::newFromText( __METHOD__ ), 'A' ); + $this->assertRedirectTableCountForPageId( $page->getId(), 0 ); + + $targetTitle = Title::newFromText( 'SomeTarget#Frag' ); + $targetTitle->mInterwiki = 'eninter'; + $page->insertRedirectEntry( $targetTitle, null ); + + $this->assertSelect( + 'redirect', + [ 'rd_from', 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ], + [ 'rd_from' => $page->getId() ], + [ [ + strval( $page->getId() ), + strval( $targetTitle->getNamespace() ), + strval( $targetTitle->getDBkey() ), + strval( $targetTitle->getFragment() ), + strval( $targetTitle->getInterwiki() ), + ] ] + ); + } + + /** + * @covers WikiPage::insertRedirectEntry + */ + public function testInsertRedirectEntry_insertsRedirectEntryWithPageLatest() { + $page = $this->createPage( Title::newFromText( __METHOD__ ), 'A' ); + $this->assertRedirectTableCountForPageId( $page->getId(), 0 ); + + $targetTitle = Title::newFromText( 'SomeTarget#Frag' ); + $targetTitle->mInterwiki = 'eninter'; + $page->insertRedirectEntry( $targetTitle, $page->getLatest() ); + + $this->assertSelect( + 'redirect', + [ 'rd_from', 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ], + [ 'rd_from' => $page->getId() ], + [ [ + strval( $page->getId() ), + strval( $targetTitle->getNamespace() ), + strval( $targetTitle->getDBkey() ), + strval( $targetTitle->getFragment() ), + strval( $targetTitle->getInterwiki() ), + ] ] + ); + } + + /** + * @covers WikiPage::insertRedirectEntry + */ + public function testInsertRedirectEntry_doesNotInsertIfPageLatestIncorrect() { + $page = $this->createPage( Title::newFromText( __METHOD__ ), 'A' ); + $this->assertRedirectTableCountForPageId( $page->getId(), 0 ); + + $targetTitle = Title::newFromText( 'SomeTarget#Frag' ); + $targetTitle->mInterwiki = 'eninter'; + $page->insertRedirectEntry( $targetTitle, 215251 ); + + $this->assertRedirectTableCountForPageId( $page->getId(), 0 ); + } + + private function getRow( array $overrides = [] ) { + $row = [ + 'page_id' => '44', + 'page_len' => '76', + 'page_is_redirect' => '1', + 'page_latest' => '99', + 'page_namespace' => '3', + 'page_title' => 'JaJaTitle', + 'page_restrictions' => 'edit=autoconfirmed,sysop:move=sysop', + 'page_touched' => '20120101020202', + 'page_links_updated' => '20140101020202', + ]; + foreach ( $overrides as $key => $value ) { + $row[$key] = $value; + } + return (object)$row; + } + + public function provideNewFromRowSuccess() { + yield 'basic row' => [ + $this->getRow(), + function ( WikiPage $wikiPage, self $test ) { + $test->assertSame( 44, $wikiPage->getId() ); + $test->assertSame( 76, $wikiPage->getTitle()->getLength() ); + $test->assertTrue( $wikiPage->isRedirect() ); + $test->assertSame( 99, $wikiPage->getLatest() ); + $test->assertSame( 3, $wikiPage->getTitle()->getNamespace() ); + $test->assertSame( 'JaJaTitle', $wikiPage->getTitle()->getDBkey() ); + $test->assertSame( + [ + 'edit' => [ 'autoconfirmed', 'sysop' ], + 'move' => [ 'sysop' ], + ], + $wikiPage->getTitle()->getAllRestrictions() + ); + $test->assertSame( '20120101020202', $wikiPage->getTouched() ); + $test->assertSame( '20140101020202', $wikiPage->getLinksTimestamp() ); + } + ]; + yield 'different timestamp formats' => [ + $this->getRow( [ + 'page_touched' => '2012-01-01 02:02:02', + 'page_links_updated' => '2014-01-01 02:02:02', + ] ), + function ( WikiPage $wikiPage, self $test ) { + $test->assertSame( '20120101020202', $wikiPage->getTouched() ); + $test->assertSame( '20140101020202', $wikiPage->getLinksTimestamp() ); + } + ]; + yield 'no restrictions' => [ + $this->getRow( [ + 'page_restrictions' => '', + ] ), + function ( WikiPage $wikiPage, self $test ) { + $test->assertSame( + [ + 'edit' => [], + 'move' => [], + ], + $wikiPage->getTitle()->getAllRestrictions() + ); + } + ]; + yield 'not redirect' => [ + $this->getRow( [ + 'page_is_redirect' => '0', + ] ), + function ( WikiPage $wikiPage, self $test ) { + $test->assertFalse( $wikiPage->isRedirect() ); + } + ]; + } + + /** + * @covers WikiPage::newFromRow + * @covers WikiPage::loadFromRow + * @dataProvider provideNewFromRowSuccess + * + * @param object $row + * @param callable $assertions + */ + public function testNewFromRow( $row, $assertions ) { + $page = WikiPage::newFromRow( $row, 'fromdb' ); + $assertions( $page, $this ); + } + + public function provideTestNewFromId_returnsNullOnBadPageId() { + yield[ 0 ]; + yield[ -11 ]; + } + + /** + * @covers WikiPage::newFromID + * @dataProvider provideTestNewFromId_returnsNullOnBadPageId + */ + public function testNewFromId_returnsNullOnBadPageId( $pageId ) { + $this->assertNull( WikiPage::newFromID( $pageId ) ); + } + + /** + * @covers WikiPage::newFromID + */ + public function testNewFromId_appearsToFetchCorrectRow() { + $createdPage = $this->createPage( __METHOD__, 'Xsfaij09' ); + $fetchedPage = WikiPage::newFromID( $createdPage->getId() ); + $this->assertSame( $createdPage->getId(), $fetchedPage->getId() ); + $this->assertEquals( + $createdPage->getContent()->getNativeData(), + $fetchedPage->getContent()->getNativeData() + ); + } + + /** + * @covers WikiPage::newFromID + */ + public function testNewFromId_returnsNullOnNonExistingId() { + $this->assertNull( WikiPage::newFromID( 73574757437437743743 ) ); + } + + public function provideTestInsertProtectNullRevision() { + // @codingStandardsIgnoreStart Generic.Files.LineLength + yield [ + 'goat-message-key', + [ 'edit' => 'sysop' ], + [ 'edit' => '20200101040404' ], + false, + 'Goat Reason', + true, + '(goat-message-key: WikiPageDbTestBase::testInsertProtectNullRevision, UTSysop)(colon-separator)Goat Reason(word-separator)(parentheses: (protect-summary-desc: (restriction-edit), (protect-level-sysop), (protect-expiring: 04:04, 1 (january) 2020, 1 (january) 2020, 04:04)))' + ]; + yield [ + 'goat-key', + [ 'edit' => 'sysop', 'move' => 'something' ], + [ 'edit' => '20200101040404', 'move' => '20210101050505' ], + false, + 'Goat Goat', + true, + '(goat-key: WikiPageDbTestBase::testInsertProtectNullRevision, UTSysop)(colon-separator)Goat Goat(word-separator)(parentheses: (protect-summary-desc: (restriction-edit), (protect-level-sysop), (protect-expiring: 04:04, 1 (january) 2020, 1 (january) 2020, 04:04))(word-separator)(protect-summary-desc: (restriction-move), (protect-level-something), (protect-expiring: 05:05, 1 (january) 2021, 1 (january) 2021, 05:05)))' + ]; + // @codingStandardsIgnoreEnd Generic.Files.LineLength + } + + /** + * @dataProvider provideTestInsertProtectNullRevision + * @covers WikiPage::insertProtectNullRevision + * @covers WikiPage::protectDescription + * + * @param string $revCommentMsg + * @param array $limit + * @param array $expiry + * @param bool $cascade + * @param string $reason + * @param bool|null $user true if the test sysop should be used, or null + * @param string $expectedComment + */ + public function testInsertProtectNullRevision( + $revCommentMsg, + array $limit, + array $expiry, + $cascade, + $reason, + $user, + $expectedComment + ) { + $this->setContentLang( 'qqx' ); + + $page = $this->createPage( __METHOD__, 'Goat' ); + + $user = $user === null ? $user : $this->getTestSysop()->getUser(); + + $result = $page->insertProtectNullRevision( + $revCommentMsg, + $limit, + $expiry, + $cascade, + $reason, + $user + ); + + $this->assertTrue( $result instanceof Revision ); + $this->assertSame( $expectedComment, $result->getComment( Revision::RAW ) ); + } + + /** + * @covers WikiPage::updateRevisionOn + */ + public function testUpdateRevisionOn_existingPage() { + $user = $this->getTestSysop()->getUser(); + $page = $this->createPage( __METHOD__, 'StartText' ); + + $revision = new Revision( + [ + 'id' => 9989, + 'page' => $page->getId(), + 'title' => $page->getTitle(), + 'comment' => __METHOD__, + 'minor_edit' => true, + 'text' => __METHOD__ . '-text', + 'len' => strlen( __METHOD__ . '-text' ), + 'user' => $user->getId(), + 'user_text' => $user->getName(), + 'timestamp' => '20170707040404', + 'content_model' => CONTENT_MODEL_WIKITEXT, + 'content_format' => CONTENT_FORMAT_WIKITEXT, + ] + ); + + $result = $page->updateRevisionOn( $this->db, $revision ); + $this->assertTrue( $result ); + $this->assertSame( 9989, $page->getLatest() ); + $this->assertEquals( $revision, $page->getRevision() ); + } + + /** + * @covers WikiPage::updateRevisionOn + */ + public function testUpdateRevisionOn_NonExistingPage() { + $user = $this->getTestSysop()->getUser(); + $page = $this->createPage( __METHOD__, 'StartText' ); + $page->doDeleteArticle( 'reason' ); + + $revision = new Revision( + [ + 'id' => 9989, + 'page' => $page->getId(), + 'title' => $page->getTitle(), + 'comment' => __METHOD__, + 'minor_edit' => true, + 'text' => __METHOD__ . '-text', + 'len' => strlen( __METHOD__ . '-text' ), + 'user' => $user->getId(), + 'user_text' => $user->getName(), + 'timestamp' => '20170707040404', + 'content_model' => CONTENT_MODEL_WIKITEXT, + 'content_format' => CONTENT_FORMAT_WIKITEXT, + ] + ); + + $result = $page->updateRevisionOn( $this->db, $revision ); + $this->assertFalse( $result ); + } + + /** + * @covers WikiPage::updateIfNewerOn + */ + public function testUpdateIfNewerOn_olderRevision() { + $user = $this->getTestSysop()->getUser(); + $page = $this->createPage( __METHOD__, 'StartText' ); + $initialRevision = $page->getRevision(); + + $olderTimeStamp = wfTimestamp( + TS_MW, + wfTimestamp( TS_UNIX, $initialRevision->getTimestamp() ) - 1 + ); + + $olderRevison = new Revision( + [ + 'id' => 9989, + 'page' => $page->getId(), + 'title' => $page->getTitle(), + 'comment' => __METHOD__, + 'minor_edit' => true, + 'text' => __METHOD__ . '-text', + 'len' => strlen( __METHOD__ . '-text' ), + 'user' => $user->getId(), + 'user_text' => $user->getName(), + 'timestamp' => $olderTimeStamp, + 'content_model' => CONTENT_MODEL_WIKITEXT, + 'content_format' => CONTENT_FORMAT_WIKITEXT, + ] + ); + + $result = $page->updateIfNewerOn( $this->db, $olderRevison ); + $this->assertFalse( $result ); + } + + /** + * @covers WikiPage::updateIfNewerOn + */ + public function testUpdateIfNewerOn_newerRevision() { + $user = $this->getTestSysop()->getUser(); + $page = $this->createPage( __METHOD__, 'StartText' ); + $initialRevision = $page->getRevision(); + + $newerTimeStamp = wfTimestamp( + TS_MW, + wfTimestamp( TS_UNIX, $initialRevision->getTimestamp() ) + 1 + ); + + $newerRevision = new Revision( + [ + 'id' => 9989, + 'page' => $page->getId(), + 'title' => $page->getTitle(), + 'comment' => __METHOD__, + 'minor_edit' => true, + 'text' => __METHOD__ . '-text', + 'len' => strlen( __METHOD__ . '-text' ), + 'user' => $user->getId(), + 'user_text' => $user->getName(), + 'timestamp' => $newerTimeStamp, + 'content_model' => CONTENT_MODEL_WIKITEXT, + 'content_format' => CONTENT_FORMAT_WIKITEXT, + ] + ); + $result = $page->updateIfNewerOn( $this->db, $newerRevision ); + $this->assertTrue( $result ); + } + + /** + * @covers WikiPage::insertOn + */ + public function testInsertOn() { + $title = Title::newFromText( __METHOD__ ); + $page = new WikiPage( $title ); + + $startTimeStamp = wfTimestampNow(); + $result = $page->insertOn( $this->db ); + $endTimeStamp = wfTimestampNow(); + + $this->assertInternalType( 'int', $result ); + $this->assertTrue( $result > 0 ); + + $condition = [ 'page_id' => $result ]; + + // Check the default fields have been filled + $this->assertSelect( + 'page', + [ + 'page_namespace', + 'page_title', + 'page_restrictions', + 'page_is_redirect', + 'page_is_new', + 'page_latest', + 'page_len', + ], + $condition, + [ [ + '0', + __METHOD__, + '', + '0', + '1', + '0', + '0', + ] ] + ); + + // Check the page_random field has been filled + $pageRandom = $this->db->selectField( 'page', 'page_random', $condition ); + $this->assertTrue( (float)$pageRandom < 1 && (float)$pageRandom > 0 ); + + // Assert the touched timestamp in the DB is roughly when we inserted the page + $pageTouched = $this->db->selectField( 'page', 'page_touched', $condition ); + $this->assertTrue( + wfTimestamp( TS_UNIX, $startTimeStamp ) + <= wfTimestamp( TS_UNIX, $pageTouched ) + ); + $this->assertTrue( + wfTimestamp( TS_UNIX, $endTimeStamp ) + >= wfTimestamp( TS_UNIX, $pageTouched ) + ); + + // Try inserting the same page again and checking the result is false (no change) + $result = $page->insertOn( $this->db ); + $this->assertFalse( $result ); + } + + /** + * @covers WikiPage::insertOn + */ + public function testInsertOn_idSpecified() { + $title = Title::newFromText( __METHOD__ ); + $page = new WikiPage( $title ); + $id = 3478952189; + + $result = $page->insertOn( $this->db, $id ); + + $this->assertSame( $id, $result ); + + $condition = [ 'page_id' => $result ]; + + // Check there is actually a row in the db + $this->assertSelect( + 'page', + [ 'page_title' ], + $condition, + [ [ __METHOD__ ] ] + ); + } + }