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