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