From 0d4e0ed253273ef7abbe94afa28fb6e17657319f Mon Sep 17 00:00:00 2001 From: Evgeniy Zyubin Date: Mon, 12 Jul 2021 17:05:59 +0300 Subject: [PATCH] Psalm improvements, add unit tests update of workflow actions (#10) * Update dev dependencies * Psalm improvements * Add more test cases for code coverage * Update .scrutinizer.yml * Update of workflow actions --- .editorconfig | 3 +++ .github/workflows/build.yml | 46 +++++++++---------------------- .github/workflows/mutation.yml | 19 +++++++------ .github/workflows/static.yml | 15 ++++++----- .scrutinizer.yml | 49 ++++++++++++++++++++++------------ composer.json | 6 ++--- psalm.xml | 6 +---- src/ServerRequest.php | 12 ++++----- src/UploadedFile.php | 2 +- src/Uri.php | 14 +++++----- tests/MessageTest.php | 28 ++++++++++++++++--- tests/ResponseTest.php | 8 ++++++ tests/ServerRequestTest.php | 21 ++++++++++++--- tests/StreamTest.php | 34 +++++++++++++++++++---- tests/UploadedFileTest.php | 49 +++++++++++++++++++++++++++++++--- 15 files changed, 208 insertions(+), 104 deletions(-) diff --git a/.editorconfig b/.editorconfig index dcd93a5..5d10651 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,6 @@ trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef7a154..ab2b0c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,10 +6,11 @@ name: build jobs: tests: - name: PHP ${{ matrix.php-version }}-${{ matrix.os }} + name: PHP ${{ matrix.php }}-${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: - extensions: curl, mbstring, dom key: cache-v1 strategy: @@ -18,19 +19,18 @@ jobs: - ubuntu-latest - windows-latest - php-version: + php: - "7.4" - "8.0" steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} + php-version: ${{ matrix.php }} ini-values: date.timezone='UTC' tools: composer:v2 coverage: pcov @@ -44,38 +44,18 @@ jobs: run: echo "COMPOSER_CACHE_DIR=~\AppData\Local\Composer" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Cache dependencies installed with composer - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: | - php${{ matrix.php-version }}-composer- + php${{ matrix.php }}-composer- - name: Update composer run: composer self-update - - name: Install dependencies with composer php 7.4 - if: matrix.php-version == '7.4' - run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader - - - name: Install dependencies with composer php 8.0 - if: matrix.php-version == '8.0' - run: composer update --ignore-platform-reqs --prefer-dist --no-interaction --no-progress --optimize-autoloader - - - name: PHPUnit run with coverage on Linux PHP 7.4 - if: matrix.os == 'ubuntu-latest' && matrix.php-version == '7.4' - run: vendor/bin/phpunit --coverage-clover=coverage.clover - - - name: PHPUnit run without coverage on Linux PHP 8.0 - if: matrix.os == 'ubuntu-latest' && matrix.php-version == '8.0' - run: vendor/bin/phpunit - - - name: PHPUnit run without coverage on Windows - if: matrix.os == 'windows-latest' - run: vendor/bin/phpunit + - name: Install dependencies with composer + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - - name: Code coverage scrutinizer on Linux PHP 7.4 - if: matrix.os == 'ubuntu-latest' && matrix.php-version == '7.4' - run: | - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - name: Run tests with phpunit + run: vendor/bin/phpunit --colors=always diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index 3b126ce..2aefdee 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -8,7 +8,7 @@ name: mutation jobs: mutation: - name: PHP ${{ matrix.php-version }}-${{ matrix.os }} + name: PHP ${{ matrix.php }}-${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -17,17 +17,17 @@ jobs: os: - ubuntu-latest - php-version: + php: - "7.4" steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} + php-version: ${{ matrix.php }} ini-values: memory_limit=-1 tools: composer:v2 coverage: pcov @@ -39,20 +39,19 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: | - php${{ matrix.php-version }}-composer- + php${{ matrix.php }}-composer- - name: Update composer run: composer self-update - name: Install dependencies with composer - run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - name: Run test mutation infection with coverage run: | - mkdir -p build/logs - vendor/bin/phpunit --coverage-xml=build/logs/coverage-xml --log-junit=build/logs/junit.xml - vendor/bin/infection --threads=2 --coverage=build/logs --show-mutations --no-progress --min-msi=10 --min-covered-msi=10 + git fetch --depth=1 origin $GITHUB_BASE_REF + vendor/bin/roave-infection-static-analysis-plugin -j2 --git-diff-filter=A --git-diff-base=origin/$GITHUB_BASE_REF --logger-github --ignore-msi-with-no-mutations --only-covered env: STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index ce5d581..550b208 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -6,7 +6,7 @@ name: static jobs: static: - name: PHP ${{ matrix.php-version }}-${{ matrix.os }} + name: PHP ${{ matrix.php }}-${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -15,17 +15,18 @@ jobs: os: - ubuntu-latest - php-version: + php: - "7.4" + - "8.0" steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} + php-version: ${{ matrix.php }} ini-values: memory_limit=-1 tools: composer:v2 @@ -36,15 +37,15 @@ jobs: uses: actions/cache@v2 with: path: ${{ env.COMPOSER_CACHE_DIR }} - key: php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: | - php${{ matrix.php-version }}-composer- + php${{ matrix.php }}-composer- - name: Update composer run: composer self-update - name: Install dependencies with composer - run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi - name: PHPCS check run: vendor/bin/phpcs diff --git a/.scrutinizer.yml b/.scrutinizer.yml index c7b177a..eca0599 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,18 +1,33 @@ -build: - environment: - php: "7.4" - nodes: - analysis: - tests: - override: - - php-scrutinizer-run -filter: - paths: - - "src/*" checks: - php: true -tools: - php_code_coverage: - enabled: true - external_code_coverage: - timeout: 600 + php: true + +filter: + paths: + - "src/*" + +build: + nodes: + analysis: + environment: + php: 7.4.12 + + tests: + override: + - php-scrutinizer-run + + tests-and-coverage: + environment: + php: 7.4.12 + + dependencies: + override: + - composer self-update + - composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + tests: + override: + - command: "./vendor/bin/phpunit --coverage-clover ./coverage.xml" + on_node: 1 + coverage: + file: coverage.xml + format: php-clover diff --git a/composer.json b/composer.json index 12fac48..1fdf8b1 100644 --- a/composer.json +++ b/composer.json @@ -24,11 +24,11 @@ "psr/http-message": "^1.0" }, "require-dev": { - "infection/infection": "^0.21", "phpunit/phpunit": "^9.5", "php-http/psr7-integration-tests": "1.1.1", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.6" + "roave/infection-static-analysis-plugin": "^1.8", + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "^4.8" }, "provide": { "psr/http-message-implementation": "1.0", diff --git a/psalm.xml b/psalm.xml index a70a5b6..62329a9 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,16 +1,12 @@ - - - diff --git a/src/ServerRequest.php b/src/ServerRequest.php index 13c7c80..c2706e4 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -103,7 +103,7 @@ public function getCookieParams(): array /** * {@inheritDoc} */ - public function withCookieParams(array $cookies): self + public function withCookieParams(array $cookies): ServerRequestInterface { $new = clone $this; $new->cookieParams = $cookies; @@ -121,7 +121,7 @@ public function getQueryParams(): array /** * {@inheritDoc} */ - public function withQueryParams(array $query): self + public function withQueryParams(array $query): ServerRequestInterface { $new = clone $this; $new->queryParams = $query; @@ -139,7 +139,7 @@ public function getUploadedFiles(): array /** * {@inheritDoc} */ - public function withUploadedFiles(array $uploadedFiles): self + public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface { $this->validateUploadedFiles($uploadedFiles); $new = clone $this; @@ -160,7 +160,7 @@ public function getParsedBody() * * @psalm-suppress DocblockTypeContradiction */ - public function withParsedBody($data): self + public function withParsedBody($data): ServerRequestInterface { if (!is_array($data) && !is_object($data) && $data !== null) { throw new InvalidArgumentException(sprintf( @@ -197,7 +197,7 @@ public function getAttribute($name, $default = null) /** * {@inheritDoc} */ - public function withAttribute($name, $value): self + public function withAttribute($name, $value): ServerRequestInterface { if (array_key_exists($name, $this->attributes) && $this->attributes[$name] === $value) { return $this; @@ -211,7 +211,7 @@ public function withAttribute($name, $value): self /** * {@inheritDoc} */ - public function withoutAttribute($name): self + public function withoutAttribute($name): ServerRequestInterface { if (!array_key_exists($name, $this->attributes)) { return $this; diff --git a/src/UploadedFile.php b/src/UploadedFile.php index 15ef446..6e2bc67 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -246,7 +246,7 @@ public function getClientMediaType(): ?string */ private function moveOrWriteFile(string $targetPath): void { - if ($this->file) { + if ($this->file !== null) { $isCliEnv = (!PHP_SAPI || strpos(PHP_SAPI, 'cli') === 0 || strpos(PHP_SAPI, 'phpdbg') === 0); if (!($isCliEnv ? rename($this->file, $targetPath) : move_uploaded_file($this->file, $targetPath))) { diff --git a/src/Uri.php b/src/Uri.php index 49c15a5..dc4ea14 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -215,7 +215,7 @@ public function getFragment(): string /** * {@inheritDoc} */ - public function withScheme($scheme): self + public function withScheme($scheme): UriInterface { $this->checkStringType($scheme, 'scheme', __METHOD__); $schema = $this->normalizeScheme($scheme); @@ -232,7 +232,7 @@ public function withScheme($scheme): self /** * {@inheritDoc} */ - public function withUserInfo($user, $password = null): self + public function withUserInfo($user, $password = null): UriInterface { $this->checkStringType($user, 'user', __METHOD__); @@ -254,7 +254,7 @@ public function withUserInfo($user, $password = null): self /** * {@inheritDoc} */ - public function withHost($host): self + public function withHost($host): UriInterface { $this->checkStringType($host, 'host', __METHOD__); $host = $this->normalizeHost($host); @@ -271,7 +271,7 @@ public function withHost($host): self /** * {@inheritDoc} */ - public function withPort($port): self + public function withPort($port): UriInterface { $port = $this->normalizePort($port); @@ -287,7 +287,7 @@ public function withPort($port): self /** * {@inheritDoc} */ - public function withPath($path): self + public function withPath($path): UriInterface { $this->checkStringType($path, 'path', __METHOD__); $path = $this->normalizePath($path); @@ -304,7 +304,7 @@ public function withPath($path): self /** * {@inheritDoc} */ - public function withQuery($query): self + public function withQuery($query): UriInterface { $this->checkStringType($query, 'query string', __METHOD__); $query = $this->normalizeQuery($query); @@ -321,7 +321,7 @@ public function withQuery($query): self /** * {@inheritDoc} */ - public function withFragment($fragment): self + public function withFragment($fragment): UriInterface { $this->checkStringType($fragment, 'URI fragment', __METHOD__); $fragment = $this->normalizeFragment($fragment); diff --git a/tests/MessageTest.php b/tests/MessageTest.php index 2d6a81c..dbfcab0 100644 --- a/tests/MessageTest.php +++ b/tests/MessageTest.php @@ -109,9 +109,25 @@ public function testBodyPassingInConstructorThrowExceptionForFileCannotBeOpened( public function testWithBody(): void { $stream = $this->createMock(StreamInterface::class); - $message = $this->message->withBody($stream); - $this->assertNotSame($this->message, $message); - $this->assertSame($stream, $message->getBody()); + + $message1 = $this->message->withBody($stream); + $this->assertNotSame($this->message, $message1); + $this->assertSame($stream, $message1->getBody()); + + $message2 = $message1->withBody($stream); + $this->assertSame($message1, $message2); + $this->assertSame($stream, $message2->getBody()); + } + + public function testWithAndGetHeader(): void + { + $message1 = $this->message->withHeader('Name', 'Value1'); + $this->assertNotSame($this->message, $message1); + $this->assertSame(['Value1'], $message1->getHeader('Name')); + + $message2 = $message1->withHeader('Name', 'Value2'); + $this->assertNotSame($message1, $message2); + $this->assertSame(['Value2'], $message2->getHeader('Name')); } public function testWithAndGetHeaders(): void @@ -126,6 +142,7 @@ public function testWithAddedAndGetHeaders(): void $firstMessage = $this->message->withAddedHeader('Name', 'FirstValue'); $this->assertNotSame($this->message, $firstMessage); $this->assertSame(['Name' => ['FirstValue']], $firstMessage->getHeaders()); + $secondMessage = $firstMessage->withAddedHeader('Name', 'SecondValue'); $this->assertNotSame($firstMessage, $secondMessage); $this->assertSame(['Name' => ['FirstValue', 'SecondValue']], $secondMessage->getHeaders()); @@ -136,9 +153,14 @@ public function testWithoutAndGetHeaders(): void $firstMessage = $this->message->withHeader('Name', 'Value'); $this->assertNotSame($this->message, $firstMessage); $this->assertSame(['Name' => ['Value']], $firstMessage->getHeaders()); + $secondMessage = $firstMessage->withoutHeader('Name'); $this->assertNotSame($firstMessage, $secondMessage); $this->assertSame([], $secondMessage->getHeaders()); + + $thirdMessage = $secondMessage->withoutHeader('Name'); + $this->assertSame($secondMessage, $thirdMessage); + $this->assertSame([], $thirdMessage->getHeaders()); } public function testHasHeaderFail(): void diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index ae86ad2..47599f3 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -64,6 +64,14 @@ public function testWithStatus(): void $this->assertSame('Not Found', $response->getReasonPhrase()); } + public function testWithStatusWithStringNumericCode(): void + { + $response = $this->response->withStatus($code = '404'); + $this->assertNotSame($this->response, $response); + $this->assertSame((int) $code, $response->getStatusCode()); + $this->assertSame('Not Found', $response->getReasonPhrase()); + } + public function testWithStatusAndCustomReasonPhrase(): void { $response = $this->response->withStatus($code = 404, $customPhrase = 'Custom Phrase'); diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php index c35e5f3..3a8cca8 100644 --- a/tests/ServerRequestTest.php +++ b/tests/ServerRequestTest.php @@ -139,10 +139,13 @@ public function testUriPassingInConstructorThrowExceptionInvalidUri($uri): void public function testWithAttributeAndGetAttributes(): void { - $request = $this->request->withAttribute('name', 'value'); - $this->assertNotSame($this->request, $request); - $this->assertSame('value', $request->getAttribute('name')); - $this->assertSame(['name' => 'value'], $request->getAttributes()); + $firstRequest = $this->request->withAttribute('name', 'value'); + $this->assertNotSame($this->request, $firstRequest); + $this->assertSame('value', $firstRequest->getAttribute('name')); + + $secondRequest = $firstRequest->withAttribute('name', 'value'); + $this->assertSame($firstRequest, $secondRequest); + $this->assertSame('value', $secondRequest->getAttribute('name')); } public function testWithoutAttributeAndGetAttributes(): void @@ -150,10 +153,16 @@ public function testWithoutAttributeAndGetAttributes(): void $firstRequest = $this->request->withAttribute('name', 'value'); $this->assertNotSame($this->request, $firstRequest); $this->assertSame('value', $firstRequest->getAttribute('name')); + $secondRequest = $firstRequest->withoutAttribute('name'); $this->assertNotSame($firstRequest, $secondRequest); $this->assertNull($secondRequest->getAttribute('name')); $this->assertSame([], $secondRequest->getAttributes()); + + $thirdRequest = $secondRequest->withoutAttribute('name'); + $this->assertSame($secondRequest, $thirdRequest); + $this->assertNull($thirdRequest->getAttribute('name')); + $this->assertSame([], $thirdRequest->getAttributes()); } public function testGetAttributePassedDefaultValue(): void @@ -236,6 +245,10 @@ public function testWithUploadedFiles(): void $uploadedFiles = [ new UploadedFile('file.txt', 1024, UPLOAD_ERR_OK), new UploadedFile('image.png', 67890, UPLOAD_ERR_OK), + [ + new UploadedFile('file.md', 1024, UPLOAD_ERR_OK), + new UploadedFile('image.jpg', 67890, UPLOAD_ERR_OK), + ], ]; $request = $this->request->withUploadedFiles($uploadedFiles); diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 22cf4d4..13d3d2f 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -5,11 +5,14 @@ namespace HttpSoft\Tests\Message; use HttpSoft\Message\Stream; +use InvalidArgumentException; +use Phar; use RuntimeException; use PHPUnit\Framework\TestCase; use function file_exists; use function fopen; +use function stream_context_create; use function stream_get_meta_data; use function sys_get_temp_dir; use function tempnam; @@ -67,6 +70,7 @@ public function testGetMetadata() $this->assertSame(0, $this->stream->getMetadata('unread_bytes')); $this->assertSame(true, $this->stream->getMetadata('seekable')); $this->assertSame('php://temp', $this->stream->getMetadata('uri')); + $this->assertSame(null, $this->stream->getMetadata('not-exist')); } public function testIsWritableAndWriteAndToString(): void @@ -139,38 +143,58 @@ public function testIsSeekableReturnFalse(): void $this->assertFalse($stream->isSeekable()); } - public function testTellThrowExceptionForInvalidResource(): void + public function testReadThrowExceptionForStreamIsNotReadable(): void + { + $stream = new Stream($this->tmpFile, 'w'); + $this->expectException(RuntimeException::class); + $stream->read(1); + } + + public function testWriteThrowExceptionForStreamIsNotWritable(): void { + $stream = new Stream($this->tmpFile, 'r'); $this->expectException(RuntimeException::class); + $stream->write('content'); + } + + public function testTellThrowExceptionForInvalidResource(): void + { $this->stream->close(); + $this->expectException(RuntimeException::class); $this->stream->tell(); } public function testSeekThrowExceptionForInvalidResource(): void { - $this->expectException(RuntimeException::class); $this->stream->close(); + $this->expectException(RuntimeException::class); $this->stream->seek(1); } public function testWriteThrowExceptionForInvalidResource(): void { - $this->expectException(RuntimeException::class); $this->stream->close(); + $this->expectException(RuntimeException::class); $this->stream->write('content'); } public function testReadThrowExceptionForInvalidResource(): void { - $this->expectException(RuntimeException::class); $this->stream->close(); + $this->expectException(RuntimeException::class); $this->stream->read(1); } public function testGetContentThrowExceptionForInvalidResource(): void { - $this->expectException(RuntimeException::class); $this->stream->close(); + $this->expectException(RuntimeException::class); $this->stream->getContents(); } + + public function testConstructorThrowExceptionForInvalidResource(): void + { + $this->expectException(InvalidArgumentException::class); + new Stream(stream_context_create(['phar' => ['compress' => Phar::GZ]])); + } } diff --git a/tests/UploadedFileTest.php b/tests/UploadedFileTest.php index e331d98..58adef9 100644 --- a/tests/UploadedFileTest.php +++ b/tests/UploadedFileTest.php @@ -63,13 +63,20 @@ public function testGetDefault(): void public function testGetStream() { - $uploadedFile = new UploadedFile($this->stream, $size = 1024, UPLOAD_ERR_OK); + $uploadedFile = new UploadedFile($this->stream, 1024, UPLOAD_ERR_OK); $this->assertSame($this->stream, $uploadedFile->getStream()); $this->stream->write($content = 'Content'); $this->assertSame($content, (string) $uploadedFile->getStream()); $this->assertSame((string) $this->stream, (string) $uploadedFile->getStream()); - $uploadedFile = new UploadedFile($this->tmpFile, $size = 1024, UPLOAD_ERR_OK); + $uploadedFile = new UploadedFile($this->tmpFile, 1024, UPLOAD_ERR_OK); + $this->assertInstanceOf(StreamInterface::class, $uploadedFile->getStream()); + $this->assertInstanceOf(Stream::class, $uploadedFile->getStream()); + $stream = $uploadedFile->getStream(); + $stream->write($content = 'Content'); + $this->assertSame($content, (string) $uploadedFile->getStream()); + + $uploadedFile = new UploadedFile(fopen($this->tmpFile, 'wb+'), 1024, UPLOAD_ERR_OK); $this->assertInstanceOf(StreamInterface::class, $uploadedFile->getStream()); $this->assertInstanceOf(Stream::class, $uploadedFile->getStream()); $stream = $uploadedFile->getStream(); @@ -127,6 +134,34 @@ public function testMoveToThrowExceptionForHasBeenMoved(): void $uploadedFile->moveTo($this->tmpFile); } + /** + * @return array + */ + public function invalidStreamOrFilePathProvider(): array + { + return [ + 'null' => [null], + 'true' => [true], + 'false' => [false], + 'int' => [1], + 'float' => [1.1], + 'array' => [['']], + 'empty-array' => [[]], + 'object' => [new StdClass()], + 'callable' => [fn() => null], + ]; + } + + /** + * @dataProvider invalidStreamOrFilePathProvider + * @param mixed $streamOrFile + */ + public function testConstructorThrowExceptionForInvalidStreamOrFile($streamOrFile): void + { + $this->expectException(InvalidArgumentException::class); + new UploadedFile($streamOrFile, 1024, UPLOAD_ERR_OK); + } + /** * @return array */ @@ -145,7 +180,7 @@ public function testConstructorThrowExceptionForInvalidErrorStatus($errorStatus) new UploadedFile($this->stream, 1024, $errorStatus); } - public function notOkErrorStatusProvider() + public function notOkErrorStatusProvider(): array { return [ 'UPLOAD_ERR_INI_SIZE' => [UPLOAD_ERR_INI_SIZE], @@ -191,6 +226,7 @@ public function invalidTargetPathProvider(): array 'false' => [false], 'int' => [1], 'float' => [1.1], + 'string' => [''], 'array' => [['']], 'empty-array' => [[]], 'object' => [new StdClass()], @@ -208,4 +244,11 @@ public function testMoveToThrowExceptionForInvalidTargetPath($targetPath): void $this->expectException(InvalidArgumentException::class); $uploadedFile->moveTo($targetPath); } + + public function testMoveToThrowExceptionForTargetPathIsNotExist(): void + { + $uploadedFile = new UploadedFile($this->stream, 1024, UPLOAD_ERR_OK); + $this->expectException(RuntimeException::class); + $uploadedFile->moveTo('path/to/not-exist'); + } }