gallery: Fix phan annotation for ImageGalleryBase::getImages
[mediawiki.git] / tests / phpunit / includes / upload / UploadFromUrlTest.php
blob7710053bea4d7a2ef2ebdc1653a419546e1ac831
1 <?php
3 use MediaWiki\Api\ApiUsageException;
4 use MediaWiki\MainConfigNames;
5 use MediaWiki\Page\File\FileDeleteForm;
6 use MediaWiki\Tests\Api\ApiTestCase;
7 use MediaWiki\Title\Title;
8 use MediaWiki\User\User;
9 use Wikimedia\TestingAccessWrapper;
11 /**
12 * @group large
13 * @group Upload
14 * @group Database
16 * @covers \UploadFromUrl
18 class UploadFromUrlTest extends ApiTestCase {
19 use MockHttpTrait;
21 /** @var User */
22 private $user;
24 protected function setUp(): void {
25 parent::setUp();
26 $this->user = $this->getTestSysop()->getUser();
28 $this->overrideConfigValues( [
29 MainConfigNames::EnableUploads => true,
30 MainConfigNames::AllowCopyUploads => true,
31 ] );
32 $this->setGroupPermissions( 'sysop', 'upload_by_url', true );
34 if ( $this->getServiceContainer()->getRepoGroup()->getLocalRepo()
35 ->newFile( 'UploadFromUrlTest.png' )->exists()
36 ) {
37 $this->deleteFile( 'UploadFromUrlTest.png' );
40 $this->installMockHttp();
43 /**
44 * Ensure that the job queue is empty before continuing
46 public function testClearQueue() {
47 $jobQueueGroup = $this->getServiceContainer()->getJobQueueGroup();
48 $job = $jobQueueGroup->pop();
49 while ( $job ) {
50 $job = $jobQueueGroup->pop();
52 $this->assertFalse( $job );
55 public function testIsAllowedHostEmpty() {
56 $this->overrideConfigValues( [
57 MainConfigNames::CopyUploadsDomains => [],
58 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => false,
59 ] );
61 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://foo.bar' ) );
64 public function testIsAllowedHostDirectMatch() {
65 $this->overrideConfigValues( [
66 MainConfigNames::CopyUploadsDomains => [
67 'foo.baz',
68 'bar.example.baz',
70 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => false,
71 ] );
73 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://example.com' ) );
75 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://foo.baz' ) );
76 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://.foo.baz' ) );
78 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://example.baz' ) );
79 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://bar.example.baz' ) );
82 public function testIsAllowedHostLastWildcard() {
83 $this->overrideConfigValues( [
84 MainConfigNames::CopyUploadsDomains => [
85 '*.baz',
87 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => false,
88 ] );
90 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://baz' ) );
91 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.example' ) );
92 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.example.baz' ) );
93 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo/bar.baz' ) );
95 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://foo.baz' ) );
96 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://subdomain.foo.baz' ) );
99 public function testIsAllowedHostWildcardInMiddle() {
100 $this->overrideConfigValues( [
101 MainConfigNames::CopyUploadsDomains => [
102 'foo.*.baz',
104 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => false,
105 ] );
107 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.baz' ) );
108 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.bar.bar.baz' ) );
109 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.bar.baz.baz' ) );
110 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.com/.baz' ) );
112 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://foo.example.baz' ) );
113 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://foo.bar.baz' ) );
116 public function testOnWikiDomainConfigEnabled() {
117 $this->overrideConfigValues( [
118 MainConfigNames::CopyUploadsDomains => [ 'example.com' ],
119 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => true,
120 ] );
122 $messageContent = "example.org # this is a comment\n# this too is commented foo.example.com\nexample.net";
123 $mock = $this->createMock( MessageCache::class );
124 $mock->method( 'get' )->willReturn( $messageContent );
125 $this->setService( 'MessageCache', $mock );
127 $this->assertEquals(
128 [ 'example.com', 'example.org', 'example.net' ],
129 TestingAccessWrapper::newFromClass( UploadFromUrl::class )->getAllowedHosts()
132 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://example.com' ) );
133 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://example.org' ) );
134 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://foo.example.com' ) );
137 public function testOnWikiDomainConfigDisabled() {
138 $this->overrideConfigValues( [
139 MainConfigNames::CopyUploadsDomains => [ 'example.com' ],
140 MainConfigNames::CopyUploadAllowOnWikiDomainConfig => false,
141 ] );
143 $mock = $this->createMock( MessageCache::class );
144 $mock->expects( $this->never() )->method( 'get' );
145 $this->setService( 'MessageCache', $mock );
147 $this->assertEquals(
148 [ 'example.com' ],
149 TestingAccessWrapper::newFromClass( UploadFromUrl::class )->getAllowedHosts()
152 $this->assertTrue( UploadFromUrl::isAllowedHost( 'https://example.com' ) );
153 $this->assertFalse( UploadFromUrl::isAllowedHost( 'https://example.org' ) );
157 * @depends testClearQueue
159 public function testSetupUrlDownload( $data ) {
160 $token = $this->user->getEditToken();
161 $exception = false;
163 try {
164 $this->doApiRequest( [
165 'action' => 'upload',
166 ] );
167 } catch ( ApiUsageException $e ) {
168 $exception = true;
169 $this->assertApiErrorCode( 'missingparam', $e );
171 $this->assertTrue( $exception, "Got exception" );
173 $exception = false;
174 try {
175 $this->doApiRequest( [
176 'action' => 'upload',
177 'token' => $token,
178 ], $data );
179 } catch ( ApiUsageException $e ) {
180 $exception = true;
181 $this->assertApiErrorCode( 'missingparam', $e );
183 $this->assertTrue( $exception, "Got exception" );
185 $exception = false;
186 try {
187 $this->doApiRequest( [
188 'action' => 'upload',
189 'url' => 'http://www.example.com/test.png',
190 'token' => $token,
191 ], $data );
192 } catch ( ApiUsageException $e ) {
193 $exception = true;
194 $this->assertApiErrorCode( 'nofilename', $e );
196 $this->assertTrue( $exception, "Got exception" );
198 $this->getServiceContainer()->getUserGroupManager()->removeUserFromGroup( $this->user, 'sysop' );
199 $exception = false;
200 try {
201 $this->doApiRequest( [
202 'action' => 'upload',
203 'url' => 'http://www.example.com/test.png',
204 'filename' => 'UploadFromUrlTest.png',
205 'token' => $token,
206 ], $data );
207 } catch ( ApiUsageException $e ) {
208 $this->assertApiErrorCode( 'permissiondenied', $e );
209 $exception = true;
211 $this->assertTrue( $exception, "Got exception" );
214 private function assertUploadOk( UploadBase $upload ) {
215 $verificationResult = $upload->verifyUpload();
217 if ( $verificationResult['status'] !== UploadBase::OK ) {
218 $this->fail(
219 'Upload verification returned ' . $upload->getVerificationErrorCode(
220 $verificationResult['status']
227 * @depends testClearQueue
229 public function testSyncDownload( $data ) {
230 $file = __DIR__ . '/../../data/upload/png-plain.png';
231 $this->installMockHttp( file_get_contents( $file ) );
233 $this->getServiceContainer()->getUserGroupManager()->addUserToGroup( $this->user, 'users' );
234 $data = $this->doApiRequestWithToken( [
235 'action' => 'upload',
236 'filename' => 'UploadFromUrlTest.png',
237 'url' => 'http://upload.wikimedia.org/wikipedia/mediawiki/b/bc/Wiki.png',
238 'ignorewarnings' => true,
239 ], $data );
241 $this->assertEquals( 'Success', $data[0]['upload']['result'] );
242 $this->deleteFile( 'UploadFromUrlTest.png' );
244 return $data;
247 protected function deleteFile( $name ) {
248 $t = Title::newFromText( $name, NS_FILE );
249 $this->assertTrue( $t->exists(), "File '$name' exists" );
251 if ( $t->exists() ) {
252 $file = $this->getServiceContainer()->getRepoGroup()
253 ->findFile( $name, [ 'ignoreRedirect' => true ] );
254 $empty = "";
255 FileDeleteForm::doDelete( $t, $file, $empty, "none", true, $this->user );
256 $page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $t );
257 $this->deletePage( $page );
259 $t = Title::newFromText( $name, NS_FILE );
261 $this->assertFalse( $t->exists(), "File '$name' was deleted" );
264 public function testUploadFromUrl() {
265 $file = __DIR__ . '/../../data/upload/png-plain.png';
266 $this->installMockHttp( file_get_contents( $file ) );
268 $upload = new UploadFromUrl();
269 $upload->initialize( 'Test.png', 'http://www.example.com/test.png' );
270 $status = $upload->fetchFile();
272 $this->assertStatusOK( $status );
273 $this->assertUploadOk( $upload );
276 public function testUploadFromUrlWithRedirect() {
277 $file = __DIR__ . '/../../data/upload/png-plain.png';
278 $this->installMockHttp( [
279 // First response is a redirect
280 $this->makeFakeHttpRequest(
281 'Blaba',
282 302,
283 [ 'Location' => 'http://static.example.com/files/test.png' ]
285 // Second response is a file
286 $this->makeFakeHttpRequest(
287 file_get_contents( $file )
289 ] );
291 $upload = new UploadFromUrl();
292 $upload->initialize( 'Test.png', 'http://www.example.com/test.png' );
293 $status = $upload->fetchFile();
295 $this->assertStatusOK( $status );
296 $this->assertUploadOk( $upload );
299 public function testUploadFromUrlCacheKey() {
300 // Test we get back a properly formatted sha1 key out
301 $key = UploadFromUrl::getCacheKey( [ 'filename' => 'test.png', 'url' => 'https://example.com/example.png' ] );
302 $this->assertNotEmpty( $key );
303 $this->assertMatchesRegularExpression( "/^[0-9a-f]{40}$/", $key );
306 public function testUploadFromUrlCacheKeyMissingParam() {
307 $this->assertSame( "", UploadFromUrl::getCacheKey( [] ) );