9 use InvalidArgumentException;
22 use PHPUnit_Framework_MockObject_MockObject;
71 $this->tablesUsed[] =
'archive';
72 $this->tablesUsed[] =
'page';
73 $this->tablesUsed[] =
'revision';
74 $this->tablesUsed[] =
'comment';
96 if ( $this->testPageTitle ) {
107 if ( $this->testPage ) {
114 if ( !$this->testPage->exists() ) {
118 $user = static::getTestSysop()->getUser();
120 $this->testPage->doEditContent(
122 'UTPageSummary-' . __CLASS__,
136 $domain =
new DatabaseDomain( $server[
'dbname'],
null, $server[
'tablePrefix'] );
139 ->setMethods( [
'reallyOpenConnection' ] )
140 ->setConstructorArgs( [
141 [
'servers' => [ $server ],
'localDomain' => $domain ]
145 $lb->method(
'reallyOpenConnection' )->willReturnCallback(
146 function (
array $server, $dbNameOverride ) {
159 ->setMethods( [
'select',
'doQuery',
'open',
'closeConnection',
'isOpen' ] )
160 ->setConstructorArgs( [
$params ] )
164 $db->method(
'isOpen' )->willReturn(
true );
170 yield [
false,
'test',
'' ];
171 yield [
'test',
'test',
'' ];
173 yield [
false,
'test',
'foo_' ];
174 yield [
'test-foo_',
'test',
'foo_' ];
176 yield [
false,
'dash-test',
'' ];
177 yield [
'dash-test',
'dash-test',
'' ];
179 yield [
false,
'underscore_test',
'foo_' ];
180 yield [
'underscore_test-foo_',
'underscore_test',
'foo_' ];
190 'wgDBname' => $dbName,
191 'wgDBprefix' => $dbPrefix,
198 'dbDirectory' =>
'*dummy*',
200 'password' =>
'test',
209 'connLogger' =>
new \Psr\Log\NullLogger(),
210 'queryLogger' =>
new \Psr\Log\NullLogger(),
211 'errorLogger' =>
function () {
213 'deprecationLogger' =>
function () {
217 'tablePrefix' => $dbPrefix,
224 ->disableOriginalConstructor()
239 $count = $store->countRevisionsByPageId(
$db, 0 );
243 $this->assertSame( 0, $count );
268 $this->assertEquals( $r1->
getUser()->getName(), $r2->
getUser()->getName() );
269 $this->assertEquals( $r1->
getUser()->getId(), $r2->
getUser()->getId() );
329 if ( isset( $details[
'user'] ) && $details[
'user'] ===
true ) {
330 $details[
'user'] = $this->
getTestUser()->getUser();
332 if ( isset( $details[
'page'] ) && $details[
'page'] ===
true ) {
335 if ( isset( $details[
'parent'] ) && $details[
'parent'] ===
true ) {
336 $details[
'parent'] = $this->
getTestPage()->getLatest();
341 isset( $details[
'slot'] ) ?
$rev->setSlot( $details[
'slot'] ) :
null;
342 isset( $details[
'parent'] ) ?
$rev->setParentId( $details[
'parent'] ) :
null;
343 isset( $details[
'page'] ) ?
$rev->setPageId( $details[
'page'] ) :
null;
344 isset( $details[
'size'] ) ?
$rev->setSize( $details[
'size'] ) :
null;
345 isset( $details[
'sha1'] ) ?
$rev->setSha1( $details[
'sha1'] ) :
null;
346 isset( $details[
'comment'] ) ?
$rev->setComment( $details[
'comment'] ) :
null;
347 isset( $details[
'timestamp'] ) ?
$rev->setTimestamp( $details[
'timestamp'] ) :
null;
348 isset( $details[
'minor'] ) ?
$rev->setMinorEdit( $details[
'minor'] ) :
null;
349 isset( $details[
'user'] ) ?
$rev->setUser( $details[
'user'] ) :
null;
350 isset( $details[
'visibility'] ) ?
$rev->setVisibility( $details[
'visibility'] ) :
null;
351 isset( $details[
'id'] ) ?
$rev->setId( $details[
'id'] ) :
null;
353 if ( isset( $details[
'content'] ) ) {
354 foreach ( $details[
'content']
as $role =>
$content ) {
363 yield
'Bare minimum revision insertion' => [
368 'timestamp' =>
'20171117010101',
372 yield
'Detailed revision insertion' => [
378 'timestamp' =>
'20171117010101',
397 array $revDetails = []
412 $loaded = $store->getRevisionById( $return->getId() );
424 unset( $row->rev_content_model );
425 unset( $row->rev_content_format );
428 unset( $row->rev_comment_text );
429 unset( $row->rev_comment_data );
430 unset( $row->rev_comment_cid );
431 unset( $row->rev_comment_id );
434 $queryInfo = $store->getQueryInfo( [
'user' ] );
436 $row = get_object_vars( $row );
438 $queryInfo[
'tables'],
440 [
'rev_id' =>
$rev->getId() ],
441 [ array_values( $row ) ],
465 'timestamp' =>
'20171117010101',
491 $this->assertNotSame( $firstReturn->getId(), $secondReturn->getId() );
494 $this->assertSame( $firstReturn->getId(), $firstMainSlot->getRevision() );
495 $this->assertSame( $secondReturn->getId(), $secondMainSlot->getRevision() );
502 'timestamp' =>
'20171117010101',
505 new InvalidArgumentException(
'main slot must be provided' )
507 yield
'no main slot' => [
511 'timestamp' =>
'20171117010101',
514 new InvalidArgumentException(
'main slot must be provided' )
516 yield
'no timestamp' => [
524 yield
'no comment' => [
527 'timestamp' =>
'20171117010101',
536 'timestamp' =>
'20171117010101',
547 array $revDetails = [],
554 $this->setExpectedException(
555 get_class( $exception ),
556 $exception->getMessage(),
557 $exception->getCode()
588 if ( !$page->exists() ) {
592 $revDetails[
'page'] = $page->getId();
595 $revDetails[
'user'] =
$user;
601 $baseRev = $store->insertRevisionOn( $baseRev, $dbw );
602 $page->updateRevisionOn( $dbw,
new Revision( $baseRev ), $page->getLatest() );
604 $record = $store->newNullRevision(
612 $this->assertEquals(
$title->getNamespace(), $record->getPageAsLinkTarget()->getNamespace() );
613 $this->assertEquals(
$title->getDBkey(), $record->getPageAsLinkTarget()->getDBkey() );
614 $this->assertEquals( $comment, $record->getComment() );
615 $this->assertEquals( $minor, $record->isMinor() );
616 $this->assertEquals(
$user->getName(), $record->getUser()->getName() );
617 $this->assertEquals( $baseRev->getId(), $record->getParentId() );
620 $baseRev->getSlotRoles(),
621 $record->getSlotRoles()
624 foreach ( $baseRev->getSlotRoles()
as $role ) {
625 $parentSlot = $baseRev->getSlot( $role );
626 $slot = $record->getSlot( $role );
628 $this->assertTrue( $slot->isInherited(),
'isInherited' );
629 $this->assertSame( $parentSlot->getOrigin(), $slot->getOrigin(),
'getOrigin' );
639 $record = $store->newNullRevision(
646 $this->assertNull( $record );
659 $revisionRecord = $store->getRevisionById(
$rev->getId() );
660 $result = $store->getRcIdIfUnpatrolled( $revisionRecord );
662 $this->assertGreaterThan( 0,
$result );
664 $store->getRecentChange( $revisionRecord )->getAttribute(
'rc_id' ),
676 $status = $page->doEditContent(
687 $revisionRecord = $store->getRevisionById(
$rev->getId() );
688 $result = $store->getRcIdIfUnpatrolled( $revisionRecord );
690 $this->assertSame( 0,
$result );
704 $revRecord = $store->getRevisionById(
$rev->getId() );
705 $recentChange = $store->getRecentChange( $revRecord );
707 $this->assertEquals(
$rev->getId(), $recentChange->getAttribute(
'rc_this_oldid' ) );
708 $this->assertEquals(
$rev->getRecentChange(), $recentChange );
722 $revRecord = $store->getRevisionById(
$rev->getId() );
724 $this->assertSame(
$rev->getId(), $revRecord->getId() );
726 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
740 $revRecord = $store->getRevisionByTitle( $page->getTitle() );
742 $this->assertSame(
$rev->getId(), $revRecord->getId() );
744 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
758 $revRecord = $store->getRevisionByPageId( $page->getId() );
760 $this->assertSame(
$rev->getId(), $revRecord->getId() );
762 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
780 $revRecord = $store->getRevisionByTimestamp(
785 $this->assertSame(
$rev->getId(), $revRecord->getId() );
787 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
797 'rev_timestamp' => $this->db->timestamp(
$rev->getTimestamp() ),
798 'rev_user_text' => (
string)
$rev->getUserText(),
800 'rev_minor_edit' =>
$rev->isMinor() ?
'1' :
'0',
801 'rev_deleted' => (
string)
$rev->getVisibility(),
803 'rev_parent_id' => (
string)
$rev->getParentId(),
807 if ( in_array(
'page',
$options ) ) {
809 'page_namespace' => (
string)$page->getTitle()->getNamespace(),
810 'page_title' => $page->getTitle()->getDBkey(),
811 'page_id' => (
string)$page->getId(),
812 'page_latest' => (
string)$page->getLatest(),
813 'page_is_redirect' => $page->isRedirect() ?
'1' :
'0',
814 'page_len' => (
string)$page->getContent()->getSize(),
818 if ( in_array(
'user',
$options ) ) {
824 if ( in_array(
'comment',
$options ) ) {
826 'rev_comment_text' =>
$rev->getComment(),
827 'rev_comment_data' =>
null,
828 'rev_comment_cid' =>
null,
832 if (
$rev->getId() ) {
838 return (
object)$fields;
845 $this->assertSame(
$rev->getId(), $record->
getId() );
848 $this->assertSame(
$rev->getUserText(), $record->
getUser()->getName() );
849 $this->assertSame(
$rev->getUser(), $record->
getUser()->getId() );
850 $this->assertSame(
$rev->isMinor(), $record->
isMinor() );
852 $this->assertSame(
$rev->getSize(), $record->
getSize() );
857 $expectedParent =
$rev->getParentId();
858 if ( $expectedParent ===
null ) {
861 $this->assertSame( $expectedParent, $record->
getParentId() );
862 $this->assertSame(
$rev->getSha1(), $record->
getSha1() );
863 $this->assertSame(
$rev->getComment(), $record->
getComment()->text );
864 $this->assertSame(
$rev->getContentFormat(),
869 $revRec =
$rev->getRevisionRecord();
873 $this->assertSame( $revMain->hasOrigin(), $recMain->hasOrigin(),
'hasOrigin' );
874 $this->assertSame( $revMain->hasAddress(), $recMain->hasAddress(),
'hasAddress' );
875 $this->assertSame( $revMain->hasContentId(), $recMain->hasContentId(),
'hasContentId' );
877 if ( $revMain->hasOrigin() ) {
878 $this->assertSame( $revMain->getOrigin(), $recMain->getOrigin(),
'getOrigin' );
881 if ( $revMain->hasAddress() ) {
882 $this->assertSame( $revMain->getAddress(), $recMain->getAddress(),
'getAddress' );
885 if ( $revMain->hasContentId() ) {
886 $this->assertSame( $revMain->getContentId(), $recMain->getContentId(),
'getContentId' );
896 $text = __METHOD__ .
'a-ä';
898 $rev = $page->doEditContent(
901 )->value[
'revision'];
904 $info = $store->getQueryInfo();
905 $row = $this->db->selectRow(
908 [
'rev_id' =>
$rev->getId() ],
913 $record = $store->newRevisionFromRow(
919 $this->assertSame( $text,
$rev->getContent()->serialize() );
927 $text = __METHOD__ .
'a-ä';
929 $rev = $page->doEditContent(
932 )->value[
'revision'];
935 $record = $store->newRevisionFromRow(
941 $this->assertSame( $text,
$rev->getContent()->serialize() );
948 $this->
setMwGlobals(
'wgLegacyEncoding',
'windows-1252' );
951 $text = __METHOD__ .
'a-ä';
953 $rev = $page->doEditContent(
956 )->value[
'revision'];
959 $record = $store->newRevisionFromRow(
965 $this->assertSame( $text,
$rev->getContent()->serialize() );
973 $text = __METHOD__ .
'b-ä';
975 $rev = $page->doEditContent(
981 )->value[
'revision'];
984 $record = $store->newRevisionFromRow(
990 $this->assertSame( $text,
$rev->getContent()->serialize() );
1000 $text = __METHOD__ .
'-bä';
1003 $orig = $page->doEditContent(
new WikitextContent( $text ), __METHOD__ )
1004 ->value[
'revision'];
1005 $page->doDeleteArticle( __METHOD__ );
1008 $arQuery = $store->getArchiveQueryInfo();
1010 $arQuery[
'tables'], $arQuery[
'fields'], [
'ar_rev_id' => $orig->getId() ],
1011 __METHOD__, [], $arQuery[
'joins']
1013 $this->assertTrue( is_object(
$res ),
'query failed' );
1015 $row =
$res->fetchObject();
1017 $record = $store->newRevisionFromArchiveRow( $row );
1020 $this->assertSame( $text, $record->getContent(
SlotRecord::MAIN )->serialize() );
1027 $this->
setMwGlobals(
'wgLegacyEncoding',
'windows-1252' );
1031 $text = __METHOD__ .
'-bä';
1034 $orig = $page->doEditContent(
new WikitextContent( $text ), __METHOD__ )
1035 ->value[
'revision'];
1036 $page->doDeleteArticle( __METHOD__ );
1039 $arQuery = $store->getArchiveQueryInfo();
1041 $arQuery[
'tables'], $arQuery[
'fields'], [
'ar_rev_id' => $orig->getId() ],
1042 __METHOD__, [], $arQuery[
'joins']
1044 $this->assertTrue( is_object(
$res ),
'query failed' );
1046 $row =
$res->fetchObject();
1048 $record = $store->newRevisionFromArchiveRow( $row );
1051 $this->assertSame( $text, $record->getContent(
SlotRecord::MAIN )->serialize() );
1062 'ar_page_id' =>
'2',
1063 'ar_namespace' =>
'0',
1064 'ar_title' =>
'Something',
1066 'ar_text_id' =>
'47',
1067 'ar_timestamp' =>
'20180528192356',
1068 'ar_minor_edit' =>
'0',
1069 'ar_deleted' =>
'0',
1071 'ar_parent_id' =>
'0',
1072 'ar_sha1' =>
'deadbeef',
1073 'ar_comment_text' =>
'whatever',
1074 'ar_comment_data' =>
null,
1075 'ar_comment_cid' =>
null,
1077 'ar_user_text' =>
'',
1079 'ar_content_format' =>
null,
1080 'ar_content_model' =>
null,
1083 \Wikimedia\suppressWarnings();
1084 $record = $store->newRevisionFromArchiveRow( $row );
1085 \Wikimedia\suppressWarnings(
true );
1089 $this->assertSame(
'Unknown user', $record->getUser()->getName() );
1102 'page_namespace' =>
'0',
1103 'page_title' =>
$title->getText(),
1104 'rev_text_id' =>
'47',
1105 'rev_timestamp' =>
'20180528192356',
1106 'rev_minor_edit' =>
'0',
1107 'rev_deleted' =>
'0',
1109 'rev_parent_id' =>
'0',
1110 'rev_sha1' =>
'deadbeef',
1111 'rev_comment_text' =>
'whatever',
1112 'rev_comment_data' =>
null,
1113 'rev_comment_cid' =>
null,
1115 'rev_user_text' =>
'',
1116 'rev_actor' =>
null,
1117 'rev_content_format' =>
null,
1118 'rev_content_model' =>
null,
1121 \Wikimedia\suppressWarnings();
1122 $record = $store->newRevisionFromRow( $row, 0,
$title );
1123 \Wikimedia\suppressWarnings(
true );
1125 $this->assertNotNull( $record );
1126 $this->assertNotNull( $record->getUser() );
1127 $this->assertNotEmpty( $record->getUser()->getName() );
1142 $page->doEditContent(
new WikitextContent(
"First" ), __METHOD__ .
'-first' );
1143 $origRev = $page->doEditContent(
new WikitextContent(
"Foo" ), __METHOD__ )
1144 ->value[
'revision'];
1145 $orig = $origRev->getRevisionRecord();
1146 $page->doDeleteArticle( __METHOD__ );
1152 $arQuery = $store->getArchiveQueryInfo();
1153 $row =
$db->selectRow(
1154 $arQuery[
'tables'], $arQuery[
'fields'], [
'ar_rev_id' => $orig->getId() ],
1155 __METHOD__, [], $arQuery[
'joins']
1158 $this->assertNotFalse( $row,
'query failed' );
1160 $record = $store->newRevisionFromArchiveRow(
1164 [
'page_id' =>
$title->getArticleID() ]
1167 $restored = $store->insertRevisionOn( $record,
$db );
1176 $this->assertSame( $recMain->getAddress(), $restMain->getAddress() );
1177 $this->assertSame( $recMain->getContentId(), $restMain->getContentId() );
1178 $this->assertSame( $recMain->getOrigin(), $restMain->getOrigin() );
1179 $this->assertSame(
'Foo', $restMain->getContent()->serialize() );
1182 $loaded = $store->getRevisionById( $restored->getId() );
1183 $this->assertNotNull( $loaded );
1199 ->value[
'revision'];
1214 ->value[
'revision'];
1229 ->value[
'revision'];
1243 $revOne = $page->doEditContent(
new WikitextContent( __METHOD__ ), __METHOD__ )
1244 ->value[
'revision'];
1248 $revTwo = $page->doEditContent(
new WikitextContent( __METHOD__ .
'a' ),
'' )
1249 ->value[
'revision'];
1257 $store->loadRevisionFromTimestamp(
1260 $revOne->getTimestamp()
1265 $store->loadRevisionFromTimestamp(
1268 $revTwo->getTimestamp()
1279 $revOne = $page->doEditContent(
1281 )->value[
'revision'];
1283 $revTwo = $page->doEditContent(
1285 )->value[
'revision'];
1290 $revOne->getId() => strlen( __METHOD__ ),
1292 $store->listRevisionSizes(
1294 [ $revOne->getId() ]
1299 $revOne->getId() => strlen( __METHOD__ ),
1300 $revTwo->getId() => strlen( __METHOD__ ) + 1,
1302 $store->listRevisionSizes(
1304 [ $revOne->getId(), $revTwo->getId() ]
1315 $revOne = $page->doEditContent(
1317 )->value[
'revision'];
1319 $revTwo = $page->doEditContent(
1321 )->value[
'revision'];
1325 $store->getPreviousRevision( $store->getRevisionById( $revOne->getId() ) )
1329 $store->getPreviousRevision( $store->getRevisionById( $revTwo->getId() ) )->getId()
1339 $revOne = $page->doEditContent(
1341 )->value[
'revision'];
1343 $revTwo = $page->doEditContent(
1345 )->value[
'revision'];
1350 $store->getNextRevision( $store->getRevisionById( $revOne->getId() ) )->getId()
1353 $store->getNextRevision( $store->getRevisionById( $revTwo->getId() ) )
1364 ->value[
'revision'];
1367 $result = $store->getTimestampFromId(
1372 $this->assertSame(
$rev->getTimestamp(),
$result );
1382 ->value[
'revision'];
1385 $result = $store->getTimestampFromId(
1390 $this->assertFalse(
$result );
1445 $page->doEditContent(
new WikitextContent( __METHOD__ ), __METHOD__ );
1448 $result = $store->userWasLastToEdit(
1454 $this->assertFalse(
$result );
1464 $page->doEditContent(
1473 $result = $store->userWasLastToEdit(
1488 $rev = $page->doEditContent(
1494 )->value[
'revision'];
1497 $record = $store->getKnownCurrentRevision(
1506 yield
'Basic array, content object' => [
1510 'timestamp' =>
'20171017114835',
1511 'user_text' =>
'111.0.1.2',
1513 'minor_edit' =>
false,
1517 'sha1' =>
'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1518 'comment' =>
'Goat Comment!',
1522 yield
'Basic array, serialized text' => [
1526 'timestamp' =>
'20171017114835',
1527 'user_text' =>
'111.0.1.2',
1529 'minor_edit' =>
false,
1533 'sha1' =>
'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1534 'comment' =>
'Goat Comment!',
1538 yield
'Basic array, serialized text, utf-8 flags' => [
1542 'timestamp' =>
'20171017114835',
1543 'user_text' =>
'111.0.1.2',
1545 'minor_edit' =>
false,
1549 'sha1' =>
'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1550 'comment' =>
'Goat Comment!',
1555 yield
'Basic array, with title' => [
1558 'timestamp' =>
'20171017114835',
1559 'user_text' =>
'111.0.1.2',
1561 'minor_edit' =>
false,
1565 'sha1' =>
'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1566 'comment' =>
'Goat Comment!',
1570 yield
'Basic array, no user field' => [
1574 'timestamp' =>
'20171017114835',
1575 'user_text' =>
'111.0.1.3',
1576 'minor_edit' =>
false,
1580 'sha1' =>
'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1581 'comment' =>
'Goat Comment!',
1595 if ( isset( $array[
'page'] ) ) {
1597 if ( !
$t || !
$t->exists() ) {
1600 $array[
'page'] = $info[
'id'];
1604 $result = $store->newMutableRevisionFromArray( $array );
1606 if ( isset( $array[
'id'] ) ) {
1607 $this->assertSame( $array[
'id'],
$result->getId() );
1609 if ( isset( $array[
'page'] ) ) {
1610 $this->assertSame( $array[
'page'],
$result->getPageId() );
1612 $this->assertSame( $array[
'timestamp'],
$result->getTimestamp() );
1613 $this->assertSame( $array[
'user_text'],
$result->getUser()->getName() );
1614 if ( isset( $array[
'user'] ) ) {
1615 $this->assertSame( $array[
'user'],
$result->getUser()->getId() );
1617 $this->assertSame( (
bool)$array[
'minor_edit'],
$result->isMinor() );
1618 $this->assertSame( $array[
'deleted'],
$result->getVisibility() );
1619 $this->assertSame( $array[
'len'],
$result->getSize() );
1620 $this->assertSame( $array[
'parent_id'],
$result->getParentId() );
1621 $this->assertSame( $array[
'sha1'],
$result->getSha1() );
1622 $this->assertSame( $array[
'comment'],
$result->getComment()->text );
1623 if ( isset( $array[
'content'] ) ) {
1624 foreach ( $array[
'content']
as $role =>
$content ) {
1629 } elseif ( isset( $array[
'text'] ) ) {
1630 $this->assertSame( $array[
'text'],
1632 } elseif ( isset( $array[
'content_format'] ) ) {
1634 $array[
'content_format'],
1652 ->setMethods( [
'newBlobStore',
'newSqlBlobStore' ] )
1653 ->disableOriginalConstructor()
1655 $factory->expects( $this->
any() )
1656 ->method(
'newBlobStore' )
1657 ->willReturn( $blobStore );
1658 $factory->expects( $this->
any() )
1659 ->method(
'newSqlBlobStore' )
1660 ->willReturn( $blobStore );
1662 $this->
setService(
'BlobStoreFactory', $factory );