Merge "Fix ORMRow::insert() on PostgreSQL."
[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 } catch ( Exception $e ) {
111 $this->markTestIncomplete( $e->getMessage() );
112 }
113
114 $filePath = $filePaths[0];
115 $fileSize = filesize( $filePath );
116 $fileName = basename( $filePath );
117
118 $this->deleteFileByFileName( $fileName );
119 $this->deleteFileByContent( $filePath );
120
121
122 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
123 $this->markTestIncomplete( "Couldn't upload file!\n" );
124 }
125
126 $params = array(
127 'action' => 'upload',
128 'filename' => $fileName,
129 'file' => 'dummy content',
130 'comment' => 'dummy comment',
131 'text' => "This is the page text for $fileName",
132 );
133
134 $exception = false;
135 try {
136 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
137 self::$users['uploader']->user );
138 } catch ( UsageException $e ) {
139 $exception = true;
140 }
141 $this->assertTrue( isset( $result['upload'] ) );
142 $this->assertEquals( 'Success', $result['upload']['result'] );
143 $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
144 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
145 $this->assertFalse( $exception );
146
147 // clean up
148 $this->deleteFileByFilename( $fileName );
149 unlink( $filePath );
150 }
151
152
153 /**
154 * @depends testLogin
155 */
156 public function testUploadZeroLength( $session ) {
157 $mimeType = 'image/png';
158
159 $filePath = tempnam( wfTempDir(), "" );
160 $fileName = "apiTestUploadZeroLength.png";
161
162 $this->deleteFileByFileName( $fileName );
163
164 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
165 $this->markTestIncomplete( "Couldn't upload file!\n" );
166 }
167
168 $params = array(
169 'action' => 'upload',
170 'filename' => $fileName,
171 'file' => 'dummy content',
172 'comment' => 'dummy comment',
173 'text' => "This is the page text for $fileName",
174 );
175
176 $exception = false;
177 try {
178 $this->doApiRequestWithToken( $params, $session, self::$users['uploader']->user );
179 } catch ( UsageException $e ) {
180 $this->assertContains( 'The file you submitted was empty', $e->getMessage() );
181 $exception = true;
182 }
183 $this->assertTrue( $exception );
184
185 // clean up
186 $this->deleteFileByFilename( $fileName );
187 unlink( $filePath );
188 }
189
190
191 /**
192 * @depends testLogin
193 */
194 public function testUploadSameFileName( $session ) {
195 $extension = 'png';
196 $mimeType = 'image/png';
197
198 try {
199 $randomImageGenerator = new RandomImageGenerator();
200 $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() );
201 } catch ( Exception $e ) {
202 $this->markTestIncomplete( $e->getMessage() );
203 }
204
205 // we'll reuse this filename
206 $fileName = basename( $filePaths[0] );
207
208 // clear any other files with the same name
209 $this->deleteFileByFileName( $fileName );
210
211 // we reuse these params
212 $params = array(
213 'action' => 'upload',
214 'filename' => $fileName,
215 'file' => 'dummy content',
216 'comment' => 'dummy comment',
217 'text' => "This is the page text for $fileName",
218 );
219
220 // first upload .... should succeed
221
222 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] ) ) {
223 $this->markTestIncomplete( "Couldn't upload file!\n" );
224 }
225
226 $exception = false;
227 try {
228 list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session,
229 self::$users['uploader']->user );
230 } catch ( UsageException $e ) {
231 $exception = true;
232 }
233 $this->assertTrue( isset( $result['upload'] ) );
234 $this->assertEquals( 'Success', $result['upload']['result'] );
235 $this->assertFalse( $exception );
236
237 // second upload with the same name (but different content)
238
239 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] ) ) {
240 $this->markTestIncomplete( "Couldn't upload file!\n" );
241 }
242
243 $exception = false;
244 try {
245 list( $result, , ) = $this->doApiRequestWithToken( $params, $session,
246 self::$users['uploader']->user ); // FIXME: leaks a temporary file
247 } catch ( UsageException $e ) {
248 $exception = true;
249 }
250 $this->assertTrue( isset( $result['upload'] ) );
251 $this->assertEquals( 'Warning', $result['upload']['result'] );
252 $this->assertTrue( isset( $result['upload']['warnings'] ) );
253 $this->assertTrue( isset( $result['upload']['warnings']['exists'] ) );
254 $this->assertFalse( $exception );
255
256 // clean up
257 $this->deleteFileByFilename( $fileName );
258 unlink( $filePaths[0] );
259 unlink( $filePaths[1] );
260 }
261
262
263 /**
264 * @depends testLogin
265 */
266 public function testUploadSameContent( $session ) {
267 $extension = 'png';
268 $mimeType = 'image/png';
269
270 try {
271 $randomImageGenerator = new RandomImageGenerator();
272 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
273 } catch ( Exception $e ) {
274 $this->markTestIncomplete( $e->getMessage() );
275 }
276
277 $fileNames[0] = basename( $filePaths[0] );
278 $fileNames[1] = "SameContentAs" . $fileNames[0];
279
280 // clear any other files with the same name or content
281 $this->deleteFileByContent( $filePaths[0] );
282 $this->deleteFileByFileName( $fileNames[0] );
283 $this->deleteFileByFileName( $fileNames[1] );
284
285 // first upload .... should succeed
286
287 $params = array(
288 'action' => 'upload',
289 'filename' => $fileNames[0],
290 'file' => 'dummy content',
291 'comment' => 'dummy comment',
292 'text' => "This is the page text for " . $fileNames[0],
293 );
294
295 if ( !$this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePaths[0] ) ) {
296 $this->markTestIncomplete( "Couldn't upload file!\n" );
297 }
298
299 $exception = false;
300 try {
301 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
302 self::$users['uploader']->user );
303 } catch ( UsageException $e ) {
304 $exception = true;
305 }
306 $this->assertTrue( isset( $result['upload'] ) );
307 $this->assertEquals( 'Success', $result['upload']['result'] );
308 $this->assertFalse( $exception );
309
310
311 // second upload with the same content (but different name)
312
313 if ( !$this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePaths[0] ) ) {
314 $this->markTestIncomplete( "Couldn't upload file!\n" );
315 }
316
317 $params = array(
318 'action' => 'upload',
319 'filename' => $fileNames[1],
320 'file' => 'dummy content',
321 'comment' => 'dummy comment',
322 'text' => "This is the page text for " . $fileNames[1],
323 );
324
325 $exception = false;
326 try {
327 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
328 self::$users['uploader']->user ); // FIXME: leaks a temporary file
329 } catch ( UsageException $e ) {
330 $exception = true;
331 }
332 $this->assertTrue( isset( $result['upload'] ) );
333 $this->assertEquals( 'Warning', $result['upload']['result'] );
334 $this->assertTrue( isset( $result['upload']['warnings'] ) );
335 $this->assertTrue( isset( $result['upload']['warnings']['duplicate'] ) );
336 $this->assertFalse( $exception );
337
338 // clean up
339 $this->deleteFileByFilename( $fileNames[0] );
340 $this->deleteFileByFilename( $fileNames[1] );
341 unlink( $filePaths[0] );
342 }
343
344
345 /**
346 * @depends testLogin
347 */
348 public function testUploadStash( $session ) {
349 $this->setMwGlobals( array(
350 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere
351 ) );
352
353 $extension = 'png';
354 $mimeType = 'image/png';
355
356 try {
357 $randomImageGenerator = new RandomImageGenerator();
358 $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() );
359 } catch ( Exception $e ) {
360 $this->markTestIncomplete( $e->getMessage() );
361 }
362
363 $filePath = $filePaths[0];
364 $fileSize = filesize( $filePath );
365 $fileName = basename( $filePath );
366
367 $this->deleteFileByFileName( $fileName );
368 $this->deleteFileByContent( $filePath );
369
370 if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) {
371 $this->markTestIncomplete( "Couldn't upload file!\n" );
372 }
373
374 $params = array(
375 'action' => 'upload',
376 'stash' => 1,
377 'filename' => $fileName,
378 'file' => 'dummy content',
379 'comment' => 'dummy comment',
380 'text' => "This is the page text for $fileName",
381 );
382
383 $exception = false;
384 try {
385 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
386 self::$users['uploader']->user ); // FIXME: leaks a temporary file
387 } catch ( UsageException $e ) {
388 $exception = true;
389 }
390 $this->assertFalse( $exception );
391 $this->assertTrue( isset( $result['upload'] ) );
392 $this->assertEquals( 'Success', $result['upload']['result'] );
393 $this->assertEquals( $fileSize, ( int )$result['upload']['imageinfo']['size'] );
394 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
395 $this->assertTrue( isset( $result['upload']['filekey'] ) );
396 $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
397 $filekey = $result['upload']['filekey'];
398
399 // it should be visible from Special:UploadStash
400 // XXX ...but how to test this, with a fake WebRequest with the session?
401
402 // now we should try to release the file from stash
403 $params = array(
404 'action' => 'upload',
405 'filekey' => $filekey,
406 'filename' => $fileName,
407 'comment' => 'dummy comment',
408 'text' => "This is the page text for $fileName, altered",
409 );
410
411 $this->clearFakeUploads();
412 $exception = false;
413 try {
414 list( $result, $request, $session ) = $this->doApiRequestWithToken( $params, $session,
415 self::$users['uploader']->user );
416 } catch ( UsageException $e ) {
417 $exception = true;
418 }
419 $this->assertTrue( isset( $result['upload'] ) );
420 $this->assertEquals( 'Success', $result['upload']['result'] );
421 $this->assertFalse( $exception, "No UsageException exception." );
422
423 // clean up
424 $this->deleteFileByFilename( $fileName );
425 unlink( $filePath );
426 }
427
428 /**
429 * @depends testLogin
430 */
431 public function testUploadChunks( $session ) {
432 $this->setMwGlobals( array(
433 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere
434 ) );
435
436 $chunkSize = 1048576;
437 // Download a large image file
438 // ( using RandomImageGenerator for large files is not stable )
439 $mimeType = 'image/jpeg';
440 $url = 'http://upload.wikimedia.org/wikipedia/commons/e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG';
441 $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg';
442 try {
443 // Only download if the file is not avaliable in the temp location:
444 if ( !is_file( $filePath ) ) {
445 copy( $url, $filePath );
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 }