Merge "(bug 40154) On action=info show where this page redirects to and whether it...
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiUploadTest.php
1 <?php
2
3 /**
4 * @group API
5 * @group Database
6 */
7
8 /**
9 * n.b. Ensure that you can write to the images/ directory as the
10 * user that will run tests.
11 */
12
13 // Note for reviewers: this intentionally duplicates functionality already in "ApiSetup" and so on.
14 // This framework works better IMO and has less strangeness (such as test cases inheriting from "ApiSetup"...)
15 // (and in the case of the other Upload tests, this flat out just actually works... )
16
17 // TODO: port the other Upload tests, and other API tests to this framework
18
19 require_once( 'ApiTestCaseUpload.php' );
20
21 /**
22 * @group Database
23 * @group Broken
24 * Broken test, reports false errors from time to time.
25 * See https://bugzilla.wikimedia.org/26169
26 *
27 * This is pretty sucky... needs to be prettified.
28 */
29 class ApiUploadTest extends ApiTestCaseUpload {
30
31 /**
32 * Testing login
33 * XXX this is a funny way of getting session context
34 */
35 function testLogin() {
36 $user = self::$users['uploader'];
37
38 $params = array(
39 'action' => 'login',
40 'lgname' => $user->username,
41 'lgpassword' => $user->password
42 );
43 list( $result, , $session ) = $this->doApiRequest( $params );
44 $this->assertArrayHasKey( "login", $result );
45 $this->assertArrayHasKey( "result", $result['login'] );
46 $this->assertEquals( "NeedToken", $result['login']['result'] );
47 $token = $result['login']['token'];
48
49 $params = array(
50 'action' => 'login',
51 'lgtoken' => $token,
52 'lgname' => $user->username,
53 'lgpassword' => $user->password
54 );
55 list( $result, , $session ) = $this->doApiRequest( $params, $session );
56 $this->assertArrayHasKey( "login", $result );
57 $this->assertArrayHasKey( "result", $result['login'] );
58 $this->assertEquals( "Success", $result['login']['result'] );
59 $this->assertArrayHasKey( 'lgtoken', $result['login'] );
60
61 $this->assertNotEmpty( $session, 'API Login must return a session' );
62 return $session;
63
64 }
65
66 /**
67 * @depends testLogin
68 */
69 public function testUploadRequiresToken( $session ) {
70 $exception = false;
71 try {
72 $this->doApiRequest( array(
73 'action' => 'upload'
74 ) );
75 } catch ( UsageException $e ) {
76 $exception = true;
77 $this->assertEquals( "The token parameter must be set", $e->getMessage() );
78 }
79 $this->assertTrue( $exception, "Got exception" );
80 }
81
82 /**
83 * @depends testLogin
84 */
85 public function testUploadMissingParams( $session ) {
86 $exception = false;
87 try {
88 $this->doApiRequestWithToken( array(
89 'action' => 'upload',
90 ), $session, self::$users['uploader']->user );
91 } catch ( UsageException $e ) {
92 $exception = true;
93 $this->assertEquals( "One of the parameters filekey, file, url, statuskey is required",
94 $e->getMessage() );
95 }
96 $this->assertTrue( $exception, "Got exception" );
97 }
98
99
100 /**
101 * @depends testLogin
102 */
103 public function testUpload( $session ) {
104 $extension = 'png';
105 $mimeType = 'image/png';
106
107 try {
108 $randomImageGenerator = new RandomImageGenerator();
109 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
110 }
111 catch ( Exception $e ) {
112 $this->markTestIncomplete( $e->getMessage() );
113 }
114
115 $filePath = $filePaths[0];
116 $fileSize = filesize( $filePath );
117 $fileName = basename( $filePath );
118
119 $this->deleteFileByFileName( $fileName );
120 $this->deleteFileByContent( $filePath );
121
122
123 if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
124 $this->markTestIncomplete( "Couldn't upload file!\n" );
125 }
126
127 $params = array(
128 'action' => 'upload',
129 'filename' => $fileName,
130 'file' => 'dummy content',
131 'comment' => 'dummy comment',
132 'text' => "This is the page text for $fileName",
133 );
134
135 $exception = false;
136 try {
137 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
138 self::$users['uploader']->user );
139 } catch ( UsageException $e ) {
140 $exception = true;
141 }
142 $this->assertTrue( isset( $result['upload'] ) );
143 $this->assertEquals( 'Success', $result['upload']['result'] );
144 $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
145 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
146 $this->assertFalse( $exception );
147
148 // clean up
149 $this->deleteFileByFilename( $fileName );
150 unlink( $filePath );
151 }
152
153
154 /**
155 * @depends testLogin
156 */
157 public function testUploadZeroLength( $session ) {
158 $mimeType = 'image/png';
159
160 $filePath = tempnam( wfTempDir(), "" );
161 $fileName = "apiTestUploadZeroLength.png";
162
163 $this->deleteFileByFileName( $fileName );
164
165 if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
166 $this->markTestIncomplete( "Couldn't upload file!\n" );
167 }
168
169 $params = array(
170 'action' => 'upload',
171 'filename' => $fileName,
172 'file' => 'dummy content',
173 'comment' => 'dummy comment',
174 'text' => "This is the page text for $fileName",
175 );
176
177 $exception = false;
178 try {
179 $this->doApiRequestWithToken( $params, $session, self::$users['uploader']->user );
180 } catch ( UsageException $e ) {
181 $this->assertContains( 'The file you submitted was empty', $e->getMessage() );
182 $exception = true;
183 }
184 $this->assertTrue( $exception );
185
186 // clean up
187 $this->deleteFileByFilename( $fileName );
188 unlink( $filePath );
189 }
190
191
192 /**
193 * @depends testLogin
194 */
195 public function testUploadSameFileName( $session ) {
196 $extension = 'png';
197 $mimeType = 'image/png';
198
199 try {
200 $randomImageGenerator = new RandomImageGenerator();
201 $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() );
202 }
203 catch ( Exception $e ) {
204 $this->markTestIncomplete( $e->getMessage() );
205 }
206
207 // we'll reuse this filename
208 $fileName = basename( $filePaths[0] );
209
210 // clear any other files with the same name
211 $this->deleteFileByFileName( $fileName );
212
213 // we reuse these params
214 $params = array(
215 'action' => 'upload',
216 'filename' => $fileName,
217 'file' => 'dummy content',
218 'comment' => 'dummy comment',
219 'text' => "This is the page text for $fileName",
220 );
221
222 // first upload .... should succeed
223
224 if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] ) ) {
225 $this->markTestIncomplete( "Couldn't upload file!\n" );
226 }
227
228 $exception = false;
229 try {
230 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
231 self::$users['uploader']->user );
232 } catch ( UsageException $e ) {
233 $exception = true;
234 }
235 $this->assertTrue( isset( $result['upload'] ) );
236 $this->assertEquals( 'Success', $result['upload']['result'] );
237 $this->assertFalse( $exception );
238
239 // second upload with the same name (but different content)
240
241 if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] ) ) {
242 $this->markTestIncomplete( "Couldn't upload file!\n" );
243 }
244
245 $exception = false;
246 try {
247 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
248 self::$users['uploader']->user ); // FIXME: leaks a temporary file
249 } catch ( UsageException $e ) {
250 $exception = true;
251 }
252 $this->assertTrue( isset( $result['upload'] ) );
253 $this->assertEquals( 'Warning', $result['upload']['result'] );
254 $this->assertTrue( isset( $result['upload']['warnings'] ) );
255 $this->assertTrue( isset( $result['upload']['warnings']['exists'] ) );
256 $this->assertFalse( $exception );
257
258 // clean up
259 $this->deleteFileByFilename( $fileName );
260 unlink( $filePaths[0] );
261 unlink( $filePaths[1] );
262 }
263
264
265 /**
266 * @depends testLogin
267 */
268 public function testUploadSameContent( $session ) {
269 $extension = 'png';
270 $mimeType = 'image/png';
271
272 try {
273 $randomImageGenerator = new RandomImageGenerator();
274 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
275 }
276 catch ( Exception $e ) {
277 $this->markTestIncomplete( $e->getMessage() );
278 }
279
280 $fileNames[0] = basename( $filePaths[0] );
281 $fileNames[1] = "SameContentAs" . $fileNames[0];
282
283 // clear any other files with the same name or content
284 $this->deleteFileByContent( $filePaths[0] );
285 $this->deleteFileByFileName( $fileNames[0] );
286 $this->deleteFileByFileName( $fileNames[1] );
287
288 // first upload .... should succeed
289
290 $params = array(
291 'action' => 'upload',
292 'filename' => $fileNames[0],
293 'file' => 'dummy content',
294 'comment' => 'dummy comment',
295 'text' => "This is the page text for " . $fileNames[0],
296 );
297
298 if (! $this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePaths[0] ) ) {
299 $this->markTestIncomplete( "Couldn't upload file!\n" );
300 }
301
302 $exception = false;
303 try {
304 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
305 self::$users['uploader']->user );
306 } catch ( UsageException $e ) {
307 $exception = true;
308 }
309 $this->assertTrue( isset( $result['upload'] ) );
310 $this->assertEquals( 'Success', $result['upload']['result'] );
311 $this->assertFalse( $exception );
312
313
314 // second upload with the same content (but different name)
315
316 if (! $this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePaths[0] ) ) {
317 $this->markTestIncomplete( "Couldn't upload file!\n" );
318 }
319
320 $params = array(
321 'action' => 'upload',
322 'filename' => $fileNames[1],
323 'file' => 'dummy content',
324 'comment' => 'dummy comment',
325 'text' => "This is the page text for " . $fileNames[1],
326 );
327
328 $exception = false;
329 try {
330 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
331 self::$users['uploader']->user ); // FIXME: leaks a temporary file
332 } catch ( UsageException $e ) {
333 $exception = true;
334 }
335 $this->assertTrue( isset( $result['upload'] ) );
336 $this->assertEquals( 'Warning', $result['upload']['result'] );
337 $this->assertTrue( isset( $result['upload']['warnings'] ) );
338 $this->assertTrue( isset( $result['upload']['warnings']['duplicate'] ) );
339 $this->assertFalse( $exception );
340
341 // clean up
342 $this->deleteFileByFilename( $fileNames[0] );
343 $this->deleteFileByFilename( $fileNames[1] );
344 unlink( $filePaths[0] );
345 }
346
347
348 /**
349 * @depends testLogin
350 */
351 public function testUploadStash( $session ) {
352 global $wgUser;
353 $wgUser = self::$users['uploader']->user; // @todo FIXME: still used somewhere
354
355 $extension = 'png';
356 $mimeType = 'image/png';
357
358 try {
359 $randomImageGenerator = new RandomImageGenerator();
360 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
361 }
362 catch ( Exception $e ) {
363 $this->markTestIncomplete( $e->getMessage() );
364 }
365
366 $filePath = $filePaths[0];
367 $fileSize = filesize( $filePath );
368 $fileName = basename( $filePath );
369
370 $this->deleteFileByFileName( $fileName );
371 $this->deleteFileByContent( $filePath );
372
373 if (! $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
374 $this->markTestIncomplete( "Couldn't upload file!\n" );
375 }
376
377 $params = array(
378 'action' => 'upload',
379 'stash' => 1,
380 'filename' => $fileName,
381 'file' => 'dummy content',
382 'comment' => 'dummy comment',
383 'text' => "This is the page text for $fileName",
384 );
385
386 $exception = false;
387 try {
388 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
389 self::$users['uploader']->user ); // FIXME: leaks a temporary file
390 } catch ( UsageException $e ) {
391 $exception = true;
392 }
393 $this->assertFalse( $exception );
394 $this->assertTrue( isset( $result['upload'] ) );
395 $this->assertEquals( 'Success', $result['upload']['result'] );
396 $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
397 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
398 $this->assertTrue( isset( $result['upload']['filekey'] ) );
399 $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
400 $filekey = $result['upload']['filekey'];
401
402 // it should be visible from Special:UploadStash
403 // XXX ...but how to test this, with a fake WebRequest with the session?
404
405 // now we should try to release the file from stash
406 $params = array(
407 'action' => 'upload',
408 'filekey' => $filekey,
409 'filename' => $fileName,
410 'comment' => 'dummy comment',
411 'text' => "This is the page text for $fileName, altered",
412 );
413
414 $this->clearFakeUploads();
415 $exception = false;
416 try {
417 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
418 self::$users['uploader']->user );
419 } catch ( UsageException $e ) {
420 $exception = true;
421 }
422 $this->assertTrue( isset( $result['upload'] ) );
423 $this->assertEquals( 'Success', $result['upload']['result'] );
424 $this->assertFalse( $exception, "No UsageException exception." );
425
426 // clean up
427 $this->deleteFileByFilename( $fileName );
428 unlink( $filePath );
429 }
430
431
432 /**
433 * @depends testLogin
434 */
435 public function testUploadChunks( $session ) {
436 global $wgUser;
437 $wgUser = self::$users['uploader']->user; // @todo FIXME: still used somewhere
438
439 $chunkSize = 1048576;
440 // Download a large image file
441 // ( using RandomImageGenerator for large files is not stable )
442 $mimeType = 'image/jpeg';
443 $url = 'http://upload.wikimedia.org/wikipedia/commons/e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG';
444 $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg';
445 try {
446 // Only download if the file is not avaliable in the temp location:
447 if( !is_file( $filePath ) ){
448 copy($url, $filePath);
449 }
450 }
451 catch ( Exception $e ) {
452 $this->markTestIncomplete( $e->getMessage() );
453 }
454
455 $fileSize = filesize( $filePath );
456 $fileName = basename( $filePath );
457
458 $this->deleteFileByFileName( $fileName );
459 $this->deleteFileByContent( $filePath );
460
461 // Base upload params:
462 $params = array(
463 'action' => 'upload',
464 'stash' => 1,
465 'filename' => $fileName,
466 'filesize' => $fileSize,
467 'offset' => 0,
468 );
469
470 // Upload chunks
471 $chunkSessionKey = false;
472 $resultOffset = 0;
473 // Open the file:
474 $handle = @fopen ($filePath, "r");
475 if( $handle === false ){
476 $this->markTestIncomplete( "could not open file: $filePath" );
477 }
478 while (!feof ($handle)) {
479 // Get the current chunk
480 $chunkData = @fread( $handle, $chunkSize );
481
482 // Upload the current chunk into the $_FILE object:
483 $this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData );
484
485 // Check for chunkSessionKey
486 if( !$chunkSessionKey ){
487 // Upload fist chunk ( and get the session key )
488 try {
489 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
490 self::$users['uploader']->user );
491 } catch ( UsageException $e ) {
492 $this->markTestIncomplete( $e->getMessage() );
493 }
494 // Make sure we got a valid chunk continue:
495 $this->assertTrue( isset( $result['upload'] ) );
496 $this->assertTrue( isset( $result['upload']['filekey'] ) );
497 // If we don't get a session key mark test incomplete.
498 if( ! isset( $result['upload']['filekey'] ) ){
499 $this->markTestIncomplete( "no filekey provided" );
500 }
501 $chunkSessionKey = $result['upload']['filekey'];
502 $this->assertEquals( 'Continue', $result['upload']['result'] );
503 // First chunk should have chunkSize == offset
504 $this->assertEquals( $chunkSize, $result['upload']['offset'] );
505 $resultOffset = $result['upload']['offset'];
506 continue;
507 }
508 // Filekey set to chunk session
509 $params['filekey'] = $chunkSessionKey;
510 // Update the offset ( always add chunkSize for subquent chunks should be in-sync with $result['upload']['offset'] )
511 $params['offset'] += $chunkSize;
512 // Make sure param offset is insync with resultOffset:
513 $this->assertEquals( $resultOffset, $params['offset'] );
514 // Upload current chunk
515 try {
516 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
517 self::$users['uploader']->user );
518 } catch ( UsageException $e ) {
519 $this->markTestIncomplete( $e->getMessage() );
520 }
521 // Make sure we got a valid chunk continue:
522 $this->assertTrue( isset( $result['upload'] ) );
523 $this->assertTrue( isset( $result['upload']['filekey'] ) );
524
525 // Check if we were on the last chunk:
526 if( $params['offset'] + $chunkSize >= $fileSize ){
527 $this->assertEquals( 'Success', $result['upload']['result'] );
528 break;
529 } else {
530 $this->assertEquals( 'Continue', $result['upload']['result'] );
531 // update $resultOffset
532 $resultOffset = $result['upload']['offset'];
533 }
534 }
535 fclose ($handle);
536
537 // Check that we got a valid file result:
538 wfDebug( __METHOD__ . " hohoh filesize {$fileSize} info {$result['upload']['imageinfo']['size']}\n\n");
539 $this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] );
540 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
541 $this->assertTrue( isset( $result['upload']['filekey'] ) );
542 $filekey = $result['upload']['filekey'];
543
544 // Now we should try to release the file from stash
545 $params = array(
546 'action' => 'upload',
547 'filekey' => $filekey,
548 'filename' => $fileName,
549 'comment' => 'dummy comment',
550 'text' => "This is the page text for $fileName, altered",
551 );
552 $this->clearFakeUploads();
553 $exception = false;
554 try {
555 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
556 self::$users['uploader']->user );
557 } catch ( UsageException $e ) {
558 $exception = true;
559 }
560 $this->assertTrue( isset( $result['upload'] ) );
561 $this->assertEquals( 'Success', $result['upload']['result'] );
562 $this->assertFalse( $exception );
563
564 // clean up
565 $this->deleteFileByFilename( $fileName );
566 // don't remove downloaded temporary file for fast subquent tests.
567 //unlink( $filePath );
568 }
569 }