Fix @param tags
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / PageUpdaterTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Storage;
4
5 use CommentStoreComment;
6 use Content;
7 use MediaWiki\MediaWikiServices;
8 use MediaWiki\Revision\RevisionRecord;
9 use MediaWiki\Revision\SlotRecord;
10 use MediaWikiTestCase;
11 use ParserOptions;
12 use RecentChange;
13 use Revision;
14 use TextContent;
15 use Title;
16 use User;
17 use WikiPage;
18
19 /**
20 * @covers \MediaWiki\Storage\PageUpdater
21 * @group Database
22 */
23 class PageUpdaterTest extends MediaWikiTestCase {
24
25 public function setUp() {
26 parent::setUp();
27
28 MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
29 'aux',
30 CONTENT_MODEL_WIKITEXT
31 );
32
33 $this->tablesUsed[] = 'logging';
34 $this->tablesUsed[] = 'recentchanges';
35 }
36
37 private function getDummyTitle( $method ) {
38 return Title::newFromText( $method, $this->getDefaultWikitextNS() );
39 }
40
41 /**
42 * @param int $revId
43 *
44 * @return null|RecentChange
45 */
46 private function getRecentChangeFor( $revId ) {
47 $qi = RecentChange::getQueryInfo();
48 $row = $this->db->selectRow(
49 $qi['tables'],
50 $qi['fields'],
51 [ 'rc_this_oldid' => $revId ],
52 __METHOD__,
53 [],
54 $qi['joins']
55 );
56
57 return $row ? RecentChange::newFromRow( $row ) : null;
58 }
59
60 // TODO: test setAjaxEditStash();
61
62 /**
63 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
64 * @covers \WikiPage::newPageUpdater()
65 */
66 public function testCreatePage() {
67 $user = $this->getTestUser()->getUser();
68
69 $title = $this->getDummyTitle( __METHOD__ );
70 $page = WikiPage::factory( $title );
71 $updater = $page->newPageUpdater( $user );
72
73 $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
74
75 $this->assertFalse( $updater->wasCommitted(), 'wasCommitted' );
76 $this->assertFalse( $updater->getOriginalRevisionId(), 'getOriginalRevisionId' );
77 $this->assertSame( 0, $updater->getUndidRevisionId(), 'getUndidRevisionId' );
78
79 $updater->addTag( 'foo' );
80 $updater->addTags( [ 'bar', 'qux' ] );
81
82 $tags = $updater->getExplicitTags();
83 sort( $tags );
84 $this->assertSame( [ 'bar', 'foo', 'qux' ], $tags, 'getExplicitTags' );
85
86 // TODO: MCR: test additional slots
87 $content = new TextContent( 'Lorem Ipsum' );
88 $updater->setContent( SlotRecord::MAIN, $content );
89
90 $parent = $updater->grabParentRevision();
91
92 $this->assertNull( $parent, 'getParentRevision' );
93 $this->assertFalse( $updater->wasCommitted(), 'wasCommitted' );
94
95 // TODO: test that hasEditConflict() grabs the parent revision
96 $this->assertFalse( $updater->hasEditConflict( 0 ), 'hasEditConflict' );
97 $this->assertTrue( $updater->hasEditConflict( 1 ), 'hasEditConflict' );
98
99 // TODO: test failure with EDIT_UPDATE
100 // TODO: test EDIT_MINOR, EDIT_BOT, etc
101 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
102 $rev = $updater->saveRevision( $summary );
103
104 $this->assertNotNull( $rev );
105 $this->assertSame( 0, $rev->getParentId() );
106 $this->assertSame( $summary->text, $rev->getComment( RevisionRecord::RAW )->text );
107 $this->assertSame( $user->getName(), $rev->getUser( RevisionRecord::RAW )->getName() );
108
109 $this->assertTrue( $updater->wasCommitted(), 'wasCommitted()' );
110 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
111 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
112 $this->assertTrue( $updater->isNew(), 'isNew()' );
113 $this->assertFalse( $updater->isUnchanged(), 'isUnchanged()' );
114 $this->assertNotNull( $updater->getNewRevision(), 'getNewRevision()' );
115 $this->assertInstanceOf( Revision::class, $updater->getStatus()->value['revision'] );
116
117 $rev = $updater->getNewRevision();
118 $revContent = $rev->getContent( SlotRecord::MAIN );
119 $this->assertSame( 'Lorem Ipsum', $revContent->serialize(), 'revision content' );
120
121 // were the WikiPage and Title objects updated?
122 $this->assertTrue( $page->exists(), 'WikiPage::exists()' );
123 $this->assertTrue( $title->exists(), 'Title::exists()' );
124 $this->assertSame( $rev->getId(), $page->getLatest(), 'WikiPage::getRevision()' );
125 $this->assertNotNull( $page->getRevision(), 'WikiPage::getRevision()' );
126
127 // re-load
128 $page2 = WikiPage::factory( $title );
129 $this->assertTrue( $page2->exists(), 'WikiPage::exists()' );
130 $this->assertSame( $rev->getId(), $page2->getLatest(), 'WikiPage::getRevision()' );
131 $this->assertNotNull( $page2->getRevision(), 'WikiPage::getRevision()' );
132
133 // Check RC entry
134 $rc = $this->getRecentChangeFor( $rev->getId() );
135 $this->assertNotNull( $rc, 'RecentChange' );
136
137 // check site stats - this asserts that derived data updates where run.
138 $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
139 $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
140 $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
141
142 // re-edit with same content - should be a "null-edit"
143 $updater = $page->newPageUpdater( $user );
144 $updater->setContent( SlotRecord::MAIN, $content );
145
146 $summary = CommentStoreComment::newUnsavedComment( 'to to re-edit' );
147 $rev = $updater->saveRevision( $summary );
148 $status = $updater->getStatus();
149
150 $this->assertNull( $rev, 'getNewRevision()' );
151 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
152 $this->assertTrue( $updater->isUnchanged(), 'isUnchanged' );
153 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
154 $this->assertTrue( $status->isOK(), 'getStatus()->isOK()' );
155 $this->assertTrue( $status->hasMessage( 'edit-no-change' ), 'edit-no-change' );
156 }
157
158 /**
159 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
160 * @covers \WikiPage::newPageUpdater()
161 */
162 public function testUpdatePage() {
163 $user = $this->getTestUser()->getUser();
164
165 $title = $this->getDummyTitle( __METHOD__ );
166 $this->insertPage( $title );
167
168 $page = WikiPage::factory( $title );
169 $parentId = $page->getLatest();
170
171 $updater = $page->newPageUpdater( $user );
172
173 $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
174
175 $updater->setOriginalRevisionId( 7 );
176 $this->assertSame( 7, $updater->getOriginalRevisionId(), 'getOriginalRevisionId' );
177
178 $this->assertFalse( $updater->hasEditConflict( $parentId ), 'hasEditConflict' );
179 $this->assertTrue( $updater->hasEditConflict( $parentId - 1 ), 'hasEditConflict' );
180 $this->assertTrue( $updater->hasEditConflict( 0 ), 'hasEditConflict' );
181
182 // TODO: MCR: test additional slots
183 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
184
185 // TODO: test all flags for saveRevision()!
186 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
187 $rev = $updater->saveRevision( $summary );
188
189 $this->assertNotNull( $rev );
190 $this->assertSame( $parentId, $rev->getParentId() );
191 $this->assertSame( $summary->text, $rev->getComment( RevisionRecord::RAW )->text );
192 $this->assertSame( $user->getName(), $rev->getUser( RevisionRecord::RAW )->getName() );
193
194 $this->assertTrue( $updater->wasCommitted(), 'wasCommitted()' );
195 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
196 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
197 $this->assertFalse( $updater->isNew(), 'isNew()' );
198 $this->assertNotNull( $updater->getNewRevision(), 'getNewRevision()' );
199 $this->assertInstanceOf( Revision::class, $updater->getStatus()->value['revision'] );
200 $this->assertFalse( $updater->isUnchanged(), 'isUnchanged()' );
201
202 // TODO: Test null revision (with different user): new revision!
203
204 $rev = $updater->getNewRevision();
205 $revContent = $rev->getContent( SlotRecord::MAIN );
206 $this->assertSame( 'Lorem Ipsum', $revContent->serialize(), 'revision content' );
207
208 // were the WikiPage and Title objects updated?
209 $this->assertTrue( $page->exists(), 'WikiPage::exists()' );
210 $this->assertTrue( $title->exists(), 'Title::exists()' );
211 $this->assertSame( $rev->getId(), $page->getLatest(), 'WikiPage::getRevision()' );
212 $this->assertNotNull( $page->getRevision(), 'WikiPage::getRevision()' );
213
214 // re-load
215 $page2 = WikiPage::factory( $title );
216 $this->assertTrue( $page2->exists(), 'WikiPage::exists()' );
217 $this->assertSame( $rev->getId(), $page2->getLatest(), 'WikiPage::getRevision()' );
218 $this->assertNotNull( $page2->getRevision(), 'WikiPage::getRevision()' );
219
220 // Check RC entry
221 $rc = $this->getRecentChangeFor( $rev->getId() );
222 $this->assertNotNull( $rc, 'RecentChange' );
223
224 // re-edit
225 $updater = $page->newPageUpdater( $user );
226 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
227
228 $summary = CommentStoreComment::newUnsavedComment( 're-edit' );
229 $updater->saveRevision( $summary );
230 $this->assertTrue( $updater->wasSuccessful(), 'wasSuccessful()' );
231 $this->assertTrue( $updater->getStatus()->isOK(), 'getStatus()->isOK()' );
232
233 // check site stats - this asserts that derived data updates where run.
234 $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
235 $this->assertNotNull( $stats, 'site_stats' );
236 $this->assertSame( $oldStats->ss_total_pages + 0, (int)$stats->ss_total_pages );
237 $this->assertSame( $oldStats->ss_total_edits + 2, (int)$stats->ss_total_edits );
238 }
239
240 /**
241 * Creates a revision in the database.
242 *
243 * @param WikiPage $page
244 * @param string|Message|CommentStoreComment $summary
245 * @param null|string|Content $content
246 *
247 * @return RevisionRecord|null
248 */
249 private function createRevision( WikiPage $page, $summary, $content = null ) {
250 $user = $this->getTestUser()->getUser();
251 $comment = CommentStoreComment::newUnsavedComment( $summary );
252
253 if ( !$content instanceof Content ) {
254 $content = new TextContent( $content ?? $summary );
255 }
256
257 $updater = $page->newPageUpdater( $user );
258 $updater->setContent( SlotRecord::MAIN, $content );
259 $rev = $updater->saveRevision( $comment );
260 return $rev;
261 }
262
263 /**
264 * @covers \MediaWiki\Storage\PageUpdater::grabParentRevision()
265 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
266 */
267 public function testCompareAndSwapFailure() {
268 $user = $this->getTestUser()->getUser();
269
270 $title = $this->getDummyTitle( __METHOD__ );
271
272 // start editing non-existing page
273 $page = WikiPage::factory( $title );
274 $updater = $page->newPageUpdater( $user );
275 $updater->grabParentRevision();
276
277 // create page concurrently
278 $concurrentPage = WikiPage::factory( $title );
279 $this->createRevision( $concurrentPage, __METHOD__ . '-one' );
280
281 // try creating the page - should trigger CAS failure.
282 $summary = CommentStoreComment::newUnsavedComment( 'create?!' );
283 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
284 $updater->saveRevision( $summary );
285 $status = $updater->getStatus();
286
287 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
288 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
289 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
290 $this->assertTrue( $status->hasMessage( 'edit-already-exists' ), 'edit-conflict' );
291
292 // start editing existing page
293 $page = WikiPage::factory( $title );
294 $updater = $page->newPageUpdater( $user );
295 $updater->grabParentRevision();
296
297 // update page concurrently
298 $concurrentPage = WikiPage::factory( $title );
299 $this->createRevision( $concurrentPage, __METHOD__ . '-two' );
300
301 // try creating the page - should trigger CAS failure.
302 $summary = CommentStoreComment::newUnsavedComment( 'edit?!' );
303 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
304 $updater->saveRevision( $summary );
305 $status = $updater->getStatus();
306
307 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
308 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
309 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
310 $this->assertTrue( $status->hasMessage( 'edit-conflict' ), 'edit-conflict' );
311 }
312
313 /**
314 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
315 */
316 public function testFailureOnEditFlags() {
317 $user = $this->getTestUser()->getUser();
318
319 $title = $this->getDummyTitle( __METHOD__ );
320
321 // start editing non-existing page
322 $page = WikiPage::factory( $title );
323 $updater = $page->newPageUpdater( $user );
324
325 // update with EDIT_UPDATE flag should fail
326 $summary = CommentStoreComment::newUnsavedComment( 'udpate?!' );
327 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
328 $updater->saveRevision( $summary, EDIT_UPDATE );
329 $status = $updater->getStatus();
330
331 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
332 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
333 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
334 $this->assertTrue( $status->hasMessage( 'edit-gone-missing' ), 'edit-gone-missing' );
335
336 // create the page
337 $this->createRevision( $page, __METHOD__ );
338
339 // update with EDIT_NEW flag should fail
340 $summary = CommentStoreComment::newUnsavedComment( 'create?!' );
341 $updater = $page->newPageUpdater( $user );
342 $updater->setContent( SlotRecord::MAIN, new TextContent( 'dolor sit amet' ) );
343 $updater->saveRevision( $summary, EDIT_NEW );
344 $status = $updater->getStatus();
345
346 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
347 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
348 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
349 $this->assertTrue( $status->hasMessage( 'edit-already-exists' ), 'edit-already-exists' );
350 }
351
352 /**
353 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
354 */
355 public function testFailureOnBadContentModel() {
356 $user = $this->getTestUser()->getUser();
357 $title = $this->getDummyTitle( __METHOD__ );
358
359 // start editing non-existing page
360 $page = WikiPage::factory( $title );
361 $updater = $page->newPageUpdater( $user );
362
363 // plain text content should fail in aux slot (the main slot doesn't care)
364 $updater->setContent( 'main', new TextContent( 'Main Content' ) );
365 $updater->setContent( 'aux', new TextContent( 'Aux Content' ) );
366
367 $summary = CommentStoreComment::newUnsavedComment( 'udpate?!' );
368 $updater->saveRevision( $summary, EDIT_UPDATE );
369 $status = $updater->getStatus();
370
371 $this->assertFalse( $updater->wasSuccessful(), 'wasSuccessful()' );
372 $this->assertNull( $updater->getNewRevision(), 'getNewRevision()' );
373 $this->assertFalse( $status->isOK(), 'getStatus()->isOK()' );
374 $this->assertTrue(
375 $status->hasMessage( 'content-not-allowed-here' ),
376 'content-not-allowed-here'
377 );
378 }
379
380 public function provideSetRcPatrolStatus( $patrolled ) {
381 yield [ RecentChange::PRC_UNPATROLLED ];
382 yield [ RecentChange::PRC_AUTOPATROLLED ];
383 }
384
385 /**
386 * @dataProvider provideSetRcPatrolStatus
387 * @covers \MediaWiki\Storage\PageUpdater::setRcPatrolStatus()
388 */
389 public function testSetRcPatrolStatus( $patrolled ) {
390 $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
391
392 $user = $this->getTestUser()->getUser();
393
394 $title = $this->getDummyTitle( __METHOD__ );
395
396 $page = WikiPage::factory( $title );
397 $updater = $page->newPageUpdater( $user );
398
399 $summary = CommentStoreComment::newUnsavedComment( 'Lorem ipsum ' . $patrolled );
400 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum ' . $patrolled ) );
401 $updater->setRcPatrolStatus( $patrolled );
402 $rev = $updater->saveRevision( $summary );
403
404 $rc = $revisionStore->getRecentChange( $rev );
405 $this->assertEquals( $patrolled, $rc->getAttribute( 'rc_patrolled' ) );
406 }
407
408 /**
409 * @covers \MediaWiki\Storage\PageUpdater::inheritSlot()
410 * @covers \MediaWiki\Storage\PageUpdater::setContent()
411 */
412 public function testInheritSlot() {
413 $user = $this->getTestUser()->getUser();
414 $title = $this->getDummyTitle( __METHOD__ );
415 $page = WikiPage::factory( $title );
416
417 $updater = $page->newPageUpdater( $user );
418 $summary = CommentStoreComment::newUnsavedComment( 'one' );
419 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem ipsum' ) );
420 $rev1 = $updater->saveRevision( $summary, EDIT_NEW );
421
422 $updater = $page->newPageUpdater( $user );
423 $summary = CommentStoreComment::newUnsavedComment( 'two' );
424 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Foo Bar' ) );
425 $rev2 = $updater->saveRevision( $summary, EDIT_UPDATE );
426
427 $updater = $page->newPageUpdater( $user );
428 $summary = CommentStoreComment::newUnsavedComment( 'three' );
429 $updater->inheritSlot( $rev1->getSlot( SlotRecord::MAIN ) );
430 $rev3 = $updater->saveRevision( $summary, EDIT_UPDATE );
431
432 $this->assertNotSame( $rev1->getId(), $rev3->getId() );
433 $this->assertNotSame( $rev2->getId(), $rev3->getId() );
434
435 $main1 = $rev1->getSlot( SlotRecord::MAIN );
436 $main3 = $rev3->getSlot( SlotRecord::MAIN );
437
438 $this->assertNotSame( $main1->getRevision(), $main3->getRevision() );
439 $this->assertSame( $main1->getAddress(), $main3->getAddress() );
440 $this->assertTrue( $main1->getContent()->equals( $main3->getContent() ) );
441 }
442
443 // TODO: MCR: test adding multiple slots, inheriting parent slots, and removing slots.
444
445 public function testSetUseAutomaticEditSummaries() {
446 $this->setContentLang( 'qqx' );
447 $user = $this->getTestUser()->getUser();
448
449 $title = $this->getDummyTitle( __METHOD__ );
450 $page = WikiPage::factory( $title );
451
452 $updater = $page->newPageUpdater( $user );
453 $updater->setUseAutomaticEditSummaries( true );
454 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
455
456 // empty comment triggers auto-summary
457 $summary = CommentStoreComment::newUnsavedComment( '' );
458 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
459
460 $rev = $updater->getNewRevision();
461 $comment = $rev->getComment( RevisionRecord::RAW );
462 $this->assertSame( '(autosumm-new: Lorem Ipsum)', $comment->text, 'comment text' );
463
464 // check that this also works when blanking the page
465 $updater = $page->newPageUpdater( $user );
466 $updater->setUseAutomaticEditSummaries( true );
467 $updater->setContent( SlotRecord::MAIN, new TextContent( '' ) );
468
469 $summary = CommentStoreComment::newUnsavedComment( '' );
470 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
471
472 $rev = $updater->getNewRevision();
473 $comment = $rev->getComment( RevisionRecord::RAW );
474 $this->assertSame( '(autosumm-blank)', $comment->text, 'comment text' );
475
476 // check that we can also disable edit-summaries
477 $title2 = $this->getDummyTitle( __METHOD__ . '/2' );
478 $page2 = WikiPage::factory( $title2 );
479
480 $updater = $page2->newPageUpdater( $user );
481 $updater->setUseAutomaticEditSummaries( false );
482 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
483
484 $summary = CommentStoreComment::newUnsavedComment( '' );
485 $updater->saveRevision( $summary, EDIT_AUTOSUMMARY );
486
487 $rev = $updater->getNewRevision();
488 $comment = $rev->getComment( RevisionRecord::RAW );
489 $this->assertSame( '', $comment->text, 'comment text should still be lank' );
490
491 // check that we don't do auto.summaries without the EDIT_AUTOSUMMARY flag
492 $updater = $page2->newPageUpdater( $user );
493 $updater->setUseAutomaticEditSummaries( true );
494 $updater->setContent( SlotRecord::MAIN, new TextContent( '' ) );
495
496 $summary = CommentStoreComment::newUnsavedComment( '' );
497 $updater->saveRevision( $summary, 0 );
498
499 $rev = $updater->getNewRevision();
500 $comment = $rev->getComment( RevisionRecord::RAW );
501 $this->assertSame( '', $comment->text, 'comment text' );
502 }
503
504 public function provideSetUsePageCreationLog() {
505 yield [ true, [ [ 'create', 'create' ] ] ];
506 yield [ false, [] ];
507 }
508
509 /**
510 * @dataProvider provideSetUsePageCreationLog
511 * @param bool $use
512 */
513 public function testSetUsePageCreationLog( $use, $expected ) {
514 $user = $this->getTestUser()->getUser();
515 $title = $this->getDummyTitle( __METHOD__ . ( $use ? '_logged' : '_unlogged' ) );
516 $page = WikiPage::factory( $title );
517
518 $updater = $page->newPageUpdater( $user );
519 $updater->setUsePageCreationLog( $use );
520 $summary = CommentStoreComment::newUnsavedComment( 'cmt' );
521 $updater->setContent( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
522 $updater->saveRevision( $summary, EDIT_NEW );
523
524 $rev = $updater->getNewRevision();
525 $this->assertSelect(
526 'logging',
527 [ 'log_type', 'log_action' ],
528 [ 'log_page' => $rev->getPageId() ],
529 $expected
530 );
531 }
532
533 public function provideMagicWords() {
534 yield 'PAGEID' => [
535 'Test {{PAGEID}} Test',
536 function ( RevisionRecord $rev ) {
537 return $rev->getPageId();
538 }
539 ];
540
541 yield 'REVISIONID' => [
542 'Test {{REVISIONID}} Test',
543 function ( RevisionRecord $rev ) {
544 return $rev->getId();
545 }
546 ];
547
548 yield 'REVISIONUSER' => [
549 'Test {{REVISIONUSER}} Test',
550 function ( RevisionRecord $rev ) {
551 return $rev->getUser()->getName();
552 }
553 ];
554
555 yield 'REVISIONTIMESTAMP' => [
556 'Test {{REVISIONTIMESTAMP}} Test',
557 function ( RevisionRecord $rev ) {
558 return $rev->getTimestamp();
559 }
560 ];
561
562 yield 'subst:REVISIONUSER' => [
563 'Test {{subst:REVISIONUSER}} Test',
564 function ( RevisionRecord $rev ) {
565 return $rev->getUser()->getName();
566 },
567 'subst'
568 ];
569
570 yield 'subst:PAGENAME' => [
571 'Test {{subst:PAGENAME}} Test',
572 function ( RevisionRecord $rev ) {
573 return 'PageUpdaterTest::testMagicWords';
574 },
575 'subst'
576 ];
577 }
578
579 /**
580 * @covers \MediaWiki\Storage\PageUpdater::saveRevision()
581 *
582 * Integration test for PageUpdater, DerivedPageDataUpdater, RevisionRenderer
583 * and RenderedRevision, that ensures that magic words depending on revision meta-data
584 * are handled correctly. Note that each magic word needs to be tested separately,
585 * to assert correct behavior for each "vary" flag in the ParserOutput.
586 *
587 * @dataProvider provideMagicWords
588 */
589 public function testMagicWords( $wikitext, $callback, $subst = false ) {
590 $user = User::newFromName( 'A user for ' . __METHOD__ );
591 $user->addToDatabase();
592
593 $title = $this->getDummyTitle( __METHOD__ . '-' . $this->getName() );
594 $this->insertPage( $title );
595
596 $page = WikiPage::factory( $title );
597 $updater = $page->newPageUpdater( $user );
598
599 $updater->setContent( SlotRecord::MAIN, new \WikitextContent( $wikitext ) );
600
601 $summary = CommentStoreComment::newUnsavedComment( 'Just a test' );
602 $rev = $updater->saveRevision( $summary, EDIT_UPDATE );
603
604 if ( !$rev ) {
605 $this->fail( $updater->getStatus()->getWikiText() );
606 }
607
608 $expected = strval( $callback( $rev ) );
609
610 $output = $page->getParserOutput( ParserOptions::newCanonical( 'canonical' ) );
611 $html = $output->getText();
612 $text = $rev->getContent( SlotRecord::MAIN )->serialize();
613
614 if ( $subst ) {
615 $this->assertContains( $expected, $text, 'In Wikitext' );
616 }
617
618 $this->assertContains( $expected, $html, 'In HTML' );
619 }
620
621 }