]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - tests/phpunit/includes/api/ApiEditPageTest.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / tests / phpunit / includes / api / ApiEditPageTest.php
1 <?php
2
3 /**
4  * Tests for MediaWiki api.php?action=edit.
5  *
6  * @author Daniel Kinzler
7  *
8  * @group API
9  * @group Database
10  * @group medium
11  *
12  * @covers ApiEditPage
13  */
14 class ApiEditPageTest extends ApiTestCase {
15
16         protected function setUp() {
17                 global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
18
19                 parent::setUp();
20
21                 $this->setMwGlobals( [
22                         'wgExtraNamespaces' => $wgExtraNamespaces,
23                         'wgNamespaceContentModels' => $wgNamespaceContentModels,
24                         'wgContentHandlers' => $wgContentHandlers,
25                         'wgContLang' => $wgContLang,
26                 ] );
27
28                 $wgExtraNamespaces[12312] = 'Dummy';
29                 $wgExtraNamespaces[12313] = 'Dummy_talk';
30                 $wgExtraNamespaces[12314] = 'DummyNonText';
31                 $wgExtraNamespaces[12315] = 'DummyNonText_talk';
32
33                 $wgNamespaceContentModels[12312] = "testing";
34                 $wgNamespaceContentModels[12314] = "testing-nontext";
35
36                 $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
37                 $wgContentHandlers["testing-nontext"] = 'DummyNonTextContentHandler';
38
39                 MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
40                 $wgContLang->resetNamespaces(); # reset namespace cache
41
42                 $this->doLogin();
43         }
44
45         protected function tearDown() {
46                 MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
47                 parent::tearDown();
48         }
49
50         public function testEdit() {
51                 $name = 'Help:ApiEditPageTest_testEdit'; // assume Help namespace to default to wikitext
52
53                 // -- test new page --------------------------------------------
54                 $apiResult = $this->doApiRequestWithToken( [
55                         'action' => 'edit',
56                         'title' => $name,
57                         'text' => 'some text',
58                 ] );
59                 $apiResult = $apiResult[0];
60
61                 // Validate API result data
62                 $this->assertArrayHasKey( 'edit', $apiResult );
63                 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
64                 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
65
66                 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
67                 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
68
69                 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
70
71                 // -- test existing page, no change ----------------------------
72                 $data = $this->doApiRequestWithToken( [
73                         'action' => 'edit',
74                         'title' => $name,
75                         'text' => 'some text',
76                 ] );
77
78                 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
79
80                 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
81                 $this->assertArrayHasKey( 'nochange', $data[0]['edit'] );
82
83                 // -- test existing page, with change --------------------------
84                 $data = $this->doApiRequestWithToken( [
85                         'action' => 'edit',
86                         'title' => $name,
87                         'text' => 'different text'
88                 ] );
89
90                 $this->assertEquals( 'Success', $data[0]['edit']['result'] );
91
92                 $this->assertArrayNotHasKey( 'new', $data[0]['edit'] );
93                 $this->assertArrayNotHasKey( 'nochange', $data[0]['edit'] );
94
95                 $this->assertArrayHasKey( 'oldrevid', $data[0]['edit'] );
96                 $this->assertArrayHasKey( 'newrevid', $data[0]['edit'] );
97                 $this->assertNotEquals(
98                         $data[0]['edit']['newrevid'],
99                         $data[0]['edit']['oldrevid'],
100                         "revision id should change after edit"
101                 );
102         }
103
104         /**
105          * @return array
106          */
107         public static function provideEditAppend() {
108                 return [
109                         [ # 0: append
110                                 'foo', 'append', 'bar', "foobar"
111                         ],
112                         [ # 1: prepend
113                                 'foo', 'prepend', 'bar', "barfoo"
114                         ],
115                         [ # 2: append to empty page
116                                 '', 'append', 'foo', "foo"
117                         ],
118                         [ # 3: prepend to empty page
119                                 '', 'prepend', 'foo', "foo"
120                         ],
121                         [ # 4: append to non-existing page
122                                 null, 'append', 'foo', "foo"
123                         ],
124                         [ # 5: prepend to non-existing page
125                                 null, 'prepend', 'foo', "foo"
126                         ],
127                 ];
128         }
129
130         /**
131          * @dataProvider provideEditAppend
132          */
133         public function testEditAppend( $text, $op, $append, $expected ) {
134                 static $count = 0;
135                 $count++;
136
137                 // assume NS_HELP defaults to wikitext
138                 $name = "Help:ApiEditPageTest_testEditAppend_$count";
139
140                 // -- create page (or not) -----------------------------------------
141                 if ( $text !== null ) {
142                         list( $re ) = $this->doApiRequestWithToken( [
143                                 'action' => 'edit',
144                                 'title' => $name,
145                                 'text' => $text, ] );
146
147                         $this->assertEquals( 'Success', $re['edit']['result'] ); // sanity
148                 }
149
150                 // -- try append/prepend --------------------------------------------
151                 list( $re ) = $this->doApiRequestWithToken( [
152                         'action' => 'edit',
153                         'title' => $name,
154                         $op . 'text' => $append, ] );
155
156                 $this->assertEquals( 'Success', $re['edit']['result'] );
157
158                 // -- validate -----------------------------------------------------
159                 $page = new WikiPage( Title::newFromText( $name ) );
160                 $content = $page->getContent();
161                 $this->assertNotNull( $content, 'Page should have been created' );
162
163                 $text = $content->getNativeData();
164
165                 $this->assertEquals( $expected, $text );
166         }
167
168         /**
169          * Test editing of sections
170          */
171         public function testEditSection() {
172                 $name = 'Help:ApiEditPageTest_testEditSection';
173                 $page = WikiPage::factory( Title::newFromText( $name ) );
174                 $text = "==section 1==\ncontent 1\n==section 2==\ncontent2";
175                 // Preload the page with some text
176                 $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), 'summary' );
177
178                 list( $re ) = $this->doApiRequestWithToken( [
179                         'action' => 'edit',
180                         'title' => $name,
181                         'section' => '1',
182                         'text' => "==section 1==\nnew content 1",
183                 ] );
184                 $this->assertEquals( 'Success', $re['edit']['result'] );
185                 $newtext = WikiPage::factory( Title::newFromText( $name ) )
186                         ->getContent( Revision::RAW )
187                         ->getNativeData();
188                 $this->assertEquals( "==section 1==\nnew content 1\n\n==section 2==\ncontent2", $newtext );
189
190                 // Test that we raise a 'nosuchsection' error
191                 try {
192                         $this->doApiRequestWithToken( [
193                                 'action' => 'edit',
194                                 'title' => $name,
195                                 'section' => '9999',
196                                 'text' => 'text',
197                         ] );
198                         $this->fail( "Should have raised an ApiUsageException" );
199                 } catch ( ApiUsageException $e ) {
200                         $this->assertTrue( self::apiExceptionHasCode( $e, 'nosuchsection' ) );
201                 }
202         }
203
204         /**
205          * Test action=edit&section=new
206          * Run it twice so we test adding a new section on a
207          * page that doesn't exist (T54830) and one that
208          * does exist
209          */
210         public function testEditNewSection() {
211                 $name = 'Help:ApiEditPageTest_testEditNewSection';
212
213                 // Test on a page that does not already exist
214                 $this->assertFalse( Title::newFromText( $name )->exists() );
215                 list( $re ) = $this->doApiRequestWithToken( [
216                         'action' => 'edit',
217                         'title' => $name,
218                         'section' => 'new',
219                         'text' => 'test',
220                         'summary' => 'header',
221                 ] );
222
223                 $this->assertEquals( 'Success', $re['edit']['result'] );
224                 // Check the page text is correct
225                 $text = WikiPage::factory( Title::newFromText( $name ) )
226                         ->getContent( Revision::RAW )
227                         ->getNativeData();
228                 $this->assertEquals( "== header ==\n\ntest", $text );
229
230                 // Now on one that does
231                 $this->assertTrue( Title::newFromText( $name )->exists() );
232                 list( $re2 ) = $this->doApiRequestWithToken( [
233                         'action' => 'edit',
234                         'title' => $name,
235                         'section' => 'new',
236                         'text' => 'test',
237                         'summary' => 'header',
238                 ] );
239
240                 $this->assertEquals( 'Success', $re2['edit']['result'] );
241                 $text = WikiPage::factory( Title::newFromText( $name ) )
242                         ->getContent( Revision::RAW )
243                         ->getNativeData();
244                 $this->assertEquals( "== header ==\n\ntest\n\n== header ==\n\ntest", $text );
245         }
246
247         /**
248          * Ensure we can edit through a redirect, if adding a section
249          */
250         public function testEdit_redirect() {
251                 static $count = 0;
252                 $count++;
253
254                 // assume NS_HELP defaults to wikitext
255                 $name = "Help:ApiEditPageTest_testEdit_redirect_$count";
256                 $title = Title::newFromText( $name );
257                 $page = WikiPage::factory( $title );
258
259                 $rname = "Help:ApiEditPageTest_testEdit_redirect_r$count";
260                 $rtitle = Title::newFromText( $rname );
261                 $rpage = WikiPage::factory( $rtitle );
262
263                 // base edit for content
264                 $page->doEditContent( new WikitextContent( "Foo" ),
265                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
266                 $this->forceRevisionDate( $page, '20120101000000' );
267                 $baseTime = $page->getRevision()->getTimestamp();
268
269                 // base edit for redirect
270                 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
271                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
272                 $this->forceRevisionDate( $rpage, '20120101000000' );
273
274                 // conflicting edit to redirect
275                 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ),
276                         "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->getUser() );
277                 $this->forceRevisionDate( $rpage, '20120101020202' );
278
279                 // try to save edit, following the redirect
280                 list( $re, , ) = $this->doApiRequestWithToken( [
281                         'action' => 'edit',
282                         'title' => $rname,
283                         'text' => 'nix bar!',
284                         'basetimestamp' => $baseTime,
285                         'section' => 'new',
286                         'redirect' => true,
287                 ], null, self::$users['sysop']->getUser() );
288
289                 $this->assertEquals( 'Success', $re['edit']['result'],
290                         "no problems expected when following redirect" );
291         }
292
293         /**
294          * Ensure we cannot edit through a redirect, if attempting to overwrite content
295          */
296         public function testEdit_redirectText() {
297                 static $count = 0;
298                 $count++;
299
300                 // assume NS_HELP defaults to wikitext
301                 $name = "Help:ApiEditPageTest_testEdit_redirectText_$count";
302                 $title = Title::newFromText( $name );
303                 $page = WikiPage::factory( $title );
304
305                 $rname = "Help:ApiEditPageTest_testEdit_redirectText_r$count";
306                 $rtitle = Title::newFromText( $rname );
307                 $rpage = WikiPage::factory( $rtitle );
308
309                 // base edit for content
310                 $page->doEditContent( new WikitextContent( "Foo" ),
311                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
312                 $this->forceRevisionDate( $page, '20120101000000' );
313                 $baseTime = $page->getRevision()->getTimestamp();
314
315                 // base edit for redirect
316                 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
317                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
318                 $this->forceRevisionDate( $rpage, '20120101000000' );
319
320                 // conflicting edit to redirect
321                 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ),
322                         "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->getUser() );
323                 $this->forceRevisionDate( $rpage, '20120101020202' );
324
325                 // try to save edit, following the redirect but without creating a section
326                 try {
327                         $this->doApiRequestWithToken( [
328                                 'action' => 'edit',
329                                 'title' => $rname,
330                                 'text' => 'nix bar!',
331                                 'basetimestamp' => $baseTime,
332                                 'redirect' => true,
333                         ], null, self::$users['sysop']->getUser() );
334
335                         $this->fail( 'redirect-appendonly error expected' );
336                 } catch ( ApiUsageException $ex ) {
337                         $this->assertTrue( self::apiExceptionHasCode( $ex, 'redirect-appendonly' ) );
338                 }
339         }
340
341         public function testEditConflict() {
342                 static $count = 0;
343                 $count++;
344
345                 // assume NS_HELP defaults to wikitext
346                 $name = "Help:ApiEditPageTest_testEditConflict_$count";
347                 $title = Title::newFromText( $name );
348
349                 $page = WikiPage::factory( $title );
350
351                 // base edit
352                 $page->doEditContent( new WikitextContent( "Foo" ),
353                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
354                 $this->forceRevisionDate( $page, '20120101000000' );
355                 $baseTime = $page->getRevision()->getTimestamp();
356
357                 // conflicting edit
358                 $page->doEditContent( new WikitextContent( "Foo bar" ),
359                         "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->getUser() );
360                 $this->forceRevisionDate( $page, '20120101020202' );
361
362                 // try to save edit, expect conflict
363                 try {
364                         $this->doApiRequestWithToken( [
365                                 'action' => 'edit',
366                                 'title' => $name,
367                                 'text' => 'nix bar!',
368                                 'basetimestamp' => $baseTime,
369                         ], null, self::$users['sysop']->getUser() );
370
371                         $this->fail( 'edit conflict expected' );
372                 } catch ( ApiUsageException $ex ) {
373                         $this->assertTrue( self::apiExceptionHasCode( $ex, 'editconflict' ) );
374                 }
375         }
376
377         /**
378          * Ensure that editing using section=new will prevent simple conflicts
379          */
380         public function testEditConflict_newSection() {
381                 static $count = 0;
382                 $count++;
383
384                 // assume NS_HELP defaults to wikitext
385                 $name = "Help:ApiEditPageTest_testEditConflict_newSection_$count";
386                 $title = Title::newFromText( $name );
387
388                 $page = WikiPage::factory( $title );
389
390                 // base edit
391                 $page->doEditContent( new WikitextContent( "Foo" ),
392                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
393                 $this->forceRevisionDate( $page, '20120101000000' );
394                 $baseTime = $page->getRevision()->getTimestamp();
395
396                 // conflicting edit
397                 $page->doEditContent( new WikitextContent( "Foo bar" ),
398                         "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->getUser() );
399                 $this->forceRevisionDate( $page, '20120101020202' );
400
401                 // try to save edit, expect no conflict
402                 list( $re, , ) = $this->doApiRequestWithToken( [
403                         'action' => 'edit',
404                         'title' => $name,
405                         'text' => 'nix bar!',
406                         'basetimestamp' => $baseTime,
407                         'section' => 'new',
408                 ], null, self::$users['sysop']->getUser() );
409
410                 $this->assertEquals( 'Success', $re['edit']['result'],
411                         "no edit conflict expected here" );
412         }
413
414         public function testEditConflict_bug41990() {
415                 static $count = 0;
416                 $count++;
417
418                 /*
419                 * T43990: if the target page has a newer revision than the redirect, then editing the
420                 * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erroneously
421                 * caused an edit conflict to be detected.
422                 */
423
424                 // assume NS_HELP defaults to wikitext
425                 $name = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_$count";
426                 $title = Title::newFromText( $name );
427                 $page = WikiPage::factory( $title );
428
429                 $rname = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_r$count";
430                 $rtitle = Title::newFromText( $rname );
431                 $rpage = WikiPage::factory( $rtitle );
432
433                 // base edit for content
434                 $page->doEditContent( new WikitextContent( "Foo" ),
435                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
436                 $this->forceRevisionDate( $page, '20120101000000' );
437
438                 // base edit for redirect
439                 $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ),
440                         "testing 1", EDIT_NEW, false, self::$users['sysop']->getUser() );
441                 $this->forceRevisionDate( $rpage, '20120101000000' );
442
443                 // new edit to content
444                 $page->doEditContent( new WikitextContent( "Foo bar" ),
445                         "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->getUser() );
446                 $this->forceRevisionDate( $rpage, '20120101020202' );
447
448                 // try to save edit; should work, following the redirect.
449                 list( $re, , ) = $this->doApiRequestWithToken( [
450                         'action' => 'edit',
451                         'title' => $rname,
452                         'text' => 'nix bar!',
453                         'section' => 'new',
454                         'redirect' => true,
455                 ], null, self::$users['sysop']->getUser() );
456
457                 $this->assertEquals( 'Success', $re['edit']['result'],
458                         "no edit conflict expected here" );
459         }
460
461         /**
462          * @param WikiPage $page
463          * @param string|int $timestamp
464          */
465         protected function forceRevisionDate( WikiPage $page, $timestamp ) {
466                 $dbw = wfGetDB( DB_MASTER );
467
468                 $dbw->update( 'revision',
469                         [ 'rev_timestamp' => $dbw->timestamp( $timestamp ) ],
470                         [ 'rev_id' => $page->getLatest() ] );
471
472                 $page->clear();
473         }
474
475         public function testCheckDirectApiEditingDisallowed_forNonTextContent() {
476                 $this->setExpectedException(
477                         'ApiUsageException',
478                         'Direct editing via API is not supported for content model ' .
479                                 'testing used by Dummy:ApiEditPageTest_nonTextPageEdit'
480                 );
481
482                 $this->doApiRequestWithToken( [
483                         'action' => 'edit',
484                         'title' => 'Dummy:ApiEditPageTest_nonTextPageEdit',
485                         'text' => '{"animals":["kittens!"]}'
486                 ] );
487         }
488
489         public function testSupportsDirectApiEditing_withContentHandlerOverride() {
490                 $name = 'DummyNonText:ApiEditPageTest_testNonTextEdit';
491                 $data = serialize( 'some bla bla text' );
492
493                 $result = $this->doApiRequestWithToken( [
494                         'action' => 'edit',
495                         'title' => $name,
496                         'text' => $data,
497                 ] );
498
499                 $apiResult = $result[0];
500
501                 // Validate API result data
502                 $this->assertArrayHasKey( 'edit', $apiResult );
503                 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
504                 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
505
506                 $this->assertArrayHasKey( 'new', $apiResult['edit'] );
507                 $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
508
509                 $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
510
511                 // validate resulting revision
512                 $page = WikiPage::factory( Title::newFromText( $name ) );
513                 $this->assertEquals( "testing-nontext", $page->getContentModel() );
514                 $this->assertEquals( $data, $page->getContent()->serialize() );
515         }
516
517         /**
518          * This test verifies that after changing the content model
519          * of a page, undoing that edit via the API will also
520          * undo the content model change.
521          */
522         public function testUndoAfterContentModelChange() {
523                 $name = 'Help:' . __FUNCTION__;
524                 $uploader = self::$users['uploader']->getUser();
525                 $sysop = self::$users['sysop']->getUser();
526                 $apiResult = $this->doApiRequestWithToken( [
527                         'action' => 'edit',
528                         'title' => $name,
529                         'text' => 'some text',
530                 ], null, $sysop )[0];
531
532                 // Check success
533                 $this->assertArrayHasKey( 'edit', $apiResult );
534                 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
535                 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
536                 $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
537                 // Content model is wikitext
538                 $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
539
540                 // Convert the page to JSON
541                 $apiResult = $this->doApiRequestWithToken( [
542                         'action' => 'edit',
543                         'title' => $name,
544                         'text' => '{}',
545                         'contentmodel' => 'json',
546                 ], null, $uploader )[0];
547
548                 // Check success
549                 $this->assertArrayHasKey( 'edit', $apiResult );
550                 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
551                 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
552                 $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
553                 $this->assertEquals( 'json', $apiResult['edit']['contentmodel'] );
554
555                 $apiResult = $this->doApiRequestWithToken( [
556                         'action' => 'edit',
557                         'title' => $name,
558                         'undo' => $apiResult['edit']['newrevid']
559                 ], null, $sysop )[0];
560
561                 // Check success
562                 $this->assertArrayHasKey( 'edit', $apiResult );
563                 $this->assertArrayHasKey( 'result', $apiResult['edit'] );
564                 $this->assertEquals( 'Success', $apiResult['edit']['result'] );
565                 $this->assertArrayHasKey( 'contentmodel', $apiResult['edit'] );
566                 // Check that the contentmodel is back to wikitext now.
567                 $this->assertEquals( 'wikitext', $apiResult['edit']['contentmodel'] );
568         }
569 }