Merge "Add meta=userinfo&uiprop=latestcontrib"
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiMoveTest.php
1 <?php
2
3 /**
4 * @group API
5 * @group Database
6 * @group medium
7 *
8 * @covers ApiMove
9 */
10 class ApiMoveTest extends ApiTestCase {
11 /**
12 * @param string $from Prefixed name of source
13 * @param string $to Prefixed name of destination
14 * @param string $id Page id of the page to move
15 * @param array|string|null $opts Options: 'noredirect' to expect no redirect
16 */
17 protected function assertMoved( $from, $to, $id, $opts = null ) {
18 $opts = (array)$opts;
19
20 Title::clearCaches();
21 $fromTitle = Title::newFromText( $from );
22 $toTitle = Title::newFromText( $to );
23
24 $this->assertTrue( $toTitle->exists(),
25 "Destination {$toTitle->getPrefixedText()} does not exist" );
26
27 if ( in_array( 'noredirect', $opts ) ) {
28 $this->assertFalse( $fromTitle->exists(),
29 "Source {$fromTitle->getPrefixedText()} exists" );
30 } else {
31 $this->assertTrue( $fromTitle->exists(),
32 "Source {$fromTitle->getPrefixedText()} does not exist" );
33 $this->assertTrue( $fromTitle->isRedirect(),
34 "Source {$fromTitle->getPrefixedText()} is not a redirect" );
35
36 $target = Revision::newFromTitle( $fromTitle )->getContent()->getRedirectTarget();
37 $this->assertSame( $toTitle->getPrefixedText(), $target->getPrefixedText() );
38 }
39
40 $this->assertSame( $id, $toTitle->getArticleID() );
41 }
42
43 /**
44 * Shortcut function to create a page and return its id.
45 *
46 * @param string $name Page to create
47 * @return int ID of created page
48 */
49 protected function createPage( $name ) {
50 return $this->editPage( $name, 'Content' )->value['revision']->getPage();
51 }
52
53 public function testFromWithFromid() {
54 $this->setExpectedException( ApiUsageException::class,
55 'The parameters "from" and "fromid" can not be used together.' );
56
57 $this->doApiRequestWithToken( [
58 'action' => 'move',
59 'from' => 'Some page',
60 'fromid' => 123,
61 'to' => 'Some other page',
62 ] );
63 }
64
65 public function testMove() {
66 $name = ucfirst( __FUNCTION__ );
67
68 $id = $this->createPage( $name );
69
70 $res = $this->doApiRequestWithToken( [
71 'action' => 'move',
72 'from' => $name,
73 'to' => "$name 2",
74 ] );
75
76 $this->assertMoved( $name, "$name 2", $id );
77 $this->assertArrayNotHasKey( 'warnings', $res[0] );
78 }
79
80 public function testMoveById() {
81 $name = ucfirst( __FUNCTION__ );
82
83 $id = $this->createPage( $name );
84
85 $res = $this->doApiRequestWithToken( [
86 'action' => 'move',
87 'fromid' => $id,
88 'to' => "$name 2",
89 ] );
90
91 $this->assertMoved( $name, "$name 2", $id );
92 $this->assertArrayNotHasKey( 'warnings', $res[0] );
93 }
94
95 public function testMoveNonexistent() {
96 $this->setExpectedException( ApiUsageException::class,
97 "The page you specified doesn't exist." );
98
99 $this->doApiRequestWithToken( [
100 'action' => 'move',
101 'from' => 'Nonexistent page',
102 'to' => 'Different page'
103 ] );
104 }
105
106 public function testMoveNonexistentId() {
107 $this->setExpectedException( ApiUsageException::class,
108 'There is no page with ID 2147483647.' );
109
110 $this->doApiRequestWithToken( [
111 'action' => 'move',
112 'fromid' => pow( 2, 31 ) - 1,
113 'to' => 'Different page',
114 ] );
115 }
116
117 public function testMoveToInvalidPageName() {
118 $this->setExpectedException( ApiUsageException::class, 'Bad title "[".' );
119
120 $name = ucfirst( __FUNCTION__ );
121 $id = $this->createPage( $name );
122
123 try {
124 $this->doApiRequestWithToken( [
125 'action' => 'move',
126 'from' => $name,
127 'to' => '[',
128 ] );
129 } finally {
130 $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
131 }
132 }
133
134 public function testMoveWhileBlocked() {
135 $this->assertNull( Block::newFromTarget( '127.0.0.1' ), 'Sanity check' );
136
137 $block = new Block( [
138 'address' => self::$users['sysop']->getUser()->getName(),
139 'by' => self::$users['sysop']->getUser()->getId(),
140 'reason' => 'Capriciousness',
141 'timestamp' => '19370101000000',
142 'expiry' => 'infinity',
143 'enableAutoblock' => true,
144 ] );
145 $block->insert();
146
147 $name = ucfirst( __FUNCTION__ );
148 $id = $this->createPage( $name );
149
150 try {
151 $this->doApiRequestWithToken( [
152 'action' => 'move',
153 'from' => $name,
154 'to' => "$name 2",
155 ] );
156 $this->fail( 'Expected exception not thrown' );
157 } catch ( ApiUsageException $ex ) {
158 $this->assertSame( 'You have been blocked from editing.', $ex->getMessage() );
159 $this->assertNotNull( Block::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
160 } finally {
161 $block->delete();
162 self::$users['sysop']->getUser()->clearInstanceCache();
163 $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
164 }
165 }
166
167 // @todo File moving
168
169 public function testPingLimiter() {
170 $this->setExpectedException( ApiUsageException::class,
171 "You've exceeded your rate limit. Please wait some time and try again." );
172
173 $name = ucfirst( __FUNCTION__ );
174
175 $this->setMwGlobals( 'wgMainCacheType', 'hash' );
176
177 $this->mergeMwGlobalArrayValue( 'wgRateLimits',
178 [ 'move' => [ '&can-bypass' => false, 'user' => [ 1, 60 ] ] ] );
179
180 $id = $this->createPage( $name );
181
182 $res = $this->doApiRequestWithToken( [
183 'action' => 'move',
184 'from' => $name,
185 'to' => "$name 2",
186 ] );
187
188 $this->assertMoved( $name, "$name 2", $id );
189 $this->assertArrayNotHasKey( 'warnings', $res[0] );
190
191 try {
192 $this->doApiRequestWithToken( [
193 'action' => 'move',
194 'from' => "$name 2",
195 'to' => "$name 3",
196 ] );
197 } finally {
198 $this->assertSame( $id, Title::newFromText( "$name 2" )->getArticleID() );
199 $this->assertFalse( Title::newFromText( "$name 3" )->exists(),
200 "\"$name 3\" should not exist" );
201 }
202 }
203
204 public function testTagsNoPermission() {
205 $this->setExpectedException( ApiUsageException::class,
206 'You do not have permission to apply change tags along with your changes.' );
207
208 $name = ucfirst( __FUNCTION__ );
209
210 ChangeTags::defineTag( 'custom tag' );
211
212 $this->setGroupPermissions( 'user', 'applychangetags', false );
213
214 $id = $this->createPage( $name );
215
216 try {
217 $this->doApiRequestWithToken( [
218 'action' => 'move',
219 'from' => $name,
220 'to' => "$name 2",
221 'tags' => 'custom tag',
222 ] );
223 } finally {
224 $this->assertSame( $id, Title::newFromText( $name )->getArticleID() );
225 $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
226 "\"$name 2\" should not exist" );
227 }
228 }
229
230 public function testSelfMove() {
231 $this->setExpectedException( ApiUsageException::class,
232 'The title is the same; cannot move a page over itself.' );
233
234 $name = ucfirst( __FUNCTION__ );
235 $this->createPage( $name );
236
237 $this->doApiRequestWithToken( [
238 'action' => 'move',
239 'from' => $name,
240 'to' => $name,
241 ] );
242 }
243
244 public function testMoveTalk() {
245 $name = ucfirst( __FUNCTION__ );
246
247 $id = $this->createPage( $name );
248 $talkId = $this->createPage( "Talk:$name" );
249
250 $res = $this->doApiRequestWithToken( [
251 'action' => 'move',
252 'from' => $name,
253 'to' => "$name 2",
254 'movetalk' => '',
255 ] );
256
257 $this->assertMoved( $name, "$name 2", $id );
258 $this->assertMoved( "Talk:$name", "Talk:$name 2", $talkId );
259
260 $this->assertArrayNotHasKey( 'warnings', $res[0] );
261 }
262
263 public function testMoveTalkFailed() {
264 $name = ucfirst( __FUNCTION__ );
265
266 $id = $this->createPage( $name );
267 $talkId = $this->createPage( "Talk:$name" );
268 $talkDestinationId = $this->createPage( "Talk:$name 2" );
269
270 $res = $this->doApiRequestWithToken( [
271 'action' => 'move',
272 'from' => $name,
273 'to' => "$name 2",
274 'movetalk' => '',
275 ] );
276
277 $this->assertMoved( $name, "$name 2", $id );
278 $this->assertSame( $talkId, Title::newFromText( "Talk:$name" )->getArticleID() );
279 $this->assertSame( $talkDestinationId,
280 Title::newFromText( "Talk:$name 2" )->getArticleID() );
281 $this->assertSame( [ [
282 'message' => 'articleexists',
283 'params' => [],
284 'code' => 'articleexists',
285 'type' => 'error',
286 ] ], $res[0]['move']['talkmove-errors'] );
287
288 $this->assertArrayNotHasKey( 'warnings', $res[0] );
289 }
290
291 public function testMoveSubpages() {
292 $name = ucfirst( __FUNCTION__ );
293
294 $this->mergeMwGlobalArrayValue( 'wgNamespacesWithSubpages', [ NS_MAIN => true ] );
295
296 $pages = [ $name, "$name/1", "$name/2", "Talk:$name", "Talk:$name/1", "Talk:$name/3" ];
297 $ids = [];
298 foreach ( array_merge( $pages, [ "$name/error", "$name 2/error" ] ) as $page ) {
299 $ids[$page] = $this->createPage( $page );
300 }
301
302 $res = $this->doApiRequestWithToken( [
303 'action' => 'move',
304 'from' => $name,
305 'to' => "$name 2",
306 'movetalk' => '',
307 'movesubpages' => '',
308 ] );
309
310 foreach ( $pages as $page ) {
311 $this->assertMoved( $page, str_replace( $name, "$name 2", $page ), $ids[$page] );
312 }
313
314 $this->assertSame( $ids["$name/error"],
315 Title::newFromText( "$name/error" )->getArticleID() );
316 $this->assertSame( $ids["$name 2/error"],
317 Title::newFromText( "$name 2/error" )->getArticleID() );
318
319 $results = array_merge( $res[0]['move']['subpages'], $res[0]['move']['subpages-talk'] );
320 foreach ( $results as $arr ) {
321 if ( $arr['from'] === "$name/error" ) {
322 $this->assertSame( [ [
323 'message' => 'articleexists',
324 'params' => [],
325 'code' => 'articleexists',
326 'type' => 'error'
327 ] ], $arr['errors'] );
328 } else {
329 $this->assertSame( str_replace( $name, "$name 2", $arr['from'] ), $arr['to'] );
330 }
331 $this->assertCount( 2, $arr );
332 }
333
334 $this->assertArrayNotHasKey( 'warnings', $res[0] );
335 }
336
337 public function testMoveNoPermission() {
338 $this->setExpectedException( ApiUsageException::class,
339 'You must be a registered user and [[Special:UserLogin|logged in]] to move a page.' );
340
341 $name = ucfirst( __FUNCTION__ );
342
343 $id = $this->createPage( $name );
344
345 $user = new User();
346
347 try {
348 $this->doApiRequestWithToken( [
349 'action' => 'move',
350 'from' => $name,
351 'to' => "$name 2",
352 ], null, $user );
353 } finally {
354 $this->assertSame( $id, Title::newFromText( "$name" )->getArticleID() );
355 $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
356 "\"$name 2\" should not exist" );
357 }
358 }
359
360 public function testSuppressRedirect() {
361 $name = ucfirst( __FUNCTION__ );
362
363 $id = $this->createPage( $name );
364
365 $res = $this->doApiRequestWithToken( [
366 'action' => 'move',
367 'from' => $name,
368 'to' => "$name 2",
369 'noredirect' => '',
370 ] );
371
372 $this->assertMoved( $name, "$name 2", $id, 'noredirect' );
373 $this->assertArrayNotHasKey( 'warnings', $res[0] );
374 }
375
376 public function testSuppressRedirectNoPermission() {
377 $name = ucfirst( __FUNCTION__ );
378
379 $this->setGroupPermissions( 'sysop', 'suppressredirect', false );
380
381 $id = $this->createPage( $name );
382
383 $res = $this->doApiRequestWithToken( [
384 'action' => 'move',
385 'from' => $name,
386 'to' => "$name 2",
387 'noredirect' => '',
388 ] );
389
390 $this->assertMoved( $name, "$name 2", $id );
391 $this->assertArrayNotHasKey( 'warnings', $res[0] );
392 }
393
394 public function testMoveSubpagesError() {
395 $name = ucfirst( __FUNCTION__ );
396
397 // Subpages are allowed in talk but not main
398 $idBase = $this->createPage( "Talk:$name" );
399 $idSub = $this->createPage( "Talk:$name/1" );
400
401 $res = $this->doApiRequestWithToken( [
402 'action' => 'move',
403 'from' => "Talk:$name",
404 'to' => $name,
405 'movesubpages' => '',
406 ] );
407
408 $this->assertMoved( "Talk:$name", $name, $idBase );
409 $this->assertSame( $idSub, Title::newFromText( "Talk:$name/1" )->getArticleID() );
410 $this->assertFalse( Title::newFromText( "$name/1" )->exists(),
411 "\"$name/1\" should not exist" );
412
413 $this->assertSame( [ 'errors' => [ [
414 'message' => 'namespace-nosubpages',
415 'params' => [ '' ],
416 'code' => 'namespace-nosubpages',
417 'type' => 'error',
418 ] ] ], $res[0]['move']['subpages'] );
419
420 $this->assertArrayNotHasKey( 'warnings', $res[0] );
421 }
422 }