Add `actor` table and code to start using it
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / RevisionStoreDbTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Storage;
4
5 use CommentStoreComment;
6 use Exception;
7 use HashBagOStuff;
8 use InvalidArgumentException;
9 use Language;
10 use MediaWiki\Linker\LinkTarget;
11 use MediaWiki\MediaWikiServices;
12 use MediaWiki\Storage\BlobStoreFactory;
13 use MediaWiki\Storage\IncompleteRevisionException;
14 use MediaWiki\Storage\MutableRevisionRecord;
15 use MediaWiki\Storage\RevisionRecord;
16 use MediaWiki\Storage\RevisionStore;
17 use MediaWiki\Storage\SlotRecord;
18 use MediaWiki\Storage\SqlBlobStore;
19 use MediaWikiTestCase;
20 use Revision;
21 use TestUserRegistry;
22 use Title;
23 use WANObjectCache;
24 use Wikimedia\Rdbms\Database;
25 use Wikimedia\Rdbms\DatabaseSqlite;
26 use Wikimedia\Rdbms\FakeResultWrapper;
27 use Wikimedia\Rdbms\LoadBalancer;
28 use Wikimedia\Rdbms\TransactionProfiler;
29 use WikiPage;
30 use WikitextContent;
31
32 /**
33 * @group Database
34 */
35 class RevisionStoreDbTest extends MediaWikiTestCase {
36
37 public function setUp() {
38 parent::setUp();
39 $this->tablesUsed[] = 'archive';
40 $this->tablesUsed[] = 'page';
41 $this->tablesUsed[] = 'revision';
42 $this->tablesUsed[] = 'comment';
43 }
44
45 /**
46 * @return LoadBalancer
47 */
48 private function getLoadBalancerMock( array $server ) {
49 $lb = $this->getMockBuilder( LoadBalancer::class )
50 ->setMethods( [ 'reallyOpenConnection' ] )
51 ->setConstructorArgs( [ [ 'servers' => [ $server ] ] ] )
52 ->getMock();
53
54 $lb->method( 'reallyOpenConnection' )->willReturnCallback(
55 function ( array $server, $dbNameOverride ) {
56 return $this->getDatabaseMock( $server );
57 }
58 );
59
60 return $lb;
61 }
62
63 /**
64 * @return Database
65 */
66 private function getDatabaseMock( array $params ) {
67 $db = $this->getMockBuilder( DatabaseSqlite::class )
68 ->setMethods( [ 'select', 'doQuery', 'open', 'closeConnection', 'isOpen' ] )
69 ->setConstructorArgs( [ $params ] )
70 ->getMock();
71
72 $db->method( 'select' )->willReturn( new FakeResultWrapper( [] ) );
73 $db->method( 'isOpen' )->willReturn( true );
74
75 return $db;
76 }
77
78 public function provideDomainCheck() {
79 yield [ false, 'test', '' ];
80 yield [ 'test', 'test', '' ];
81
82 yield [ false, 'test', 'foo_' ];
83 yield [ 'test-foo_', 'test', 'foo_' ];
84
85 yield [ false, 'dash-test', '' ];
86 yield [ 'dash-test', 'dash-test', '' ];
87
88 yield [ false, 'underscore_test', 'foo_' ];
89 yield [ 'underscore_test-foo_', 'underscore_test', 'foo_' ];
90 }
91
92 /**
93 * @dataProvider provideDomainCheck
94 * @covers \MediaWiki\Storage\RevisionStore::checkDatabaseWikiId
95 */
96 public function testDomainCheck( $wikiId, $dbName, $dbPrefix ) {
97 $this->setMwGlobals(
98 [
99 'wgDBname' => $dbName,
100 'wgDBprefix' => $dbPrefix,
101 ]
102 );
103
104 $loadBalancer = $this->getLoadBalancerMock(
105 [
106 'host' => '*dummy*',
107 'dbDirectory' => '*dummy*',
108 'user' => 'test',
109 'password' => 'test',
110 'flags' => 0,
111 'variables' => [],
112 'schema' => '',
113 'cliMode' => true,
114 'agent' => '',
115 'load' => 100,
116 'profiler' => null,
117 'trxProfiler' => new TransactionProfiler(),
118 'connLogger' => new \Psr\Log\NullLogger(),
119 'queryLogger' => new \Psr\Log\NullLogger(),
120 'errorLogger' => new \Psr\Log\NullLogger(),
121 'type' => 'test',
122 'dbname' => $dbName,
123 'tablePrefix' => $dbPrefix,
124 ]
125 );
126 $db = $loadBalancer->getConnection( DB_REPLICA );
127
128 $blobStore = $this->getMockBuilder( SqlBlobStore::class )
129 ->disableOriginalConstructor()
130 ->getMock();
131
132 $store = new RevisionStore(
133 $loadBalancer,
134 $blobStore,
135 new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ),
136 MediaWikiServices::getInstance()->getCommentStore(),
137 MediaWikiServices::getInstance()->getActorMigration(),
138 $wikiId
139 );
140
141 $count = $store->countRevisionsByPageId( $db, 0 );
142
143 // Dummy check to make PhpUnit happy. We are really only interested in
144 // countRevisionsByPageId not failing due to the DB domain check.
145 $this->assertSame( 0, $count );
146 }
147
148 private function assertLinkTargetsEqual( LinkTarget $l1, LinkTarget $l2 ) {
149 $this->assertEquals( $l1->getDBkey(), $l2->getDBkey() );
150 $this->assertEquals( $l1->getNamespace(), $l2->getNamespace() );
151 $this->assertEquals( $l1->getFragment(), $l2->getFragment() );
152 $this->assertEquals( $l1->getInterwiki(), $l2->getInterwiki() );
153 }
154
155 private function assertRevisionRecordsEqual( RevisionRecord $r1, RevisionRecord $r2 ) {
156 $this->assertEquals( $r1->getUser()->getName(), $r2->getUser()->getName() );
157 $this->assertEquals( $r1->getUser()->getId(), $r2->getUser()->getId() );
158 $this->assertEquals( $r1->getComment(), $r2->getComment() );
159 $this->assertEquals( $r1->getPageAsLinkTarget(), $r2->getPageAsLinkTarget() );
160 $this->assertEquals( $r1->getTimestamp(), $r2->getTimestamp() );
161 $this->assertEquals( $r1->getVisibility(), $r2->getVisibility() );
162 $this->assertEquals( $r1->getSha1(), $r2->getSha1() );
163 $this->assertEquals( $r1->getParentId(), $r2->getParentId() );
164 $this->assertEquals( $r1->getSize(), $r2->getSize() );
165 $this->assertEquals( $r1->getPageId(), $r2->getPageId() );
166 $this->assertEquals( $r1->getSlotRoles(), $r2->getSlotRoles() );
167 $this->assertEquals( $r1->getWikiId(), $r2->getWikiId() );
168 $this->assertEquals( $r1->isMinor(), $r2->isMinor() );
169 foreach ( $r1->getSlotRoles() as $role ) {
170 $this->assertEquals( $r1->getSlot( $role ), $r2->getSlot( $role ) );
171 $this->assertEquals( $r1->getContent( $role ), $r2->getContent( $role ) );
172 }
173 foreach ( [
174 RevisionRecord::DELETED_TEXT,
175 RevisionRecord::DELETED_COMMENT,
176 RevisionRecord::DELETED_USER,
177 RevisionRecord::DELETED_RESTRICTED,
178 ] as $field ) {
179 $this->assertEquals( $r1->isDeleted( $field ), $r2->isDeleted( $field ) );
180 }
181 }
182
183 /**
184 * @param mixed[] $details
185 *
186 * @return RevisionRecord
187 */
188 private function getRevisionRecordFromDetailsArray( $title, $details = [] ) {
189 // Convert some values that can't be provided by dataProviders
190 $page = WikiPage::factory( $title );
191 if ( isset( $details['user'] ) && $details['user'] === true ) {
192 $details['user'] = $this->getTestUser()->getUser();
193 }
194 if ( isset( $details['page'] ) && $details['page'] === true ) {
195 $details['page'] = $page->getId();
196 }
197 if ( isset( $details['parent'] ) && $details['parent'] === true ) {
198 $details['parent'] = $page->getLatest();
199 }
200
201 // Create the RevisionRecord with any available data
202 $rev = new MutableRevisionRecord( $title );
203 isset( $details['slot'] ) ? $rev->setSlot( $details['slot'] ) : null;
204 isset( $details['parent'] ) ? $rev->setParentId( $details['parent'] ) : null;
205 isset( $details['page'] ) ? $rev->setPageId( $details['page'] ) : null;
206 isset( $details['size'] ) ? $rev->setSize( $details['size'] ) : null;
207 isset( $details['sha1'] ) ? $rev->setSha1( $details['sha1'] ) : null;
208 isset( $details['comment'] ) ? $rev->setComment( $details['comment'] ) : null;
209 isset( $details['timestamp'] ) ? $rev->setTimestamp( $details['timestamp'] ) : null;
210 isset( $details['minor'] ) ? $rev->setMinorEdit( $details['minor'] ) : null;
211 isset( $details['user'] ) ? $rev->setUser( $details['user'] ) : null;
212 isset( $details['visibility'] ) ? $rev->setVisibility( $details['visibility'] ) : null;
213 isset( $details['id'] ) ? $rev->setId( $details['id'] ) : null;
214
215 return $rev;
216 }
217
218 private function getRandomCommentStoreComment() {
219 return CommentStoreComment::newUnsavedComment( __METHOD__ . '.' . rand( 0, 1000 ) );
220 }
221
222 public function provideInsertRevisionOn_successes() {
223 yield 'Bare minimum revision insertion' => [
224 Title::newFromText( 'UTPage' ),
225 [
226 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
227 'parent' => true,
228 'comment' => $this->getRandomCommentStoreComment(),
229 'timestamp' => '20171117010101',
230 'user' => true,
231 ],
232 ];
233 yield 'Detailed revision insertion' => [
234 Title::newFromText( 'UTPage' ),
235 [
236 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
237 'parent' => true,
238 'page' => true,
239 'comment' => $this->getRandomCommentStoreComment(),
240 'timestamp' => '20171117010101',
241 'user' => true,
242 'minor' => true,
243 'visibility' => RevisionRecord::DELETED_RESTRICTED,
244 ],
245 ];
246 }
247
248 /**
249 * @dataProvider provideInsertRevisionOn_successes
250 * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
251 */
252 public function testInsertRevisionOn_successes( Title $title, array $revDetails = [] ) {
253 $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
254
255 $store = MediaWikiServices::getInstance()->getRevisionStore();
256 $return = $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) );
257
258 $this->assertLinkTargetsEqual( $title, $return->getPageAsLinkTarget() );
259 $this->assertRevisionRecordsEqual( $rev, $return );
260 }
261
262 /**
263 * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
264 */
265 public function testInsertRevisionOn_blobAddressExists() {
266 $title = Title::newFromText( 'UTPage' );
267 $revDetails = [
268 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
269 'parent' => true,
270 'comment' => $this->getRandomCommentStoreComment(),
271 'timestamp' => '20171117010101',
272 'user' => true,
273 ];
274
275 $store = MediaWikiServices::getInstance()->getRevisionStore();
276
277 // Insert the first revision
278 $revOne = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
279 $firstReturn = $store->insertRevisionOn( $revOne, wfGetDB( DB_MASTER ) );
280 $this->assertLinkTargetsEqual( $title, $firstReturn->getPageAsLinkTarget() );
281 $this->assertRevisionRecordsEqual( $revOne, $firstReturn );
282
283 // Insert a second revision inheriting the same blob address
284 $revDetails['slot'] = SlotRecord::newInherited( $firstReturn->getSlot( 'main' ) );
285 $revTwo = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
286 $secondReturn = $store->insertRevisionOn( $revTwo, wfGetDB( DB_MASTER ) );
287 $this->assertLinkTargetsEqual( $title, $secondReturn->getPageAsLinkTarget() );
288 $this->assertRevisionRecordsEqual( $revTwo, $secondReturn );
289
290 // Assert that the same blob address has been used.
291 $this->assertEquals(
292 $firstReturn->getSlot( 'main' )->getAddress(),
293 $secondReturn->getSlot( 'main' )->getAddress()
294 );
295 // And that different revisions have been created.
296 $this->assertNotSame(
297 $firstReturn->getId(),
298 $secondReturn->getId()
299 );
300 }
301
302 public function provideInsertRevisionOn_failures() {
303 yield 'no slot' => [
304 Title::newFromText( 'UTPage' ),
305 [
306 'comment' => $this->getRandomCommentStoreComment(),
307 'timestamp' => '20171117010101',
308 'user' => true,
309 ],
310 new InvalidArgumentException( 'At least one slot needs to be defined!' )
311 ];
312 yield 'slot that is not main slot' => [
313 Title::newFromText( 'UTPage' ),
314 [
315 'slot' => SlotRecord::newUnsaved( 'lalala', new WikitextContent( 'Chicken' ) ),
316 'comment' => $this->getRandomCommentStoreComment(),
317 'timestamp' => '20171117010101',
318 'user' => true,
319 ],
320 new InvalidArgumentException( 'Only the main slot is supported for now!' )
321 ];
322 yield 'no timestamp' => [
323 Title::newFromText( 'UTPage' ),
324 [
325 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
326 'comment' => $this->getRandomCommentStoreComment(),
327 'user' => true,
328 ],
329 new IncompleteRevisionException( 'timestamp field must not be NULL!' )
330 ];
331 yield 'no comment' => [
332 Title::newFromText( 'UTPage' ),
333 [
334 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
335 'timestamp' => '20171117010101',
336 'user' => true,
337 ],
338 new IncompleteRevisionException( 'comment must not be NULL!' )
339 ];
340 yield 'no user' => [
341 Title::newFromText( 'UTPage' ),
342 [
343 'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
344 'comment' => $this->getRandomCommentStoreComment(),
345 'timestamp' => '20171117010101',
346 ],
347 new IncompleteRevisionException( 'user must not be NULL!' )
348 ];
349 }
350
351 /**
352 * @dataProvider provideInsertRevisionOn_failures
353 * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
354 */
355 public function testInsertRevisionOn_failures(
356 Title $title,
357 array $revDetails = [],
358 Exception $exception ) {
359 $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
360
361 $store = MediaWikiServices::getInstance()->getRevisionStore();
362
363 $this->setExpectedException(
364 get_class( $exception ),
365 $exception->getMessage(),
366 $exception->getCode()
367 );
368 $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) );
369 }
370
371 public function provideNewNullRevision() {
372 yield [
373 Title::newFromText( 'UTPage' ),
374 CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment1' ),
375 true,
376 ];
377 yield [
378 Title::newFromText( 'UTPage' ),
379 CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment2', [ 'a' => 1 ] ),
380 false,
381 ];
382 }
383
384 /**
385 * @dataProvider provideNewNullRevision
386 * @covers \MediaWiki\Storage\RevisionStore::newNullRevision
387 */
388 public function testNewNullRevision( Title $title, $comment, $minor ) {
389 $store = MediaWikiServices::getInstance()->getRevisionStore();
390 $user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser();
391 $record = $store->newNullRevision(
392 wfGetDB( DB_MASTER ),
393 $title,
394 $comment,
395 $minor,
396 $user
397 );
398
399 $this->assertEquals( $title->getNamespace(), $record->getPageAsLinkTarget()->getNamespace() );
400 $this->assertEquals( $title->getDBkey(), $record->getPageAsLinkTarget()->getDBkey() );
401 $this->assertEquals( $comment, $record->getComment() );
402 $this->assertEquals( $minor, $record->isMinor() );
403 $this->assertEquals( $user->getName(), $record->getUser()->getName() );
404 }
405
406 /**
407 * @covers \MediaWiki\Storage\RevisionStore::newNullRevision
408 */
409 public function testNewNullRevision_nonExistingTitle() {
410 $store = MediaWikiServices::getInstance()->getRevisionStore();
411 $record = $store->newNullRevision(
412 wfGetDB( DB_MASTER ),
413 Title::newFromText( __METHOD__ . '.iDontExist!' ),
414 CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment' ),
415 false,
416 TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser()
417 );
418 $this->assertNull( $record );
419 }
420
421 /**
422 * @covers \MediaWiki\Storage\RevisionStore::getRcIdIfUnpatrolled
423 */
424 public function testGetRcIdIfUnpatrolled_returnsRecentChangesId() {
425 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
426 $status = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
427 /** @var Revision $rev */
428 $rev = $status->value['revision'];
429
430 $store = MediaWikiServices::getInstance()->getRevisionStore();
431 $revisionRecord = $store->getRevisionById( $rev->getId() );
432 $result = $store->getRcIdIfUnpatrolled( $revisionRecord );
433
434 $this->assertGreaterThan( 0, $result );
435 $this->assertSame(
436 $page->getRevision()->getRecentChange()->getAttribute( 'rc_id' ),
437 $result
438 );
439 }
440
441 /**
442 * @covers \MediaWiki\Storage\RevisionStore::getRcIdIfUnpatrolled
443 */
444 public function testGetRcIdIfUnpatrolled_returnsZeroIfPatrolled() {
445 // This assumes that sysops are auto patrolled
446 $sysop = $this->getTestSysop()->getUser();
447 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
448 $status = $page->doEditContent(
449 new WikitextContent( __METHOD__ ),
450 __METHOD__,
451 0,
452 false,
453 $sysop
454 );
455 /** @var Revision $rev */
456 $rev = $status->value['revision'];
457
458 $store = MediaWikiServices::getInstance()->getRevisionStore();
459 $revisionRecord = $store->getRevisionById( $rev->getId() );
460 $result = $store->getRcIdIfUnpatrolled( $revisionRecord );
461
462 $this->assertSame( 0, $result );
463 }
464
465 /**
466 * @covers \MediaWiki\Storage\RevisionStore::getRecentChange
467 */
468 public function testGetRecentChange() {
469 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
470 $content = new WikitextContent( __METHOD__ );
471 $status = $page->doEditContent( $content, __METHOD__ );
472 /** @var Revision $rev */
473 $rev = $status->value['revision'];
474
475 $store = MediaWikiServices::getInstance()->getRevisionStore();
476 $revRecord = $store->getRevisionById( $rev->getId() );
477 $recentChange = $store->getRecentChange( $revRecord );
478
479 $this->assertEquals( $rev->getId(), $recentChange->getAttribute( 'rc_this_oldid' ) );
480 $this->assertEquals( $rev->getRecentChange(), $recentChange );
481 }
482
483 /**
484 * @covers \MediaWiki\Storage\RevisionStore::getRevisionById
485 */
486 public function testGetRevisionById() {
487 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
488 $content = new WikitextContent( __METHOD__ );
489 $status = $page->doEditContent( $content, __METHOD__ );
490 /** @var Revision $rev */
491 $rev = $status->value['revision'];
492
493 $store = MediaWikiServices::getInstance()->getRevisionStore();
494 $revRecord = $store->getRevisionById( $rev->getId() );
495
496 $this->assertSame( $rev->getId(), $revRecord->getId() );
497 $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) );
498 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
499 }
500
501 /**
502 * @covers \MediaWiki\Storage\RevisionStore::getRevisionByTitle
503 */
504 public function testGetRevisionByTitle() {
505 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
506 $content = new WikitextContent( __METHOD__ );
507 $status = $page->doEditContent( $content, __METHOD__ );
508 /** @var Revision $rev */
509 $rev = $status->value['revision'];
510
511 $store = MediaWikiServices::getInstance()->getRevisionStore();
512 $revRecord = $store->getRevisionByTitle( $page->getTitle() );
513
514 $this->assertSame( $rev->getId(), $revRecord->getId() );
515 $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) );
516 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
517 }
518
519 /**
520 * @covers \MediaWiki\Storage\RevisionStore::getRevisionByPageId
521 */
522 public function testGetRevisionByPageId() {
523 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
524 $content = new WikitextContent( __METHOD__ );
525 $status = $page->doEditContent( $content, __METHOD__ );
526 /** @var Revision $rev */
527 $rev = $status->value['revision'];
528
529 $store = MediaWikiServices::getInstance()->getRevisionStore();
530 $revRecord = $store->getRevisionByPageId( $page->getId() );
531
532 $this->assertSame( $rev->getId(), $revRecord->getId() );
533 $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) );
534 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
535 }
536
537 /**
538 * @covers \MediaWiki\Storage\RevisionStore::getRevisionByTimestamp
539 */
540 public function testGetRevisionByTimestamp() {
541 // Make sure there is 1 second between the last revision and the rev we create...
542 // Otherwise we might not get the correct revision and the test may fail...
543 // :(
544 sleep( 1 );
545 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
546 $content = new WikitextContent( __METHOD__ );
547 $status = $page->doEditContent( $content, __METHOD__ );
548 /** @var Revision $rev */
549 $rev = $status->value['revision'];
550
551 $store = MediaWikiServices::getInstance()->getRevisionStore();
552 $revRecord = $store->getRevisionByTimestamp(
553 $page->getTitle(),
554 $rev->getTimestamp()
555 );
556
557 $this->assertSame( $rev->getId(), $revRecord->getId() );
558 $this->assertTrue( $revRecord->getSlot( 'main' )->getContent()->equals( $content ) );
559 $this->assertSame( __METHOD__, $revRecord->getComment()->text );
560 }
561
562 private function revisionToRow( Revision $rev ) {
563 $page = WikiPage::factory( $rev->getTitle() );
564
565 return (object)[
566 'rev_id' => (string)$rev->getId(),
567 'rev_page' => (string)$rev->getPage(),
568 'rev_text_id' => (string)$rev->getTextId(),
569 'rev_timestamp' => (string)$rev->getTimestamp(),
570 'rev_user_text' => (string)$rev->getUserText(),
571 'rev_user' => (string)$rev->getUser(),
572 'rev_minor_edit' => $rev->isMinor() ? '1' : '0',
573 'rev_deleted' => (string)$rev->getVisibility(),
574 'rev_len' => (string)$rev->getSize(),
575 'rev_parent_id' => (string)$rev->getParentId(),
576 'rev_sha1' => (string)$rev->getSha1(),
577 'rev_comment_text' => $rev->getComment(),
578 'rev_comment_data' => null,
579 'rev_comment_cid' => null,
580 'rev_content_format' => $rev->getContentFormat(),
581 'rev_content_model' => $rev->getContentModel(),
582 'page_namespace' => (string)$page->getTitle()->getNamespace(),
583 'page_title' => $page->getTitle()->getDBkey(),
584 'page_id' => (string)$page->getId(),
585 'page_latest' => (string)$page->getLatest(),
586 'page_is_redirect' => $page->isRedirect() ? '1' : '0',
587 'page_len' => (string)$page->getContent()->getSize(),
588 'user_name' => (string)$rev->getUserText(),
589 ];
590 }
591
592 private function assertRevisionRecordMatchesRevision(
593 Revision $rev,
594 RevisionRecord $record
595 ) {
596 $this->assertSame( $rev->getId(), $record->getId() );
597 $this->assertSame( $rev->getPage(), $record->getPageId() );
598 $this->assertSame( $rev->getTimestamp(), $record->getTimestamp() );
599 $this->assertSame( $rev->getUserText(), $record->getUser()->getName() );
600 $this->assertSame( $rev->getUser(), $record->getUser()->getId() );
601 $this->assertSame( $rev->isMinor(), $record->isMinor() );
602 $this->assertSame( $rev->getVisibility(), $record->getVisibility() );
603 $this->assertSame( $rev->getSize(), $record->getSize() );
604 /**
605 * @note As of MW 1.31, the database schema allows the parent ID to be
606 * NULL to indicate that it is unknown.
607 */
608 $expectedParent = $rev->getParentId();
609 if ( $expectedParent === null ) {
610 $expectedParent = 0;
611 }
612 $this->assertSame( $expectedParent, $record->getParentId() );
613 $this->assertSame( $rev->getSha1(), $record->getSha1() );
614 $this->assertSame( $rev->getComment(), $record->getComment()->text );
615 $this->assertSame( $rev->getContentFormat(), $record->getContent( 'main' )->getDefaultFormat() );
616 $this->assertSame( $rev->getContentModel(), $record->getContent( 'main' )->getModel() );
617 $this->assertLinkTargetsEqual( $rev->getTitle(), $record->getPageAsLinkTarget() );
618 }
619
620 /**
621 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
622 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
623 */
624 public function testNewRevisionFromRow_anonEdit() {
625 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
626 $this->overrideMwServices();
627
628 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
629 $text = __METHOD__ . 'a-ä';
630 /** @var Revision $rev */
631 $rev = $page->doEditContent(
632 new WikitextContent( $text ),
633 __METHOD__ . 'a'
634 )->value['revision'];
635
636 $store = MediaWikiServices::getInstance()->getRevisionStore();
637 $record = $store->newRevisionFromRow(
638 $this->revisionToRow( $rev ),
639 [],
640 $page->getTitle()
641 );
642 $this->assertRevisionRecordMatchesRevision( $rev, $record );
643 $this->assertSame( $text, $rev->getContent()->serialize() );
644 }
645
646 /**
647 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
648 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
649 */
650 public function testNewRevisionFromRow_anonEdit_legacyEncoding() {
651 $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
652 $this->overrideMwServices();
653 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
654 $text = __METHOD__ . 'a-ä';
655 /** @var Revision $rev */
656 $rev = $page->doEditContent(
657 new WikitextContent( $text ),
658 __METHOD__. 'a'
659 )->value['revision'];
660
661 $store = MediaWikiServices::getInstance()->getRevisionStore();
662 $record = $store->newRevisionFromRow(
663 $this->revisionToRow( $rev ),
664 [],
665 $page->getTitle()
666 );
667 $this->assertRevisionRecordMatchesRevision( $rev, $record );
668 $this->assertSame( $text, $rev->getContent()->serialize() );
669 }
670
671 /**
672 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
673 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
674 */
675 public function testNewRevisionFromRow_userEdit() {
676 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_WRITE_BOTH );
677 $this->overrideMwServices();
678
679 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
680 $text = __METHOD__ . 'b-ä';
681 /** @var Revision $rev */
682 $rev = $page->doEditContent(
683 new WikitextContent( $text ),
684 __METHOD__ . 'b',
685 0,
686 false,
687 $this->getTestUser()->getUser()
688 )->value['revision'];
689
690 $store = MediaWikiServices::getInstance()->getRevisionStore();
691 $record = $store->newRevisionFromRow(
692 $this->revisionToRow( $rev ),
693 [],
694 $page->getTitle()
695 );
696 $this->assertRevisionRecordMatchesRevision( $rev, $record );
697 $this->assertSame( $text, $rev->getContent()->serialize() );
698 }
699
700 /**
701 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow
702 */
703 public function testNewRevisionFromArchiveRow() {
704 $store = MediaWikiServices::getInstance()->getRevisionStore();
705 $title = Title::newFromText( __METHOD__ );
706 $text = __METHOD__ . '-bä';
707 $page = WikiPage::factory( $title );
708 /** @var Revision $orig */
709 $orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
710 ->value['revision'];
711 $page->doDeleteArticle( __METHOD__ );
712
713 $db = wfGetDB( DB_MASTER );
714 $arQuery = $store->getArchiveQueryInfo();
715 $res = $db->select(
716 $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
717 __METHOD__, [], $arQuery['joins']
718 );
719 $this->assertTrue( is_object( $res ), 'query failed' );
720
721 $row = $res->fetchObject();
722 $res->free();
723 $record = $store->newRevisionFromArchiveRow( $row );
724
725 $this->assertRevisionRecordMatchesRevision( $orig, $record );
726 $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
727 }
728
729 /**
730 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow
731 */
732 public function testNewRevisionFromArchiveRow_legacyEncoding() {
733 $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
734 $this->overrideMwServices();
735 $store = MediaWikiServices::getInstance()->getRevisionStore();
736 $title = Title::newFromText( __METHOD__ );
737 $text = __METHOD__ . '-bä';
738 $page = WikiPage::factory( $title );
739 /** @var Revision $orig */
740 $orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
741 ->value['revision'];
742 $page->doDeleteArticle( __METHOD__ );
743
744 $db = wfGetDB( DB_MASTER );
745 $arQuery = $store->getArchiveQueryInfo();
746 $res = $db->select(
747 $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
748 __METHOD__, [], $arQuery['joins']
749 );
750 $this->assertTrue( is_object( $res ), 'query failed' );
751
752 $row = $res->fetchObject();
753 $res->free();
754 $record = $store->newRevisionFromArchiveRow( $row );
755
756 $this->assertRevisionRecordMatchesRevision( $orig, $record );
757 $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
758 }
759
760 /**
761 * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromId
762 */
763 public function testLoadRevisionFromId() {
764 $title = Title::newFromText( __METHOD__ );
765 $page = WikiPage::factory( $title );
766 /** @var Revision $rev */
767 $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
768 ->value['revision'];
769
770 $store = MediaWikiServices::getInstance()->getRevisionStore();
771 $result = $store->loadRevisionFromId( wfGetDB( DB_MASTER ), $rev->getId() );
772 $this->assertRevisionRecordMatchesRevision( $rev, $result );
773 }
774
775 /**
776 * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromPageId
777 */
778 public function testLoadRevisionFromPageId() {
779 $title = Title::newFromText( __METHOD__ );
780 $page = WikiPage::factory( $title );
781 /** @var Revision $rev */
782 $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
783 ->value['revision'];
784
785 $store = MediaWikiServices::getInstance()->getRevisionStore();
786 $result = $store->loadRevisionFromPageId( wfGetDB( DB_MASTER ), $page->getId() );
787 $this->assertRevisionRecordMatchesRevision( $rev, $result );
788 }
789
790 /**
791 * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromTitle
792 */
793 public function testLoadRevisionFromTitle() {
794 $title = Title::newFromText( __METHOD__ );
795 $page = WikiPage::factory( $title );
796 /** @var Revision $rev */
797 $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
798 ->value['revision'];
799
800 $store = MediaWikiServices::getInstance()->getRevisionStore();
801 $result = $store->loadRevisionFromTitle( wfGetDB( DB_MASTER ), $title );
802 $this->assertRevisionRecordMatchesRevision( $rev, $result );
803 }
804
805 /**
806 * @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromTimestamp
807 */
808 public function testLoadRevisionFromTimestamp() {
809 $title = Title::newFromText( __METHOD__ );
810 $page = WikiPage::factory( $title );
811 /** @var Revision $revOne */
812 $revOne = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
813 ->value['revision'];
814 // Sleep to ensure different timestamps... )(evil)
815 sleep( 1 );
816 /** @var Revision $revTwo */
817 $revTwo = $page->doEditContent( new WikitextContent( __METHOD__ . 'a' ), '' )
818 ->value['revision'];
819
820 $store = MediaWikiServices::getInstance()->getRevisionStore();
821 $this->assertNull(
822 $store->loadRevisionFromTimestamp( wfGetDB( DB_MASTER ), $title, '20150101010101' )
823 );
824 $this->assertSame(
825 $revOne->getId(),
826 $store->loadRevisionFromTimestamp(
827 wfGetDB( DB_MASTER ),
828 $title,
829 $revOne->getTimestamp()
830 )->getId()
831 );
832 $this->assertSame(
833 $revTwo->getId(),
834 $store->loadRevisionFromTimestamp(
835 wfGetDB( DB_MASTER ),
836 $title,
837 $revTwo->getTimestamp()
838 )->getId()
839 );
840 }
841
842 /**
843 * @covers \MediaWiki\Storage\RevisionStore::listRevisionSizes
844 */
845 public function testGetParentLengths() {
846 $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
847 /** @var Revision $revOne */
848 $revOne = $page->doEditContent(
849 new WikitextContent( __METHOD__ ), __METHOD__
850 )->value['revision'];
851 /** @var Revision $revTwo */
852 $revTwo = $page->doEditContent(
853 new WikitextContent( __METHOD__ . '2' ), __METHOD__
854 )->value['revision'];
855
856 $store = MediaWikiServices::getInstance()->getRevisionStore();
857 $this->assertSame(
858 [
859 $revOne->getId() => strlen( __METHOD__ ),
860 ],
861 $store->listRevisionSizes(
862 wfGetDB( DB_MASTER ),
863 [ $revOne->getId() ]
864 )
865 );
866 $this->assertSame(
867 [
868 $revOne->getId() => strlen( __METHOD__ ),
869 $revTwo->getId() => strlen( __METHOD__ ) + 1,
870 ],
871 $store->listRevisionSizes(
872 wfGetDB( DB_MASTER ),
873 [ $revOne->getId(), $revTwo->getId() ]
874 )
875 );
876 }
877
878 /**
879 * @covers \MediaWiki\Storage\RevisionStore::getPreviousRevision
880 */
881 public function testGetPreviousRevision() {
882 $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
883 /** @var Revision $revOne */
884 $revOne = $page->doEditContent(
885 new WikitextContent( __METHOD__ ), __METHOD__
886 )->value['revision'];
887 /** @var Revision $revTwo */
888 $revTwo = $page->doEditContent(
889 new WikitextContent( __METHOD__ . '2' ), __METHOD__
890 )->value['revision'];
891
892 $store = MediaWikiServices::getInstance()->getRevisionStore();
893 $this->assertNull(
894 $store->getPreviousRevision( $store->getRevisionById( $revOne->getId() ) )
895 );
896 $this->assertSame(
897 $revOne->getId(),
898 $store->getPreviousRevision( $store->getRevisionById( $revTwo->getId() ) )->getId()
899 );
900 }
901
902 /**
903 * @covers \MediaWiki\Storage\RevisionStore::getNextRevision
904 */
905 public function testGetNextRevision() {
906 $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
907 /** @var Revision $revOne */
908 $revOne = $page->doEditContent(
909 new WikitextContent( __METHOD__ ), __METHOD__
910 )->value['revision'];
911 /** @var Revision $revTwo */
912 $revTwo = $page->doEditContent(
913 new WikitextContent( __METHOD__ . '2' ), __METHOD__
914 )->value['revision'];
915
916 $store = MediaWikiServices::getInstance()->getRevisionStore();
917 $this->assertSame(
918 $revTwo->getId(),
919 $store->getNextRevision( $store->getRevisionById( $revOne->getId() ) )->getId()
920 );
921 $this->assertNull(
922 $store->getNextRevision( $store->getRevisionById( $revTwo->getId() ) )
923 );
924 }
925
926 /**
927 * @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId
928 */
929 public function testGetTimestampFromId_found() {
930 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
931 /** @var Revision $rev */
932 $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
933 ->value['revision'];
934
935 $store = MediaWikiServices::getInstance()->getRevisionStore();
936 $result = $store->getTimestampFromId(
937 $page->getTitle(),
938 $rev->getId()
939 );
940
941 $this->assertSame( $rev->getTimestamp(), $result );
942 }
943
944 /**
945 * @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId
946 */
947 public function testGetTimestampFromId_notFound() {
948 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
949 /** @var Revision $rev */
950 $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
951 ->value['revision'];
952
953 $store = MediaWikiServices::getInstance()->getRevisionStore();
954 $result = $store->getTimestampFromId(
955 $page->getTitle(),
956 $rev->getId() + 1
957 );
958
959 $this->assertFalse( $result );
960 }
961
962 /**
963 * @covers \MediaWiki\Storage\RevisionStore::countRevisionsByPageId
964 */
965 public function testCountRevisionsByPageId() {
966 $store = MediaWikiServices::getInstance()->getRevisionStore();
967 $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
968
969 $this->assertSame(
970 0,
971 $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() )
972 );
973 $page->doEditContent( new WikitextContent( 'a' ), 'a' );
974 $this->assertSame(
975 1,
976 $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() )
977 );
978 $page->doEditContent( new WikitextContent( 'b' ), 'b' );
979 $this->assertSame(
980 2,
981 $store->countRevisionsByPageId( wfGetDB( DB_MASTER ), $page->getId() )
982 );
983 }
984
985 /**
986 * @covers \MediaWiki\Storage\RevisionStore::countRevisionsByTitle
987 */
988 public function testCountRevisionsByTitle() {
989 $store = MediaWikiServices::getInstance()->getRevisionStore();
990 $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
991
992 $this->assertSame(
993 0,
994 $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() )
995 );
996 $page->doEditContent( new WikitextContent( 'a' ), 'a' );
997 $this->assertSame(
998 1,
999 $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() )
1000 );
1001 $page->doEditContent( new WikitextContent( 'b' ), 'b' );
1002 $this->assertSame(
1003 2,
1004 $store->countRevisionsByTitle( wfGetDB( DB_MASTER ), $page->getTitle() )
1005 );
1006 }
1007
1008 /**
1009 * @covers \MediaWiki\Storage\RevisionStore::userWasLastToEdit
1010 */
1011 public function testUserWasLastToEdit_false() {
1012 $sysop = $this->getTestSysop()->getUser();
1013 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
1014 $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1015
1016 $store = MediaWikiServices::getInstance()->getRevisionStore();
1017 $result = $store->userWasLastToEdit(
1018 wfGetDB( DB_MASTER ),
1019 $page->getId(),
1020 $sysop->getId(),
1021 '20160101010101'
1022 );
1023 $this->assertFalse( $result );
1024 }
1025
1026 /**
1027 * @covers \MediaWiki\Storage\RevisionStore::userWasLastToEdit
1028 */
1029 public function testUserWasLastToEdit_true() {
1030 $startTime = wfTimestampNow();
1031 $sysop = $this->getTestSysop()->getUser();
1032 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
1033 $page->doEditContent(
1034 new WikitextContent( __METHOD__ ),
1035 __METHOD__,
1036 0,
1037 false,
1038 $sysop
1039 );
1040
1041 $store = MediaWikiServices::getInstance()->getRevisionStore();
1042 $result = $store->userWasLastToEdit(
1043 wfGetDB( DB_MASTER ),
1044 $page->getId(),
1045 $sysop->getId(),
1046 $startTime
1047 );
1048 $this->assertTrue( $result );
1049 }
1050
1051 /**
1052 * @covers \MediaWiki\Storage\RevisionStore::getKnownCurrentRevision
1053 */
1054 public function testGetKnownCurrentRevision() {
1055 $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
1056 /** @var Revision $rev */
1057 $rev = $page->doEditContent(
1058 new WikitextContent( __METHOD__ . 'b' ),
1059 __METHOD__ . 'b',
1060 0,
1061 false,
1062 $this->getTestUser()->getUser()
1063 )->value['revision'];
1064
1065 $store = MediaWikiServices::getInstance()->getRevisionStore();
1066 $record = $store->getKnownCurrentRevision(
1067 $page->getTitle(),
1068 $rev->getId()
1069 );
1070
1071 $this->assertRevisionRecordMatchesRevision( $rev, $record );
1072 }
1073
1074 public function provideNewMutableRevisionFromArray() {
1075 yield 'Basic array, with page & id' => [
1076 [
1077 'id' => 2,
1078 'page' => 1,
1079 'text_id' => 2,
1080 'timestamp' => '20171017114835',
1081 'user_text' => '111.0.1.2',
1082 'user' => 0,
1083 'minor_edit' => false,
1084 'deleted' => 0,
1085 'len' => 46,
1086 'parent_id' => 1,
1087 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1088 'comment' => 'Goat Comment!',
1089 'content_format' => 'text/x-wiki',
1090 'content_model' => 'wikitext',
1091 ]
1092 ];
1093 yield 'Basic array, content object' => [
1094 [
1095 'id' => 2,
1096 'page' => 1,
1097 'timestamp' => '20171017114835',
1098 'user_text' => '111.0.1.2',
1099 'user' => 0,
1100 'minor_edit' => false,
1101 'deleted' => 0,
1102 'len' => 46,
1103 'parent_id' => 1,
1104 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1105 'comment' => 'Goat Comment!',
1106 'content' => new WikitextContent( 'Some Content' ),
1107 ]
1108 ];
1109 yield 'Basic array, serialized text' => [
1110 [
1111 'id' => 2,
1112 'page' => 1,
1113 'timestamp' => '20171017114835',
1114 'user_text' => '111.0.1.2',
1115 'user' => 0,
1116 'minor_edit' => false,
1117 'deleted' => 0,
1118 'len' => 46,
1119 'parent_id' => 1,
1120 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1121 'comment' => 'Goat Comment!',
1122 'text' => ( new WikitextContent( 'Söme Content' ) )->serialize(),
1123 ]
1124 ];
1125 yield 'Basic array, serialized text, utf-8 flags' => [
1126 [
1127 'id' => 2,
1128 'page' => 1,
1129 'timestamp' => '20171017114835',
1130 'user_text' => '111.0.1.2',
1131 'user' => 0,
1132 'minor_edit' => false,
1133 'deleted' => 0,
1134 'len' => 46,
1135 'parent_id' => 1,
1136 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1137 'comment' => 'Goat Comment!',
1138 'text' => ( new WikitextContent( 'Söme Content' ) )->serialize(),
1139 'flags' => 'utf-8',
1140 ]
1141 ];
1142 yield 'Basic array, with title' => [
1143 [
1144 'title' => Title::newFromText( 'SomeText' ),
1145 'text_id' => 2,
1146 'timestamp' => '20171017114835',
1147 'user_text' => '111.0.1.2',
1148 'user' => 0,
1149 'minor_edit' => false,
1150 'deleted' => 0,
1151 'len' => 46,
1152 'parent_id' => 1,
1153 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1154 'comment' => 'Goat Comment!',
1155 'content_format' => 'text/x-wiki',
1156 'content_model' => 'wikitext',
1157 ]
1158 ];
1159 yield 'Basic array, no user field' => [
1160 [
1161 'id' => 2,
1162 'page' => 1,
1163 'text_id' => 2,
1164 'timestamp' => '20171017114835',
1165 'user_text' => '111.0.1.3',
1166 'minor_edit' => false,
1167 'deleted' => 0,
1168 'len' => 46,
1169 'parent_id' => 1,
1170 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
1171 'comment' => 'Goat Comment!',
1172 'content_format' => 'text/x-wiki',
1173 'content_model' => 'wikitext',
1174 ]
1175 ];
1176 }
1177
1178 /**
1179 * @dataProvider provideNewMutableRevisionFromArray
1180 * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray
1181 */
1182 public function testNewMutableRevisionFromArray( array $array ) {
1183 $store = MediaWikiServices::getInstance()->getRevisionStore();
1184
1185 $result = $store->newMutableRevisionFromArray( $array );
1186
1187 if ( isset( $array['id'] ) ) {
1188 $this->assertSame( $array['id'], $result->getId() );
1189 }
1190 if ( isset( $array['page'] ) ) {
1191 $this->assertSame( $array['page'], $result->getPageId() );
1192 }
1193 $this->assertSame( $array['timestamp'], $result->getTimestamp() );
1194 $this->assertSame( $array['user_text'], $result->getUser()->getName() );
1195 if ( isset( $array['user'] ) ) {
1196 $this->assertSame( $array['user'], $result->getUser()->getId() );
1197 }
1198 $this->assertSame( (bool)$array['minor_edit'], $result->isMinor() );
1199 $this->assertSame( $array['deleted'], $result->getVisibility() );
1200 $this->assertSame( $array['len'], $result->getSize() );
1201 $this->assertSame( $array['parent_id'], $result->getParentId() );
1202 $this->assertSame( $array['sha1'], $result->getSha1() );
1203 $this->assertSame( $array['comment'], $result->getComment()->text );
1204 if ( isset( $array['content'] ) ) {
1205 $this->assertTrue(
1206 $result->getSlot( 'main' )->getContent()->equals( $array['content'] )
1207 );
1208 } elseif ( isset( $array['text'] ) ) {
1209 $this->assertSame( $array['text'], $result->getSlot( 'main' )->getContent()->serialize() );
1210 } else {
1211 $this->assertSame(
1212 $array['content_format'],
1213 $result->getSlot( 'main' )->getContent()->getDefaultFormat()
1214 );
1215 $this->assertSame( $array['content_model'], $result->getSlot( 'main' )->getModel() );
1216 }
1217 }
1218
1219 /**
1220 * @dataProvider provideNewMutableRevisionFromArray
1221 * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray
1222 */
1223 public function testNewMutableRevisionFromArray_legacyEncoding( array $array ) {
1224 $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
1225 $blobStore = new SqlBlobStore( wfGetLB(), $cache );
1226 $blobStore->setLegacyEncoding( 'windows-1252', Language::factory( 'en' ) );
1227
1228 $factory = $this->getMockBuilder( BlobStoreFactory::class )
1229 ->setMethods( [ 'newBlobStore', 'newSqlBlobStore' ] )
1230 ->disableOriginalConstructor()
1231 ->getMock();
1232 $factory->expects( $this->any() )
1233 ->method( 'newBlobStore' )
1234 ->willReturn( $blobStore );
1235 $factory->expects( $this->any() )
1236 ->method( 'newSqlBlobStore' )
1237 ->willReturn( $blobStore );
1238
1239 $this->setService( 'BlobStoreFactory', $factory );
1240
1241 $this->testNewMutableRevisionFromArray( $array );
1242 }
1243
1244 }