Merge "Provide command to adjust phpunit.xml for code coverage"
[lhc/web/wiklou.git] / tests / phpunit / includes / page / PageArchiveTestBase.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use MediaWiki\Revision\RevisionRecord;
5
6 /**
7 * Base class for tests of PageArchive against different database schemas.
8 */
9 abstract class PageArchiveTestBase extends MediaWikiTestCase {
10
11 /**
12 * @var int
13 */
14 protected $pageId;
15
16 /**
17 * @var PageArchive $archivedPage
18 */
19 protected $archivedPage;
20
21 /**
22 * A logged out user who edited the page before it was archived.
23 * @var string $ipEditor
24 */
25 protected $ipEditor;
26
27 /**
28 * Revision of the first (initial) edit
29 * @var RevisionRecord
30 */
31 protected $firstRev;
32
33 /**
34 * Revision of the IP edit (the second edit)
35 * @var RevisionRecord
36 */
37 protected $ipRev;
38
39 function __construct( $name = null, array $data = [], $dataName = '' ) {
40 parent::__construct( $name, $data, $dataName );
41
42 $this->tablesUsed = array_merge(
43 $this->tablesUsed,
44 [
45 'page',
46 'revision',
47 'revision_comment_temp',
48 'ip_changes',
49 'text',
50 'archive',
51 'recentchanges',
52 'logging',
53 'page_props',
54 'comment',
55 ]
56 );
57 }
58
59 protected function addCoreDBData() {
60 // Blank out to avoid failures when schema overrides imposed by subclasses
61 // affect revision storage.
62 }
63
64 /**
65 * @return int
66 */
67 abstract protected function getMcrMigrationStage();
68
69 /**
70 * @return string[]
71 */
72 abstract protected function getMcrTablesToReset();
73
74 /**
75 * @return bool
76 */
77 protected function getContentHandlerUseDB() {
78 return true;
79 }
80
81 protected function setUp() {
82 parent::setUp();
83
84 $this->tablesUsed += $this->getMcrTablesToReset();
85
86 $this->setMwGlobals( [
87 'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
88 'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
89 ] );
90
91 // First create our dummy page
92 $page = Title::newFromText( 'PageArchiveTest_thePage' );
93 $page = new WikiPage( $page );
94 $content = ContentHandler::makeContent(
95 'testing',
96 $page->getTitle(),
97 CONTENT_MODEL_WIKITEXT
98 );
99
100 $user = $this->getTestUser()->getUser();
101 $page->doEditContent( $content, 'testing', EDIT_NEW, false, $user );
102
103 $this->pageId = $page->getId();
104 $this->firstRev = $page->getRevision()->getRevisionRecord();
105
106 // Insert IP revision
107 $this->ipEditor = '2001:db8::1';
108
109 $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
110
111 $ipTimestamp = wfTimestamp(
112 TS_MW,
113 wfTimestamp( TS_UNIX, $this->firstRev->getTimestamp() ) + 1
114 );
115
116 $rev = $revisionStore->newMutableRevisionFromArray( [
117 'text' => 'Lorem Ipsum',
118 'comment' => 'just a test',
119 'page' => $page->getId(),
120 'user_text' => $this->ipEditor,
121 'timestamp' => $ipTimestamp,
122 ] );
123
124 $dbw = wfGetDB( DB_MASTER );
125 $this->ipRev = $revisionStore->insertRevisionOn( $rev, $dbw );
126
127 // Delete the page
128 $page->doDeleteArticleReal( 'Just a test deletion' );
129
130 $this->archivedPage = new PageArchive( $page->getTitle() );
131 }
132
133 /**
134 * @covers PageArchive::undelete
135 * @covers PageArchive::undeleteRevisions
136 */
137 public function testUndeleteRevisions() {
138 // TODO: MCR: Test undeletion with multiple slots. Check that slots remain untouched.
139
140 // First make sure old revisions are archived
141 $dbr = wfGetDB( DB_REPLICA );
142 $arQuery = Revision::getArchiveQueryInfo();
143 $row = $dbr->selectRow(
144 $arQuery['tables'],
145 $arQuery['fields'],
146 [ 'ar_rev_id' => $this->ipRev->getId() ],
147 __METHOD__,
148 [],
149 $arQuery['joins']
150 );
151 $this->assertEquals( $this->ipEditor, $row->ar_user_text );
152
153 // Should not be in revision
154 $row = $dbr->selectRow( 'revision', '1', [ 'rev_id' => $this->ipRev->getId() ] );
155 $this->assertFalse( $row );
156
157 // Should not be in ip_changes
158 $row = $dbr->selectRow( 'ip_changes', '1', [ 'ipc_rev_id' => $this->ipRev->getId() ] );
159 $this->assertFalse( $row );
160
161 // Restore the page
162 $this->archivedPage->undelete( [] );
163
164 // Should be back in revision
165 $revQuery = Revision::getQueryInfo();
166 $row = $dbr->selectRow(
167 $revQuery['tables'],
168 $revQuery['fields'],
169 [ 'rev_id' => $this->ipRev->getId() ],
170 __METHOD__,
171 [],
172 $revQuery['joins']
173 );
174 $this->assertNotFalse( $row, 'row exists in revision table' );
175 $this->assertEquals( $this->ipEditor, $row->rev_user_text );
176
177 // Should be back in ip_changes
178 $row = $dbr->selectRow( 'ip_changes', [ 'ipc_hex' ], [ 'ipc_rev_id' => $this->ipRev->getId() ] );
179 $this->assertNotFalse( $row, 'row exists in ip_changes table' );
180 $this->assertEquals( IP::toHex( $this->ipEditor ), $row->ipc_hex );
181 }
182
183 abstract protected function getExpectedArchiveRows();
184
185 /**
186 * @covers PageArchive::listRevisions
187 */
188 public function testListRevisions() {
189 $revisions = $this->archivedPage->listRevisions();
190 $this->assertEquals( 2, $revisions->numRows() );
191
192 // Get the rows as arrays
193 $row0 = (array)$revisions->current();
194 $row1 = (array)$revisions->next();
195
196 $expectedRows = $this->getExpectedArchiveRows();
197
198 $this->assertEquals(
199 $expectedRows[0],
200 $row0
201 );
202 $this->assertEquals(
203 $expectedRows[1],
204 $row1
205 );
206 }
207
208 /**
209 * @covers PageArchive::listPagesBySearch
210 */
211 public function testListPagesBySearch() {
212 $pages = PageArchive::listPagesBySearch( 'PageArchiveTest_thePage' );
213 $this->assertSame( 1, $pages->numRows() );
214
215 $page = (array)$pages->current();
216
217 $this->assertSame(
218 [
219 'ar_namespace' => '0',
220 'ar_title' => 'PageArchiveTest_thePage',
221 'count' => '2',
222 ],
223 $page
224 );
225 }
226
227 /**
228 * @covers PageArchive::listPagesBySearch
229 */
230 public function testListPagesByPrefix() {
231 $pages = PageArchive::listPagesByPrefix( 'PageArchiveTest' );
232 $this->assertSame( 1, $pages->numRows() );
233
234 $page = (array)$pages->current();
235
236 $this->assertSame(
237 [
238 'ar_namespace' => '0',
239 'ar_title' => 'PageArchiveTest_thePage',
240 'count' => '2',
241 ],
242 $page
243 );
244 }
245
246 public function provideGetTextFromRowThrowsInvalidArgumentException() {
247 yield 'missing ar_text_id field' => [ [] ];
248 yield 'ar_text_id is null' => [ [ 'ar_text_id' => null ] ];
249 yield 'ar_text_id is zero' => [ [ 'ar_text_id' => 0 ] ];
250 yield 'ar_text_id is "0"' => [ [ 'ar_text_id' => '0' ] ];
251 }
252
253 /**
254 * @covers PageArchive::getLastRevisionId
255 */
256 public function testGetLastRevisionId() {
257 $id = $this->archivedPage->getLastRevisionId();
258 $this->assertSame( $this->ipRev->getId(), $id );
259 }
260
261 /**
262 * @covers PageArchive::isDeleted
263 */
264 public function testIsDeleted() {
265 $this->assertTrue( $this->archivedPage->isDeleted() );
266 }
267
268 /**
269 * @covers PageArchive::getRevision
270 */
271 public function testGetRevision() {
272 $rev = $this->archivedPage->getRevision( $this->ipRev->getTimestamp() );
273 $this->assertNotNull( $rev );
274 $this->assertSame( $this->pageId, $rev->getPage() );
275
276 $rev = $this->archivedPage->getRevision( '22991212115555' );
277 $this->assertNull( $rev );
278 }
279
280 /**
281 * @covers PageArchive::getRevision
282 */
283 public function testGetArchivedRevision() {
284 $rev = $this->archivedPage->getArchivedRevision( $this->ipRev->getId() );
285 $this->assertNotNull( $rev );
286 $this->assertSame( $this->ipRev->getTimestamp(), $rev->getTimestamp() );
287 $this->assertSame( $this->pageId, $rev->getPage() );
288
289 $rev = $this->archivedPage->getArchivedRevision( 632546 );
290 $this->assertNull( $rev );
291 }
292
293 /**
294 * @covers PageArchive::getPreviousRevision
295 */
296 public function testGetPreviousRevision() {
297 $rev = $this->archivedPage->getPreviousRevision( $this->ipRev->getTimestamp() );
298 $this->assertNotNull( $rev );
299 $this->assertSame( $this->firstRev->getId(), $rev->getId() );
300
301 $rev = $this->archivedPage->getPreviousRevision( $this->firstRev->getTimestamp() );
302 $this->assertNull( $rev );
303
304 // Re-create our dummy page
305 $title = Title::newFromText( 'PageArchiveTest_thePage' );
306 $page = new WikiPage( $title );
307 $content = ContentHandler::makeContent(
308 'testing again',
309 $page->getTitle(),
310 CONTENT_MODEL_WIKITEXT
311 );
312
313 $user = $this->getTestUser()->getUser();
314 $status = $page->doEditContent( $content, 'testing', EDIT_NEW, false, $user );
315
316 /** @var Revision $newRev */
317 $newRev = $status->value['revision'];
318
319 // force the revision timestamp
320 $newTimestamp = wfTimestamp(
321 TS_MW,
322 wfTimestamp( TS_UNIX, $this->ipRev->getTimestamp() ) + 1
323 );
324
325 $this->db->update(
326 'revision',
327 [ 'rev_timestamp' => $this->db->timestamp( $newTimestamp ) ],
328 [ 'rev_id' => $newRev->getId() ]
329 );
330
331 // check that we don't get the existing revision too soon.
332 $rev = $this->archivedPage->getPreviousRevision( $newTimestamp );
333 $this->assertNotNull( $rev );
334 $this->assertSame( $this->ipRev->getId(), $rev->getId() );
335
336 // check that we do get the existing revision when appropriate.
337 $afterNewTimestamp = wfTimestamp(
338 TS_MW,
339 wfTimestamp( TS_UNIX, $newTimestamp ) + 1
340 );
341
342 $rev = $this->archivedPage->getPreviousRevision( $afterNewTimestamp );
343 $this->assertNotNull( $rev );
344 $this->assertSame( $newRev->getId(), $rev->getId() );
345 }
346
347 }