Completed
Push — develop ( a19d95...ea9703 )
by Schlaefer
07:57
created

testIndexFailureUploadBelongsToDifferentUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace ImageUploader\Test\TestCase\Controller;
14
15
use Api\Error\Exception\GenericApiException;
16
use Authentication\Authenticator\UnauthenticatedException;
17
use Cake\Core\Configure;
18
use Cake\Core\Plugin;
19
use Cake\Datasource\Exception\RecordNotFoundException;
20
use Cake\Filesystem\File;
21
use Cake\ORM\TableRegistry;
22
use ImageUploader\Model\Table\UploadsTable;
23
use Saito\Exception\SaitoForbiddenException;
24
use Saito\Test\IntegrationTestCase;
25
26
class UploadsControllerTest extends IntegrationTestCase
27
{
28
    public $fixtures = [
29
        'app.Category',
30
        'app.Entry',
31
        'app.Setting',
32
        'app.User',
33
        'app.UserBlock',
34
        'app.UserIgnore',
35
        'app.UserRead',
36
        'app.UserOnline',
37
        'plugin.ImageUploader.Uploads',
38
    ];
39
40
    /**
41
     * @var File dummy file
42
     */
43
    private $file;
44
45
    public function setUp()
46
    {
47
        parent::setUp();
48
49
        $this->file = new File(TMP . 'my new-upload.png');
50
        $this->mockMediaFile($this->file);
51
    }
52
53
    public function tearDown()
54
    {
55
        $this->file->delete();
56
        unset($this->file);
57
58
        parent::tearDown();
59
    }
60
61
    public function testAddNotAuthorized()
62
    {
63
        $this->expectException(UnauthenticatedException::class);
64
65
        $this->post('api/v2/uploads', []);
66
    }
67
68
    public function testAddFailureUploadBelongsToDifferentUser()
69
    {
70
        $this->loginJwt(3);
71
72
        $this->expectException(SaitoForbiddenException::class);
73
74
        $this->upload($this->file, 1);
75
    }
76
77
    public function testAddFailureUserDoesNotExist()
78
    {
79
        $this->loginJwt(1);
80
81
        $this->expectException(RecordNotFoundException::class);
82
83
        $this->upload($this->file, 9999);
84
    }
85
86
    /**
87
     * png is successfully uploaded and converted to jpeg
88
     */
89
    public function testAddSuccess()
90
    {
91
        $this->loginJwt(1);
92
93
        $this->upload($this->file, 1);
94
        $response = json_decode((string)$this->_response->getBody(), true);
0 ignored issues
show
Bug introduced by
The method getBody() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
        $response = json_decode((string)$this->_response->/** @scrutinizer ignore-call */ getBody(), true);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
95
96
        $this->assertResponseCode(200);
97
98
        $this->assertWithinRange(
99
            time(),
100
            strtotime($response['data']['attributes']['created']),
101
            3
102
        );
103
        unset($response['data']['attributes']['created']);
104
105
        $this->assertGreaterThan(0, $response['data']['attributes']['size']);
106
        unset($response['data']['attributes']['size']);
107
108
        $expected = [
109
            'data' => [
110
                'id' => 3,
111
                'type' => 'uploads',
112
                'attributes' => [
113
                    'id' => 3,
114
                    'mime' => 'image/jpeg',
115
                    'name' => '1_ebd536d37aff03f2b570329b20ece832.jpg',
116
                    'thumbnail_url' => '/api/v2/uploads/thumb/3?h=e1fddb2ea8f448fac14ec06b88d4ce94',
117
                    'title' => 'my new-upload.png',
118
                    'url' => '/useruploads/1_ebd536d37aff03f2b570329b20ece832.jpg',
119
                ],
120
            ],
121
        ];
122
        $this->assertEquals($expected, $response);
123
124
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
125
        $upload = $Uploads->get(3);
126
127
        $this->assertSame('1_ebd536d37aff03f2b570329b20ece832.jpg', $upload->get('name'));
128
        $this->assertSame('image/jpeg', $upload->get('type'));
129
        $this->assertTrue($upload->get('file')->exists());
130
    }
131
132
    public function testAddSvg()
133
    {
134
        $this->loginJwt(1);
135
136
        $this->file = new File(TMP . 'tmp_svg.svg');
137
        $this->file->write('<?xml version="1.0" encoding="UTF-8" ?>
138
            <svg width="9" height="9" style="background:red;"></svg>');
139
        $this->upload($this->file);
140
141
        $response = json_decode((string)$this->_response->getBody(), true);
142
143
        $this->assertResponseCode(200);
144
145
        $this->assertWithinRange(
146
            time(),
147
            strtotime($response['data']['attributes']['created']),
148
            3
149
        );
150
        unset($response['data']['attributes']['created']);
151
152
        $expected = [
153
            'data' => [
154
                'id' => 3,
155
                'type' => 'uploads',
156
                'attributes' => [
157
                    'id' => 3,
158
                    'mime' => 'image/svg+xml',
159
                    'name' => '1_853fe7aa4ef213b0c11f4b739cf444a8.svg',
160
                    'size' => 108,
161
                    'thumbnail_url' => '/api/v2/uploads/thumb/3?h=1d57b148ad44d4caf90fa1cd98729678',
162
                    'title' => 'tmp_svg.svg',
163
                    'url' => '/useruploads/1_853fe7aa4ef213b0c11f4b739cf444a8.svg',
164
                ],
165
            ],
166
        ];
167
        $this->assertEquals($expected, $response);
168
169
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
170
        $upload = $Uploads->get(3);
171
172
        $this->assertSame('1_853fe7aa4ef213b0c11f4b739cf444a8.svg', $upload->get('name'));
173
        $this->assertSame('image/svg+xml', $upload->get('type'));
174
        $this->assertTrue($upload->get('file')->exists());
175
    }
176
177
    public function testAddMimeTypeConversion()
178
    {
179
        $this->loginJwt(1);
180
181
        $this->file = new File(TMP . 'test.mp4');
182
        $fixture = new File(Plugin::path('ImageUploader') . 'tests/Fixture/test-application-octo.mp4');
183
        $fixture->copy($this->file->path);
184
        $this->assertEquals('application/octet-stream', $this->file->mime());
185
186
        $this->upload($this->file);
187
188
        $this->assertResponseOk();
189
190
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
191
        $upload = $Uploads->get(3);
192
        $this->assertSame('test.mp4', $upload->get('title'));
193
        $this->assertSame('video/mp4', $upload->get('type'));
194
    }
195
196
    public function testRemoveExifData()
197
    {
198
        $this->loginJwt(1);
199
        unset($this->file);
200
        $this->file = new File(TMP . 'tmp_exif.jpg');
201
202
        $fixture = new File($path = Plugin::path('ImageUploader') . 'tests/Fixture/exif-with-location.jpg');
203
        $fixture->copy($this->file->path);
204
205
        $readExif = function (File $file) {
206
            //@codingStandardsIgnoreStart
207
            return @exif_read_data($file->path);
208
            //@codingStandardsIgnoreEnd
209
        };
210
        $exif = $readExif($this->file);
211
        $this->assertNotEmpty($exif['SectionsFound']);
212
        $this->assertContains('EXIF', $exif['SectionsFound']);
213
        $this->assertContains('IFD0', $exif['SectionsFound']);
214
215
        $this->upload($this->file);
216
217
        $response = json_decode((string)$this->_response->getBody(), true);
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
218
219
        $this->assertResponseCode(200);
220
221
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
222
        $upload = $Uploads->find('all')->last();
223
224
        $exif = $readExif($upload->get('file'));
225
        $this->assertNotContains('EXIF', $exif['SectionsFound']);
226
        $this->assertNotContains('IFD0', $exif['SectionsFound']);
227
    }
228
229
    public function testAddFailureMaxUploadsPerUser()
230
    {
231
        Configure::read('Saito.Settings.uploader')->setMaxNumberOfUploadsPerUser(1);
232
        $this->loginJwt(1);
233
234
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
235
        $count = $Uploads->find()->count();
236
237
        $this->expectException(GenericApiException::class);
238
        $this->expectExceptionMessage('Error: Reached the maximal number of 1 uploads.');
239
240
        $this->upload($this->file);
241
242
        $this->assertEquals($count, $Uploads->find()->count());
243
    }
244
245
    public function testAddFailureMaxDocumentSize()
246
    {
247
        Configure::read('Saito.Settings.uploader')
248
            ->setMaxNumberOfUploadsPerUser(10)
249
            ->addType('image/png', 10);
250
251
        $this->loginJwt(1);
252
253
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
254
        $count = $Uploads->find()->count();
255
256
        $this->expectException(GenericApiException::class);
257
        $this->expectExceptionMessage('Error: File size exceeds allowed limit of 10 Bytes.');
258
259
        $this->upload($this->file);
260
261
        $this->assertEquals($count, $Uploads->find()->count());
262
    }
263
264
    public function testAddFailureDoubleUpload()
265
    {
266
        $this->loginJwt(1);
267
        // Make sure to test a file that may get transformed on upload (e.g. PNG
268
        // to JEPG).
269
        $file = new File(TMP . 'my new-upload.png');
270
        $this->mockMediaFile($file);
271
        $this->upload($file);
272
273
        $this->expectException(GenericApiException::class);
274
        $this->expectExceptionCode(400);
275
        $this->expectExceptionMessage('File with same name already uploaded');
276
277
        $this->loginJwt(1);
278
        $this->upload($file);
279
280
        $file->delete();
281
    }
282
283
    public function testAddFailureFilenameToLong()
284
    {
285
        $this->loginJwt(1);
286
        $max = UploadsTable::FILENAME_MAXLENGTH;
287
        $file = new File(TMP . str_pad('', $max + 1, '0') . '.png');
288
        $this->mockMediaFile($file);
289
290
        $this->expectException(GenericApiException::class);
291
        $this->expectExceptionCode(400);
292
        $this->expectExceptionMessage((string)$max);
293
        $this->upload($file);
294
295
        $file->delete();
296
    }
297
298
    public function testIndexNoAuthorization()
299
    {
300
        $this->expectException(UnauthenticatedException::class);
301
302
        $this->get('api/v2/uploads');
303
    }
304
305
    public function testIndexFailureUploadBelongsToDifferentUser()
306
    {
307
        $this->loginJwt(3);
308
309
        $this->expectException(SaitoForbiddenException::class);
310
311
        $this->get('api/v2/uploads/?id=1');
312
    }
313
314
    public function testIndexSuccess()
315
    {
316
        $this->loginJwt(3);
317
318
        $this->get('api/v2/uploads?id=3');
319
320
        $response = json_decode((string)$this->_response->getBody(), true);
321
322
        $this->assertResponseCode(200);
323
324
        $this->assertEquals(
325
            1526404380,
326
            strtotime($response['data'][0]['attributes']['created'])
327
        );
328
        unset($response['data'][0]['attributes']['created']);
329
330
        $expected = [
331
            'data' => [
332
                [
333
                    'id' => 2,
334
                    'type' => 'uploads',
335
                    'attributes' => [
336
                        'id' => 2,
337
                        'mime' => 'image/jpeg',
338
                        'name' => '3-another-upload.jpg',
339
                        'size' => 50000,
340
                        'thumbnail_url' => '/api/v2/uploads/thumb/2?h=be7ef71551c4245f82223d0c8e652eee',
341
                        'title' => '3-another-upload.jpg',
342
                        'url' => '/useruploads/3-another-upload.jpg',
343
                    ],
344
                ],
345
            ],
346
        ];
347
        $this->assertEquals($expected, $response);
348
    }
349
350
    public function testDeleteNoAuthorization()
351
    {
352
        $this->expectException(UnauthenticatedException::class);
353
354
        $this->delete('api/v2/uploads/1');
355
    }
356
357
    public function testDeleteFailureUploadBelongsToDifferentUser()
358
    {
359
        $this->loginJwt(3);
360
361
        $this->expectException(SaitoForbiddenException::class);
362
363
        $this->delete('api/v2/uploads/1');
364
    }
365
366
    public function testUploadDeleteFailureNotFound()
367
    {
368
        $this->loginJwt(1);
369
        $this->expectException(RecordNotFoundException::class);
370
        $this->delete('api/v2/uploads/9999');
371
    }
372
373
    public function testDeleteSuccess()
374
    {
375
        $this->loginJwt(1);
376
        $Uploads = TableRegistry::getTableLocator()->get('ImageUploader.Uploads');
377
        $upload = $Uploads->get(1);
378
        $this->assertNotEmpty($Uploads->get(1));
379
        $this->mockMediaFile($upload->get('file'));
380
381
        $this->delete('api/v2/uploads/1');
382
383
        $response = json_decode((string)$this->_response->getBody(), true);
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
384
385
        $this->assertResponseCode(204);
386
387
        $this->assertFalse($Uploads->exists(1));
388
    }
389
390
    /**
391
     * Sends a file to upload api
392
     *
393
     * @param File $file The file to send
394
     * @param mixed $userId The user-ID to upload to
395
     */
396
    private function upload(File $file, $userId = 1)
397
    {
398
        $data = [
399
            'upload' => [
400
                0 => [
401
                    'file' => [
402
                        'tmp_name' => $file->path,
403
                        'name' => $file->name() . '.' . $file->ext(),
0 ignored issues
show
Bug introduced by
Are you sure $file->ext() of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

403
                        'name' => $file->name() . '.' . /** @scrutinizer ignore-type */ $file->ext(),
Loading history...
Bug introduced by
Are you sure $file->name() of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

403
                        'name' => /** @scrutinizer ignore-type */ $file->name() . '.' . $file->ext(),
Loading history...
404
                        'size' => $file->size(),
405
                        'type' => $file->mime(),
406
                    ]
407
                ]
408
            ]
409
        ];
410
        if ($userId) {
411
            $data['userId'] = (string)$userId;
412
        }
413
        $this->post('api/v2/uploads.json', $data);
414
    }
415
}
416