3 namespace MediaWiki\Tests\Storage
;
7 use MediaWiki\Storage\RevisionAccessException
;
8 use MediaWiki\Storage\RevisionStore
;
9 use MediaWiki\Storage\SqlBlobStore
;
10 use MediaWikiTestCase
;
13 use Wikimedia\Rdbms\Database
;
14 use Wikimedia\Rdbms\LoadBalancer
;
16 class RevisionStoreTest
extends MediaWikiTestCase
{
19 * @param LoadBalancer $loadBalancer
20 * @param SqlBlobStore $blobStore
21 * @param WANObjectCache $WANObjectCache
23 * @return RevisionStore
25 private function getRevisionStore(
28 $WANObjectCache = null
30 return new RevisionStore(
31 $loadBalancer ?
$loadBalancer : $this->getMockLoadBalancer(),
32 $blobStore ?
$blobStore : $this->getMockSqlBlobStore(),
33 $WANObjectCache ?
$WANObjectCache : $this->getHashWANObjectCache()
38 * @return \PHPUnit_Framework_MockObject_MockObject|LoadBalancer
40 private function getMockLoadBalancer() {
41 return $this->getMockBuilder( LoadBalancer
::class )
42 ->disableOriginalConstructor()->getMock();
46 * @return \PHPUnit_Framework_MockObject_MockObject|Database
48 private function getMockDatabase() {
49 return $this->getMockBuilder( Database
::class )
50 ->disableOriginalConstructor()->getMock();
54 * @return \PHPUnit_Framework_MockObject_MockObject|SqlBlobStore
56 private function getMockSqlBlobStore() {
57 return $this->getMockBuilder( SqlBlobStore
::class )
58 ->disableOriginalConstructor()->getMock();
61 private function getHashWANObjectCache() {
62 return new WANObjectCache( [ 'cache' => new \
HashBagOStuff() ] );
66 * @covers \MediaWiki\Storage\RevisionStore::getContentHandlerUseDB
67 * @covers \MediaWiki\Storage\RevisionStore::setContentHandlerUseDB
69 public function testGetSetContentHandlerDb() {
70 $store = $this->getRevisionStore();
71 $this->assertTrue( $store->getContentHandlerUseDB() );
72 $store->setContentHandlerUseDB( false );
73 $this->assertFalse( $store->getContentHandlerUseDB() );
74 $store->setContentHandlerUseDB( true );
75 $this->assertTrue( $store->getContentHandlerUseDB() );
78 private function getDefaultQueryFields() {
94 private function getCommentQueryFields() {
96 'rev_comment_text' => 'rev_comment',
97 'rev_comment_data' => 'NULL',
98 'rev_comment_cid' => 'NULL',
102 private function getContentHandlerQueryFields() {
104 'rev_content_format',
109 public function provideGetQueryInfo() {
114 'tables' => [ 'revision' ],
115 'fields' => array_merge(
116 $this->getDefaultQueryFields(),
117 $this->getCommentQueryFields(),
118 $this->getContentHandlerQueryFields()
127 'tables' => [ 'revision' ],
128 'fields' => array_merge(
129 $this->getDefaultQueryFields(),
130 $this->getCommentQueryFields()
139 'tables' => [ 'revision', 'page' ],
140 'fields' => array_merge(
141 $this->getDefaultQueryFields(),
142 $this->getCommentQueryFields(),
153 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
161 'tables' => [ 'revision', 'user' ],
162 'fields' => array_merge(
163 $this->getDefaultQueryFields(),
164 $this->getCommentQueryFields(),
170 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
178 'tables' => [ 'revision', 'text' ],
179 'fields' => array_merge(
180 $this->getDefaultQueryFields(),
181 $this->getCommentQueryFields(),
188 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ],
194 [ 'page', 'user', 'text' ],
196 'tables' => [ 'revision', 'page', 'user', 'text' ],
197 'fields' => array_merge(
198 $this->getDefaultQueryFields(),
199 $this->getCommentQueryFields(),
200 $this->getContentHandlerQueryFields(),
214 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
215 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
216 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ],
223 * @dataProvider provideGetQueryInfo
224 * @covers \MediaWiki\Storage\RevisionStore::getQueryInfo
226 public function testGetQueryInfo( $contentHandlerUseDb, $options, $expected ) {
227 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD
);
228 $this->overrideMwServices();
229 $store = $this->getRevisionStore();
230 $store->setContentHandlerUseDB( $contentHandlerUseDb );
231 $this->assertEquals( $expected, $store->getQueryInfo( $options ) );
234 private function getDefaultArchiveFields() {
255 * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
257 public function testGetArchiveQueryInfo_contentHandlerDb() {
258 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD
);
259 $this->overrideMwServices();
260 $store = $this->getRevisionStore();
261 $store->setContentHandlerUseDB( true );
267 'fields' => array_merge(
268 $this->getDefaultArchiveFields(),
270 'ar_comment_text' => 'ar_comment',
271 'ar_comment_data' => 'NULL',
272 'ar_comment_cid' => 'NULL',
279 $store->getArchiveQueryInfo()
284 * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
286 public function testGetArchiveQueryInfo_noContentHandlerDb() {
287 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD
);
288 $this->overrideMwServices();
289 $store = $this->getRevisionStore();
290 $store->setContentHandlerUseDB( false );
296 'fields' => array_merge(
297 $this->getDefaultArchiveFields(),
299 'ar_comment_text' => 'ar_comment',
300 'ar_comment_data' => 'NULL',
301 'ar_comment_cid' => 'NULL',
306 $store->getArchiveQueryInfo()
310 public function testGetTitle_successFromPageId() {
311 $mockLoadBalancer = $this->getMockLoadBalancer();
312 // Title calls wfGetDB() so we have to set the main service
313 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
315 $db = $this->getMockDatabase();
316 // Title calls wfGetDB() which uses a regular Connection
317 $mockLoadBalancer->expects( $this->atLeastOnce() )
318 ->method( 'getConnection' )
321 // First call to Title::newFromID, faking no result (db lag?)
322 $db->expects( $this->at( 0 ) )
323 ->method( 'selectRow' )
329 ->willReturn( (object)[
330 'page_namespace' => '1',
331 'page_title' => 'Food',
334 $store = $this->getRevisionStore( $mockLoadBalancer );
335 $title = $store->getTitle( 1, 2, RevisionStore
::READ_NORMAL
);
337 $this->assertSame( 1, $title->getNamespace() );
338 $this->assertSame( 'Food', $title->getDBkey() );
341 public function testGetTitle_successFromRevId() {
342 $mockLoadBalancer = $this->getMockLoadBalancer();
343 // Title calls wfGetDB() so we have to set the main service
344 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
346 $db = $this->getMockDatabase();
347 // Title calls wfGetDB() which uses a regular Connection
348 $mockLoadBalancer->expects( $this->atLeastOnce() )
349 ->method( 'getConnection' )
351 // RevisionStore getTitle uses a ConnectionRef
352 $mockLoadBalancer->expects( $this->atLeastOnce() )
353 ->method( 'getConnectionRef' )
356 // First call to Title::newFromID, faking no result (db lag?)
357 $db->expects( $this->at( 0 ) )
358 ->method( 'selectRow' )
364 ->willReturn( false );
366 // First select using rev_id, faking no result (db lag?)
367 $db->expects( $this->at( 1 ) )
368 ->method( 'selectRow' )
370 [ 'revision', 'page' ],
374 ->willReturn( (object)[
375 'page_namespace' => '1',
376 'page_title' => 'Food2',
379 $store = $this->getRevisionStore( $mockLoadBalancer );
380 $title = $store->getTitle( 1, 2, RevisionStore
::READ_NORMAL
);
382 $this->assertSame( 1, $title->getNamespace() );
383 $this->assertSame( 'Food2', $title->getDBkey() );
387 * @covers \MediaWiki\Storage\RevisionStore::getTitle
389 public function testGetTitle_throwsExceptionAfterFallbacks() {
390 $mockLoadBalancer = $this->getMockLoadBalancer();
391 // Title calls wfGetDB() so we have to set the main service
392 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
394 $db = $this->getMockDatabase();
395 // Title calls wfGetDB() which uses a regular Connection
396 $mockLoadBalancer->expects( $this->atLeastOnce() )
397 ->method( 'getConnection' )
399 // RevisionStore getTitle uses a ConnectionRef
400 $mockLoadBalancer->expects( $this->atLeastOnce() )
401 ->method( 'getConnectionRef' )
404 // First call to Title::newFromID, faking no result (db lag?)
405 $db->expects( $this->at( 0 ) )
406 ->method( 'selectRow' )
412 ->willReturn( false );
414 // First select using rev_id, faking no result (db lag?)
415 $db->expects( $this->at( 1 ) )
416 ->method( 'selectRow' )
418 [ 'revision', 'page' ],
422 ->willReturn( false );
424 $store = $this->getRevisionStore( $mockLoadBalancer );
426 $this->setExpectedException( RevisionAccessException
::class );
427 $store->getTitle( 1, 2, RevisionStore
::READ_NORMAL
);
430 public function provideNewRevisionFromRow_legacyEncoding_applied() {
431 yield
'windows-1252, old_flags is empty' => [
436 'old_text' => "S\xF6me Content",
441 yield
'windows-1252, old_flags is null' => [
446 'old_text' => "S\xF6me Content",
453 * @dataProvider provideNewRevisionFromRow_legacyEncoding_applied
455 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
456 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
458 public function testNewRevisionFromRow_legacyEncoding_applied( $encoding, $locale, $row, $text ) {
459 $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
461 $blobStore = new SqlBlobStore( wfGetLB(), $cache );
462 $blobStore->setLegacyEncoding( $encoding, Language
::factory( $locale ) );
464 $store = new RevisionStore( wfGetLB(), $blobStore, $cache );
466 $record = $store->newRevisionFromRow(
467 $this->makeRow( $row ),
469 Title
::newFromText( __METHOD__
. '-UTPage' )
472 $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
476 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
477 * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
479 public function testNewRevisionFromRow_legacyEncoding_ignored() {
481 'old_flags' => 'utf-8',
482 'old_text' => 'Söme Content',
485 $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
487 $blobStore = new SqlBlobStore( wfGetLB(), $cache );
488 $blobStore->setLegacyEncoding( 'windows-1252', Language
::factory( 'en' ) );
490 $store = new RevisionStore( wfGetLB(), $blobStore, $cache );
492 $record = $store->newRevisionFromRow(
493 $this->makeRow( $row ),
495 Title
::newFromText( __METHOD__
. '-UTPage' )
497 $this->assertSame( 'Söme Content', $record->getContent( 'main' )->serialize() );
500 private function makeRow( array $array ) {
505 'rev_timestamp' => '20110101000000',
506 'rev_user_text' => 'Tester',
508 'rev_minor_edit' => 0,
511 'rev_parent_id' => 0,
512 'rev_sha1' => 'deadbeef',
513 'rev_comment_text' => 'Testing',
514 'rev_comment_data' => '{}',
515 'rev_comment_cid' => 111,
516 'rev_content_format' => CONTENT_FORMAT_TEXT
,
517 'rev_content_model' => CONTENT_MODEL_TEXT
,
518 'page_namespace' => 0,
519 'page_title' => 'TEST',
522 'page_is_redirect' => 0,
524 'user_name' => 'Tester',
526 'old_text' => 'Hello World',
527 'old_flags' => 'utf-8',