Merge "Refactor test for WikiPage::doRollback"
[lhc/web/wiklou.git] / tests / phpunit / includes / page / WikiPageDbTestBase.php
1 <?php
2
3 abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
4
5 private $pagesToDelete;
6
7 public function __construct( $name = null, array $data = [], $dataName = '' ) {
8 parent::__construct( $name, $data, $dataName );
9
10 $this->tablesUsed = array_merge(
11 $this->tablesUsed,
12 [ 'page',
13 'revision',
14 'archive',
15 'ip_changes',
16 'text',
17
18 'recentchanges',
19 'logging',
20
21 'page_props',
22 'pagelinks',
23 'categorylinks',
24 'langlinks',
25 'externallinks',
26 'imagelinks',
27 'templatelinks',
28 'iwlinks' ] );
29 }
30
31 protected function setUp() {
32 parent::setUp();
33 $this->setMwGlobals( 'wgContentHandlerUseDB', $this->getContentHandlerUseDB() );
34 $this->pagesToDelete = [];
35 }
36
37 protected function tearDown() {
38 foreach ( $this->pagesToDelete as $p ) {
39 /* @var $p WikiPage */
40
41 try {
42 if ( $p->exists() ) {
43 $p->doDeleteArticle( "testing done." );
44 }
45 } catch ( MWException $ex ) {
46 // fail silently
47 }
48 }
49 parent::tearDown();
50 }
51
52 abstract protected function getContentHandlerUseDB();
53
54 /**
55 * @param Title|string $title
56 * @param string|null $model
57 * @return WikiPage
58 */
59 private function newPage( $title, $model = null ) {
60 if ( is_string( $title ) ) {
61 $ns = $this->getDefaultWikitextNS();
62 $title = Title::newFromText( $title, $ns );
63 }
64
65 $p = new WikiPage( $title );
66
67 $this->pagesToDelete[] = $p;
68
69 return $p;
70 }
71
72 /**
73 * @param string|Title|WikiPage $page
74 * @param string $text
75 * @param int $model
76 *
77 * @return WikiPage
78 */
79 protected function createPage( $page, $text, $model = null ) {
80 if ( is_string( $page ) || $page instanceof Title ) {
81 $page = $this->newPage( $page, $model );
82 }
83
84 $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
85 $page->doEditContent( $content, "testing", EDIT_NEW );
86
87 return $page;
88 }
89
90 /**
91 * @covers WikiPage::doEditContent
92 * @covers WikiPage::doModify
93 * @covers WikiPage::doCreate
94 * @covers WikiPage::doEditUpdates
95 */
96 public function testDoEditContent() {
97 $page = $this->newPage( __METHOD__ );
98 $title = $page->getTitle();
99
100 $content = ContentHandler::makeContent(
101 "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
102 . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
103 $title,
104 CONTENT_MODEL_WIKITEXT
105 );
106
107 $page->doEditContent( $content, "[[testing]] 1" );
108
109 $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
110 $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
111 $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
112 $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
113
114 $id = $page->getId();
115
116 # ------------------------
117 $dbr = wfGetDB( DB_REPLICA );
118 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
119 $n = $res->numRows();
120 $res->free();
121
122 $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
123
124 # ------------------------
125 $page = new WikiPage( $title );
126
127 $retrieved = $page->getContent();
128 $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
129
130 # ------------------------
131 $content = ContentHandler::makeContent(
132 "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
133 . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
134 $title,
135 CONTENT_MODEL_WIKITEXT
136 );
137
138 $page->doEditContent( $content, "testing 2" );
139
140 # ------------------------
141 $page = new WikiPage( $title );
142
143 $retrieved = $page->getContent();
144 $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
145
146 # ------------------------
147 $dbr = wfGetDB( DB_REPLICA );
148 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
149 $n = $res->numRows();
150 $res->free();
151
152 $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
153 }
154
155 /**
156 * @covers WikiPage::doDeleteArticle
157 * @covers WikiPage::doDeleteArticleReal
158 */
159 public function testDoDeleteArticle() {
160 $page = $this->createPage(
161 __METHOD__,
162 "[[original text]] foo",
163 CONTENT_MODEL_WIKITEXT
164 );
165 $id = $page->getId();
166
167 $page->doDeleteArticle( "testing deletion" );
168
169 $this->assertFalse(
170 $page->getTitle()->getArticleID() > 0,
171 "Title object should now have page id 0"
172 );
173 $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
174 $this->assertFalse(
175 $page->exists(),
176 "WikiPage::exists should return false after page was deleted"
177 );
178 $this->assertNull(
179 $page->getContent(),
180 "WikiPage::getContent should return null after page was deleted"
181 );
182
183 $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
184 $this->assertFalse(
185 $t->exists(),
186 "Title::exists should return false after page was deleted"
187 );
188
189 // Run the job queue
190 JobQueueGroup::destroySingletons();
191 $jobs = new RunJobs;
192 $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
193 $jobs->execute();
194
195 # ------------------------
196 $dbr = wfGetDB( DB_REPLICA );
197 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
198 $n = $res->numRows();
199 $res->free();
200
201 $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
202 }
203
204 /**
205 * @covers WikiPage::doDeleteUpdates
206 */
207 public function testDoDeleteUpdates() {
208 $page = $this->createPage(
209 __METHOD__,
210 "[[original text]] foo",
211 CONTENT_MODEL_WIKITEXT
212 );
213 $id = $page->getId();
214
215 // Similar to MovePage logic
216 wfGetDB( DB_MASTER )->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
217 $page->doDeleteUpdates( $id );
218
219 // Run the job queue
220 JobQueueGroup::destroySingletons();
221 $jobs = new RunJobs;
222 $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
223 $jobs->execute();
224
225 # ------------------------
226 $dbr = wfGetDB( DB_REPLICA );
227 $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
228 $n = $res->numRows();
229 $res->free();
230
231 $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
232 }
233
234 /**
235 * @covers WikiPage::getRevision
236 */
237 public function testGetRevision() {
238 $page = $this->newPage( __METHOD__ );
239
240 $rev = $page->getRevision();
241 $this->assertNull( $rev );
242
243 # -----------------
244 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
245
246 $rev = $page->getRevision();
247
248 $this->assertEquals( $page->getLatest(), $rev->getId() );
249 $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
250 }
251
252 /**
253 * @covers WikiPage::getContent
254 */
255 public function testGetContent() {
256 $page = $this->newPage( __METHOD__ );
257
258 $content = $page->getContent();
259 $this->assertNull( $content );
260
261 # -----------------
262 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
263
264 $content = $page->getContent();
265 $this->assertEquals( "some text", $content->getNativeData() );
266 }
267
268 /**
269 * @covers WikiPage::exists
270 */
271 public function testExists() {
272 $page = $this->newPage( __METHOD__ );
273 $this->assertFalse( $page->exists() );
274
275 # -----------------
276 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
277 $this->assertTrue( $page->exists() );
278
279 $page = new WikiPage( $page->getTitle() );
280 $this->assertTrue( $page->exists() );
281
282 # -----------------
283 $page->doDeleteArticle( "done testing" );
284 $this->assertFalse( $page->exists() );
285
286 $page = new WikiPage( $page->getTitle() );
287 $this->assertFalse( $page->exists() );
288 }
289
290 public function provideHasViewableContent() {
291 return [
292 [ 'WikiPageTest_testHasViewableContent', false, true ],
293 [ 'Special:WikiPageTest_testHasViewableContent', false ],
294 [ 'MediaWiki:WikiPageTest_testHasViewableContent', false ],
295 [ 'Special:Userlogin', true ],
296 [ 'MediaWiki:help', true ],
297 ];
298 }
299
300 /**
301 * @dataProvider provideHasViewableContent
302 * @covers WikiPage::hasViewableContent
303 */
304 public function testHasViewableContent( $title, $viewable, $create = false ) {
305 $page = $this->newPage( $title );
306 $this->assertEquals( $viewable, $page->hasViewableContent() );
307
308 if ( $create ) {
309 $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
310 $this->assertTrue( $page->hasViewableContent() );
311
312 $page = new WikiPage( $page->getTitle() );
313 $this->assertTrue( $page->hasViewableContent() );
314 }
315 }
316
317 public function provideGetRedirectTarget() {
318 return [
319 [ 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ],
320 [
321 'WikiPageTest_testGetRedirectTarget_2',
322 CONTENT_MODEL_WIKITEXT,
323 "#REDIRECT [[hello world]]",
324 "Hello world"
325 ],
326 ];
327 }
328
329 /**
330 * @dataProvider provideGetRedirectTarget
331 * @covers WikiPage::getRedirectTarget
332 */
333 public function testGetRedirectTarget( $title, $model, $text, $target ) {
334 $this->setMwGlobals( [
335 'wgCapitalLinks' => true,
336 ] );
337
338 $page = $this->createPage( $title, $text, $model );
339
340 # sanity check, because this test seems to fail for no reason for some people.
341 $c = $page->getContent();
342 $this->assertEquals( 'WikitextContent', get_class( $c ) );
343
344 # now, test the actual redirect
345 $t = $page->getRedirectTarget();
346 $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
347 }
348
349 /**
350 * @dataProvider provideGetRedirectTarget
351 * @covers WikiPage::isRedirect
352 */
353 public function testIsRedirect( $title, $model, $text, $target ) {
354 $page = $this->createPage( $title, $text, $model );
355 $this->assertEquals( !is_null( $target ), $page->isRedirect() );
356 }
357
358 public function provideIsCountable() {
359 return [
360
361 // any
362 [ 'WikiPageTest_testIsCountable',
363 CONTENT_MODEL_WIKITEXT,
364 '',
365 'any',
366 true
367 ],
368 [ 'WikiPageTest_testIsCountable',
369 CONTENT_MODEL_WIKITEXT,
370 'Foo',
371 'any',
372 true
373 ],
374
375 // comma
376 [ 'WikiPageTest_testIsCountable',
377 CONTENT_MODEL_WIKITEXT,
378 'Foo',
379 'comma',
380 false
381 ],
382 [ 'WikiPageTest_testIsCountable',
383 CONTENT_MODEL_WIKITEXT,
384 'Foo, bar',
385 'comma',
386 true
387 ],
388
389 // link
390 [ 'WikiPageTest_testIsCountable',
391 CONTENT_MODEL_WIKITEXT,
392 'Foo',
393 'link',
394 false
395 ],
396 [ 'WikiPageTest_testIsCountable',
397 CONTENT_MODEL_WIKITEXT,
398 'Foo [[bar]]',
399 'link',
400 true
401 ],
402
403 // redirects
404 [ 'WikiPageTest_testIsCountable',
405 CONTENT_MODEL_WIKITEXT,
406 '#REDIRECT [[bar]]',
407 'any',
408 false
409 ],
410 [ 'WikiPageTest_testIsCountable',
411 CONTENT_MODEL_WIKITEXT,
412 '#REDIRECT [[bar]]',
413 'comma',
414 false
415 ],
416 [ 'WikiPageTest_testIsCountable',
417 CONTENT_MODEL_WIKITEXT,
418 '#REDIRECT [[bar]]',
419 'link',
420 false
421 ],
422
423 // not a content namespace
424 [ 'Talk:WikiPageTest_testIsCountable',
425 CONTENT_MODEL_WIKITEXT,
426 'Foo',
427 'any',
428 false
429 ],
430 [ 'Talk:WikiPageTest_testIsCountable',
431 CONTENT_MODEL_WIKITEXT,
432 'Foo, bar',
433 'comma',
434 false
435 ],
436 [ 'Talk:WikiPageTest_testIsCountable',
437 CONTENT_MODEL_WIKITEXT,
438 'Foo [[bar]]',
439 'link',
440 false
441 ],
442
443 // not a content namespace, different model
444 [ 'MediaWiki:WikiPageTest_testIsCountable.js',
445 null,
446 'Foo',
447 'any',
448 false
449 ],
450 [ 'MediaWiki:WikiPageTest_testIsCountable.js',
451 null,
452 'Foo, bar',
453 'comma',
454 false
455 ],
456 [ 'MediaWiki:WikiPageTest_testIsCountable.js',
457 null,
458 'Foo [[bar]]',
459 'link',
460 false
461 ],
462 ];
463 }
464
465 /**
466 * @dataProvider provideIsCountable
467 * @covers WikiPage::isCountable
468 */
469 public function testIsCountable( $title, $model, $text, $mode, $expected ) {
470 global $wgContentHandlerUseDB;
471
472 $this->setMwGlobals( 'wgArticleCountMethod', $mode );
473
474 $title = Title::newFromText( $title );
475
476 if ( !$wgContentHandlerUseDB
477 && $model
478 && ContentHandler::getDefaultModelFor( $title ) != $model
479 ) {
480 $this->markTestSkipped( "Can not use non-default content model $model for "
481 . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
482 }
483
484 $page = $this->createPage( $title, $text, $model );
485
486 $editInfo = $page->prepareContentForEdit( $page->getContent() );
487
488 $v = $page->isCountable();
489 $w = $page->isCountable( $editInfo );
490
491 $this->assertEquals(
492 $expected,
493 $v,
494 "isCountable( null ) returned unexpected value " . var_export( $v, true )
495 . " instead of " . var_export( $expected, true )
496 . " in mode `$mode` for text \"$text\""
497 );
498
499 $this->assertEquals(
500 $expected,
501 $w,
502 "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
503 . " instead of " . var_export( $expected, true )
504 . " in mode `$mode` for text \"$text\""
505 );
506 }
507
508 public function provideGetParserOutput() {
509 return [
510 [
511 CONTENT_MODEL_WIKITEXT,
512 "hello ''world''\n",
513 "<div class=\"mw-parser-output\"><p>hello <i>world</i></p></div>"
514 ],
515 // @todo more...?
516 ];
517 }
518
519 /**
520 * @dataProvider provideGetParserOutput
521 * @covers WikiPage::getParserOutput
522 */
523 public function testGetParserOutput( $model, $text, $expectedHtml ) {
524 $page = $this->createPage( __METHOD__, $text, $model );
525
526 $opt = $page->makeParserOptions( 'canonical' );
527 $po = $page->getParserOutput( $opt );
528 $text = $po->getText();
529
530 $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
531 $text = preg_replace( '!\s*(</p>|</div>)!sm', '\1', $text ); # don't let tidy confuse us
532
533 $this->assertEquals( $expectedHtml, $text );
534
535 return $po;
536 }
537
538 /**
539 * @covers WikiPage::getParserOutput
540 */
541 public function testGetParserOutput_nonexisting() {
542 $page = new WikiPage( Title::newFromText( __METHOD__ ) );
543
544 $opt = new ParserOptions();
545 $po = $page->getParserOutput( $opt );
546
547 $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
548 }
549
550 /**
551 * @covers WikiPage::getParserOutput
552 */
553 public function testGetParserOutput_badrev() {
554 $page = $this->createPage( __METHOD__, 'dummy', CONTENT_MODEL_WIKITEXT );
555
556 $opt = new ParserOptions();
557 $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
558
559 // @todo would be neat to also test deleted revision
560
561 $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
562 }
563
564 public static $sections =
565
566 "Intro
567
568 == stuff ==
569 hello world
570
571 == test ==
572 just a test
573
574 == foo ==
575 more stuff
576 ";
577
578 public function dataReplaceSection() {
579 // NOTE: assume the Help namespace to contain wikitext
580 return [
581 [ 'Help:WikiPageTest_testReplaceSection',
582 CONTENT_MODEL_WIKITEXT,
583 self::$sections,
584 "0",
585 "No more",
586 null,
587 trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
588 ],
589 [ 'Help:WikiPageTest_testReplaceSection',
590 CONTENT_MODEL_WIKITEXT,
591 self::$sections,
592 "",
593 "No more",
594 null,
595 "No more"
596 ],
597 [ 'Help:WikiPageTest_testReplaceSection',
598 CONTENT_MODEL_WIKITEXT,
599 self::$sections,
600 "2",
601 "== TEST ==\nmore fun",
602 null,
603 trim( preg_replace( '/^== test ==.*== foo ==/sm',
604 "== TEST ==\nmore fun\n\n== foo ==",
605 self::$sections ) )
606 ],
607 [ 'Help:WikiPageTest_testReplaceSection',
608 CONTENT_MODEL_WIKITEXT,
609 self::$sections,
610 "8",
611 "No more",
612 null,
613 trim( self::$sections )
614 ],
615 [ 'Help:WikiPageTest_testReplaceSection',
616 CONTENT_MODEL_WIKITEXT,
617 self::$sections,
618 "new",
619 "No more",
620 "New",
621 trim( self::$sections ) . "\n\n== New ==\n\nNo more"
622 ],
623 ];
624 }
625
626 /**
627 * @dataProvider dataReplaceSection
628 * @covers WikiPage::replaceSectionContent
629 */
630 public function testReplaceSectionContent( $title, $model, $text, $section,
631 $with, $sectionTitle, $expected
632 ) {
633 $page = $this->createPage( $title, $text, $model );
634
635 $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
636 $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
637
638 $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
639 }
640
641 /**
642 * @dataProvider dataReplaceSection
643 * @covers WikiPage::replaceSectionAtRev
644 */
645 public function testReplaceSectionAtRev( $title, $model, $text, $section,
646 $with, $sectionTitle, $expected
647 ) {
648 $page = $this->createPage( $title, $text, $model );
649 $baseRevId = $page->getLatest();
650
651 $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
652 $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
653
654 $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
655 }
656
657 /**
658 * @covers WikiPage::getOldestRevision
659 */
660 public function testGetOldestRevision() {
661 $page = $this->newPage( __METHOD__ );
662 $page->doEditContent(
663 new WikitextContent( 'one' ),
664 "first edit",
665 EDIT_NEW
666 );
667 $rev1 = $page->getRevision();
668
669 $page = new WikiPage( $page->getTitle() );
670 $page->doEditContent(
671 new WikitextContent( 'two' ),
672 "second edit",
673 EDIT_UPDATE
674 );
675
676 $page = new WikiPage( $page->getTitle() );
677 $page->doEditContent(
678 new WikitextContent( 'three' ),
679 "third edit",
680 EDIT_UPDATE
681 );
682
683 // sanity check
684 $this->assertNotEquals(
685 $rev1->getId(),
686 $page->getRevision()->getId(),
687 '$page->getRevision()->getId()'
688 );
689
690 // actual test
691 $this->assertEquals(
692 $rev1->getId(),
693 $page->getOldestRevision()->getId(),
694 '$page->getOldestRevision()->getId()'
695 );
696 }
697
698 /**
699 * @covers WikiPage::doRollback
700 * @covers WikiPage::commitRollback
701 */
702 public function testDoRollback() {
703 $admin = $this->getTestSysop()->getUser();
704 $user1 = $this->getTestUser()->getUser();
705 // Use the confirmed group for user2 to make sure the user is different
706 $user2 = $this->getTestUser( [ 'confirmed' ] )->getUser();
707
708 $page = $this->newPage( __METHOD__ );
709
710 // Make some edits
711 $text = "one";
712 $status1 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
713 "section one", EDIT_NEW, false, $admin );
714
715 $text .= "\n\ntwo";
716 $status2 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
717 "adding section two", 0, false, $user1 );
718
719 $text .= "\n\nthree";
720 $status3 = $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
721 "adding section three", 0, false, $user2 );
722
723 /** @var Revision $rev1 */
724 /** @var Revision $rev2 */
725 /** @var Revision $rev3 */
726 $rev1 = $status1->getValue()['revision'];
727 $rev2 = $status2->getValue()['revision'];
728 $rev3 = $status3->getValue()['revision'];
729
730 /**
731 * We are having issues with doRollback spuriously failing. Apparently
732 * the last revision somehow goes missing or not committed under some
733 * circumstances. So, make sure the revisions have the correct usernames.
734 */
735 $this->assertEquals( 3, Revision::countByPageId( wfGetDB( DB_REPLICA ), $page->getId() ) );
736 $this->assertEquals( $admin->getName(), $rev1->getUserText() );
737 $this->assertEquals( $user1->getName(), $rev2->getUserText() );
738 $this->assertEquals( $user2->getName(), $rev3->getUserText() );
739
740 // Now, try the actual rollback
741 $token = $admin->getEditToken( 'rollback' );
742 $rollbackErrors = $page->doRollback(
743 $user2->getName(),
744 "testing rollback",
745 $token,
746 false,
747 $resultDetails,
748 $admin
749 );
750
751 if ( $rollbackErrors ) {
752 $this->fail(
753 "Rollback failed:\n" .
754 print_r( $rollbackErrors, true ) . ";\n" .
755 print_r( $resultDetails, true )
756 );
757 }
758
759 $page = new WikiPage( $page->getTitle() );
760 $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
761 "rollback did not revert to the correct revision" );
762 $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
763 }
764
765 /**
766 * @covers WikiPage::doRollback
767 * @covers WikiPage::commitRollback
768 */
769 public function testDoRollback_simple() {
770 $admin = $this->getTestSysop()->getUser();
771
772 $text = "one";
773 $page = $this->newPage( __METHOD__ );
774 $page->doEditContent(
775 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
776 "section one",
777 EDIT_NEW,
778 false,
779 $admin
780 );
781 $rev1 = $page->getRevision();
782
783 $user1 = $this->getTestUser()->getUser();
784 $text .= "\n\ntwo";
785 $page = new WikiPage( $page->getTitle() );
786 $page->doEditContent(
787 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
788 "adding section two",
789 0,
790 false,
791 $user1
792 );
793
794 # now, try the rollback
795 $token = $admin->getEditToken( 'rollback' );
796 $errors = $page->doRollback(
797 $user1->getName(),
798 "testing revert",
799 $token,
800 false,
801 $details,
802 $admin
803 );
804
805 if ( $errors ) {
806 $this->fail( "Rollback failed:\n" . print_r( $errors, true )
807 . ";\n" . print_r( $details, true ) );
808 }
809
810 $page = new WikiPage( $page->getTitle() );
811 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
812 "rollback did not revert to the correct revision" );
813 $this->assertEquals( "one", $page->getContent()->getNativeData() );
814 }
815
816 /**
817 * @covers WikiPage::doRollback
818 * @covers WikiPage::commitRollback
819 */
820 public function testDoRollbackFailureSameContent() {
821 $admin = $this->getTestSysop()->getUser();
822
823 $text = "one";
824 $page = $this->newPage( __METHOD__ );
825 $page->doEditContent(
826 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
827 "section one",
828 EDIT_NEW,
829 false,
830 $admin
831 );
832 $rev1 = $page->getRevision();
833
834 $user1 = $this->getTestUser( [ 'sysop' ] )->getUser();
835 $text .= "\n\ntwo";
836 $page = new WikiPage( $page->getTitle() );
837 $page->doEditContent(
838 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
839 "adding section two",
840 0,
841 false,
842 $user1
843 );
844
845 # now, do a the rollback from the same user was doing the edit before
846 $resultDetails = [];
847 $token = $user1->getEditToken( 'rollback' );
848 $errors = $page->doRollback(
849 $user1->getName(),
850 "testing revert same user",
851 $token,
852 false,
853 $resultDetails,
854 $admin
855 );
856
857 $this->assertEquals( [], $errors, "Rollback failed same user" );
858
859 # now, try the rollback
860 $resultDetails = [];
861 $token = $admin->getEditToken( 'rollback' );
862 $errors = $page->doRollback(
863 $user1->getName(),
864 "testing revert",
865 $token,
866 false,
867 $resultDetails,
868 $admin
869 );
870
871 $this->assertEquals(
872 [
873 [
874 'alreadyrolled',
875 __METHOD__,
876 $user1->getName(),
877 $admin->getName(),
878 ],
879 ],
880 $errors,
881 "Rollback not failed"
882 );
883
884 $page = new WikiPage( $page->getTitle() );
885 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
886 "rollback did not revert to the correct revision" );
887 $this->assertEquals( "one", $page->getContent()->getNativeData() );
888 }
889
890 /**
891 * Tests tagging for edits that do rollback action
892 * @covers WikiPage::doRollback
893 */
894 public function testDoRollbackTagging() {
895 if ( !in_array( 'mw-rollback', ChangeTags::getSoftwareTags() ) ) {
896 $this->markTestSkipped( 'Rollback tag deactivated, skipped the test.' );
897 }
898
899 $admin = new User();
900 $admin->setName( 'Administrator' );
901 $admin->addToDatabase();
902
903 $text = 'First line';
904 $page = $this->newPage( 'WikiPageTest_testDoRollbackTagging' );
905 $page->doEditContent(
906 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
907 'Added first line',
908 EDIT_NEW,
909 false,
910 $admin
911 );
912
913 $secondUser = new User();
914 $secondUser->setName( '92.65.217.32' );
915 $text .= '\n\nSecond line';
916 $page = new WikiPage( $page->getTitle() );
917 $page->doEditContent(
918 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
919 'Adding second line',
920 0,
921 false,
922 $secondUser
923 );
924
925 // Now, try the rollback
926 $admin->addGroup( 'sysop' ); // Make the test user a sysop
927 $token = $admin->getEditToken( 'rollback' );
928 $errors = $page->doRollback(
929 $secondUser->getName(),
930 'testing rollback',
931 $token,
932 false,
933 $resultDetails,
934 $admin
935 );
936
937 // If doRollback completed without errors
938 if ( $errors === [] ) {
939 $tags = $resultDetails[ 'tags' ];
940 $this->assertContains( 'mw-rollback', $tags );
941 }
942 }
943
944 public function provideGetAutoDeleteReason() {
945 return [
946 [
947 [],
948 false,
949 false
950 ],
951
952 [
953 [
954 [ "first edit", null ],
955 ],
956 "/first edit.*only contributor/",
957 false
958 ],
959
960 [
961 [
962 [ "first edit", null ],
963 [ "second edit", null ],
964 ],
965 "/second edit.*only contributor/",
966 true
967 ],
968
969 [
970 [
971 [ "first edit", "127.0.2.22" ],
972 [ "second edit", "127.0.3.33" ],
973 ],
974 "/second edit/",
975 true
976 ],
977
978 [
979 [
980 [
981 "first edit: "
982 . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
983 . " nonumy eirmod tempor invidunt ut labore et dolore magna "
984 . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
985 . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
986 . "no sea takimata sanctus est Lorem ipsum dolor sit amet.'",
987 null
988 ],
989 ],
990 '/first edit:.*\.\.\."/',
991 false
992 ],
993
994 [
995 [
996 [ "first edit", "127.0.2.22" ],
997 [ "", "127.0.3.33" ],
998 ],
999 "/before blanking.*first edit/",
1000 true
1001 ],
1002
1003 ];
1004 }
1005
1006 /**
1007 * @dataProvider provideGetAutoDeleteReason
1008 * @covers WikiPage::getAutoDeleteReason
1009 */
1010 public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
1011 global $wgUser;
1012
1013 // NOTE: assume Help namespace to contain wikitext
1014 $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
1015
1016 $c = 1;
1017
1018 foreach ( $edits as $edit ) {
1019 $user = new User();
1020
1021 if ( !empty( $edit[1] ) ) {
1022 $user->setName( $edit[1] );
1023 } else {
1024 $user = $wgUser;
1025 }
1026
1027 $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
1028
1029 $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
1030
1031 $c += 1;
1032 }
1033
1034 $reason = $page->getAutoDeleteReason( $hasHistory );
1035
1036 if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
1037 $this->assertEquals( $expectedResult, $reason );
1038 } else {
1039 $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
1040 "Autosummary didn't match expected pattern $expectedResult: $reason" );
1041 }
1042
1043 $this->assertEquals( $expectedHistory, $hasHistory,
1044 "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
1045
1046 $page->doDeleteArticle( "done" );
1047 }
1048
1049 public function providePreSaveTransform() {
1050 return [
1051 [ 'hello this is ~~~',
1052 "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
1053 ],
1054 [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1055 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1056 ],
1057 ];
1058 }
1059
1060 /**
1061 * @covers WikiPage::factory
1062 */
1063 public function testWikiPageFactory() {
1064 $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
1065 $page = WikiPage::factory( $title );
1066 $this->assertEquals( 'WikiFilePage', get_class( $page ) );
1067
1068 $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
1069 $page = WikiPage::factory( $title );
1070 $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
1071
1072 $title = Title::makeTitle( NS_MAIN, 'SomePage' );
1073 $page = WikiPage::factory( $title );
1074 $this->assertEquals( 'WikiPage', get_class( $page ) );
1075 }
1076
1077 /**
1078 * @dataProvider provideCommentMigrationOnDeletion
1079 *
1080 * @param int $writeStage
1081 * @param int $readStage
1082 */
1083 public function testCommentMigrationOnDeletion( $writeStage, $readStage ) {
1084 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $writeStage );
1085 $dbr = wfGetDB( DB_REPLICA );
1086
1087 $page = $this->createPage(
1088 __METHOD__,
1089 "foo",
1090 CONTENT_MODEL_WIKITEXT
1091 );
1092 $revid = $page->getLatest();
1093 if ( $writeStage > MIGRATION_OLD ) {
1094 $comment_id = $dbr->selectField(
1095 'revision_comment_temp',
1096 'revcomment_comment_id',
1097 [ 'revcomment_rev' => $revid ],
1098 __METHOD__
1099 );
1100 }
1101
1102 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $readStage );
1103
1104 $page->doDeleteArticle( "testing deletion" );
1105
1106 if ( $readStage > MIGRATION_OLD ) {
1107 // Didn't leave behind any 'revision_comment_temp' rows
1108 $n = $dbr->selectField(
1109 'revision_comment_temp', 'COUNT(*)', [ 'revcomment_rev' => $revid ], __METHOD__
1110 );
1111 $this->assertEquals( 0, $n, 'no entry in revision_comment_temp after deletion' );
1112
1113 // Copied or upgraded the comment_id, as applicable
1114 $ar_comment_id = $dbr->selectField(
1115 'archive',
1116 'ar_comment_id',
1117 [ 'ar_rev_id' => $revid ],
1118 __METHOD__
1119 );
1120 if ( $writeStage > MIGRATION_OLD ) {
1121 $this->assertSame( $comment_id, $ar_comment_id );
1122 } else {
1123 $this->assertNotEquals( 0, $ar_comment_id );
1124 }
1125 }
1126
1127 // Copied rev_comment, if applicable
1128 if ( $readStage <= MIGRATION_WRITE_BOTH && $writeStage <= MIGRATION_WRITE_BOTH ) {
1129 $ar_comment = $dbr->selectField(
1130 'archive',
1131 'ar_comment',
1132 [ 'ar_rev_id' => $revid ],
1133 __METHOD__
1134 );
1135 $this->assertSame( 'testing', $ar_comment );
1136 }
1137 }
1138
1139 public function provideCommentMigrationOnDeletion() {
1140 return [
1141 [ MIGRATION_OLD, MIGRATION_OLD ],
1142 [ MIGRATION_OLD, MIGRATION_WRITE_BOTH ],
1143 [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
1144 [ MIGRATION_WRITE_BOTH, MIGRATION_OLD ],
1145 [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_BOTH ],
1146 [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
1147 [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
1148 [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_BOTH ],
1149 [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_NEW ],
1150 [ MIGRATION_WRITE_NEW, MIGRATION_NEW ],
1151 [ MIGRATION_NEW, MIGRATION_WRITE_BOTH ],
1152 [ MIGRATION_NEW, MIGRATION_WRITE_NEW ],
1153 [ MIGRATION_NEW, MIGRATION_NEW ],
1154 ];
1155 }
1156
1157 }