3 namespace MediaWiki\Tests\Api
;
6 use MediaWiki\MainConfigNames
;
7 use MediaWiki\Permissions\Authority
;
8 use MediaWiki\Title\Title
;
9 use MediaWiki\WikiMap\WikiMap
;
11 use Wikimedia\FileBackend\FSFileBackend
;
12 use Wikimedia\Mime\MimeAnalyzer
;
19 * @covers \MediaWiki\Api\ApiUpload
21 class ApiUploadTest
extends ApiUploadTestCase
{
22 private ?Authority
$uploader = null;
24 private function filePath( $fileName ) {
25 return __DIR__
. '/../../data/media/' . $fileName;
28 protected function setUp(): void
{
31 $this->setService( 'RepoGroup', new RepoGroup(
33 'class' => LocalRepo
::class,
35 'backend' => new FSFileBackend( [
36 'name' => 'temp-backend',
37 'wikiId' => WikiMap
::getCurrentWikiId(),
38 'basePath' => $this->getNewTempDirectory()
42 $this->getServiceContainer()->getMainWANObjectCache(),
43 $this->createMock( MimeAnalyzer
::class )
46 $this->overrideConfigValue( MainConfigNames
::WatchlistExpiry
, true );
47 $this->uploader
= $this->getTestUser()->getAuthority();
50 public function testUploadRequiresToken() {
51 $this->expectApiErrorCode( 'missingparam' );
52 $this->doApiRequest( [
57 public function testUploadMissingParams() {
58 $this->expectApiErrorCode( 'missingparam' );
59 $this->doApiRequestWithToken( [
61 ], null, $this->uploader
);
64 public function testUploadWithWatch() {
65 $mimeType = 'image/jpeg';
66 $filePath = $this->filePath( 'yuv420.jpg' );
67 $title = Title
::makeTitle( NS_FILE
, 'TestUpload.jpg' );
68 $user = $this->uploader
;
70 $this->fakeUploadFile( 'file', $title->getText(), $mimeType, $filePath );
71 [ $result ] = $this->doApiRequestWithToken( [
73 'filename' => $title->getText(),
74 'file' => 'dummy content',
75 'comment' => 'dummy comment',
76 'text' => "This is the page text for {$title->getText()}",
77 'watchlist' => 'watch',
78 'watchlistexpiry' => '99990123000000',
81 $this->assertArrayHasKey( 'upload', $result );
82 $this->assertEquals( 'Success', $result['upload']['result'] );
83 $this->assertSame( filesize( $filePath ), (int)$result['upload']['imageinfo']['size'] );
84 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
85 $this->assertTrue( $this->getServiceContainer()->getWatchlistManager()->isTempWatched( $user, $title ) );
88 public function testUploadZeroLength() {
89 $filePath = $this->getNewTempFile();
90 $mimeType = 'image/jpeg';
91 $fileName = "ApiTestUploadZeroLength.jpg";
93 $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
95 $this->expectApiErrorCode( 'empty-file' );
96 $this->doApiRequestWithToken( [
98 'filename' => $fileName,
99 'file' => 'dummy content',
100 'comment' => 'dummy comment',
101 'text' => "This is the page text for $fileName",
102 ], null, $this->uploader
);
105 public function testUploadSameFileName() {
106 $fileName = 'TestUploadSameFileName.jpg';
107 $mimeType = 'image/jpeg';
109 $this->filePath( 'yuv420.jpg' ),
110 $this->filePath( 'yuv444.jpg' )
113 // we reuse these params
115 'action' => 'upload',
116 'filename' => $fileName,
117 'file' => 'dummy content',
118 'comment' => 'dummy comment',
119 'text' => "This is the page text for $fileName",
122 // first upload .... should succeed
124 $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] );
125 [ $result ] = $this->doApiRequestWithToken( $params, null,
127 $this->assertArrayHasKey( 'upload', $result );
128 $this->assertEquals( 'Success', $result['upload']['result'] );
130 // second upload with the same name (but different content)
132 $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] );
133 [ $result ] = $this->doApiRequestWithToken( $params, null,
135 $this->assertArrayHasKey( 'upload', $result );
136 $this->assertEquals( 'Warning', $result['upload']['result'] );
137 $this->assertArrayHasKey( 'warnings', $result['upload'] );
138 $this->assertArrayHasKey( 'exists', $result['upload']['warnings'] );
141 public function testUploadSameContent() {
142 $fileNames = [ 'TestUploadSameContent_1.jpg', 'TestUploadSameContent_2.jpg' ];
143 $mimeType = 'image/jpeg';
144 $filePath = $this->filePath( 'yuv420.jpg' );
146 // first upload .... should succeed
147 $this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePath );
148 [ $result ] = $this->doApiRequestWithToken( [
149 'action' => 'upload',
150 'filename' => $fileNames[0],
151 'file' => 'dummy content',
152 'comment' => 'dummy comment',
153 'text' => "This is the page text for {$fileNames[0]}",
154 ], null, $this->uploader
);
155 $this->assertArrayHasKey( 'upload', $result );
156 $this->assertEquals( 'Success', $result['upload']['result'] );
158 // second upload with the same content (but different name)
159 $this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePath );
160 [ $result ] = $this->doApiRequestWithToken( [
161 'action' => 'upload',
162 'filename' => $fileNames[1],
163 'file' => 'dummy content',
164 'comment' => 'dummy comment',
165 'text' => "This is the page text for {$fileNames[1]}",
166 ], null, $this->uploader
);
168 $this->assertArrayHasKey( 'upload', $result );
169 $this->assertEquals( 'Warning', $result['upload']['result'] );
170 $this->assertArrayHasKey( 'warnings', $result['upload'] );
171 $this->assertArrayHasKey( 'duplicate', $result['upload']['warnings'] );
172 $this->assertArrayEquals( [ $fileNames[0] ], $result['upload']['warnings']['duplicate'] );
173 $this->assertArrayNotHasKey( 'exists', $result['upload']['warnings'] );
176 public function testUploadStash() {
177 $fileName = 'TestUploadStash.jpg';
178 $mimeType = 'image/jpeg';
179 $filePath = $this->filePath( 'yuv420.jpg' );
181 $this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath );
182 [ $result ] = $this->doApiRequestWithToken( [
183 'action' => 'upload',
185 'filename' => $fileName,
186 'file' => 'dummy content',
187 'comment' => 'dummy comment',
188 'text' => "This is the page text for $fileName",
189 ], null, $this->uploader
);
191 $this->assertArrayHasKey( 'upload', $result );
192 $this->assertEquals( 'Success', $result['upload']['result'] );
193 $this->assertSame( filesize( $filePath ), (int)$result['upload']['imageinfo']['size'] );
194 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
195 $this->assertArrayHasKey( 'filekey', $result['upload'] );
196 $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] );
197 $filekey = $result['upload']['filekey'];
199 // it should be visible from Special:UploadStash
200 // XXX ...but how to test this, with a fake WebRequest with the session?
202 // now we should try to release the file from stash
203 $this->clearFakeUploads();
204 [ $result ] = $this->doApiRequestWithToken( [
205 'action' => 'upload',
206 'filekey' => $filekey,
207 'filename' => $fileName,
208 'comment' => 'dummy comment',
209 'text' => "This is the page text for $fileName, altered",
210 ], null, $this->uploader
);
211 $this->assertArrayHasKey( 'upload', $result );
212 $this->assertEquals( 'Success', $result['upload']['result'] );
215 public function testUploadChunks() {
216 $fileName = 'TestUploadChunks.jpg';
217 $mimeType = 'image/jpeg';
218 $filePath = $this->filePath( 'yuv420.jpg' );
219 $fileSize = filesize( $filePath );
220 $chunkSize = 20 * 1024; // The file is ~60 KiB, use 20 KiB chunks
222 $this->overrideConfigValue( MainConfigNames
::MinUploadChunkSize
, $chunkSize );
224 // Base upload params:
226 'action' => 'upload',
228 'filename' => $fileName,
229 'filesize' => $fileSize,
234 $handle = fopen( $filePath, "r" );
237 while ( !feof( $handle ) ) {
238 $chunkData = fread( $handle, $chunkSize );
240 // Upload the current chunk into the $_FILE object:
241 $this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData );
243 [ $result ] = $this->doApiRequestWithToken( $params, null,
245 // Make sure we got a valid chunk continue:
246 $this->assertArrayHasKey( 'upload', $result );
247 $this->assertArrayHasKey( 'filekey', $result['upload'] );
248 $this->assertEquals( 'Continue', $result['upload']['result'] );
249 $this->assertEquals( $chunkSize, $result['upload']['offset'] );
251 $filekey = $result['upload']['filekey'];
252 $resultOffset = $result['upload']['offset'];
254 // Filekey set to chunk session
255 $params['filekey'] = $filekey;
256 // Update the offset ( always add chunkSize for subquent chunks
257 // should be in-sync with $result['upload']['offset'] )
258 $params['offset'] +
= $chunkSize;
259 // Make sure param offset is insync with resultOffset:
260 $this->assertEquals( $resultOffset, $params['offset'] );
261 // Upload current chunk
262 [ $result ] = $this->doApiRequestWithToken( $params, null,
264 // Make sure we got a valid chunk continue:
265 $this->assertArrayHasKey( 'upload', $result );
266 $this->assertArrayHasKey( 'filekey', $result['upload'] );
268 // Check if we were on the last chunk:
269 if ( $params['offset'] +
$chunkSize >= $fileSize ) {
270 $this->assertEquals( 'Success', $result['upload']['result'] );
273 $this->assertEquals( 'Continue', $result['upload']['result'] );
274 $resultOffset = $result['upload']['offset'];
280 // Check that we got a valid file result:
281 $this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] );
282 $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] );
283 $this->assertArrayHasKey( 'filekey', $result['upload'] );
284 $filekey = $result['upload']['filekey'];
286 // Now we should try to release the file from stash
287 $this->clearFakeUploads();
288 [ $result ] = $this->doApiRequestWithToken( [
289 'action' => 'upload',
290 'filekey' => $filekey,
291 'filename' => $fileName,
292 'comment' => 'dummy comment',
293 'text' => "This is the page text for $fileName, altered",
294 ], null, $this->uploader
);
295 $this->assertArrayHasKey( 'upload', $result );
296 $this->assertEquals( 'Success', $result['upload']['result'] );