Merge "WikiPage tests @covers for doDeleteArticleReal & commitRollback"
[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 * @todo FIXME: this is a better rollback test than the one below, but it
700 * keeps failing in jenkins for some reason.
701 */
702 public function broken_testDoRollback() {
703 $admin = $this->getTestSysop()->getUser();
704
705 $text = "one";
706 $page = $this->newPage( __METHOD__ );
707 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
708 "section one", EDIT_NEW, false, $admin );
709
710 $user1 = $this->getTestUser()->getUser();
711 $text .= "\n\ntwo";
712 $page = new WikiPage( $page->getTitle() );
713 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
714 "adding section two", 0, false, $user1 );
715
716 $user2 = $this->getTestUser()->getUser();
717 $text .= "\n\nthree";
718 $page = new WikiPage( $page->getTitle() );
719 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
720 "adding section three", 0, false, $user2 );
721
722 # we are having issues with doRollback spuriously failing. Apparently
723 # the last revision somehow goes missing or not committed under some
724 # circumstances. So, make sure the last revision has the right user name.
725 $dbr = wfGetDB( DB_REPLICA );
726 $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
727
728 $page = new WikiPage( $page->getTitle() );
729 $rev3 = $page->getRevision();
730 $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
731
732 $rev2 = $rev3->getPrevious();
733 $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
734
735 $rev1 = $rev2->getPrevious();
736 $this->assertEquals( 'Admin', $rev1->getUserText() );
737
738 # now, try the actual rollback
739 $token = $admin->getEditToken(
740 [ $page->getTitle()->getPrefixedText(), $user2->getName() ],
741 null
742 );
743 $errors = $page->doRollback(
744 $user2->getName(),
745 "testing revert",
746 $token,
747 false,
748 $details,
749 $admin
750 );
751
752 if ( $errors ) {
753 $this->fail( "Rollback failed:\n" . print_r( $errors, true )
754 . ";\n" . print_r( $details, true ) );
755 }
756
757 $page = new WikiPage( $page->getTitle() );
758 $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
759 "rollback did not revert to the correct revision" );
760 $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
761 }
762
763 /**
764 * @todo FIXME: the above rollback test is better, but it keeps failing in jenkins for some reason.
765 * @covers WikiPage::doRollback
766 * @covers WikiPage::commitRollback
767 */
768 public function testDoRollback() {
769 $admin = $this->getTestSysop()->getUser();
770
771 $text = "one";
772 $page = $this->newPage( __METHOD__ );
773 $page->doEditContent(
774 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
775 "section one",
776 EDIT_NEW,
777 false,
778 $admin
779 );
780 $rev1 = $page->getRevision();
781
782 $user1 = $this->getTestUser()->getUser();
783 $text .= "\n\ntwo";
784 $page = new WikiPage( $page->getTitle() );
785 $page->doEditContent(
786 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
787 "adding section two",
788 0,
789 false,
790 $user1
791 );
792
793 # now, try the rollback
794 $token = $admin->getEditToken( 'rollback' );
795 $errors = $page->doRollback(
796 $user1->getName(),
797 "testing revert",
798 $token,
799 false,
800 $details,
801 $admin
802 );
803
804 if ( $errors ) {
805 $this->fail( "Rollback failed:\n" . print_r( $errors, true )
806 . ";\n" . print_r( $details, true ) );
807 }
808
809 $page = new WikiPage( $page->getTitle() );
810 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
811 "rollback did not revert to the correct revision" );
812 $this->assertEquals( "one", $page->getContent()->getNativeData() );
813 }
814
815 /**
816 * @covers WikiPage::doRollback
817 * @covers WikiPage::commitRollback
818 */
819 public function testDoRollbackFailureSameContent() {
820 $admin = $this->getTestSysop()->getUser();
821
822 $text = "one";
823 $page = $this->newPage( __METHOD__ );
824 $page->doEditContent(
825 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
826 "section one",
827 EDIT_NEW,
828 false,
829 $admin
830 );
831 $rev1 = $page->getRevision();
832
833 $user1 = $this->getTestUser( [ 'sysop' ] )->getUser();
834 $text .= "\n\ntwo";
835 $page = new WikiPage( $page->getTitle() );
836 $page->doEditContent(
837 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
838 "adding section two",
839 0,
840 false,
841 $user1
842 );
843
844 # now, do a the rollback from the same user was doing the edit before
845 $resultDetails = [];
846 $token = $user1->getEditToken( 'rollback' );
847 $errors = $page->doRollback(
848 $user1->getName(),
849 "testing revert same user",
850 $token,
851 false,
852 $resultDetails,
853 $admin
854 );
855
856 $this->assertEquals( [], $errors, "Rollback failed same user" );
857
858 # now, try the rollback
859 $resultDetails = [];
860 $token = $admin->getEditToken( 'rollback' );
861 $errors = $page->doRollback(
862 $user1->getName(),
863 "testing revert",
864 $token,
865 false,
866 $resultDetails,
867 $admin
868 );
869
870 $this->assertEquals(
871 [
872 [
873 'alreadyrolled',
874 __METHOD__,
875 $user1->getName(),
876 $admin->getName(),
877 ],
878 ],
879 $errors,
880 "Rollback not failed"
881 );
882
883 $page = new WikiPage( $page->getTitle() );
884 $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
885 "rollback did not revert to the correct revision" );
886 $this->assertEquals( "one", $page->getContent()->getNativeData() );
887 }
888
889 /**
890 * Tests tagging for edits that do rollback action
891 * @covers WikiPage::doRollback
892 */
893 public function testDoRollbackTagging() {
894 if ( !in_array( 'mw-rollback', ChangeTags::getSoftwareTags() ) ) {
895 $this->markTestSkipped( 'Rollback tag deactivated, skipped the test.' );
896 }
897
898 $admin = new User();
899 $admin->setName( 'Administrator' );
900 $admin->addToDatabase();
901
902 $text = 'First line';
903 $page = $this->newPage( 'WikiPageTest_testDoRollbackTagging' );
904 $page->doEditContent(
905 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
906 'Added first line',
907 EDIT_NEW,
908 false,
909 $admin
910 );
911
912 $secondUser = new User();
913 $secondUser->setName( '92.65.217.32' );
914 $text .= '\n\nSecond line';
915 $page = new WikiPage( $page->getTitle() );
916 $page->doEditContent(
917 ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
918 'Adding second line',
919 0,
920 false,
921 $secondUser
922 );
923
924 // Now, try the rollback
925 $admin->addGroup( 'sysop' ); // Make the test user a sysop
926 $token = $admin->getEditToken( 'rollback' );
927 $errors = $page->doRollback(
928 $secondUser->getName(),
929 'testing rollback',
930 $token,
931 false,
932 $resultDetails,
933 $admin
934 );
935
936 // If doRollback completed without errors
937 if ( $errors === [] ) {
938 $tags = $resultDetails[ 'tags' ];
939 $this->assertContains( 'mw-rollback', $tags );
940 }
941 }
942
943 public function provideGetAutoDeleteReason() {
944 return [
945 [
946 [],
947 false,
948 false
949 ],
950
951 [
952 [
953 [ "first edit", null ],
954 ],
955 "/first edit.*only contributor/",
956 false
957 ],
958
959 [
960 [
961 [ "first edit", null ],
962 [ "second edit", null ],
963 ],
964 "/second edit.*only contributor/",
965 true
966 ],
967
968 [
969 [
970 [ "first edit", "127.0.2.22" ],
971 [ "second edit", "127.0.3.33" ],
972 ],
973 "/second edit/",
974 true
975 ],
976
977 [
978 [
979 [
980 "first edit: "
981 . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
982 . " nonumy eirmod tempor invidunt ut labore et dolore magna "
983 . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
984 . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
985 . "no sea takimata sanctus est Lorem ipsum dolor sit amet.'",
986 null
987 ],
988 ],
989 '/first edit:.*\.\.\."/',
990 false
991 ],
992
993 [
994 [
995 [ "first edit", "127.0.2.22" ],
996 [ "", "127.0.3.33" ],
997 ],
998 "/before blanking.*first edit/",
999 true
1000 ],
1001
1002 ];
1003 }
1004
1005 /**
1006 * @dataProvider provideGetAutoDeleteReason
1007 * @covers WikiPage::getAutoDeleteReason
1008 */
1009 public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
1010 global $wgUser;
1011
1012 // NOTE: assume Help namespace to contain wikitext
1013 $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
1014
1015 $c = 1;
1016
1017 foreach ( $edits as $edit ) {
1018 $user = new User();
1019
1020 if ( !empty( $edit[1] ) ) {
1021 $user->setName( $edit[1] );
1022 } else {
1023 $user = $wgUser;
1024 }
1025
1026 $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
1027
1028 $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
1029
1030 $c += 1;
1031 }
1032
1033 $reason = $page->getAutoDeleteReason( $hasHistory );
1034
1035 if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
1036 $this->assertEquals( $expectedResult, $reason );
1037 } else {
1038 $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
1039 "Autosummary didn't match expected pattern $expectedResult: $reason" );
1040 }
1041
1042 $this->assertEquals( $expectedHistory, $hasHistory,
1043 "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
1044
1045 $page->doDeleteArticle( "done" );
1046 }
1047
1048 public function providePreSaveTransform() {
1049 return [
1050 [ 'hello this is ~~~',
1051 "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
1052 ],
1053 [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1054 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1055 ],
1056 ];
1057 }
1058
1059 /**
1060 * @covers WikiPage::factory
1061 */
1062 public function testWikiPageFactory() {
1063 $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
1064 $page = WikiPage::factory( $title );
1065 $this->assertEquals( 'WikiFilePage', get_class( $page ) );
1066
1067 $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
1068 $page = WikiPage::factory( $title );
1069 $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
1070
1071 $title = Title::makeTitle( NS_MAIN, 'SomePage' );
1072 $page = WikiPage::factory( $title );
1073 $this->assertEquals( 'WikiPage', get_class( $page ) );
1074 }
1075
1076 /**
1077 * @dataProvider provideCommentMigrationOnDeletion
1078 *
1079 * @param int $writeStage
1080 * @param int $readStage
1081 */
1082 public function testCommentMigrationOnDeletion( $writeStage, $readStage ) {
1083 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $writeStage );
1084 $dbr = wfGetDB( DB_REPLICA );
1085
1086 $page = $this->createPage(
1087 __METHOD__,
1088 "foo",
1089 CONTENT_MODEL_WIKITEXT
1090 );
1091 $revid = $page->getLatest();
1092 if ( $writeStage > MIGRATION_OLD ) {
1093 $comment_id = $dbr->selectField(
1094 'revision_comment_temp',
1095 'revcomment_comment_id',
1096 [ 'revcomment_rev' => $revid ],
1097 __METHOD__
1098 );
1099 }
1100
1101 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $readStage );
1102
1103 $page->doDeleteArticle( "testing deletion" );
1104
1105 if ( $readStage > MIGRATION_OLD ) {
1106 // Didn't leave behind any 'revision_comment_temp' rows
1107 $n = $dbr->selectField(
1108 'revision_comment_temp', 'COUNT(*)', [ 'revcomment_rev' => $revid ], __METHOD__
1109 );
1110 $this->assertEquals( 0, $n, 'no entry in revision_comment_temp after deletion' );
1111
1112 // Copied or upgraded the comment_id, as applicable
1113 $ar_comment_id = $dbr->selectField(
1114 'archive',
1115 'ar_comment_id',
1116 [ 'ar_rev_id' => $revid ],
1117 __METHOD__
1118 );
1119 if ( $writeStage > MIGRATION_OLD ) {
1120 $this->assertSame( $comment_id, $ar_comment_id );
1121 } else {
1122 $this->assertNotEquals( 0, $ar_comment_id );
1123 }
1124 }
1125
1126 // Copied rev_comment, if applicable
1127 if ( $readStage <= MIGRATION_WRITE_BOTH && $writeStage <= MIGRATION_WRITE_BOTH ) {
1128 $ar_comment = $dbr->selectField(
1129 'archive',
1130 'ar_comment',
1131 [ 'ar_rev_id' => $revid ],
1132 __METHOD__
1133 );
1134 $this->assertSame( 'testing', $ar_comment );
1135 }
1136 }
1137
1138 public function provideCommentMigrationOnDeletion() {
1139 return [
1140 [ MIGRATION_OLD, MIGRATION_OLD ],
1141 [ MIGRATION_OLD, MIGRATION_WRITE_BOTH ],
1142 [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
1143 [ MIGRATION_WRITE_BOTH, MIGRATION_OLD ],
1144 [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_BOTH ],
1145 [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
1146 [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
1147 [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_BOTH ],
1148 [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_NEW ],
1149 [ MIGRATION_WRITE_NEW, MIGRATION_NEW ],
1150 [ MIGRATION_NEW, MIGRATION_WRITE_BOTH ],
1151 [ MIGRATION_NEW, MIGRATION_WRITE_NEW ],
1152 [ MIGRATION_NEW, MIGRATION_NEW ],
1153 ];
1154 }
1155
1156 }