filestack/filestack-php

View on GitHub
tests/UploadProcessorTest.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
namespace Filestack\Tests;

use Filestack\FilestackConfig;
use Filestack\UploadProcessor;
use Filestack\FilestackException;

class UploadProcessorTest extends BaseTest
{
    private $file_metadata;
    private $upload_data;

    protected function setUp(): void
    {
        $this->test_filepath = __DIR__ . '/testfiles/landscape_24mb_img.jpg';
        $this->file_metadata = [
            'filepath' => $this->test_filepath,
            'filename' => basename($this->test_filepath),
            'filesize' => filesize($this->test_filepath),
            'mimetype' => 'text',
            'location' => 's3'
        ];

        $this->upload_data = [
            'uri'       => 'http://someuri',
            'region'    => 'some-region',
            'upload_id' => 1234
        ];
    }

    /**
     * Test initializing UploadProcessor with an API Key
     */
    public function testUploadProcessorInitialized()
    {
        $upload_processor = new UploadProcessor(
            $this->test_api_key,
            $this->test_security
        );
        $this->assertEquals($upload_processor->api_key, $this->test_api_key);
    }

    /**
     * Test register upload task
     */
    public function testRegisterUploadTaskSuccess()
    {
        $expected_upload_id = 'some_upload_id';
        $mock_response = new MockHttpResponse(
            200,
            '{"upload_id": "' . $expected_upload_id . '"}'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $json = $upload_processor->registerUploadTask($this->test_api_key,
            $this->file_metadata);

        $this->assertEquals($expected_upload_id, $json['upload_id']);
    }

    /**
     * Test register upload task throws exception
     */
    public function testRegisterUploadTaskException()
    {
        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $mock_response = new MockHttpResponse(
            400,
            'bad data format'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $upload_processor->registerUploadTask($this->test_api_key,
            $this->file_metadata);
    }

    /**
     * Test create parts
     */
    public function testCreatePartsSuccess()
    {
        $expected_upload_id = 'some_upload_id';
        $mock_response = new MockHttpResponse(
            200,
            '{"upload_id": "' . $expected_upload_id . '"}'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $max_part_size = FilestackConfig::UPLOAD_PART_SIZE;

        $expected_num_parts = ceil($this->file_metadata['filesize']
                                   / $max_part_size);
        $expected_num_chunks = 1;

        $this->assertEquals($expected_num_parts, count($parts));
        $this->assertEquals($expected_num_chunks, count($parts[0]['chunks']));
    }

    /**
     * Test create parts for intelligent ingestion flow
     */
    public function testCreatePartsIntelligentSuccess()
    {
        $mock_response = new MockHttpResponse(200,
            json_encode([
                'filename'  => 'somefilename.jpg',
                'size'      => '1000',
                'type'      => 'image/jpg',
                'url'       => 'https://cdn.filestack.com/handle?partNum=1',
                'uri'       => 'https://uploaduri/handle?partNum=1',
                'region'    => 'us-east-1',
                'upload_id' => 'test-upload-id'
            ])
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );
        $upload_processor->setIntelligent(true);

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $max_part_size = FilestackConfig::UPLOAD_PART_SIZE;
        $max_chunk_size = FilestackConfig::UPLOAD_CHUNK_SIZE;

        $expected_num_parts = ceil($this->file_metadata['filesize']
                                    / $max_part_size);
        $expected_num_chunks = ceil($max_part_size / $max_chunk_size);

        $this->assertEquals($expected_num_parts, count($parts));
        $this->assertEquals($expected_num_chunks, count($parts[0]['chunks']));
    }

    /**
     * Test process parts
     */
    public function testProcessPartsSuccess()
    {
        $test_headers = ['ETag' =>['some-etag']];
        $mock_response = new MockHttpResponse(200,
            json_encode([
                'filename'  => 'somefilename.jpg',
                'size'      => '1000',
                'type'      => 'image/jpg',
                'url'       => 'https://cdn.filestack.com/filehandle?partNum=1',
                'uri'       => 'https://uploaduri/handle?partNum=1',
                'region'    => 'us-east-1',
                'upload_id' => 'test-upload-id',
                'headers'    => []
            ]),
            $test_headers
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $result = $upload_processor->callProcessParts($parts);

        $this->assertNotNull($result);
        $this->assertNotFalse($result);
    }

    /**
     * Test process parts
     */
    public function testProcessPartsIntelligentSuccess()
    {
        $test_headers = ['ETag' =>['some-etag']];
        $mock_response = new MockHttpResponse(200,
            json_encode([
                'filename'  => 'somefilename.jpg',
                'size'      => '1000',
                'type'      => 'image/jpg',
                'url'       => 'https://cdn.filestack.com/filehandle?partNum=1',
                'uri'       => 'https://uploaduri/handle?partNum=1',
                'region'    => 'us-east-1',
                'upload_id' => 'test-upload-id',
                'headers'    => []
            ]),
            $test_headers
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );
        $upload_processor->setIntelligent(true);

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $result = $upload_processor->callProcessParts($parts);

        $this->assertNotNull($result);
        $this->assertNotFalse($result);
    }

    /**
     * Test process parts
     */
    public function testProcessPartsException()
    {
        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $mock_response = new MockHttpResponse(
            400,
            'bad data format'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $upload_processor->callProcessParts($parts);
    }

    public function testProcessChunks()
    {
        $mock_response1 = new MockHttpResponse(408,  '{"error": "timed out"}');
        $mock_response2 = new MockHttpResponse(504,  '{"error": "Gateway timed out"}');
        $mock_response3 = new MockHttpResponse(200,
            json_encode([
                'url'       => 'https://some-s3-url/somedata',
                'part_size' => 1024,
                'headers'   => []
            ])
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturnOnConsecutiveCalls($mock_response1, $mock_response2,
                $mock_response3,
                $mock_response3,
                $mock_response3,
                $mock_response3,
                $mock_response3,
                $mock_response3,
                $mock_response3,
                $mock_response3);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );
        $upload_processor->setIntelligent(true);

        $parts = $upload_processor->callCreateParts($this->test_api_key,
            $this->file_metadata, $this->upload_data);

        $part = $parts[0];
        $chunks = $part['chunks'];

        $promises = $upload_processor->callProcessChunks($part, $chunks);

        $this->assertTrue(count($promises) > 0);
    }

    /**
     * Test handling promises from s3
     */
    public function testUnFulfilledPromises()
    {
        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(500);

        $mock_s3_response = new MockHttpResponse(
            500,
            new MockHttpResponseBody('{"error": "some-error"}')
        );

        $s3_results = [
            [
                'state' => 'fulfilled',
                'value' => $mock_s3_response
            ],
            [
                'state' => 'rejected',
                'value' => $mock_s3_response
            ]
        ];

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $parts = $upload_processor->callHandleS3PromisesResult($s3_results);
    }

    /**
     * Test commitPart
     */
    public function testCommitPartException()
    {
        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $mock_response = new MockHttpResponse(
            400,
            'bad data format'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $part = [
            'api_key'       => $this->test_api_key,
            'uri'           => 'http://someuri',
            'location'      => 's3',
            'region'        => 'some-region',
            'upload_id'     => 'some-upload-id',
            'part_num'      => 1,
            'filesize'      => 1024,
            'filename'      => 'somefilename',
        ];
        $upload_processor->callCommitPart($part);
    }

    /**
     * Test upload chunk to s3
     */
    public function testUploadChunkToS3()
    {
        $mock_response = new MockHttpResponse(200,
            json_encode([
                'some-field' => 'some-value'
            ])
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $url = 'http://s3-url?partNumber=1';
        $headers = ['Auth' => 'x-some-auth-val'];
        $chunk = "test-content";
        $result = $upload_processor->callUploadChunkToS3($url, $headers, $chunk);

        $this->assertNotNull($result);
    }

    /**
     * Test upload chunk to s3
     */
    public function testUploadChunkToS3Exception()
    {
        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $this->expectException(FilestackException::class);
        $this->expectExceptionCode(400);

        $mock_response = new MockHttpResponse(
            400,
            'bad data format'
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
             ->willReturn($mock_response);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $url = 'http://s3-url?partNumber=1';
        $headers = ['Auth' => 'x-some-auth-val'];
        $chunk = "test-content";
        $upload_processor->callUploadChunkToS3($url, $headers, $chunk);
    }

    /**
     * Test getting eTags from fulfilled s3 promises from multipart upload calls
     */
    public function testMultipartGetTags()
    {
        $mock_response = new MockHttpResponse(
            200,
            new MockHttpResponseBody('some content'),
            ['ETag' => ['tag1']]
        );

        $s3_results = [
            ['value' => $mock_response]
        ];

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security
        );

        $parts_etags = [];
        $upload_processor->callMultipartGetTags(1, $s3_results, $parts_etags);
        $this->assertNotEmpty($parts_etags);
    }

    /**
     * Test register completed upload
     */
    public function testRegisterComplete()
    {
        $mock_response1 = new MockHttpResponse(202,  '{accepted: true}');
        $mock_response2 = new MockHttpResponse(200,
            json_encode([
                'filename'  => 'somefilename.jpg',
                'size'      => '1000',
                'type'      => 'image/jpg',
                'url'       => 'https://cdn.filestack.com/filehandle?partNum=1',
                'uri'       => 'https://uploaduri/handle?partNum=1',
                'region'    => 'us-east-1',
                'upload_id' => 'test-upload-id',
                'headers'    => []
            ])
        );

        $stub_http_client = $this->createMock(\GuzzleHttp\Client::class);
        $stub_http_client->method('request')
            ->willReturnOnConsecutiveCalls($mock_response1, $mock_response2);

        $upload_processor = new MockUploadProcessor(
            $this->test_api_key,
            $this->test_security,
            $stub_http_client
        );

        $result = $upload_processor->callRegisterComplete($this->test_api_key,
            "eTags", $this->upload_data, $this->file_metadata);

        $this->assertNotNull($result);
    }
}