aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/tests
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2021-10-15 11:57:39 +0200
committerJulius Härtl <jus@bitgrid.net>2021-10-16 09:42:07 +0200
commitdef983dc7ea11b9f8e449d56019f934ce89d9490 (patch)
tree4b99dfe4c8b29e5d234d27a1b15867f45650619b /apps/dav/tests
parentdd938dadefcbfa09fece30efcdaf09538f01d9e3 (diff)
downloadnextcloud-server-def983dc7ea11b9f8e449d56019f934ce89d9490.tar.gz
nextcloud-server-def983dc7ea11b9f8e449d56019f934ce89d9490.zip
Clean BulkUpload plugin
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'apps/dav/tests')
-rwxr-xr-xapps/dav/tests/benchmarks/benchmark.sh (renamed from apps/dav/tests/temporary/benchmark.sh)10
-rwxr-xr-xapps/dav/tests/benchmarks/bulk_upload.sh (renamed from apps/dav/tests/temporary/bundle_upload.sh)22
-rwxr-xr-xapps/dav/tests/benchmarks/single_upload.sh (renamed from apps/dav/tests/temporary/single_upload.sh)13
-rw-r--r--apps/dav/tests/unit/CapabilitiesTest.php1
-rw-r--r--apps/dav/tests/unit/Files/BundlePluginTest.php711
-rw-r--r--apps/dav/tests/unit/Files/BundledFileTest.php220
-rw-r--r--apps/dav/tests/unit/Files/MultipartContentsParserTest.php416
-rw-r--r--apps/dav/tests/unit/Files/MultipartRequestParserTest.php281
8 files changed, 313 insertions, 1361 deletions
diff --git a/apps/dav/tests/temporary/benchmark.sh b/apps/dav/tests/benchmarks/benchmark.sh
index 4a2f283e320..27d7c4ecbc7 100755
--- a/apps/dav/tests/temporary/benchmark.sh
+++ b/apps/dav/tests/benchmarks/benchmark.sh
@@ -2,6 +2,8 @@
set -eu
+# benchmark.sh
+
export KB=1000
export MB=$((KB*1000))
@@ -32,10 +34,10 @@ do
echo "- Upload of $nb tiny file of ${size}B"
echo " - Bundled"
start=$(date +%s)
- echo "$requests_count" | xargs -d ' ' -P $CONCURRENCY -I{} ./bundle_upload.sh "$nb" "$size"
+ echo "$requests_count" | xargs -d ' ' -P $CONCURRENCY -I{} ./bulk_upload.sh "$nb" "$size"
end=$(date +%s)
- bundle_exec_time=$((end-start))
- echo "${bundle_exec_time}s"
+ bulk_exec_time=$((end-start))
+ echo "${bulk_exec_time}s"
echo " - Single"
start=$(date +%s)
@@ -44,7 +46,7 @@ do
single_exec_time=$((end-start))
echo "${single_exec_time}s"
- md_output+="| $nb | $size | $bundle_exec_time | $single_exec_time |\n"
+ md_output+="| $nb | $size | $bulk_exec_time | $single_exec_time |\n"
done
echo -en "$md_output" \ No newline at end of file
diff --git a/apps/dav/tests/temporary/bundle_upload.sh b/apps/dav/tests/benchmarks/bulk_upload.sh
index 9d2b9c6f200..862ddfe461f 100755
--- a/apps/dav/tests/temporary/bundle_upload.sh
+++ b/apps/dav/tests/benchmarks/bulk_upload.sh
@@ -2,6 +2,8 @@
set -eu
+# bulk_upload.sh <nb-of-files> <size-of-files>
+
KB=${KB:-100}
MB=${MB:-$((KB*1000))}
@@ -14,10 +16,10 @@ BANDWIDTH=${BANDWIDTH:-$((100*MB/CONCURRENCY))}
USER="admin"
PASS="password"
SERVER="nextcloud.test"
-UPLOAD_PATH="/tmp/bundle_upload_request_$(openssl rand --hex 8).txt"
+UPLOAD_PATH="/tmp/bulk_upload_request_$(openssl rand --hex 8).txt"
BOUNDARY="boundary_$(openssl rand --hex 8)"
-LOCAL_FOLDER="/tmp/bundle_upload/${BOUNDARY}_${NB}_${SIZE}"
-REMOTE_FOLDER="/bundle_upload/${BOUNDARY}_${NB}_${SIZE}"
+LOCAL_FOLDER="/tmp/bulk_upload/${BOUNDARY}_${NB}_${SIZE}"
+REMOTE_FOLDER="/bulk_upload/${BOUNDARY}_${NB}_${SIZE}"
mkdir --parent "$LOCAL_FOLDER"
@@ -48,7 +50,13 @@ done
echo -en "--$BOUNDARY--\r\n" >> "$UPLOAD_PATH"
-echo "Creating folder /${BOUNDARY}_${NB}_${SIZE}"
+echo "Creating folder /bulk_upload"
+curl \
+ -X MKCOL \
+ -k \
+ "https://$USER:$PASS@$SERVER/remote.php/dav/files/$USER/bulk_upload" > /dev/null
+
+echo "Creating folder $REMOTE_FOLDER"
curl \
-X MKCOL \
-k \
@@ -56,15 +64,15 @@ curl \
echo "Uploading $NB files with total size: $(du -sh "$UPLOAD_PATH" | cut -d ' ' -f1)"
echo "Local file is: $UPLOAD_PATH"
-blackfire curl \
+curl \
-X POST \
-k \
--progress-bar \
--limit-rate "${BANDWIDTH}k" \
- --cookie "XDEBUG_PROFILE=MROW4A;path=/;" \
+ --cookie "XDEBUG_PROFILE=true;path=/;" \
-H "Content-Type: multipart/related; boundary=$BOUNDARY" \
--data-binary "@$UPLOAD_PATH" \
- "https://$USER:$PASS@$SERVER/remote.php/dav/files/bundle"
+ "https://$USER:$PASS@$SERVER/remote.php/dav/bulk"
rm -rf "${LOCAL_FOLDER:?}"
rm "$UPLOAD_PATH"
diff --git a/apps/dav/tests/temporary/single_upload.sh b/apps/dav/tests/benchmarks/single_upload.sh
index da8e414be60..ec57e66668d 100755
--- a/apps/dav/tests/temporary/single_upload.sh
+++ b/apps/dav/tests/benchmarks/single_upload.sh
@@ -2,6 +2,8 @@
set -eu
+# single_upload.sh <nb-of-files> <size-of-files>
+
export KB=${KB:-100}
export MB=${MB:-$((KB*1000))}
@@ -15,15 +17,20 @@ export USER="admin"
export PASS="password"
export SERVER="nextcloud.test"
export UPLOAD_ID="single_$(openssl rand --hex 8)"
-export LOCAL_FOLDER="/tmp/bundle_upload/${UPLOAD_ID}_${NB}_${SIZE}"
-export REMOTE_FOLDER="/bundle_upload/${UPLOAD_ID}_${NB}_${SIZE}"
+export LOCAL_FOLDER="/tmp/single_upload/${UPLOAD_ID}_${NB}_${SIZE}"
+export REMOTE_FOLDER="/single_upload/${UPLOAD_ID}_${NB}_${SIZE}"
mkdir --parent "$LOCAL_FOLDER"
curl \
-X MKCOL \
-k \
- --cookie "XDEBUG_SESSION=MROW4A;path=/;" \
+ "https://$USER:$PASS@$SERVER/remote.php/dav/files/$USER/bulk_upload" > /dev/null
+
+curl \
+ -X MKCOL \
+ -k \
+ --cookie "XDEBUG_SESSION=true;path=/;" \
"https://$USER:$PASS@$SERVER/remote.php/dav/files/$USER/$REMOTE_FOLDER"
upload_file() {
diff --git a/apps/dav/tests/unit/CapabilitiesTest.php b/apps/dav/tests/unit/CapabilitiesTest.php
index 719b62115d9..399467f6ed8 100644
--- a/apps/dav/tests/unit/CapabilitiesTest.php
+++ b/apps/dav/tests/unit/CapabilitiesTest.php
@@ -35,6 +35,7 @@ class CapabilitiesTest extends TestCase {
$expected = [
'dav' => [
'chunking' => '1.0',
+ 'bulkupload' => '1.0',
],
];
$this->assertSame($expected, $capabilities->getCapabilities());
diff --git a/apps/dav/tests/unit/Files/BundlePluginTest.php b/apps/dav/tests/unit/Files/BundlePluginTest.php
deleted file mode 100644
index 92309fe5021..00000000000
--- a/apps/dav/tests/unit/Files/BundlePluginTest.php
+++ /dev/null
@@ -1,711 +0,0 @@
-<?php
-/**
- * @author Piotr Mrowczynski <Piotr.Mrowczynski@owncloud.com>
- * @author Louis Chemineau <louis@chmn.me>
- *
- * @copyright Copyright (c) 2016, ownCloud GmbH.
- * @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/>
- *
- */
-namespace OCA\DAV\Files;
-
-use OC\Files\FileInfo;
-use OC\Files\Storage\Local;
-use Sabre\HTTP\RequestInterface;
-use Test\TestCase;
-use OC\Files\View;
-use OCP\Files\Storage;
-use Sabre\DAV\Exception;
-use OC\Files\Filesystem;
-use OCP\Files\StorageNotAvailableException;
-
-/**
- * Class BundlingPlugin
- *
- * @group DB
- *
- * @package OCA\DAV\Tests\unit\Files
- */
-class BundlingPluginTest extends TestCase {
-
- /**
- * @var string
- */
- private $user;
-
- /** @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject */
- private $view;
-
- /** @var \OC\Files\FileInfo | \PHPUnit_Framework_MockObject_MockObject */
- private $info;
-
- /**
- * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject
- */
- private $server;
-
- /**
- * @var FilesPlugin
- */
- private $plugin;
-
- /**
- * @var \Sabre\HTTP\RequestInterface | \PHPUnit_Framework_MockObject_MockObject
- */
- private $request;
- /**
- * @var \Sabre\HTTP\ResponseInterface | \PHPUnit_Framework_MockObject_MockObject
- */
- private $response;
-
- /**
- * @var MultipartContentsParser | \PHPUnit_Framework_MockObject_MockObject
- */
- private $contentHandler;
-
- const BOUNDRARY = 'test_boundrary';
-
- public function setUp() {
- parent::setUp();
-// $this->server = new \Sabre\DAV\Server();
-
- $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
- ->setConstructorArgs(array())
- ->setMethods(array('emit'))
- ->getMock();
-
- $this->server->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
- ->disableOriginalConstructor()
- ->getMock();
-
- // setup
- $storage = $this->getMockBuilder(Local::class)
- ->setMethods(["fopen","moveFromStorage","file_exists"])
- ->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]])
- ->getMock();
- $storage->method('fopen')
- ->will($this->returnCallback(
- function ($path,$mode) {
- $bodyStream = fopen('php://temp', 'r+');
- return $bodyStream;
- }
- ));
- $storage->method('moveFromStorage')
- ->will($this->returnValue(true));
- $storage->method('file_exists')
- ->will($this->returnValue(true));
-
- \OC_Hook::clear();
-
- $this->user = $this->getUniqueID('user_');
- $userManager = \OC::$server->getUserManager();
- $userManager->createUser($this->user, 'pass');
-
- $this->loginAsUser($this->user);
-
- Filesystem::mount($storage, [], $this->user . '/');
-
- $this->view = $this->getMockBuilder(View::class)
- ->setMethods(['resolvePath', 'touch', 'file_exists', 'getFileInfo'])
- ->setConstructorArgs([])
- ->getMock();
-
- $this->view->method('touch')
- ->will($this->returnValue(true));
-
- $this->view
- ->method('resolvePath')
- ->will($this->returnCallback(
- function ($path) use ($storage) {
- return [$storage, $path];
- }
- ));
-
- $this->view
- ->method('getFileInfo')
- ->will($this->returnCallback(
- function ($path) {
- $props = array();
- $props['checksum'] = null;
- $props['etag'] = $path;
- $props['fileid'] = $path;
- $info = new FileInfo($path, null, null, $props, null);
- return $info;
- }
- ));
-
- $this->info = $this->createMock('OC\Files\FileInfo', [], [], '', false);
-
- $this->request = $this->getMockBuilder(RequestInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->response = new \Sabre\HTTP\Response();
-
- $this->plugin = new BundlingPlugin(
- $this->view
- );
-
- $this->plugin->initialize($this->server);
- }
-
- /*TESTS*/
-
- /**
- * This test checks that if url endpoint is wrong, plugin with return exception
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage URL endpoint has to be instance of \OCA\DAV\Files\FilesHome
- */
- public function testHandleBundleNotHomeCollection() {
-
- $this->request
- ->expects($this->once())
- ->method('getPath')
- ->will($this->returnValue('notFilesHome.xml'));
-
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->server->tree->expects($this->once())
- ->method('getNodeForPath')
- ->with('notFilesHome.xml')
- ->will($this->returnValue($node));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate NULL request header
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Content-Type header is needed
- */
- public function testHandleBundleNoHeader() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->once())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue(null));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate empty request header
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Content-Type header must not be empty
- */
- public function testHandleBundleEmptyHeader() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->once())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue(""));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate content-type header without boundrary specification request header
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Improper Content-type format. Boundary may be missing
- */
- public function testHandleBundleNoBoundraryHeader() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue("multipart/related"));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate content-type header with wrong boundrary specification request header
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Boundary is not set
- */
- public function testHandleBundleWrongBoundraryHeader() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue("multipart/related;thisIsNotBoundrary"));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate content-type header with wrong boundrary specification request header
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Content-Type must be multipart/related
- */
- public function testHandleBundleWrongContentTypeHeader() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue("multipart/mixed; boundary=".self::BOUNDRARY));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Simulate content-type header with alternative correct boundrary specification request header
- *
- * Request with user out of quota
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage beforeWriteBundle preconditions failed
- */
- public function testHandleAlternativeBoundraryPlusBundleOutOfQuota() {
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue("multipart/related; boundary=\"".self::BOUNDRARY."\""));
-
- $this->server
- ->expects($this->once())
- ->method('emit')
- ->will($this->returnValue(false));
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Request without request body
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Unable to get request content
- */
- public function testHandleBundleWithNullBody() {
- $this->setupServerTillHeader();
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Test empty request body. This will pass getPartHeader, but exception will be raised after we ready headers
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Incorrect Content-type format. Charset might be missing
- */
- public function testHandleBundleWithEmptyBody() {
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody("");
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Test wrong request body
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Expected boundary delimiter in content part - this is not a multipart request
- */
- public function testHandleBundleWithWrongBody() {
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody("WrongBody");
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Test wrong request body, with metadata header containing no charset
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Incorrect Content-type format. Charset might be missing
- */
- public function testHandleMetadataNoCharsetType(){
- $bodyContent = 'I am wrong metadata not in utf-8';
- $headers['content-length'] = strlen($bodyContent);
- $headers['content-type'] = 'text/xml';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "--".self::BOUNDRARY
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\n\r\n"
- ."$bodyContent\r\n--".self::BOUNDRARY."--";
-
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Test wrong request body, with metadata header containing wrong content-type
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Content-Type must be text/xml
- */
- public function testHandleMetadataWrongContentType(){
- $bodyContent = 'I am wrong metadata content type';
- $headers['content-type'] = 'text/plain; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "--".self::BOUNDRARY
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\n\r\n"
- ."$bodyContent\r\n--".self::BOUNDRARY."--";
-
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Test wrong request body, with metadata header containing wrong content-type
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Bundle metadata header does not contain Content-Length. Unable to parse whole bundle request
- */
- public function testHandleMetadataNoContentLength(){
- $bodyContent = 'I am wrong metadata content type';
- //$headers['content-length'] = strlen($bodyContent);
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "--".self::BOUNDRARY
- ."\r\nContent-Type: ".$headers['content-type']
- //."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent\r\n--".self::BOUNDRARY."--";
-
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Try to parse body which is not xml
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Bundle metadata contains incorrect xml structure. Unable to parse whole bundle request
- */
- public function testHandleWrongMetadataNoXML(){
- $bodyContent = "I am not xml";
-
- $this->setupServerTillMetadata($bodyContent);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Try to parse body which has xml d:multipart element which
- * has not been declared <d:multipart xmlns:d='DAV:'>
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Bundle metadata does not contain d:multipart children elements
- */
- public function testHandleWrongMetadataWrongXMLdElement(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?><d:multipart></d:multipart>";
-
- $this->setupServerTillMetadata($bodyContent);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * This test checks that exception is raised for
- * parsed XML which contains empty(without d:part elements) d:multipart section in metadata XML
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Bundle metadata does not contain d:multipart/d:part/d:prop children elements
- */
- public function testHandleEmptyMultipartMetadataSection(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?><d:multipart xmlns:d='DAV:'></d:multipart>";
-
- $this->setupServerTillMetadata($bodyContent);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Metadata contains part properties not containing obligatory field will raise exception
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage Undefined index: oc-id
- */
- public function testHandleWrongMetadataNoPartID(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?>
- <d:multipart xmlns:d='DAV:'>
- <d:part>
- <d:prop>
- </d:prop>
- </d:part>
- </d:multipart>";
-
- $this->setupServerTillMetadata($bodyContent);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * In the request, insert two files with the same Content-ID
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage One or more files have the same Content-ID 1. Unable to parse whole bundle request
- */
- public function testHandleWrongMetadataMultipleIDs(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?>
- <d:multipart xmlns:d='DAV:'>
- <d:part>
- <d:prop>
- <d:oc-path>/test/zombie1.jpg</d:oc-path>\n
- <d:oc-mtime>1476393386</d:oc-mtime>\n
- <d:oc-id>1</d:oc-id>\n
- <d:oc-total-length>6</d:oc-total-length>\n
- </d:prop>
- </d:part>
- <d:part>
- <d:prop>
- <d:oc-path>/test/zombie2.jpg</d:oc-path>\n
- <d:oc-mtime>1476393386</d:oc-mtime>\n
- <d:oc-id>1</d:oc-id>\n
- <d:oc-total-length>6</d:oc-total-length>\n
- </d:prop>
- </d:part>
- </d:multipart>";
-
- $this->setupServerTillMetadata($bodyContent);
-
- $this->plugin->handleBundle($this->request, $this->response);
- }
-
- /**
- * Specify metadata part without corresponding binary content
- *
- */
- public function testHandleWithoutBinaryContent(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?>
- <d:multipart xmlns:d='DAV:'>
- <d:part>
- <d:prop>
- <d:oc-path>/test/zombie1.jpg</d:oc-path>\n
- <d:oc-mtime>1476393386</d:oc-mtime>\n
- <d:oc-id>1</d:oc-id>\n
- <d:oc-total-length>6</d:oc-total-length>\n
- </d:prop>
- </d:part>
- </d:multipart>";
-
- $this->setupServerTillMetadata($bodyContent);
- $this->plugin->handleBundle($this->request, $this->response);
- $return = $this->response->getBody();
- $this->assertTrue(false != $return);
- $xml = simplexml_load_string($return);
- $this->assertTrue(false != $xml);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('s','http://sabredav.org/ns');
-
- $this->assertEquals(1, count($xml->xpath('/d:multistatus')));
-
- $fileMetadataObjectXML = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertTrue(false != $fileMetadataObjectXML);
- $this->assertEquals(1, count($fileMetadataObjectXML));
- $this->assertEquals("HTTP/1.1 400 Bad Request", (string) $fileMetadataObjectXML[0]);
-
- $fileMetadataObjectXML = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:error/s:message');
- $this->assertTrue(false != $fileMetadataObjectXML);
- $this->assertEquals(1, count($fileMetadataObjectXML));
- $this->assertEquals("File parsing error", (string) $fileMetadataObjectXML[0]);
- }
-
- /**
- * This test will simulate success and failure in putFile class.
- *
- */
- public function testHandlePutFile(){
- $this->setupServerTillData();
-
- $this->view
- ->method('file_exists')
- ->will($this->onConsecutiveCalls(true, false, $this->throwException(new StorageNotAvailableException())));
-
- $this->plugin->handleBundle($this->request, $this->response);
-
- $return = $this->response->getBody();
- $this->assertTrue(false != $return);
- $xml = simplexml_load_string($return);
- $this->assertTrue(false != $xml);
- $xml->registerXPathNamespace('d','urn:DAV');
- $xml->registerXPathNamespace('s','http://sabredav.org/ns');
-
- $this->assertEquals(1, count($xml->xpath('/d:multistatus')));
-
- $fileMetadataObjectXML = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status');
- $this->assertTrue(false != $fileMetadataObjectXML);
- $this->assertEquals(3, count($fileMetadataObjectXML));
- $this->assertEquals("HTTP/1.1 400 Bad Request", (string) $fileMetadataObjectXML[0]);
- $this->assertEquals("HTTP/1.1 200 OK", (string) $fileMetadataObjectXML[1]);
- $this->assertEquals("HTTP/1.1 400 Bad Request", (string) $fileMetadataObjectXML[2]);
-
- $fileMetadataObjectXML = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:error/s:message');
- $this->assertTrue(false != $fileMetadataObjectXML);
- $this->assertEquals(2, count($fileMetadataObjectXML));
- $this->assertEquals("Bundling not supported for already existing files", (string) $fileMetadataObjectXML[0]);
- $this->assertEquals("StorageNotAvailableException raised", (string) $fileMetadataObjectXML[1]);
- }
-
- /*UTILITIES*/
-
- private function setupServerTillData(){
- $bodyContent = "<?xml version='1.0' encoding='UTF-8'?>
- <d:multipart xmlns:d='DAV:'>
- <d:part>
- <d:prop>
- <d:oc-path>/test/zombie1.jpg</d:oc-path>\n
- <d:oc-mtime>1476393386</d:oc-mtime>\n
- <d:oc-id>0</d:oc-id>\n
- <d:oc-total-length>7</d:oc-total-length>\n
- </d:prop>
- </d:part>
- <d:part>
- <d:prop>
- <d:oc-path>/test/zombie2.jpg</d:oc-path>\n
- <d:oc-mtime>1476393386</d:oc-mtime>\n
- <d:oc-id>1</d:oc-id>\n
- <d:oc-total-length>7</d:oc-total-length>\n
- </d:prop>
- </d:part>
- <d:part>
- <d:prop>
- <d:oc-path>zombie3.jpg</d:oc-path>\n
- <d:oc-mtime>1476393232</d:oc-mtime>\n
- <d:oc-id>2</d:oc-id>\n
- <d:oc-total-length>7</d:oc-total-length>\n
- </d:prop>
- </d:part>
- </d:multipart>";
-
- $headers['content-length'] = strlen($bodyContent);
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "--".self::BOUNDRARY
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent"
- ."\r\n--".self::BOUNDRARY
- ."\r\nContent-ID: 0"
- ."\r\n\r\n"
- ."zombie1"
- ."\r\n--".self::BOUNDRARY
- ."\r\nContent-ID: 1"
- ."\r\n\r\n"
- ."zombie2"
- ."\r\n--".self::BOUNDRARY
- ."\r\nContent-ID: 2"
- ."\r\n\r\n"
- ."zombie3"
- ."\r\n--".self::BOUNDRARY."--";
-
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- }
-
- private function setupServerTillMetadata($bodyContent){
- $headers['content-length'] = strlen($bodyContent);
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "--".self::BOUNDRARY
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent\r\n--".self::BOUNDRARY."--";
-
- $this->setupServerTillHeader();
-
- $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- }
-
- private function setupServerTillHeader(){
- $this->setupServerTillFilesHome();
-
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->with('Content-Type')
- ->will($this->returnValue("multipart/related; boundary=".self::BOUNDRARY));
-
- $this->server
- ->expects($this->once())
- ->method('emit')
- ->will($this->returnValue(true));
- }
-
- private function setupServerTillFilesHome(){
- $this->request
- ->expects($this->once())
- ->method('getPath')
- ->will($this->returnValue('files/admin'));
-
- $node = $this->getMockBuilder('\OCA\DAV\Files\FilesHome')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->server->tree->expects($this->once())
- ->method('getNodeForPath')
- ->with('files/admin')
- ->will($this->returnValue($node));
- }
-
- private function fillMultipartContentsParserStreamWithBody($bodyString){
- $bodyStream = fopen('php://temp', 'r+');
- fwrite($bodyStream, $bodyString);
- rewind($bodyStream);
-
- $this->request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
- }
-
- public function tearDown() {
- $userManager = \OC::$server->getUserManager();
- $userManager->get($this->user)->delete();
- unset($_SERVER['HTTP_OC_CHUNKED']);
-
- parent::tearDown();
- }
-}
diff --git a/apps/dav/tests/unit/Files/BundledFileTest.php b/apps/dav/tests/unit/Files/BundledFileTest.php
deleted file mode 100644
index e1a65459b60..00000000000
--- a/apps/dav/tests/unit/Files/BundledFileTest.php
+++ /dev/null
@@ -1,220 +0,0 @@
-<?php
-/**
- * @author Piotr Mrowczynski <Piotr.Mrowczynski@owncloud.com>
- * @author Louis Chemineau <louis@chmn.me>
- *
- * @copyright Copyright (c) 2016, ownCloud GmbH.
- * @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/>
- *
- */
-
-namespace OCA\DAV\Files;
-
-use OCP\Lock\ILockingProvider;
-
-/**
- * Class File
- *
- * @group DB
- *
- * @package OCA\DAV\Tests\unit\Connector\Sabre
- */
-class BundledFileTest extends \Test\TestCase {
-
- /**
- * @var string
- */
- private $user;
-
- /* BASICS */
-
- public function setUp() {
- parent::setUp();
-
- \OC_Hook::clear();
-
- $this->user = $this->getUniqueID('user_');
- $userManager = \OC::$server->getUserManager();
- $userManager->createUser($this->user, 'pass');
-
- $this->loginAsUser($this->user);
- }
-
- /* TESTS */
-
- /**
- * Test basic successful bundled file PutFile
- */
- public function testPutFile() {
- $bodyContent = 'blabla';
- $headers['oc-total-length'] = 6;
- $headers['oc-path'] = '/foo.txt';
- $headers['oc-mtime'] = '1473336321';
- $headers['response'] = null;
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- $this->doPutFIle($headers, $multipartContentsParser);
- }
-
- /**
- * Test basic successful bundled file PutFile
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage File requires oc-total-length header to be read
- */
- public function testPutFileNoLength() {
- $bodyContent = 'blabla';
- $headers['oc-path'] = '/foo.txt';
- $headers['oc-mtime'] = '1473336321';
- $headers['response'] = null;
-
- //this part will have some arbitrary, correct headers
- $bodyFull = "$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- $this->doPutFIle($headers, $multipartContentsParser);
- }
-
- /**
- * Test putting a single file
- *
- * @expectedException \Sabre\DAV\Exception\Forbidden
- * @expectedExceptionMessage PUT method not supported for bundling
- */
- public function testThrowIfPut() {
- $fileContents = $this->getStream('test data');
- $this->doPut('/foo.txt', $fileContents);
- }
-
- /* UTILITIES */
-
- private function getMockStorage() {
- $storage = $this->getMockBuilder('\OCP\Files\Storage')
- ->getMock();
- $storage->expects($this->any())
- ->method('getId')
- ->will($this->returnValue('home::someuser'));
- return $storage;
- }
-
- public function tearDown() {
- $userManager = \OC::$server->getUserManager();
- $userManager->get($this->user)->delete();
- unset($_SERVER['HTTP_OC_CHUNKED']);
-
- parent::tearDown();
- }
-
- /**
- * @param string $string
- */
- private function getStream($string) {
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $string);
- fseek($stream, 0);
- return $stream;
- }
-
- /**
- * Do basic put for single bundled file
- */
- private function doPutFIle($fileMetadata, $contentHandler, $view = null, $viewRoot = null) {
- $path = $fileMetadata['oc-path'];
-
- if(is_null($view)){
- $view = \OC\Files\Filesystem::getView();
- }
- if (!is_null($viewRoot)) {
- $view = new \OC\Files\View($viewRoot);
- } else {
- $viewRoot = '/' . $this->user . '/files';
- }
-
- $info = new \OC\Files\FileInfo(
- $viewRoot . '/' . ltrim($path, '/'),
- $this->getMockStorage(),
- null,
- ['permissions' => \OCP\Constants::PERMISSION_ALL],
- null
- );
-
- $file = new BundledFile($view, $info, $contentHandler);
-
- // beforeMethod locks
- $view->lockFile($path, ILockingProvider::LOCK_SHARED);
-
- $result = $file->putFile($fileMetadata);
-
- // afterMethod unlocks
- $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
-
- return $result;
- }
-
- private function fillMultipartContentsParserStreamWithBody($bodyString){
- $bodyStream = fopen('php://temp', 'r+');
- fwrite($bodyStream, $bodyString);
- rewind($bodyStream);
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- return $mcp;
- }
-
- /**
- * Simulate putting a file to the given path.
- *
- * @param string $path path to put the file into
- * @param string $viewRoot root to use for the view
- *
- * @return null|string of the PUT operaiton which is usually the etag
- */
- private function doPut($path, $fileContents, $viewRoot = null) {
- $view = \OC\Files\Filesystem::getView();
- if (!is_null($viewRoot)) {
- $view = new \OC\Files\View($viewRoot);
- } else {
- $viewRoot = '/' . $this->user . '/files';
- }
-
- $info = new \OC\Files\FileInfo(
- $viewRoot . '/' . ltrim($path, '/'),
- $this->getMockStorage(),
- null,
- ['permissions' => \OCP\Constants::PERMISSION_ALL],
- null
- );
-
- $file = new BundledFile($view, $info, null);
-
- // beforeMethod locks
- $view->lockFile($path, ILockingProvider::LOCK_SHARED);
-
- $result = $file->put($fileContents);
-
- // afterMethod unlocks
- $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
-
- return $result;
- }
-}
diff --git a/apps/dav/tests/unit/Files/MultipartContentsParserTest.php b/apps/dav/tests/unit/Files/MultipartContentsParserTest.php
deleted file mode 100644
index 6d23fe296d2..00000000000
--- a/apps/dav/tests/unit/Files/MultipartContentsParserTest.php
+++ /dev/null
@@ -1,416 +0,0 @@
-<?php
-/**
- * @author Piotr Mrowczynski <Piotr.Mrowczynski@owncloud.com>
- * @author Louis Chemineau <louis@chmn.me>
- *
- * @copyright Copyright (c) 2016, ownCloud GmbH.
- * @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/>
- *
- */
-
-namespace OCA\DAV\Tests\unit\DAV;
-
-use Test\TestCase;
-
-class MultipartContentsParserTest extends TestCase {
- private $boundrary;
-
- protected function setUp() {
- parent::setUp();
-
- $this->boundrary = 'boundary';
-
- }
-
- /*TESTS*/
-
- /**
- * Test basic gets() functionality, that if passed string instead of resource, it should fail
- *
- * @expectedException \Sabre\DAV\Exception\BadRequest
- * @expectedExceptionMessage Unable to get request content
- */
- public function testGetsThrowWrongContents() {
- //TODO
- $bodyStream = "I am not a stream, but pretend to be";
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
-
- $mcp->gets();
- }
-
- /**
- * Test function readHeaders(), so if passed empty string, it will return null
- *
- */
- public function testReadHeadersThrowEmptyHeader() {
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- $mcp->readHeaders('');
- $this->assertEquals(null, $mcp->readHeaders(''));
- }
-
- /**
- * streamRead function with incorrect parameter
- *
- * @expectedException \Sabre\DAV\Exception\BadRequest
- * @expectedExceptionMessage Method streamRead cannot read contents with negative length
- */
- public function testStreamReadToStringThrowNegativeLength() {
- $bodyContent = 'blabla';
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyContent);
- //give negative length
- $multipartContentsParser->streamReadToString(-1);
- }
-
- /**
- * streamRead function with incorrect parameter
- *
- * @expectedException \Sabre\DAV\Exception\BadRequest
- * @expectedExceptionMessage Method streamRead cannot read contents with negative length
- */
- public function testStreamReadToStreamThrowNegativeLength() {
- $target = fopen('php://temp', 'r+');
- $bodyContent = 'blabla';
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyContent);
- //give negative length
- $multipartContentsParser->streamReadToStream($target,-1);
- }
-
- public function testStreamReadToString() {
- $length = 0;
- list($multipartContentsParser, $bodyString) = $this->fillMultipartContentsParserStreamWithChars($length);
- $this->assertEquals($bodyString, $multipartContentsParser->streamReadToString($length));
-
- $length = 1000;
- list($multipartContentsParser, $bodyString) = $this->fillMultipartContentsParserStreamWithChars($length);
- $this->assertEquals($bodyString, $multipartContentsParser->streamReadToString($length));
-
- $length = 8192;
- list($multipartContentsParser, $bodyString) = $this->fillMultipartContentsParserStreamWithChars($length);
- $this->assertEquals($bodyString, $multipartContentsParser->streamReadToString($length));
-
- $length = 20000;
- list($multipartContentsParser, $bodyString) = $this->fillMultipartContentsParserStreamWithChars($length);
- $this->assertEquals($bodyString, $multipartContentsParser->streamReadToString($length));
- }
-
- public function testStreamReadToStream() {
- $length = 0;
- $this->streamReadToStreamBuilder($length);
-
- $length = 1000;
- $this->streamReadToStreamBuilder($length);
-
- $length = 8192;
- $this->streamReadToStreamBuilder($length);
-
- $length = 20000;
- $this->streamReadToStreamBuilder($length);
- }
-
- private function streamReadToStreamBuilder($length) {
- $target = fopen('php://temp', 'r+');
- list($multipartContentsParser, $bodyString) = $this->fillMultipartContentsParserStreamWithChars($length);
- $this->assertEquals(true, $multipartContentsParser->streamReadToStream($target,$length));
- rewind($target);
- $this->assertEquals($bodyString, stream_get_contents($target));
- }
-
- /**
- * @expectedException \Exception
- * @expectedExceptionMessage An error appears while reading and parsing header of content part using fgets
- */
- public function testGetPartThrowFailfgets() {
- $bodyStream = fopen('php://temp', 'r+');
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = $this->getMockBuilder('OCA\DAV\Files\MultipartContentsParser')
- ->setConstructorArgs(array($request))
- ->setMethods(array('gets'))
- ->getMock();
-
- $mcp->expects($this->any())
- ->method('gets')
- ->will($this->onConsecutiveCalls("--boundary\r\n", "Content-ID: 0\r\n", false));
-
- $mcp->getPartHeaders($this->boundrary);
- }
-
- /**
- * If one one the content parts does not contain boundrary, means that received wrong request
- *
- * @expectedException \Exception
- * @expectedExceptionMessage Expected boundary delimiter in content part
- */
- public function testGetPartThrowNoBoundraryFound() {
- // Calling multipletimes getPart on parts without contents should return null,null and signal immedietaly that endDelimiter was reached
- $bodyFull = "--boundary_wrong\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- $multipartContentsParser->getPartHeaders($this->boundrary);
- }
-
- /**
- * Reading from request which method getBody returns false
- *
- * @expectedException \Sabre\DAV\Exception\BadRequest
- * @expectedExceptionMessage Unable to get request content
- */
- public function testStreamReadThrowWrongBody() {
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn(false);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- $mcp->getPartHeaders($this->boundrary);
- }
-
- /**
- * Reading from request which method getBody returns false
- *
- */
- public function testMultipartContentSeekToContentLength() {
- $bodyStream = fopen('php://temp', 'r+');
- $bodyString = '';
- $length = 1000;
- for ($x = 0; $x < $length; $x++) {
- $bodyString .= 'k';
- }
- fwrite($bodyStream, $bodyString);
- rewind($bodyStream);
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- $this->assertEquals(true,$mcp->multipartContentSeekToContentLength($length));
- }
-
- /**
- * Test cases with wrong or incomplete boundraries
- *
- */
- public function testGetPartHeadersWrongBoundaryCases() {
- // Calling multipletimes getPart on parts without contents should return null and signal immedietaly that endDelimiter was reached
- $bodyFull = "--boundary\r\n--boundary_wrong\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- $this->assertEquals(null,$multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
-
- // Test empty content
- $bodyFull = "--boundary\r\n";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- $this->assertEquals(null, $multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
-
- // Test empty content
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody('');
- $this->assertEquals(null, $multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
-
- // Calling multipletimes getPart on parts without contents should return null and signal immedietaly that endDelimiter was reached
- // endDelimiter should be signaled after first getPart since it will read --boundrary till it finds contents.
- $bodyFull = "--boundary\r\n--boundary\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
- $this->assertEquals(null,$multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- $this->assertEquals(null,$multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- $this->assertEquals(null,$multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- $this->assertEquals(null,$multipartContentsParser->getPartHeaders($this->boundrary));
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- }
-
- /**
- * Test will check if we can correctly parse headers and content using streamReadToString
- *
- */
- public function testReadHeaderBodyCorrect() {
- //multipart part will have some content bodyContent and some headers
- $bodyContent = 'blabla';
- $headers['content-length'] = '6';
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = '--boundary'
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- //parse it
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $bodyParsed = $multipartContentsParser->streamReadToString(6);
-
- //check if end delimiter is not reached, since we just read 6 bytes, and stopped at \r\n
- $this->assertEquals(false,$multipartContentsParser->getEndDelimiterReached());
-
- //check that we parsed correct headers
- $this->assertEquals($bodyContent, $bodyParsed);
- $this->assertEquals($headers, $headersParsed);
-
- //parse further to check if there is new part. There is no, so headers are null and delimiter reached
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $this->assertEquals(null,$headersParsed);
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- }
-
- /**
- * Test will check parsing incorrect headers and content using streamReadToString
- *
- */
- public function testReadHeaderBodyIncorrect() {
-
- //multipart part will have some content bodyContent and some headers
- $bodyContent = 'blabla';
- $headers['content-length'] = '6';
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will one correct and one incorrect header
- $bodyFull = '--boundary'
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length"
- ."\r\n\r\n"
- ."$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- //parse it and expect null, since contains incorrect headers
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $this->assertEquals(null, $headersParsed);
- $this->assertEquals(false,$multipartContentsParser->getEndDelimiterReached());
-
- //parse further to check if next call with not read headers again
- //this should return null again and get to end of delimiter
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $this->assertEquals(null,$headersParsed);
- $this->assertEquals(true,$multipartContentsParser->getEndDelimiterReached());
- }
-
- /**
- * Test will check reading error in StreamReadToString
- *
- * @expectedException \Sabre\DAV\Exception\BadRequest
- * @expectedExceptionMessage Method streamRead read 20 expeceted 60
- */
- public function testReadBodyIncorrect() {
- //multipart part will have some content bodyContent and content-length header will specify to big value
- //this
- $bodyContent = 'blabla';
- $headers['content-length'] = '60';
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = '--boundary'
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- //parse headers
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $this->assertEquals($headers, $headersParsed);
-
- $this->assertEquals(true, array_key_exists('content-length',$headersParsed));
- $multipartContentsParser->streamReadToString($headersParsed['content-length']);
- }
-
- /**
- * Test will check reading error in StreamReadToString return false
- *
- */
- public function testReadBodyStreamIncorrect() {
- //multipart part will have some content bodyContent and content-length header will specify to big value
- //this
- $bodyContent = 'blabla';
- $headers['content-length'] = '60';
- $headers['content-type'] = 'text/xml; charset=utf-8';
-
- //this part will have some arbitrary, correct headers
- $bodyFull = '--boundary'
- ."\r\nContent-Type: ".$headers['content-type']
- ."\r\nContent-length: ".$headers['content-length']
- ."\r\n\r\n"
- ."$bodyContent\r\n--boundary--";
- $multipartContentsParser = $this->fillMultipartContentsParserStreamWithBody($bodyFull);
-
- //parse headers
- $headersParsed = $multipartContentsParser->getPartHeaders($this->boundrary);
- $this->assertEquals($headers, $headersParsed);
-
- $this->assertEquals(true, array_key_exists('content-length',$headersParsed));
- $target = fopen('php://temp', 'r+');
- $bodyParsed = $multipartContentsParser->streamReadToStream($target, $headersParsed['content-length']);
- $this->assertEquals(false, $bodyParsed);
- }
-
- /*UTILITIES*/
-
- private function fillMultipartContentsParserStreamWithChars($length){
- $bodyStream = fopen('php://temp', 'r+');
- $bodyString = '';
- for ($x = 0; $x < $length; $x++) {
- $bodyString .= 'k';
- }
- fwrite($bodyStream, $bodyString);
- rewind($bodyStream);
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- return array($mcp, $bodyString);
- }
-
- private function fillMultipartContentsParserStreamWithBody($bodyString){
- $bodyStream = fopen('php://temp', 'r+');
- fwrite($bodyStream, $bodyString);
- rewind($bodyStream);
- $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
- ->disableOriginalConstructor()
- ->getMock();
- $request->expects($this->any())
- ->method('getBody')
- ->willReturn($bodyStream);
-
- $mcp = new \OCA\DAV\Files\MultipartContentsParser($request);
- return $mcp;
- }
-}
diff --git a/apps/dav/tests/unit/Files/MultipartRequestParserTest.php b/apps/dav/tests/unit/Files/MultipartRequestParserTest.php
new file mode 100644
index 00000000000..ec9e2d0a383
--- /dev/null
+++ b/apps/dav/tests/unit/Files/MultipartRequestParserTest.php
@@ -0,0 +1,281 @@
+<?php
+/**
+ * @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/>
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\DAV;
+
+use Test\TestCase;
+use \OCA\DAV\BulkUpload\MultipartRequestParser;
+
+class MultipartRequestParserTest extends TestCase {
+ private function getValidBodyObject() {
+ return [
+ [
+ "headers" => [
+ "Content-Length" => 7,
+ "X-File-MD5" => "4f2377b4d911f7ec46325fe603c3af03",
+ "X-File-Path" => "/coucou.txt"
+ ],
+ "content" => "Coucou\n"
+ ]
+ ];
+ }
+
+ private function getMultipartParser(array $parts, array $headers = [], string $boundary = "boundary_azertyuiop"): MultipartRequestParser {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $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 = "";
+ foreach ($parts as $part) {
+ $body .= '--'.$boundary."\r\n";
+
+ foreach ($part['headers'] as $headerKey => $headerPart) {
+ $body .= $headerKey.": ".$headerPart."\r\n";
+ }
+
+ $body .= "\r\n";
+ $body .= $part['content']."\r\n";
+ }
+
+ $body .= '--'.$boundary."--";
+
+ $stream = fopen('php://temp','r+');
+ fwrite($stream, $body);
+ rewind($stream);
+
+ $request->expects($this->any())
+ ->method('getBody')
+ ->willReturn($stream);
+
+ return new MultipartRequestParser($request);
+ }
+
+
+ /**
+ * 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')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request->expects($this->any())
+ ->method('getBody')
+ ->willReturn($bodyStream);
+
+ $this->expectExceptionMessage('Body should be of type resource');
+ new MultipartRequestParser($request);
+ }
+
+ /**
+ * Test with valid request.
+ * - valid boundary
+ * - valid md5 hash
+ * - valid content-length
+ * - valid file content
+ * - valid file path
+ */
+ public function testValidRequest() {
+ $multipartParser = $this->getMultipartParser(
+ $this->getValidBodyObject()
+ );
+
+ [$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($content, "Coucou\n", "Content should be the same");
+ }
+
+ /**
+ * Test with invalid md5 hash.
+ */
+ public function testInvalidMd5Hash() {
+ $bodyObject = $this->getValidBodyObject();
+ $bodyObject["0"]["headers"]["X-File-MD5"] = "f2377b4d911f7ec46325fe603c3af03";
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject
+ );
+
+ $this->expectExceptionMessage('Computed md5 hash is incorrect.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with a null md5 hash.
+ */
+ public function testNullMd5Hash() {
+ $bodyObject = $this->getValidBodyObject();
+ unset($bodyObject["0"]["headers"]["X-File-MD5"]);
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject
+ );
+
+ $this->expectExceptionMessage('The X-File-MD5 header must not be null.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with a null Content-Length.
+ */
+ public function testNullContentLength() {
+ $bodyObject = $this->getValidBodyObject();
+ unset($bodyObject["0"]["headers"]["Content-Length"]);
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject
+ );
+
+ $this->expectExceptionMessage('The Content-Length header must not be null.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with a lower Content-Length.
+ */
+ public function testLowerContentLength() {
+ $bodyObject = $this->getValidBodyObject();
+ $bodyObject["0"]["headers"]["Content-Length"] = 6;
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject
+ );
+
+ $this->expectExceptionMessage('Computed md5 hash is incorrect.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with a higher Content-Length.
+ */
+ public function testHigherContentLength() {
+ $bodyObject = $this->getValidBodyObject();
+ $bodyObject["0"]["headers"]["Content-Length"] = 8;
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject
+ );
+
+ $this->expectExceptionMessage('Computed md5 hash is incorrect.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with wrong boundary in body.
+ */
+ public function testWrongBoundary() {
+ $bodyObject = $this->getValidBodyObject();
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/related; boundary=boundary_poiuytreza']
+ );
+
+ $this->expectExceptionMessage('Boundary not found where it should be.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with no boundary in request headers.
+ */
+ public function testNoBoundaryInHeader() {
+ $bodyObject = $this->getValidBodyObject();
+ $this->expectExceptionMessage('Error while parsing boundary in Content-Type header.');
+ $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/related']
+ );
+ }
+
+ /**
+ * Test with no boundary in the request's headers.
+ */
+ public function testNoBoundaryInBody() {
+ $bodyObject = $this->getValidBodyObject();
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/related; boundary=boundary_azertyuiop'],
+ ''
+ );
+
+ $this->expectExceptionMessage('Boundary not found where it should be.');
+ $multipartParser->parseNextPart();
+ }
+
+ /**
+ * Test with a boundary with quotes in the request's headers.
+ */
+ public function testBoundaryWithQuotes() {
+ $bodyObject = $this->getValidBodyObject();
+ $multipartParser = $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/related; boundary="boundary_azertyuiop"'],
+ );
+
+ $multipartParser->parseNextPart();
+
+ // Dummy assertion, we just want to test that the parsing works.
+ $this->assertTrue(true);
+ }
+
+ /**
+ * Test with a wrong Content-Type in the request's headers.
+ */
+ public function testWrongContentType() {
+ $bodyObject = $this->getValidBodyObject();
+ $this->expectExceptionMessage('Content-Type must be multipart/related');
+ $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/form-data; boundary="boundary_azertyuiop"'],
+ );
+ }
+
+ /**
+ * Test with a wrong key after the content type in the request's headers.
+ */
+ public function testWrongKeyInContentType() {
+ $bodyObject = $this->getValidBodyObject();
+ $this->expectExceptionMessage('Boundary is invalid');
+ $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => 'multipart/related; wrongkey="boundary_azertyuiop"'],
+ );
+ }
+
+ /**
+ * Test with a null Content-Type in the request's headers.
+ */
+ public function testNullContentType() {
+ $bodyObject = $this->getValidBodyObject();
+ $this->expectExceptionMessage('Content-Type can not be null');
+ $this->getMultipartParser(
+ $bodyObject,
+ ['Content-Type' => null],
+
+ );
+ }
+}