RevisionStore tests for current behaviour of getTitle
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / RevisionStoreTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Storage;
4
5 use MediaWiki\Storage\RevisionAccessException;
6 use MediaWiki\Storage\RevisionStore;
7 use MediaWiki\Storage\SqlBlobStore;
8 use MediaWikiTestCase;
9 use WANObjectCache;
10 use Wikimedia\Rdbms\Database;
11 use Wikimedia\Rdbms\LoadBalancer;
12
13 class RevisionStoreTest extends MediaWikiTestCase {
14
15 /**
16 * @param LoadBalancer $loadBalancer
17 * @param SqlBlobStore $blobStore
18 * @param WANObjectCache $WANObjectCache
19 *
20 * @return RevisionStore
21 */
22 private function getRevisionStore(
23 $loadBalancer = null,
24 $blobStore = null,
25 $WANObjectCache = null
26 ) {
27 return new RevisionStore(
28 $loadBalancer ? $loadBalancer : $this->getMockLoadBalancer(),
29 $blobStore ? $blobStore : $this->getMockSqlBlobStore(),
30 $WANObjectCache ? $WANObjectCache : $this->getHashWANObjectCache()
31 );
32 }
33
34 /**
35 * @return \PHPUnit_Framework_MockObject_MockObject|LoadBalancer
36 */
37 private function getMockLoadBalancer() {
38 return $this->getMockBuilder( LoadBalancer::class )
39 ->disableOriginalConstructor()->getMock();
40 }
41
42 /**
43 * @return \PHPUnit_Framework_MockObject_MockObject|Database
44 */
45 private function getMockDatabase() {
46 return $this->getMockBuilder( Database::class )
47 ->disableOriginalConstructor()->getMock();
48 }
49
50 /**
51 * @return \PHPUnit_Framework_MockObject_MockObject|SqlBlobStore
52 */
53 private function getMockSqlBlobStore() {
54 return $this->getMockBuilder( SqlBlobStore::class )
55 ->disableOriginalConstructor()->getMock();
56 }
57
58 private function getHashWANObjectCache() {
59 return new WANObjectCache( [ 'cache' => new \HashBagOStuff() ] );
60 }
61
62 /**
63 * @covers \MediaWiki\Storage\RevisionStore::getContentHandlerUseDB
64 * @covers \MediaWiki\Storage\RevisionStore::setContentHandlerUseDB
65 */
66 public function testGetSetContentHandlerDb() {
67 $store = $this->getRevisionStore();
68 $this->assertTrue( $store->getContentHandlerUseDB() );
69 $store->setContentHandlerUseDB( false );
70 $this->assertFalse( $store->getContentHandlerUseDB() );
71 $store->setContentHandlerUseDB( true );
72 $this->assertTrue( $store->getContentHandlerUseDB() );
73 }
74
75 private function getDefaultQueryFields() {
76 return [
77 'rev_id',
78 'rev_page',
79 'rev_text_id',
80 'rev_timestamp',
81 'rev_user_text',
82 'rev_user',
83 'rev_minor_edit',
84 'rev_deleted',
85 'rev_len',
86 'rev_parent_id',
87 'rev_sha1',
88 ];
89 }
90
91 private function getCommentQueryFields() {
92 return [
93 'rev_comment_text' => 'rev_comment',
94 'rev_comment_data' => 'NULL',
95 'rev_comment_cid' => 'NULL',
96 ];
97 }
98
99 private function getContentHandlerQueryFields() {
100 return [
101 'rev_content_format',
102 'rev_content_model',
103 ];
104 }
105
106 public function provideGetQueryInfo() {
107 yield [
108 true,
109 [],
110 [
111 'tables' => [ 'revision' ],
112 'fields' => array_merge(
113 $this->getDefaultQueryFields(),
114 $this->getCommentQueryFields(),
115 $this->getContentHandlerQueryFields()
116 ),
117 'joins' => [],
118 ]
119 ];
120 yield [
121 false,
122 [],
123 [
124 'tables' => [ 'revision' ],
125 'fields' => array_merge(
126 $this->getDefaultQueryFields(),
127 $this->getCommentQueryFields()
128 ),
129 'joins' => [],
130 ]
131 ];
132 yield [
133 false,
134 [ 'page' ],
135 [
136 'tables' => [ 'revision', 'page' ],
137 'fields' => array_merge(
138 $this->getDefaultQueryFields(),
139 $this->getCommentQueryFields(),
140 [
141 'page_namespace',
142 'page_title',
143 'page_id',
144 'page_latest',
145 'page_is_redirect',
146 'page_len',
147 ]
148 ),
149 'joins' => [
150 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
151 ],
152 ]
153 ];
154 yield [
155 false,
156 [ 'user' ],
157 [
158 'tables' => [ 'revision', 'user' ],
159 'fields' => array_merge(
160 $this->getDefaultQueryFields(),
161 $this->getCommentQueryFields(),
162 [
163 'user_name',
164 ]
165 ),
166 'joins' => [
167 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
168 ],
169 ]
170 ];
171 yield [
172 false,
173 [ 'text' ],
174 [
175 'tables' => [ 'revision', 'text' ],
176 'fields' => array_merge(
177 $this->getDefaultQueryFields(),
178 $this->getCommentQueryFields(),
179 [
180 'old_text',
181 'old_flags',
182 ]
183 ),
184 'joins' => [
185 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ],
186 ],
187 ]
188 ];
189 yield [
190 true,
191 [ 'page', 'user', 'text' ],
192 [
193 'tables' => [ 'revision', 'page', 'user', 'text' ],
194 'fields' => array_merge(
195 $this->getDefaultQueryFields(),
196 $this->getCommentQueryFields(),
197 $this->getContentHandlerQueryFields(),
198 [
199 'page_namespace',
200 'page_title',
201 'page_id',
202 'page_latest',
203 'page_is_redirect',
204 'page_len',
205 'user_name',
206 'old_text',
207 'old_flags',
208 ]
209 ),
210 'joins' => [
211 'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
212 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
213 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ],
214 ],
215 ]
216 ];
217 }
218
219 /**
220 * @dataProvider provideGetQueryInfo
221 * @covers \MediaWiki\Storage\RevisionStore::getQueryInfo
222 */
223 public function testGetQueryInfo( $contentHandlerUseDb, $options, $expected ) {
224 $store = $this->getRevisionStore();
225 $store->setContentHandlerUseDB( $contentHandlerUseDb );
226 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
227 $this->assertEquals( $expected, $store->getQueryInfo( $options ) );
228 }
229
230 private function getDefaultArchiveFields() {
231 return [
232 'ar_id',
233 'ar_page_id',
234 'ar_namespace',
235 'ar_title',
236 'ar_rev_id',
237 'ar_text',
238 'ar_text_id',
239 'ar_timestamp',
240 'ar_user_text',
241 'ar_user',
242 'ar_minor_edit',
243 'ar_deleted',
244 'ar_len',
245 'ar_parent_id',
246 'ar_sha1',
247 ];
248 }
249
250 /**
251 * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
252 */
253 public function testGetArchiveQueryInfo_contentHandlerDb() {
254 $store = $this->getRevisionStore();
255 $store->setContentHandlerUseDB( true );
256 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
257 $this->assertEquals(
258 [
259 'tables' => [
260 'archive'
261 ],
262 'fields' => array_merge(
263 $this->getDefaultArchiveFields(),
264 [
265 'ar_comment_text' => 'ar_comment',
266 'ar_comment_data' => 'NULL',
267 'ar_comment_cid' => 'NULL',
268 'ar_content_format',
269 'ar_content_model',
270 ]
271 ),
272 'joins' => [],
273 ],
274 $store->getArchiveQueryInfo()
275 );
276 }
277
278 /**
279 * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
280 */
281 public function testGetArchiveQueryInfo_noContentHandlerDb() {
282 $store = $this->getRevisionStore();
283 $store->setContentHandlerUseDB( false );
284 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
285 $this->assertEquals(
286 [
287 'tables' => [
288 'archive'
289 ],
290 'fields' => array_merge(
291 $this->getDefaultArchiveFields(),
292 [
293 'ar_comment_text' => 'ar_comment',
294 'ar_comment_data' => 'NULL',
295 'ar_comment_cid' => 'NULL',
296 ]
297 ),
298 'joins' => [],
299 ],
300 $store->getArchiveQueryInfo()
301 );
302 }
303
304 public function testGetTitle_successFromPageId() {
305 $mockLoadBalancer = $this->getMockLoadBalancer();
306 // Title calls wfGetDB() so we have to set the main service
307 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
308
309 $db = $this->getMockDatabase();
310 // Title calls wfGetDB() which uses a regular Connection
311 $mockLoadBalancer->expects( $this->atLeastOnce() )
312 ->method( 'getConnection' )
313 ->willReturn( $db );
314
315 // First call to Title::newFromID, faking no result (db lag?)
316 $db->expects( $this->at( 0 ) )
317 ->method( 'selectRow' )
318 ->with(
319 'page',
320 $this->anything(),
321 [ 'page_id' => 1 ]
322 )
323 ->willReturn( (object)[
324 'page_namespace' => '1',
325 'page_title' => 'Food',
326 ] );
327
328 $store = $this->getRevisionStore( $mockLoadBalancer );
329 $title = $store->getTitle( 1, 2, RevisionStore::READ_NORMAL );
330
331 $this->assertSame( 1, $title->getNamespace() );
332 $this->assertSame( 'Food', $title->getDBkey() );
333 }
334
335 public function testGetTitle_successFromRevId() {
336 $mockLoadBalancer = $this->getMockLoadBalancer();
337 // Title calls wfGetDB() so we have to set the main service
338 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
339
340 $db = $this->getMockDatabase();
341 // Title calls wfGetDB() which uses a regular Connection
342 $mockLoadBalancer->expects( $this->atLeastOnce() )
343 ->method( 'getConnection' )
344 ->willReturn( $db );
345 // RevisionStore getTitle uses a ConnectionRef
346 $mockLoadBalancer->expects( $this->atLeastOnce() )
347 ->method( 'getConnectionRef' )
348 ->willReturn( $db );
349
350 // First call to Title::newFromID, faking no result (db lag?)
351 $db->expects( $this->at( 0 ) )
352 ->method( 'selectRow' )
353 ->with(
354 'page',
355 $this->anything(),
356 [ 'page_id' => 1 ]
357 )
358 ->willReturn( false );
359
360 // First select using rev_id, faking no result (db lag?)
361 $db->expects( $this->at( 1 ) )
362 ->method( 'selectRow' )
363 ->with(
364 [ 'revision', 'page' ],
365 $this->anything(),
366 [ 'rev_id' => 2 ]
367 )
368 ->willReturn( (object)[
369 'page_namespace' => '1',
370 'page_title' => 'Food2',
371 ] );
372
373 $store = $this->getRevisionStore( $mockLoadBalancer );
374 $title = $store->getTitle( 1, 2, RevisionStore::READ_NORMAL );
375
376 $this->assertSame( 1, $title->getNamespace() );
377 $this->assertSame( 'Food2', $title->getDBkey() );
378 }
379
380 /**
381 * @covers \MediaWiki\Storage\RevisionStore::getTitle
382 */
383 public function testGetTitle_throwsExceptionAfterFallbacks() {
384 $mockLoadBalancer = $this->getMockLoadBalancer();
385 // Title calls wfGetDB() so we have to set the main service
386 $this->setService( 'DBLoadBalancer', $mockLoadBalancer );
387
388 $db = $this->getMockDatabase();
389 // Title calls wfGetDB() which uses a regular Connection
390 $mockLoadBalancer->expects( $this->atLeastOnce() )
391 ->method( 'getConnection' )
392 ->willReturn( $db );
393 // RevisionStore getTitle uses a ConnectionRef
394 $mockLoadBalancer->expects( $this->atLeastOnce() )
395 ->method( 'getConnectionRef' )
396 ->willReturn( $db );
397
398 // First call to Title::newFromID, faking no result (db lag?)
399 $db->expects( $this->at( 0 ) )
400 ->method( 'selectRow' )
401 ->with(
402 'page',
403 $this->anything(),
404 [ 'page_id' => 1 ]
405 )
406 ->willReturn( false );
407
408 // First select using rev_id, faking no result (db lag?)
409 $db->expects( $this->at( 1 ) )
410 ->method( 'selectRow' )
411 ->with(
412 [ 'revision', 'page' ],
413 $this->anything(),
414 [ 'rev_id' => 2 ]
415 )
416 ->willReturn( false );
417
418 $store = $this->getRevisionStore( $mockLoadBalancer );
419
420 $this->setExpectedException( RevisionAccessException::class );
421 $store->getTitle( 1, 2, RevisionStore::READ_NORMAL );
422 }
423
424 // FIXME: test getRevisionSizes
425
426 }