diff options
Diffstat (limited to 'apps/dav/tests/unit/Files/MultipartRequestParserTest.php')
-rw-r--r-- | apps/dav/tests/unit/Files/MultipartRequestParserTest.php | 205 |
1 files changed, 123 insertions, 82 deletions
diff --git a/apps/dav/tests/unit/Files/MultipartRequestParserTest.php b/apps/dav/tests/unit/Files/MultipartRequestParserTest.php index ec9e2d0a383..dc0e884f07c 100644 --- a/apps/dav/tests/unit/Files/MultipartRequestParserTest.php +++ b/apps/dav/tests/unit/Files/MultipartRequestParserTest.php @@ -1,71 +1,70 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2021, Louis Chemineau <louis@chmn.me> - * - * @author Louis Chemineau <louis@chmn.me> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ -namespace OCA\DAV\Tests\unit\DAV; +namespace OCA\DAV\Tests\unit\Files; +use OCA\DAV\BulkUpload\MultipartRequestParser; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Sabre\HTTP\RequestInterface; use Test\TestCase; -use \OCA\DAV\BulkUpload\MultipartRequestParser; class MultipartRequestParserTest extends TestCase { - private function getValidBodyObject() { + + protected LoggerInterface&MockObject $logger; + + protected function setUp(): void { + parent::setUp(); + $this->logger = $this->createMock(LoggerInterface::class); + } + + private static function getValidBodyObject(): array { return [ [ - "headers" => [ - "Content-Length" => 7, - "X-File-MD5" => "4f2377b4d911f7ec46325fe603c3af03", - "X-File-Path" => "/coucou.txt" + 'headers' => [ + 'Content-Length' => 7, + 'X-File-MD5' => '4f2377b4d911f7ec46325fe603c3af03', + 'OC-Checksum' => 'md5:4f2377b4d911f7ec46325fe603c3af03', + 'X-File-Path' => '/coucou.txt' ], - "content" => "Coucou\n" + 'content' => "Coucou\n" ] ]; } - private function getMultipartParser(array $parts, array $headers = [], string $boundary = "boundary_azertyuiop"): MultipartRequestParser { - $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + private function getMultipartParser(array $parts, array $headers = [], string $boundary = 'boundary_azertyuiop'): MultipartRequestParser { + /** @var RequestInterface&MockObject $request */ + $request = $this->getMockBuilder(RequestInterface::class) ->disableOriginalConstructor() ->getMock(); - $headers = array_merge(['Content-Type' => 'multipart/related; boundary='.$boundary], $headers); + $headers = array_merge(['Content-Type' => 'multipart/related; boundary=' . $boundary], $headers); $request->expects($this->any()) ->method('getHeader') ->willReturnCallback(function (string $key) use (&$headers) { return $headers[$key]; }); - $body = ""; + $body = ''; foreach ($parts as $part) { - $body .= '--'.$boundary."\r\n"; + $body .= '--' . $boundary . "\r\n"; foreach ($part['headers'] as $headerKey => $headerPart) { - $body .= $headerKey.": ".$headerPart."\r\n"; + $body .= $headerKey . ': ' . $headerPart . "\r\n"; } $body .= "\r\n"; - $body .= $part['content']."\r\n"; + $body .= $part['content'] . "\r\n"; } - $body .= '--'.$boundary."--"; + $body .= '--' . $boundary . '--'; - $stream = fopen('php://temp','r+'); + $stream = fopen('php://temp', 'r+'); fwrite($stream, $body); rewind($stream); @@ -73,16 +72,17 @@ class MultipartRequestParserTest extends TestCase { ->method('getBody') ->willReturn($stream); - return new MultipartRequestParser($request); + return new MultipartRequestParser($request, $this->logger); } /** * Test validation of the request's body type */ - public function testBodyTypeValidation() { - $bodyStream = "I am not a stream, but pretend to be"; - $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + public function testBodyTypeValidation(): void { + $bodyStream = 'I am not a stream, but pretend to be'; + /** @var RequestInterface&MockObject $request */ + $request = $this->getMockBuilder(RequestInterface::class) ->disableOriginalConstructor() ->getMock(); $request->expects($this->any()) @@ -90,7 +90,30 @@ class MultipartRequestParserTest extends TestCase { ->willReturn($bodyStream); $this->expectExceptionMessage('Body should be of type resource'); - new MultipartRequestParser($request); + new MultipartRequestParser($request, $this->logger); + } + + /** + * Test with valid request. + * - valid boundary + * - valid hash + * - valid content-length + * - valid file content + * - valid file path + */ + public function testValidRequest(): void { + $bodyObject = self::getValidBodyObject(); + unset($bodyObject['0']['headers']['X-File-MD5']); + + $multipartParser = $this->getMultipartParser($bodyObject); + + [$headers, $content] = $multipartParser->parseNextPart(); + + $this->assertSame((int)$headers['content-length'], 7, 'Content-Length header should be the same as provided.'); + $this->assertSame($headers['oc-checksum'], 'md5:4f2377b4d911f7ec46325fe603c3af03', 'OC-Checksum header should be the same as provided.'); + $this->assertSame($headers['x-file-path'], '/coucou.txt', 'X-File-Path header should be the same as provided.'); + + $this->assertSame($content, "Coucou\n", 'Content should be the same'); } /** @@ -101,54 +124,72 @@ class MultipartRequestParserTest extends TestCase { * - valid file content * - valid file path */ - public function testValidRequest() { - $multipartParser = $this->getMultipartParser( - $this->getValidBodyObject() - ); + public function testValidRequestWithMd5(): void { + $bodyObject = self::getValidBodyObject(); + unset($bodyObject['0']['headers']['OC-Checksum']); + + $multipartParser = $this->getMultipartParser($bodyObject); [$headers, $content] = $multipartParser->parseNextPart(); - $this->assertSame((int)$headers["content-length"], 7, "Content-Length header should be the same as provided."); - $this->assertSame($headers["x-file-md5"], "4f2377b4d911f7ec46325fe603c3af03", "X-File-MD5 header should be the same as provided."); - $this->assertSame($headers["x-file-path"], "/coucou.txt", "X-File-Path header should be the same as provided."); + $this->assertSame((int)$headers['content-length'], 7, 'Content-Length header should be the same as provided.'); + $this->assertSame($headers['x-file-md5'], '4f2377b4d911f7ec46325fe603c3af03', 'X-File-MD5 header should be the same as provided.'); + $this->assertSame($headers['x-file-path'], '/coucou.txt', 'X-File-Path header should be the same as provided.'); + + $this->assertSame($content, "Coucou\n", 'Content should be the same'); + } - $this->assertSame($content, "Coucou\n", "Content should be the same"); + /** + * Test with invalid hash. + */ + public function testInvalidHash(): void { + $bodyObject = self::getValidBodyObject(); + $bodyObject['0']['headers']['OC-Checksum'] = 'md5:f2377b4d911f7ec46325fe603c3af03'; + unset($bodyObject['0']['headers']['X-File-MD5']); + $multipartParser = $this->getMultipartParser( + $bodyObject + ); + + $this->expectExceptionMessage('Computed md5 hash is incorrect (4f2377b4d911f7ec46325fe603c3af03).'); + $multipartParser->parseNextPart(); } /** * Test with invalid md5 hash. */ - public function testInvalidMd5Hash() { - $bodyObject = $this->getValidBodyObject(); - $bodyObject["0"]["headers"]["X-File-MD5"] = "f2377b4d911f7ec46325fe603c3af03"; + public function testInvalidMd5Hash(): void { + $bodyObject = self::getValidBodyObject(); + unset($bodyObject['0']['headers']['OC-Checksum']); + $bodyObject['0']['headers']['X-File-MD5'] = 'f2377b4d911f7ec46325fe603c3af03'; $multipartParser = $this->getMultipartParser( $bodyObject ); - $this->expectExceptionMessage('Computed md5 hash is incorrect.'); + $this->expectExceptionMessage('Computed md5 hash is incorrect (4f2377b4d911f7ec46325fe603c3af03).'); $multipartParser->parseNextPart(); } /** - * Test with a null md5 hash. + * Test with a null hash headers. */ - public function testNullMd5Hash() { - $bodyObject = $this->getValidBodyObject(); - unset($bodyObject["0"]["headers"]["X-File-MD5"]); + public function testNullHash(): void { + $bodyObject = self::getValidBodyObject(); + unset($bodyObject['0']['headers']['OC-Checksum']); + unset($bodyObject['0']['headers']['X-File-MD5']); $multipartParser = $this->getMultipartParser( $bodyObject ); - $this->expectExceptionMessage('The X-File-MD5 header must not be null.'); + $this->expectExceptionMessage('The hash headers must not be null.'); $multipartParser->parseNextPart(); } /** * Test with a null Content-Length. */ - public function testNullContentLength() { - $bodyObject = $this->getValidBodyObject(); - unset($bodyObject["0"]["headers"]["Content-Length"]); + public function testNullContentLength(): void { + $bodyObject = self::getValidBodyObject(); + unset($bodyObject['0']['headers']['Content-Length']); $multipartParser = $this->getMultipartParser( $bodyObject ); @@ -160,36 +201,36 @@ class MultipartRequestParserTest extends TestCase { /** * Test with a lower Content-Length. */ - public function testLowerContentLength() { - $bodyObject = $this->getValidBodyObject(); - $bodyObject["0"]["headers"]["Content-Length"] = 6; + public function testLowerContentLength(): void { + $bodyObject = self::getValidBodyObject(); + $bodyObject['0']['headers']['Content-Length'] = 6; $multipartParser = $this->getMultipartParser( $bodyObject ); - $this->expectExceptionMessage('Computed md5 hash is incorrect.'); + $this->expectExceptionMessage('Computed md5 hash is incorrect (41060d3ddfdf63e68fc2bf196f652ee9).'); $multipartParser->parseNextPart(); } /** * Test with a higher Content-Length. */ - public function testHigherContentLength() { - $bodyObject = $this->getValidBodyObject(); - $bodyObject["0"]["headers"]["Content-Length"] = 8; + public function testHigherContentLength(): void { + $bodyObject = self::getValidBodyObject(); + $bodyObject['0']['headers']['Content-Length'] = 8; $multipartParser = $this->getMultipartParser( $bodyObject ); - $this->expectExceptionMessage('Computed md5 hash is incorrect.'); + $this->expectExceptionMessage('Computed md5 hash is incorrect (0161002bbee6a744f18741b8a914e413).'); $multipartParser->parseNextPart(); } /** * Test with wrong boundary in body. */ - public function testWrongBoundary() { - $bodyObject = $this->getValidBodyObject(); + public function testWrongBoundary(): void { + $bodyObject = self::getValidBodyObject(); $multipartParser = $this->getMultipartParser( $bodyObject, ['Content-Type' => 'multipart/related; boundary=boundary_poiuytreza'] @@ -202,8 +243,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with no boundary in request headers. */ - public function testNoBoundaryInHeader() { - $bodyObject = $this->getValidBodyObject(); + public function testNoBoundaryInHeader(): void { + $bodyObject = self::getValidBodyObject(); $this->expectExceptionMessage('Error while parsing boundary in Content-Type header.'); $this->getMultipartParser( $bodyObject, @@ -214,8 +255,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with no boundary in the request's headers. */ - public function testNoBoundaryInBody() { - $bodyObject = $this->getValidBodyObject(); + public function testNoBoundaryInBody(): void { + $bodyObject = self::getValidBodyObject(); $multipartParser = $this->getMultipartParser( $bodyObject, ['Content-Type' => 'multipart/related; boundary=boundary_azertyuiop'], @@ -229,8 +270,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with a boundary with quotes in the request's headers. */ - public function testBoundaryWithQuotes() { - $bodyObject = $this->getValidBodyObject(); + public function testBoundaryWithQuotes(): void { + $bodyObject = self::getValidBodyObject(); $multipartParser = $this->getMultipartParser( $bodyObject, ['Content-Type' => 'multipart/related; boundary="boundary_azertyuiop"'], @@ -245,8 +286,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with a wrong Content-Type in the request's headers. */ - public function testWrongContentType() { - $bodyObject = $this->getValidBodyObject(); + public function testWrongContentType(): void { + $bodyObject = self::getValidBodyObject(); $this->expectExceptionMessage('Content-Type must be multipart/related'); $this->getMultipartParser( $bodyObject, @@ -257,8 +298,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with a wrong key after the content type in the request's headers. */ - public function testWrongKeyInContentType() { - $bodyObject = $this->getValidBodyObject(); + public function testWrongKeyInContentType(): void { + $bodyObject = self::getValidBodyObject(); $this->expectExceptionMessage('Boundary is invalid'); $this->getMultipartParser( $bodyObject, @@ -269,8 +310,8 @@ class MultipartRequestParserTest extends TestCase { /** * Test with a null Content-Type in the request's headers. */ - public function testNullContentType() { - $bodyObject = $this->getValidBodyObject(); + public function testNullContentType(): void { + $bodyObject = self::getValidBodyObject(); $this->expectExceptionMessage('Content-Type can not be null'); $this->getMultipartParser( $bodyObject, |