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