10 class ApiMoveTest
extends ApiTestCase
{
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
17 protected function assertMoved( $from, $to, $id, $opts = null ) {
21 $fromTitle = Title
::newFromText( $from );
22 $toTitle = Title
::newFromText( $to );
24 $this->assertTrue( $toTitle->exists(),
25 "Destination {$toTitle->getPrefixedText()} does not exist" );
27 if ( in_array( 'noredirect', $opts ) ) {
28 $this->assertFalse( $fromTitle->exists(),
29 "Source {$fromTitle->getPrefixedText()} exists" );
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" );
36 $target = Revision
::newFromTitle( $fromTitle )->getContent()->getRedirectTarget();
37 $this->assertSame( $toTitle->getPrefixedText(), $target->getPrefixedText() );
40 $this->assertSame( $id, $toTitle->getArticleID() );
44 * Shortcut function to create a page and return its id.
46 * @param string $name Page to create
47 * @return int ID of created page
49 protected function createPage( $name ) {
50 return $this->editPage( $name, 'Content' )->value
['revision']->getPage();
53 public function testFromWithFromid() {
54 $this->setExpectedException( ApiUsageException
::class,
55 'The parameters "from" and "fromid" can not be used together.' );
57 $this->doApiRequestWithToken( [
59 'from' => 'Some page',
61 'to' => 'Some other page',
65 public function testMove() {
66 $name = ucfirst( __FUNCTION__
);
68 $id = $this->createPage( $name );
70 $res = $this->doApiRequestWithToken( [
76 $this->assertMoved( $name, "$name 2", $id );
77 $this->assertArrayNotHasKey( 'warnings', $res[0] );
80 public function testMoveById() {
81 $name = ucfirst( __FUNCTION__
);
83 $id = $this->createPage( $name );
85 $res = $this->doApiRequestWithToken( [
91 $this->assertMoved( $name, "$name 2", $id );
92 $this->assertArrayNotHasKey( 'warnings', $res[0] );
95 public function testMoveNonexistent() {
96 $this->setExpectedException( ApiUsageException
::class,
97 "The page you specified doesn't exist." );
99 $this->doApiRequestWithToken( [
101 'from' => 'Nonexistent page',
102 'to' => 'Different page'
106 public function testMoveNonexistentId() {
107 $this->setExpectedException( ApiUsageException
::class,
108 'There is no page with ID 2147483647.' );
110 $this->doApiRequestWithToken( [
112 'fromid' => pow( 2, 31 ) - 1,
113 'to' => 'Different page',
117 public function testMoveToInvalidPageName() {
118 $this->setExpectedException( ApiUsageException
::class, 'Bad title "[".' );
120 $name = ucfirst( __FUNCTION__
);
121 $id = $this->createPage( $name );
124 $this->doApiRequestWithToken( [
130 $this->assertSame( $id, Title
::newFromText( $name )->getArticleID() );
134 public function testMoveWhileBlocked() {
135 $this->assertNull( Block
::newFromTarget( '127.0.0.1' ), 'Sanity check' );
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,
147 $name = ucfirst( __FUNCTION__
);
148 $id = $this->createPage( $name );
151 $this->doApiRequestWithToken( [
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' );
162 self
::$users['sysop']->getUser()->clearInstanceCache();
163 $this->assertSame( $id, Title
::newFromText( $name )->getArticleID() );
169 public function testPingLimiter() {
170 $this->setExpectedException( ApiUsageException
::class,
171 "You've exceeded your rate limit. Please wait some time and try again." );
173 $name = ucfirst( __FUNCTION__
);
175 $this->setMwGlobals( 'wgMainCacheType', 'hash' );
177 $this->mergeMwGlobalArrayValue( 'wgRateLimits',
178 [ 'move' => [ '&can-bypass' => false, 'user' => [ 1, 60 ] ] ] );
180 $id = $this->createPage( $name );
182 $res = $this->doApiRequestWithToken( [
188 $this->assertMoved( $name, "$name 2", $id );
189 $this->assertArrayNotHasKey( 'warnings', $res[0] );
192 $this->doApiRequestWithToken( [
198 $this->assertSame( $id, Title
::newFromText( "$name 2" )->getArticleID() );
199 $this->assertFalse( Title
::newFromText( "$name 3" )->exists(),
200 "\"$name 3\" should not exist" );
204 public function testTagsNoPermission() {
205 $this->setExpectedException( ApiUsageException
::class,
206 'You do not have permission to apply change tags along with your changes.' );
208 $name = ucfirst( __FUNCTION__
);
210 ChangeTags
::defineTag( 'custom tag' );
212 $this->setGroupPermissions( 'user', 'applychangetags', false );
214 $id = $this->createPage( $name );
217 $this->doApiRequestWithToken( [
221 'tags' => 'custom tag',
224 $this->assertSame( $id, Title
::newFromText( $name )->getArticleID() );
225 $this->assertFalse( Title
::newFromText( "$name 2" )->exists(),
226 "\"$name 2\" should not exist" );
230 public function testSelfMove() {
231 $this->setExpectedException( ApiUsageException
::class,
232 'The title is the same; cannot move a page over itself.' );
234 $name = ucfirst( __FUNCTION__
);
235 $this->createPage( $name );
237 $this->doApiRequestWithToken( [
244 public function testMoveTalk() {
245 $name = ucfirst( __FUNCTION__
);
247 $id = $this->createPage( $name );
248 $talkId = $this->createPage( "Talk:$name" );
250 $res = $this->doApiRequestWithToken( [
257 $this->assertMoved( $name, "$name 2", $id );
258 $this->assertMoved( "Talk:$name", "Talk:$name 2", $talkId );
260 $this->assertArrayNotHasKey( 'warnings', $res[0] );
263 public function testMoveTalkFailed() {
264 $name = ucfirst( __FUNCTION__
);
266 $id = $this->createPage( $name );
267 $talkId = $this->createPage( "Talk:$name" );
268 $talkDestinationId = $this->createPage( "Talk:$name 2" );
270 $res = $this->doApiRequestWithToken( [
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',
284 'code' => 'articleexists',
286 ] ], $res[0]['move']['talkmove-errors'] );
288 $this->assertArrayNotHasKey( 'warnings', $res[0] );
291 public function testMoveSubpages() {
292 $name = ucfirst( __FUNCTION__
);
294 $this->mergeMwGlobalArrayValue( 'wgNamespacesWithSubpages', [ NS_MAIN
=> true ] );
296 $pages = [ $name, "$name/1", "$name/2", "Talk:$name", "Talk:$name/1", "Talk:$name/3" ];
298 foreach ( array_merge( $pages, [ "$name/error", "$name 2/error" ] ) as $page ) {
299 $ids[$page] = $this->createPage( $page );
302 $res = $this->doApiRequestWithToken( [
307 'movesubpages' => '',
310 foreach ( $pages as $page ) {
311 $this->assertMoved( $page, str_replace( $name, "$name 2", $page ), $ids[$page] );
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() );
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',
325 'code' => 'articleexists',
327 ] ], $arr['errors'] );
329 $this->assertSame( str_replace( $name, "$name 2", $arr['from'] ), $arr['to'] );
331 $this->assertCount( 2, $arr );
334 $this->assertArrayNotHasKey( 'warnings', $res[0] );
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.' );
341 $name = ucfirst( __FUNCTION__
);
343 $id = $this->createPage( $name );
348 $this->doApiRequestWithToken( [
354 $this->assertSame( $id, Title
::newFromText( "$name" )->getArticleID() );
355 $this->assertFalse( Title
::newFromText( "$name 2" )->exists(),
356 "\"$name 2\" should not exist" );
360 public function testSuppressRedirect() {
361 $name = ucfirst( __FUNCTION__
);
363 $id = $this->createPage( $name );
365 $res = $this->doApiRequestWithToken( [
372 $this->assertMoved( $name, "$name 2", $id, 'noredirect' );
373 $this->assertArrayNotHasKey( 'warnings', $res[0] );
376 public function testSuppressRedirectNoPermission() {
377 $name = ucfirst( __FUNCTION__
);
379 $this->setGroupPermissions( 'sysop', 'suppressredirect', false );
381 $id = $this->createPage( $name );
383 $res = $this->doApiRequestWithToken( [
390 $this->assertMoved( $name, "$name 2", $id );
391 $this->assertArrayNotHasKey( 'warnings', $res[0] );
394 public function testMoveSubpagesError() {
395 $name = ucfirst( __FUNCTION__
);
397 // Subpages are allowed in talk but not main
398 $idBase = $this->createPage( "Talk:$name" );
399 $idSub = $this->createPage( "Talk:$name/1" );
401 $res = $this->doApiRequestWithToken( [
403 'from' => "Talk:$name",
405 'movesubpages' => '',
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" );
413 $this->assertSame( [ 'errors' => [ [
414 'message' => 'namespace-nosubpages',
416 'code' => 'namespace-nosubpages',
418 ] ] ], $res[0]['move']['subpages'] );
420 $this->assertArrayNotHasKey( 'warnings', $res[0] );