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