Test ApiMove
[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 // @todo File moving
134
135 public function testPingLimiter() {
136 global $wgRateLimits;
137
138 $this->setExpectedException( ApiUsageException::class,
139 "You've exceeded your rate limit. Please wait some time and try again." );
140
141 $name = ucfirst( __FUNCTION__ );
142
143 $this->setMwGlobals( 'wgMainCacheType', 'hash' );
144
145 $this->stashMwGlobals( 'wgRateLimits' );
146 $wgRateLimits['move'] = [ '&can-bypass' => false, 'user' => [ 1, 60 ] ];
147
148 $id = $this->createPage( $name );
149
150 $res = $this->doApiRequestWithToken( [
151 'action' => 'move',
152 'from' => $name,
153 'to' => "$name 2",
154 ] );
155
156 $this->assertMoved( $name, "$name 2", $id );
157 $this->assertArrayNotHasKey( 'warnings', $res[0] );
158
159 try {
160 $this->doApiRequestWithToken( [
161 'action' => 'move',
162 'from' => "$name 2",
163 'to' => "$name 3",
164 ] );
165 } finally {
166 $this->assertSame( $id, Title::newFromText( "$name 2" )->getArticleId() );
167 $this->assertFalse( Title::newFromText( "$name 3" )->exists(),
168 "\"$name 3\" should not exist" );
169 }
170 }
171
172 public function testTagsNoPermission() {
173 $this->setExpectedException( ApiUsageException::class,
174 'You do not have permission to apply change tags along with your changes.' );
175
176 $name = ucfirst( __FUNCTION__ );
177
178 ChangeTags::defineTag( 'custom tag' );
179
180 $this->setGroupPermissions( 'user', 'applychangetags', false );
181
182 $id = $this->createPage( $name );
183
184 try {
185 $this->doApiRequestWithToken( [
186 'action' => 'move',
187 'from' => $name,
188 'to' => "$name 2",
189 'tags' => 'custom tag',
190 ] );
191 } finally {
192 $this->assertSame( $id, Title::newFromText( $name )->getArticleId() );
193 $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
194 "\"$name 2\" should not exist" );
195 }
196 }
197
198 public function testSelfMove() {
199 $this->setExpectedException( ApiUsageException::class,
200 'The title is the same; cannot move a page over itself.' );
201
202 $name = ucfirst( __FUNCTION__ );
203 $this->createPage( $name );
204
205 $this->doApiRequestWithToken( [
206 'action' => 'move',
207 'from' => $name,
208 'to' => $name,
209 ] );
210 }
211
212 public function testMoveTalk() {
213 $name = ucfirst( __FUNCTION__ );
214
215 $id = $this->createPage( $name );
216 $talkId = $this->createPage( "Talk:$name" );
217
218 $res = $this->doApiRequestWithToken( [
219 'action' => 'move',
220 'from' => $name,
221 'to' => "$name 2",
222 'movetalk' => '',
223 ] );
224
225 $this->assertMoved( $name, "$name 2", $id );
226 $this->assertMoved( "Talk:$name", "Talk:$name 2", $talkId );
227
228 $this->assertArrayNotHasKey( 'warnings', $res[0] );
229 }
230
231 public function testMoveTalkFailed() {
232 $name = ucfirst( __FUNCTION__ );
233
234 $id = $this->createPage( $name );
235 $talkId = $this->createPage( "Talk:$name" );
236 $talkDestinationId = $this->createPage( "Talk:$name 2" );
237
238 $res = $this->doApiRequestWithToken( [
239 'action' => 'move',
240 'from' => $name,
241 'to' => "$name 2",
242 'movetalk' => '',
243 ] );
244
245 $this->assertMoved( $name, "$name 2", $id );
246 $this->assertSame( $talkId, Title::newFromText( "Talk:$name" )->getArticleId() );
247 $this->assertSame( $talkDestinationId,
248 Title::newFromText( "Talk:$name 2" )->getArticleId() );
249 $this->assertSame( [ [
250 'message' => 'articleexists',
251 'params' => [],
252 'code' => 'articleexists',
253 'type' => 'error',
254 ] ], $res[0]['move']['talkmove-errors'] );
255
256 $this->assertArrayNotHasKey( 'warnings', $res[0] );
257 }
258
259 public function testMoveSubpages() {
260 global $wgNamespacesWithSubpages;
261
262 $name = ucfirst( __FUNCTION__ );
263
264 $this->stashMwGlobals( 'wgNamespacesWithSubpages' );
265 $wgNamespacesWithSubpages[NS_MAIN] = true;
266
267 $pages = [ $name, "$name/1", "$name/2", "Talk:$name", "Talk:$name/1", "Talk:$name/3" ];
268 $ids = [];
269 foreach ( array_merge( $pages, [ "$name/error", "$name 2/error" ] ) as $page ) {
270 $ids[$page] = $this->createPage( $page );
271 }
272
273 $res = $this->doApiRequestWithToken( [
274 'action' => 'move',
275 'from' => $name,
276 'to' => "$name 2",
277 'movetalk' => '',
278 'movesubpages' => '',
279 ] );
280
281 foreach ( $pages as $page ) {
282 $this->assertMoved( $page, str_replace( $name, "$name 2", $page ), $ids[$page] );
283 }
284
285 $this->assertSame( $ids["$name/error"],
286 Title::newFromText( "$name/error" )->getArticleId() );
287 $this->assertSame( $ids["$name 2/error"],
288 Title::newFromText( "$name 2/error" )->getArticleId() );
289
290 $results = array_merge( $res[0]['move']['subpages'], $res[0]['move']['subpages-talk'] );
291 foreach ( $results as $arr ) {
292 if ( $arr['from'] === "$name/error" ) {
293 $this->assertSame( [ [
294 'message' => 'articleexists',
295 'params' => [],
296 'code' => 'articleexists',
297 'type' => 'error'
298 ] ], $arr['errors'] );
299 } else {
300 $this->assertSame( str_replace( $name, "$name 2", $arr['from'] ), $arr['to'] );
301 }
302 $this->assertCount( 2, $arr );
303 }
304
305 $this->assertArrayNotHasKey( 'warnings', $res[0] );
306 }
307
308 public function testMoveNoPermission() {
309 $this->setExpectedException( ApiUsageException::class,
310 'You must be a registered user and [[Special:UserLogin|logged in]] to move a page.' );
311
312 $name = ucfirst( __FUNCTION__ );
313
314 $id = $this->createPage( $name );
315
316 $user = new User();
317
318 try {
319 $this->doApiRequestWithToken( [
320 'action' => 'move',
321 'from' => $name,
322 'to' => "$name 2",
323 ], null, $user );
324 } finally {
325 $this->assertSame( $id, Title::newFromText( "$name" )->getArticleId() );
326 $this->assertFalse( Title::newFromText( "$name 2" )->exists(),
327 "\"$name 2\" should not exist" );
328 }
329 }
330
331 public function testSuppressRedirect() {
332 $name = ucfirst( __FUNCTION__ );
333
334 $id = $this->createPage( $name );
335
336 $res = $this->doApiRequestWithToken( [
337 'action' => 'move',
338 'from' => $name,
339 'to' => "$name 2",
340 'noredirect' => '',
341 ] );
342
343 $this->assertMoved( $name, "$name 2", $id, 'noredirect' );
344 $this->assertArrayNotHasKey( 'warnings', $res[0] );
345 }
346
347 public function testSuppressRedirectNoPermission() {
348 $name = ucfirst( __FUNCTION__ );
349
350 $this->setGroupPermissions( 'sysop', 'suppressredirect', false );
351
352 $id = $this->createPage( $name );
353
354 $res = $this->doApiRequestWithToken( [
355 'action' => 'move',
356 'from' => $name,
357 'to' => "$name 2",
358 'noredirect' => '',
359 ] );
360
361 $this->assertMoved( $name, "$name 2", $id );
362 $this->assertArrayNotHasKey( 'warnings', $res[0] );
363 }
364
365 public function testMoveSubpagesError() {
366 $name = ucfirst( __FUNCTION__ );
367
368 // Subpages are allowed in talk but not main
369 $idBase = $this->createPage( "Talk:$name" );
370 $idSub = $this->createPage( "Talk:$name/1" );
371
372 $res = $this->doApiRequestWithToken( [
373 'action' => 'move',
374 'from' => "Talk:$name",
375 'to' => $name,
376 'movesubpages' => '',
377 ] );
378
379 $this->assertMoved( "Talk:$name", $name, $idBase );
380 $this->assertSame( $idSub, Title::newFromText( "Talk:$name/1" )->getArticleId() );
381 $this->assertFalse( Title::newFromText( "$name/1" )->exists(),
382 "\"$name/1\" should not exist" );
383
384 $this->assertSame( [ 'errors' => [ [
385 'message' => 'namespace-nosubpages',
386 'params' => [ '' ],
387 'code' => 'namespace-nosubpages',
388 'type' => 'error',
389 ] ] ], $res[0]['move']['subpages'] );
390
391 $this->assertArrayNotHasKey( 'warnings', $res[0] );
392 }
393 }