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