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