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