Error messages are not guaranteed to be stable, and these tests prevent us from improving them. Error codes are supposed to be stable, so we should be asserting that they do not change (especially since many of them are dynamically generated by the dodgy code in ApiMessageTrait). Introduce helpers assertApiErrorCode() and expectApiErrorCode() to be used instead of the usual PHPUnit assertions/expectations for this case. Change-Id: I752f82f29bf5f9405ea117ebf9e5cf70335464ad
291 lines
10 KiB
PHP
291 lines
10 KiB
PHP
<?php
|
|
|
|
use MediaWiki\MainConfigNames;
|
|
use MediaWiki\Title\Title;
|
|
use MediaWiki\WikiMap\WikiMap;
|
|
|
|
/**
|
|
* @group API
|
|
* @group Database
|
|
* @group medium
|
|
*
|
|
* @covers ApiUpload
|
|
*/
|
|
class ApiUploadTest extends ApiUploadTestCase {
|
|
private function filePath( $fileName ) {
|
|
return __DIR__ . '/../../data/media/' . $fileName;
|
|
}
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
$this->tablesUsed[] = 'watchlist'; // This test might interfere with watchlists test.
|
|
$this->tablesUsed[] = 'watchlist_expiry';
|
|
$this->tablesUsed = array_merge( $this->tablesUsed, LocalFile::getQueryInfo()['tables'] );
|
|
$this->setService( 'RepoGroup', new RepoGroup(
|
|
[
|
|
'class' => LocalRepo::class,
|
|
'name' => 'temp',
|
|
'backend' => new FSFileBackend( [
|
|
'name' => 'temp-backend',
|
|
'wikiId' => WikiMap::getCurrentWikiId(),
|
|
'basePath' => $this->getNewTempDirectory()
|
|
] )
|
|
],
|
|
[],
|
|
$this->getServiceContainer()->getMainWANObjectCache(),
|
|
$this->createMock( MimeAnalyzer::class )
|
|
) );
|
|
|
|
$this->overrideConfigValue( MainConfigNames::WatchlistExpiry, true );
|
|
}
|
|
|
|
public function testUploadRequiresToken() {
|
|
$this->expectApiErrorCode( 'missingparam' );
|
|
$this->doApiRequest( [
|
|
'action' => 'upload'
|
|
] );
|
|
}
|
|
|
|
public function testUploadMissingParams() {
|
|
$this->expectApiErrorCode( 'missingparam' );
|
|
$this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
], null, self::$users['uploader']->getUser() );
|
|
}
|
|
|
|
public function testUploadWithWatch() {
|
|
$fileName = 'TestUpload.jpg';
|
|
$mimeType = 'image/jpeg';
|
|
$filePath = $this->filePath( 'yuv420.jpg' );
|
|
$title = Title::newFromText( $fileName, NS_FILE );
|
|
$user = self::$users['uploader']->getUser();
|
|
|
|
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filename' => $fileName,
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName",
|
|
'watchlist' => 'watch',
|
|
'watchlistexpiry' => '99990123000000',
|
|
], null, $user );
|
|
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
$this->assertSame( filesize( $filePath ), (int)$result['upload']['imageinfo']['size'] );
|
|
$this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
|
|
$this->assertTrue( $this->getServiceContainer()->getWatchlistManager()->isTempWatched( $user, $title ) );
|
|
}
|
|
|
|
public function testUploadZeroLength() {
|
|
$filePath = $this->getNewTempFile();
|
|
$mimeType = 'image/jpeg';
|
|
$fileName = "ApiTestUploadZeroLength.jpg";
|
|
|
|
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
|
|
|
|
$this->expectApiErrorCode( 'empty-file' );
|
|
$this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filename' => $fileName,
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName",
|
|
], null, self::$users['uploader']->getUser() );
|
|
}
|
|
|
|
public function testUploadSameFileName() {
|
|
$fileName = 'TestUploadSameFileName.jpg';
|
|
$mimeType = 'image/jpeg';
|
|
$filePaths = [
|
|
$this->filePath( 'yuv420.jpg' ),
|
|
$this->filePath( 'yuv444.jpg' )
|
|
];
|
|
|
|
// we reuse these params
|
|
$params = [
|
|
'action' => 'upload',
|
|
'filename' => $fileName,
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName",
|
|
];
|
|
|
|
// first upload .... should succeed
|
|
|
|
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] );
|
|
[ $result ] = $this->doApiRequestWithToken( $params, null,
|
|
self::$users['uploader']->getUser() );
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
|
|
// second upload with the same name (but different content)
|
|
|
|
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] );
|
|
[ $result ] = $this->doApiRequestWithToken( $params, null,
|
|
self::$users['uploader']->getUser() );
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Warning', $result['upload']['result'] );
|
|
$this->assertArrayHasKey( 'warnings', $result['upload'] );
|
|
$this->assertArrayHasKey( 'exists', $result['upload']['warnings'] );
|
|
}
|
|
|
|
public function testUploadSameContent() {
|
|
$fileNames = [ 'TestUploadSameContent_1.jpg', 'TestUploadSameContent_2.jpg' ];
|
|
$mimeType = 'image/jpeg';
|
|
$filePath = $this->filePath( 'yuv420.jpg' );
|
|
|
|
// first upload .... should succeed
|
|
$this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePath );
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filename' => $fileNames[0],
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for {$fileNames[0]}",
|
|
], null, self::$users['uploader']->getUser() );
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
|
|
// second upload with the same content (but different name)
|
|
$this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePath );
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filename' => $fileNames[1],
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for {$fileNames[1]}",
|
|
], null, self::$users['uploader']->getUser() );
|
|
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Warning', $result['upload']['result'] );
|
|
$this->assertArrayHasKey( 'warnings', $result['upload'] );
|
|
$this->assertArrayHasKey( 'duplicate', $result['upload']['warnings'] );
|
|
$this->assertArrayEquals( [ $fileNames[0] ], $result['upload']['warnings']['duplicate'] );
|
|
$this->assertArrayNotHasKey( 'exists', $result['upload']['warnings'] );
|
|
}
|
|
|
|
public function testUploadStash() {
|
|
$fileName = 'TestUploadStash.jpg';
|
|
$mimeType = 'image/jpeg';
|
|
$filePath = $this->filePath( 'yuv420.jpg' );
|
|
|
|
$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'stash' => 1,
|
|
'filename' => $fileName,
|
|
'file' => 'dummy content',
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName",
|
|
], null, self::$users['uploader']->getUser() );
|
|
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
$this->assertSame( filesize( $filePath ), (int)$result['upload']['imageinfo']['size'] );
|
|
$this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
|
|
$this->assertArrayHasKey( 'filekey', $result['upload'] );
|
|
$this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
|
|
$filekey = $result['upload']['filekey'];
|
|
|
|
// it should be visible from Special:UploadStash
|
|
// XXX ...but how to test this, with a fake WebRequest with the session?
|
|
|
|
// now we should try to release the file from stash
|
|
$this->clearFakeUploads();
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filekey' => $filekey,
|
|
'filename' => $fileName,
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName, altered",
|
|
], null, self::$users['uploader']->getUser() );
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
}
|
|
|
|
public function testUploadChunks() {
|
|
$fileName = 'TestUploadChunks.jpg';
|
|
$mimeType = 'image/jpeg';
|
|
$filePath = $this->filePath( 'yuv420.jpg' );
|
|
$fileSize = filesize( $filePath );
|
|
$chunkSize = 20 * 1024; // The file is ~60 KiB, use 20 KiB chunks
|
|
|
|
$this->overrideConfigValue( MainConfigNames::MinUploadChunkSize, $chunkSize );
|
|
|
|
// Base upload params:
|
|
$params = [
|
|
'action' => 'upload',
|
|
'stash' => 1,
|
|
'filename' => $fileName,
|
|
'filesize' => $fileSize,
|
|
'offset' => 0,
|
|
];
|
|
|
|
// Upload chunks
|
|
$handle = fopen( $filePath, "r" );
|
|
$resultOffset = 0;
|
|
$filekey = false;
|
|
while ( !feof( $handle ) ) {
|
|
$chunkData = fread( $handle, $chunkSize );
|
|
|
|
// Upload the current chunk into the $_FILE object:
|
|
$this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData );
|
|
if ( !$filekey ) {
|
|
[ $result ] = $this->doApiRequestWithToken( $params, null,
|
|
self::$users['uploader']->getUser() );
|
|
// Make sure we got a valid chunk continue:
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertArrayHasKey( 'filekey', $result['upload'] );
|
|
$this->assertEquals( 'Continue', $result['upload']['result'] );
|
|
$this->assertEquals( $chunkSize, $result['upload']['offset'] );
|
|
|
|
$filekey = $result['upload']['filekey'];
|
|
$resultOffset = $result['upload']['offset'];
|
|
} else {
|
|
// Filekey set to chunk session
|
|
$params['filekey'] = $filekey;
|
|
// Update the offset ( always add chunkSize for subquent chunks
|
|
// should be in-sync with $result['upload']['offset'] )
|
|
$params['offset'] += $chunkSize;
|
|
// Make sure param offset is insync with resultOffset:
|
|
$this->assertEquals( $resultOffset, $params['offset'] );
|
|
// Upload current chunk
|
|
[ $result ] = $this->doApiRequestWithToken( $params, null,
|
|
self::$users['uploader']->getUser() );
|
|
// Make sure we got a valid chunk continue:
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertArrayHasKey( 'filekey', $result['upload'] );
|
|
|
|
// Check if we were on the last chunk:
|
|
if ( $params['offset'] + $chunkSize >= $fileSize ) {
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
break;
|
|
} else {
|
|
$this->assertEquals( 'Continue', $result['upload']['result'] );
|
|
$resultOffset = $result['upload']['offset'];
|
|
}
|
|
}
|
|
}
|
|
fclose( $handle );
|
|
|
|
// Check that we got a valid file result:
|
|
$this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] );
|
|
$this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
|
|
$this->assertArrayHasKey( 'filekey', $result['upload'] );
|
|
$filekey = $result['upload']['filekey'];
|
|
|
|
// Now we should try to release the file from stash
|
|
$this->clearFakeUploads();
|
|
[ $result ] = $this->doApiRequestWithToken( [
|
|
'action' => 'upload',
|
|
'filekey' => $filekey,
|
|
'filename' => $fileName,
|
|
'comment' => 'dummy comment',
|
|
'text' => "This is the page text for $fileName, altered",
|
|
], null, self::$users['uploader']->getUser() );
|
|
$this->assertArrayHasKey( 'upload', $result );
|
|
$this->assertEquals( 'Success', $result['upload']['result'] );
|
|
}
|
|
}
|