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