Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/tests/unit/Connector/Sabre')
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/AuthTest.php611
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php130
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php149
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php62
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php313
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php267
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php70
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php57
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php58
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php83
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php174
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FileTest.php987
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php469
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php611
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php73
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/NodeTest.php149
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php355
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php273
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php223
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php117
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php73
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php46
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php46
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php56
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTest.php146
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php75
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php211
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php259
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php417
29 files changed, 6560 insertions, 0 deletions
diff --git a/apps/dav/tests/unit/Connector/Sabre/AuthTest.php b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php
new file mode 100644
index 00000000000..1c29d2dbd6f
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php
@@ -0,0 +1,611 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCP\IRequest;
+use OCP\IUser;
+use Test\TestCase;
+use OCP\ISession;
+use OC\User\Session;
+
+/**
+ * Class AuthTest
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ * @group DB
+ */
+class AuthTest extends TestCase {
+ /** @var ISession */
+ private $session;
+ /** @var \OCA\DAV\Connector\Sabre\Auth */
+ private $auth;
+ /** @var Session */
+ private $userSession;
+ /** @var IRequest */
+ private $request;
+
+ public function setUp() {
+ parent::setUp();
+ $this->session = $this->getMockBuilder('\OCP\ISession')
+ ->disableOriginalConstructor()->getMock();
+ $this->userSession = $this->getMockBuilder('\OC\User\Session')
+ ->disableOriginalConstructor()->getMock();
+ $this->request = $this->getMockBuilder('\OCP\IRequest')
+ ->disableOriginalConstructor()->getMock();
+ $this->auth = new \OCA\DAV\Connector\Sabre\Auth(
+ $this->session,
+ $this->userSession,
+ $this->request
+ );
+ }
+
+ public function testIsDavAuthenticatedWithoutDavSession() {
+ $this->session
+ ->expects($this->once())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue(null));
+
+ $this->assertFalse($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser']));
+ }
+
+ public function testIsDavAuthenticatedWithWrongDavSession() {
+ $this->session
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('AnotherUser'));
+
+ $this->assertFalse($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser']));
+ }
+
+ public function testIsDavAuthenticatedWithCorrectDavSession() {
+ $this->session
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('MyTestUser'));
+
+ $this->assertTrue($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser']));
+ }
+
+ public function testValidateUserPassOfAlreadyDAVAuthenticatedUser() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->exactly(2))
+ ->method('getUID')
+ ->will($this->returnValue('MyTestUser'));
+ $this->userSession
+ ->expects($this->once())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->exactly(2))
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->session
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('MyTestUser'));
+ $this->session
+ ->expects($this->once())
+ ->method('close');
+
+ $this->assertTrue($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']));
+ }
+
+ public function testValidateUserPassOfInvalidDAVAuthenticatedUser() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->once())
+ ->method('getUID')
+ ->will($this->returnValue('MyTestUser'));
+ $this->userSession
+ ->expects($this->once())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->session
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('AnotherUser'));
+ $this->session
+ ->expects($this->once())
+ ->method('close');
+
+ $this->assertFalse($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']));
+ }
+
+ public function testValidateUserPassOfInvalidDAVAuthenticatedUserWithValidPassword() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->exactly(4))
+ ->method('getUID')
+ ->will($this->returnValue('MyTestUser'));
+ $this->userSession
+ ->expects($this->once())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->exactly(4))
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->session
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('AnotherUser'));
+ $this->userSession
+ ->expects($this->once())
+ ->method('logClientIn')
+ ->with('MyTestUser', 'MyTestPassword')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->once())
+ ->method('createSessionToken')
+ ->with($this->request, 'MyTestUser', 'MyTestUser', 'MyTestPassword');
+ $this->session
+ ->expects($this->once())
+ ->method('set')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND', 'MyTestUser');
+ $this->session
+ ->expects($this->once())
+ ->method('close');
+
+ $this->assertTrue($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']));
+ }
+
+ public function testValidateUserPassWithInvalidPassword() {
+ $this->userSession
+ ->expects($this->once())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(false));
+ $this->userSession
+ ->expects($this->once())
+ ->method('logClientIn')
+ ->with('MyTestUser', 'MyTestPassword')
+ ->will($this->returnValue(false));
+ $this->session
+ ->expects($this->once())
+ ->method('close');
+
+ $this->assertFalse($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']));
+ }
+
+
+ public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGet() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->request
+ ->expects($this->any())
+ ->method('getMethod')
+ ->willReturn('POST');
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue(null));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('MyWrongDavUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->once())
+ ->method('passesCSRFCheck')
+ ->willReturn(false);
+
+ $expectedResponse = [
+ false,
+ "No 'Authorization: Basic' header found. Either the client didn't send one, or the server is mis-configured",
+ ];
+ $response = $this->auth->check($request, $response);
+ $this->assertSame($expectedResponse, $response);
+ }
+
+ public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndCorrectlyDavAuthenticated() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->willReturn(true);
+ $this->request
+ ->expects($this->any())
+ ->method('getMethod')
+ ->willReturn('PROPFIND');
+ $this->request
+ ->expects($this->any())
+ ->method('isUserAgent')
+ ->with([
+ '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
+ '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
+ '/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
+ ])
+ ->willReturn(false);
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('LoggedInUser'));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('LoggedInUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->once())
+ ->method('passesCSRFCheck')
+ ->willReturn(false);
+ $this->auth->check($request, $response);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\NotAuthenticated
+ * @expectedExceptionMessage CSRF check not passed.
+ */
+ public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndIncorrectlyDavAuthenticated() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->willReturn(true);
+ $this->request
+ ->expects($this->any())
+ ->method('getMethod')
+ ->willReturn('PROPFIND');
+ $this->request
+ ->expects($this->any())
+ ->method('isUserAgent')
+ ->with([
+ '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
+ '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
+ '/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
+ ])
+ ->willReturn(false);
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('AnotherUser'));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('LoggedInUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->once())
+ ->method('passesCSRFCheck')
+ ->willReturn(false);
+ $this->auth->check($request, $response);
+ }
+
+ public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGetAndDesktopClient() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->request
+ ->expects($this->any())
+ ->method('getMethod')
+ ->willReturn('POST');
+ $this->request
+ ->expects($this->any())
+ ->method('isUserAgent')
+ ->with([
+ '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/',
+ '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/',
+ '/^Mozilla\/5\.0 \(iOS\) ownCloud\-iOS.*$/',
+ ])
+ ->willReturn(true);
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue(null));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('MyWrongDavUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->once())
+ ->method('passesCSRFCheck')
+ ->willReturn(false);
+
+ $this->auth->check($request, $response);
+ }
+
+ public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForGet() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue(null));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('MyWrongDavUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->any())
+ ->method('getMethod')
+ ->willReturn('GET');
+
+ $response = $this->auth->check($request, $response);
+ $this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response);
+ }
+
+ public function testAuthenticateAlreadyLoggedInWithCsrfTokenForGet() {
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->session
+ ->expects($this->any())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue(null));
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('MyWrongDavUser'));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $this->request
+ ->expects($this->once())
+ ->method('passesCSRFCheck')
+ ->willReturn(true);
+
+ $response = $this->auth->check($request, $response);
+ $this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response);
+ }
+
+ public function testAuthenticateNoBasicAuthenticateHeadersProvided() {
+ $server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->auth->check($server->httpRequest, $server->httpResponse);
+ $this->assertEquals([false, 'No \'Authorization: Basic\' header found. Either the client didn\'t send one, or the server is mis-configured'], $response);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\NotAuthenticated
+ * @expectedExceptionMessage Cannot authenticate over ajax calls
+ */
+ public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax() {
+ /** @var \Sabre\HTTP\RequestInterface $httpRequest */
+ $httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var \Sabre\HTTP\ResponseInterface $httpResponse */
+ $httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(false));
+ $httpRequest
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('X-Requested-With')
+ ->will($this->returnValue('XMLHttpRequest'));
+ $this->auth->check($httpRequest, $httpResponse);
+ }
+
+ public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjaxButUserIsStillLoggedIn() {
+ /** @var \Sabre\HTTP\RequestInterface $httpRequest */
+ $httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var \Sabre\HTTP\ResponseInterface $httpResponse */
+ $httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var IUser */
+ $user = $this->getMock('OCP\IUser');
+ $user->method('getUID')->willReturn('MyTestUser');
+ $this->userSession
+ ->expects($this->any())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->session
+ ->expects($this->atLeastOnce())
+ ->method('get')
+ ->with('AUTHENTICATED_TO_DAV_BACKEND')
+ ->will($this->returnValue('MyTestUser'));
+ $this->request
+ ->expects($this->once())
+ ->method('getMethod')
+ ->willReturn('GET');
+ $httpRequest
+ ->expects($this->atLeastOnce())
+ ->method('getHeader')
+ ->with('Authorization')
+ ->will($this->returnValue(null));
+ $this->assertEquals(
+ [true, 'principals/users/MyTestUser'],
+ $this->auth->check($httpRequest, $httpResponse)
+ );
+ }
+
+ public function testAuthenticateValidCredentials() {
+ $server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpRequest
+ ->expects($this->at(0))
+ ->method('getHeader')
+ ->with('X-Requested-With')
+ ->will($this->returnValue(null));
+ $server->httpRequest
+ ->expects($this->at(1))
+ ->method('getHeader')
+ ->with('Authorization')
+ ->will($this->returnValue('basic dXNlcm5hbWU6cGFzc3dvcmQ='));
+ $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->once())
+ ->method('logClientIn')
+ ->with('username', 'password')
+ ->will($this->returnValue(true));
+ $this->userSession
+ ->expects($this->once())
+ ->method('createSessionToken');
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->expects($this->exactly(4))
+ ->method('getUID')
+ ->will($this->returnValue('MyTestUser'));
+ $this->userSession
+ ->expects($this->exactly(4))
+ ->method('getUser')
+ ->will($this->returnValue($user));
+ $response = $this->auth->check($server->httpRequest, $server->httpResponse);
+ $this->assertEquals([true, 'principals/users/MyTestUser'], $response);
+ }
+
+ public function testAuthenticateInvalidCredentials() {
+ $server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $server->httpRequest
+ ->expects($this->at(0))
+ ->method('getHeader')
+ ->with('X-Requested-With')
+ ->will($this->returnValue(null));
+ $server->httpRequest
+ ->expects($this->at(1))
+ ->method('getHeader')
+ ->with('Authorization')
+ ->will($this->returnValue('basic dXNlcm5hbWU6cGFzc3dvcmQ='));
+ $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSession
+ ->expects($this->once())
+ ->method('logClientIn')
+ ->with('username', 'password')
+ ->will($this->returnValue(false));
+ $response = $this->auth->check($server->httpRequest, $server->httpResponse);
+ $this->assertEquals([false, 'Username or password was incorrect'], $response);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php
new file mode 100644
index 00000000000..00378fd4139
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
+use Test\TestCase;
+use OCP\IConfig;
+
+/**
+ * Class BlockLegacyClientPluginTest
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class BlockLegacyClientPluginTest extends TestCase {
+ /** @var IConfig */
+ private $config;
+ /** @var BlockLegacyClientPlugin */
+ private $blockLegacyClientVersionPlugin;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->config = $this->getMock('\OCP\IConfig');
+ $this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin($this->config);
+ }
+
+ /**
+ * @return array
+ */
+ public function oldDesktopClientProvider() {
+ return [
+ ['Mozilla/5.0 (1.5.0) mirall/1.5.0'],
+ ['mirall/1.5.0'],
+ ['mirall/1.5.4'],
+ ['mirall/1.6.0'],
+ ['Mozilla/5.0 (Bogus Text) mirall/1.6.9'],
+ ];
+ }
+
+ /**
+ * @dataProvider oldDesktopClientProvider
+ * @param string $userAgent
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ * @expectedExceptionMessage Unsupported client version.
+ */
+ public function testBeforeHandlerException($userAgent) {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue($userAgent));
+
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('minimum.supported.desktop.version', '1.7.0')
+ ->will($this->returnValue('1.7.0'));
+
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+ /**
+ * @return array
+ */
+ public function newAndAlternateDesktopClientProvider() {
+ return [
+ ['Mozilla/5.0 (1.7.0) mirall/1.7.0'],
+ ['mirall/1.8.3'],
+ ['mirall/1.7.2'],
+ ['mirall/1.7.0'],
+ ['Mozilla/5.0 (Bogus Text) mirall/1.9.3'],
+ ];
+ }
+
+ /**
+ * @dataProvider newAndAlternateDesktopClientProvider
+ * @param string $userAgent
+ */
+ public function testBeforeHandlerSuccess($userAgent) {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue($userAgent));
+
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('minimum.supported.desktop.version', '1.7.0')
+ ->will($this->returnValue('1.7.0'));
+
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+ public function testBeforeHandlerNoUserAgent() {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue(null));
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php
new file mode 100644
index 00000000000..d98c2228ebd
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin as CommentPropertiesPluginImplementation;
+
+class CommentsPropertiesPluginTest extends \Test\TestCase {
+
+ /** @var CommentPropertiesPluginImplementation */
+ protected $plugin;
+ protected $commentsManager;
+ protected $userSession;
+ protected $server;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
+
+ $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->plugin = new CommentPropertiesPluginImplementation($this->commentsManager, $this->userSession);
+ $this->plugin->initialize($this->server);
+ }
+
+ public function nodeProvider() {
+ $mocks = [];
+ foreach(['\OCA\DAV\Connector\Sabre\File', '\OCA\DAV\Connector\Sabre\Directory', '\Sabre\DAV\INode'] as $class) {
+ $mocks[] = $this->getMockBuilder($class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ return [
+ [$mocks[0], true],
+ [$mocks[1], true],
+ [$mocks[2], false]
+ ];
+ }
+
+ /**
+ * @dataProvider nodeProvider
+ * @param $node
+ * @param $expectedSuccessful
+ */
+ public function testHandleGetProperties($node, $expectedSuccessful) {
+ $propFind = $this->getMockBuilder('\Sabre\DAV\PropFind')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ if($expectedSuccessful) {
+ $propFind->expects($this->exactly(3))
+ ->method('handle');
+ } else {
+ $propFind->expects($this->never())
+ ->method('handle');
+ }
+
+ $this->plugin->handleGetProperties($propFind, $node);
+ }
+
+ public function baseUriProvider() {
+ return [
+ ['owncloud/remote.php/webdav/', '4567', 'owncloud/remote.php/dav/comments/files/4567'],
+ ['owncloud/remote.php/files/', '4567', 'owncloud/remote.php/dav/comments/files/4567'],
+ ['owncloud/wicked.php/files/', '4567', null]
+ ];
+ }
+
+ /**
+ * @dataProvider baseUriProvider
+ * @param $baseUri
+ * @param $fid
+ * @param $expectedHref
+ */
+ public function testGetCommentsLink($baseUri, $fid, $expectedHref) {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue($fid));
+
+ $this->server->expects($this->once())
+ ->method('getBaseUri')
+ ->will($this->returnValue($baseUri));
+
+ $href = $this->plugin->getCommentsLink($node);
+ $this->assertSame($expectedHref, $href);
+ }
+
+ public function userProvider() {
+ return [
+ [$this->getMock('\OCP\IUser')],
+ [null]
+ ];
+ }
+
+ /**
+ * @dataProvider userProvider
+ * @param $user
+ */
+ public function testGetUnreadCount($user) {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('4567'));
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->commentsManager->expects($this->any())
+ ->method('getNumberOfCommentsForObject')
+ ->will($this->returnValue(42));
+
+ $unread = $this->plugin->getUnreadCount($node);
+ if(is_null($user)) {
+ $this->assertNull($unread);
+ } else {
+ $this->assertSame($unread, 42);
+ }
+ }
+
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php
new file mode 100644
index 00000000000..b8695e0a5d5
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+/**
+ * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class CopyEtagHeaderPluginTest extends \Test\TestCase {
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin
+ */
+ private $plugin;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = new \Sabre\DAV\Server();
+ $this->plugin = new \OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin();
+ $this->plugin->initialize($this->server);
+ }
+
+ public function testCopyEtag() {
+ $request = new \Sabre\Http\Request();
+ $response = new \Sabre\Http\Response();
+ $response->setHeader('Etag', 'abcd');
+
+ $this->plugin->afterMethod($request, $response);
+
+ $this->assertEquals('abcd', $response->getHeader('OC-Etag'));
+ }
+
+ public function testNoopWhenEmpty() {
+ $request = new \Sabre\Http\Request();
+ $response = new \Sabre\Http\Response();
+
+ $this->plugin->afterMethod($request, $response);
+
+ $this->assertNull($response->getHeader('OC-Etag'));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php b/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php
new file mode 100644
index 00000000000..0ae6bb014ad
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php
@@ -0,0 +1,313 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+/**
+ * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * Class CustomPropertiesBackend
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class CustomPropertiesBackendTest extends \Test\TestCase {
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \Sabre\DAV\Tree
+ */
+ private $tree;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\CustomPropertiesBackend
+ */
+ private $plugin;
+
+ /**
+ * @var \OCP\IUser
+ */
+ private $user;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = new \Sabre\DAV\Server();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $userId = $this->getUniqueID('testcustompropertiesuser');
+
+ $this->user = $this->getMock('\OCP\IUser');
+ $this->user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue($userId));
+
+ $this->plugin = new \OCA\DAV\Connector\Sabre\CustomPropertiesBackend(
+ $this->tree,
+ \OC::$server->getDatabaseConnection(),
+ $this->user
+ );
+ }
+
+ public function tearDown() {
+ $connection = \OC::$server->getDatabaseConnection();
+ $deleteStatement = $connection->prepare(
+ 'DELETE FROM `*PREFIX*properties`' .
+ ' WHERE `userid` = ?'
+ );
+ $deleteStatement->execute(
+ array(
+ $this->user->getUID(),
+ )
+ );
+ $deleteStatement->closeCursor();
+ }
+
+ private function createTestNode($class) {
+ $node = $this->getMockBuilder($class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $node->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue('/dummypath'));
+
+ return $node;
+ }
+
+ private function applyDefaultProps($path = '/dummypath') {
+ // properties to set
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ 'customprop' => 'value1',
+ 'customprop2' => 'value2',
+ ));
+
+ $this->plugin->propPatch(
+ $path,
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(200, $result['customprop']);
+ $this->assertEquals(200, $result['customprop2']);
+ }
+
+ /**
+ * Test that propFind on a missing file soft fails
+ */
+ public function testPropFindMissingFileSoftFail() {
+ $this->tree->expects($this->at(0))
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->throwException(new \Sabre\DAV\Exception\NotFound()));
+
+ $this->tree->expects($this->at(1))
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->throwException(new \Sabre\DAV\Exception\ServiceUnavailable()));
+
+ $propFind = new \Sabre\DAV\PropFind(
+ '/dummypath',
+ array(
+ 'customprop',
+ 'customprop2',
+ 'unsetprop',
+ ),
+ 0
+ );
+
+ $this->plugin->propFind(
+ '/dummypath',
+ $propFind
+ );
+
+ $this->plugin->propFind(
+ '/dummypath',
+ $propFind
+ );
+
+ // no exception, soft fail
+ $this->assertTrue(true);
+ }
+
+ /**
+ * Test setting/getting properties
+ */
+ public function testSetGetPropertiesForFile() {
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ $this->applyDefaultProps();
+
+ $propFind = new \Sabre\DAV\PropFind(
+ '/dummypath',
+ array(
+ 'customprop',
+ 'customprop2',
+ 'unsetprop',
+ ),
+ 0
+ );
+
+ $this->plugin->propFind(
+ '/dummypath',
+ $propFind
+ );
+
+ $this->assertEquals('value1', $propFind->get('customprop'));
+ $this->assertEquals('value2', $propFind->get('customprop2'));
+ $this->assertEquals(array('unsetprop'), $propFind->get404Properties());
+ }
+
+ /**
+ * Test getting properties from directory
+ */
+ public function testGetPropertiesForDirectory() {
+ $rootNode = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory');
+
+ $nodeSub = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $nodeSub->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(456));
+
+ $nodeSub->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue('/dummypath/test.txt'));
+
+ $rootNode->expects($this->once())
+ ->method('getChildren')
+ ->will($this->returnValue(array($nodeSub)));
+
+ $this->tree->expects($this->at(0))
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($rootNode));
+
+ $this->tree->expects($this->at(1))
+ ->method('getNodeForPath')
+ ->with('/dummypath/test.txt')
+ ->will($this->returnValue($nodeSub));
+
+ $this->tree->expects($this->at(2))
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($rootNode));
+
+ $this->tree->expects($this->at(3))
+ ->method('getNodeForPath')
+ ->with('/dummypath/test.txt')
+ ->will($this->returnValue($nodeSub));
+
+ $this->applyDefaultProps('/dummypath');
+ $this->applyDefaultProps('/dummypath/test.txt');
+
+ $propNames = array(
+ 'customprop',
+ 'customprop2',
+ 'unsetprop',
+ );
+
+ $propFindRoot = new \Sabre\DAV\PropFind(
+ '/dummypath',
+ $propNames,
+ 1
+ );
+
+ $propFindSub = new \Sabre\DAV\PropFind(
+ '/dummypath/test.txt',
+ $propNames,
+ 0
+ );
+
+ $this->plugin->propFind(
+ '/dummypath',
+ $propFindRoot
+ );
+
+ $this->plugin->propFind(
+ '/dummypath/test.txt',
+ $propFindSub
+ );
+
+ // TODO: find a way to assert that no additional SQL queries were
+ // run while doing the second propFind
+
+ $this->assertEquals('value1', $propFindRoot->get('customprop'));
+ $this->assertEquals('value2', $propFindRoot->get('customprop2'));
+ $this->assertEquals(array('unsetprop'), $propFindRoot->get404Properties());
+
+ $this->assertEquals('value1', $propFindSub->get('customprop'));
+ $this->assertEquals('value2', $propFindSub->get('customprop2'));
+ $this->assertEquals(array('unsetprop'), $propFindSub->get404Properties());
+ }
+
+ /**
+ * Test delete property
+ */
+ public function testDeleteProperty() {
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ $this->applyDefaultProps();
+
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ 'customprop' => null,
+ ));
+
+ $this->plugin->propPatch(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(204, $result['customprop']);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php
new file mode 100644
index 00000000000..66f0a803807
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php
@@ -0,0 +1,267 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCP\Files\ForbiddenException;
+
+/**
+ * @group DB
+ */
+class DirectoryTest extends \Test\TestCase {
+
+ /** @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject */
+ private $view;
+ /** @var \OC\Files\FileInfo | \PHPUnit_Framework_MockObject_MockObject */
+ private $info;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->view = $this->getMock('OC\Files\View', array(), array(), '', false);
+ $this->info = $this->getMock('OC\Files\FileInfo', array(), array(), '', false);
+ }
+
+ private function getDir($path = '/') {
+ $this->view->expects($this->once())
+ ->method('getRelativePath')
+ ->will($this->returnValue($path));
+
+ $this->info->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue($path));
+
+ return new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteRootFolderFails() {
+ $this->info->expects($this->any())
+ ->method('isDeletable')
+ ->will($this->returnValue(true));
+ $this->view->expects($this->never())
+ ->method('rmdir');
+ $dir = $this->getDir();
+ $dir->delete();
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
+ */
+ public function testDeleteForbidden() {
+ // deletion allowed
+ $this->info->expects($this->once())
+ ->method('isDeletable')
+ ->will($this->returnValue(true));
+
+ // but fails
+ $this->view->expects($this->once())
+ ->method('rmdir')
+ ->with('sub')
+ ->willThrowException(new ForbiddenException('', true));
+
+ $dir = $this->getDir('sub');
+ $dir->delete();
+ }
+
+ /**
+ *
+ */
+ public function testDeleteFolderWhenAllowed() {
+ // deletion allowed
+ $this->info->expects($this->once())
+ ->method('isDeletable')
+ ->will($this->returnValue(true));
+
+ // but fails
+ $this->view->expects($this->once())
+ ->method('rmdir')
+ ->with('sub')
+ ->will($this->returnValue(true));
+
+ $dir = $this->getDir('sub');
+ $dir->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteFolderFailsWhenNotAllowed() {
+ $this->info->expects($this->once())
+ ->method('isDeletable')
+ ->will($this->returnValue(false));
+
+ $dir = $this->getDir('sub');
+ $dir->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteFolderThrowsWhenDeletionFailed() {
+ // deletion allowed
+ $this->info->expects($this->once())
+ ->method('isDeletable')
+ ->will($this->returnValue(true));
+
+ // but fails
+ $this->view->expects($this->once())
+ ->method('rmdir')
+ ->with('sub')
+ ->will($this->returnValue(false));
+
+ $dir = $this->getDir('sub');
+ $dir->delete();
+ }
+
+ public function testGetChildren() {
+ $info1 = $this->getMockBuilder('OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $info2 = $this->getMockBuilder('OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $info1->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue('first'));
+ $info1->expects($this->any())
+ ->method('getEtag')
+ ->will($this->returnValue('abc'));
+ $info2->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue('second'));
+ $info2->expects($this->any())
+ ->method('getEtag')
+ ->will($this->returnValue('def'));
+
+ $this->view->expects($this->once())
+ ->method('getDirectoryContent')
+ ->with('')
+ ->will($this->returnValue(array($info1, $info2)));
+
+ $this->view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnValue(''));
+
+ $dir = new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ $nodes = $dir->getChildren();
+
+ $this->assertEquals(2, count($nodes));
+
+ // calling a second time just returns the cached values,
+ // does not call getDirectoryContents again
+ $dir->getChildren();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ServiceUnavailable
+ */
+ public function testGetChildThrowStorageNotAvailableException() {
+ $this->view->expects($this->once())
+ ->method('getFileInfo')
+ ->willThrowException(new \OCP\Files\StorageNotAvailableException());
+
+ $dir = new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ $dir->getChild('.');
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\InvalidPath
+ */
+ public function testGetChildThrowInvalidPath() {
+ $this->view->expects($this->once())
+ ->method('verifyPath')
+ ->willThrowException(new \OCP\Files\InvalidPathException());
+ $this->view->expects($this->never())
+ ->method('getFileInfo');
+
+ $dir = new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ $dir->getChild('.');
+ }
+
+ public function testGetQuotaInfoUnlimited() {
+ $storage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Quota')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $storage->expects($this->any())
+ ->method('instanceOfStorage')
+ ->will($this->returnValueMap([
+ '\OC\Files\Storage\Shared' => false,
+ '\OC\Files\Storage\Wrapper\Quota' => false,
+ ]));
+
+ $storage->expects($this->never())
+ ->method('getQuota');
+
+ $storage->expects($this->once())
+ ->method('free_space')
+ ->will($this->returnValue(800));
+
+ $this->info->expects($this->once())
+ ->method('getSize')
+ ->will($this->returnValue(200));
+
+ $this->info->expects($this->once())
+ ->method('getStorage')
+ ->will($this->returnValue($storage));
+
+ $dir = new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ $this->assertEquals([200, -3], $dir->getQuotaInfo()); //200 used, unlimited
+ }
+
+ public function testGetQuotaInfoSpecific() {
+ $storage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Quota')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $storage->expects($this->any())
+ ->method('instanceOfStorage')
+ ->will($this->returnValueMap([
+ ['\OC\Files\Storage\Shared', false],
+ ['\OC\Files\Storage\Wrapper\Quota', true],
+ ]));
+
+ $storage->expects($this->once())
+ ->method('getQuota')
+ ->will($this->returnValue(1000));
+
+ $storage->expects($this->once())
+ ->method('free_space')
+ ->will($this->returnValue(800));
+
+ $this->info->expects($this->once())
+ ->method('getSize')
+ ->will($this->returnValue(200));
+
+ $this->info->expects($this->once())
+ ->method('getStorage')
+ ->will($this->returnValue($storage));
+
+ $dir = new \OCA\DAV\Connector\Sabre\Directory($this->view, $this->info);
+ $this->assertEquals([200, 800], $dir->getQuotaInfo()); //200 used, 800 free
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php b/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php
new file mode 100644
index 00000000000..125cb174c58
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\DummyGetResponsePlugin;
+use Test\TestCase;
+
+/**
+ * Class DummyGetResponsePluginTest
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class DummyGetResponsePluginTest extends TestCase {
+ /** @var DummyGetResponsePlugin */
+ private $dummyGetResponsePlugin;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->dummyGetResponsePlugin = new DummyGetResponsePlugin();
+ }
+
+ public function testInitialize() {
+ /** @var \Sabre\DAV\Server $server */
+ $server = $this->getMock('\Sabre\DAV\Server');
+ $server
+ ->expects($this->once())
+ ->method('on')
+ ->with('method:GET', [$this->dummyGetResponsePlugin, 'httpGet'], 200);
+
+ $this->dummyGetResponsePlugin->initialize($server);
+ }
+
+
+ public function testHttpGet() {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ /** @var \Sabre\HTTP\ResponseInterface $response */
+ $response = $server = $this->getMock('\Sabre\HTTP\ResponseInterface');
+ $response
+ ->expects($this->once())
+ ->method('setBody');
+ $response
+ ->expects($this->once())
+ ->method('setStatus')
+ ->with(200);
+
+ $this->assertSame(false, $this->dummyGetResponsePlugin->httpGet($request, $response));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php b/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php
new file mode 100644
index 00000000000..44e9e17b695
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\Exception;
+
+use OCA\DAV\Connector\Sabre\Exception\Forbidden;
+
+class ForbiddenTest extends \Test\TestCase {
+
+ public function testSerialization() {
+
+ // create xml doc
+ $DOM = new \DOMDocument('1.0','utf-8');
+ $DOM->formatOutput = true;
+ $error = $DOM->createElementNS('DAV:','d:error');
+ $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
+ $DOM->appendChild($error);
+
+ // serialize the exception
+ $message = "1234567890";
+ $retry = false;
+ $expectedXml = <<<EOD
+<?xml version="1.0" encoding="utf-8"?>
+<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
+ <o:retry xmlns:o="o:">false</o:retry>
+ <o:reason xmlns:o="o:">1234567890</o:reason>
+</d:error>
+
+EOD;
+
+ $ex = new Forbidden($message, $retry);
+ $server = $this->getMock('Sabre\DAV\Server');
+ $ex->serialize($server, $error);
+
+ // assert
+ $xml = $DOM->saveXML();
+ $this->assertEquals($expectedXml, $xml);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php b/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php
new file mode 100644
index 00000000000..0e1224f596a
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\Exception;
+
+use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
+
+class InvalidPathTest extends \Test\TestCase {
+
+ public function testSerialization() {
+
+ // create xml doc
+ $DOM = new \DOMDocument('1.0','utf-8');
+ $DOM->formatOutput = true;
+ $error = $DOM->createElementNS('DAV:','d:error');
+ $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
+ $DOM->appendChild($error);
+
+ // serialize the exception
+ $message = "1234567890";
+ $retry = false;
+ $expectedXml = <<<EOD
+<?xml version="1.0" encoding="utf-8"?>
+<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
+ <o:retry xmlns:o="o:">false</o:retry>
+ <o:reason xmlns:o="o:">1234567890</o:reason>
+</d:error>
+
+EOD;
+
+ $ex = new InvalidPath($message, $retry);
+ $server = $this->getMock('Sabre\DAV\Server');
+ $ex->serialize($server, $error);
+
+ // assert
+ $xml = $DOM->saveXML();
+ $this->assertEquals($expectedXml, $xml);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php
new file mode 100644
index 00000000000..f6102fa4e9b
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
+use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin as PluginToTest;
+use OC\Log;
+use OCP\ILogger;
+use PHPUnit_Framework_MockObject_MockObject;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Server;
+use Test\TestCase;
+
+class TestLogger extends Log {
+ public $message;
+ public $level;
+
+ public function __construct($logger = null) {
+ //disable original constructor
+ }
+
+ public function log($level, $message, array $context = array()) {
+ $this->level = $level;
+ $this->message = $message;
+ }
+}
+
+class ExceptionLoggerPluginTest extends TestCase {
+
+ /** @var Server */
+ private $server;
+
+ /** @var PluginToTest */
+ private $plugin;
+
+ /** @var TestLogger | PHPUnit_Framework_MockObject_MockObject */
+ private $logger;
+
+ private function init() {
+ $this->server = new Server();
+ $this->logger = new TestLogger();
+ $this->plugin = new PluginToTest('unit-test', $this->logger);
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider providesExceptions
+ */
+ public function testLogging($expectedLogLevel, $expectedMessage, $exception) {
+ $this->init();
+ $this->plugin->logException($exception);
+
+ $this->assertEquals($expectedLogLevel, $this->logger->level);
+ $this->assertStringStartsWith('Exception: {"Message":"' . $expectedMessage, $this->logger->message);
+ }
+
+ public function providesExceptions() {
+ return [
+ [0, 'HTTP\/1.1 404 Not Found', new NotFound()],
+ [4, 'HTTP\/1.1 400 This path leads to nowhere', new InvalidPath('This path leads to nowhere')]
+ ];
+ }
+
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php
new file mode 100644
index 00000000000..98edfb3c5b4
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\FakeLockerPlugin;
+use Sabre\HTTP\Response;
+use Test\TestCase;
+
+/**
+ * Class FakeLockerPluginTest
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class FakeLockerPluginTest extends TestCase {
+ /** @var FakeLockerPlugin */
+ private $fakeLockerPlugin;
+
+ public function setUp() {
+ parent::setUp();
+ $this->fakeLockerPlugin = new FakeLockerPlugin();
+ }
+
+ public function testInitialize() {
+ /** @var \Sabre\DAV\Server $server */
+ $server = $this->getMock('\Sabre\DAV\Server');
+ $server
+ ->expects($this->at(0))
+ ->method('on')
+ ->with('method:LOCK', [$this->fakeLockerPlugin, 'fakeLockProvider'], 1);
+ $server
+ ->expects($this->at(1))
+ ->method('on')
+ ->with('method:UNLOCK', [$this->fakeLockerPlugin, 'fakeUnlockProvider'], 1);
+ $server
+ ->expects($this->at(2))
+ ->method('on')
+ ->with('propFind', [$this->fakeLockerPlugin, 'propFind']);
+ $server
+ ->expects($this->at(3))
+ ->method('on')
+ ->with('validateTokens', [$this->fakeLockerPlugin, 'validateTokens']);
+
+ $this->fakeLockerPlugin->initialize($server);
+ }
+
+ public function testGetHTTPMethods() {
+ $expected = [
+ 'LOCK',
+ 'UNLOCK',
+ ];
+ $this->assertSame($expected, $this->fakeLockerPlugin->getHTTPMethods('Test'));
+ }
+
+ public function testGetFeatures() {
+ $expected = [
+ 2,
+ ];
+ $this->assertSame($expected, $this->fakeLockerPlugin->getFeatures());
+ }
+
+ public function testPropFind() {
+ $propFind = $this->getMockBuilder('\Sabre\DAV\PropFind')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node = $this->getMock('\Sabre\DAV\INode');
+
+ $propFind->expects($this->at(0))
+ ->method('handle')
+ ->with('{DAV:}supportedlock');
+ $propFind->expects($this->at(1))
+ ->method('handle')
+ ->with('{DAV:}lockdiscovery');
+
+ $this->fakeLockerPlugin->propFind($propFind, $node);
+ }
+
+ public function tokenDataProvider() {
+ return [
+ [
+ [
+ [
+ 'tokens' => [
+ [
+ 'token' => 'aToken',
+ 'validToken' => false,
+ ],
+ [],
+ [
+ 'token' => 'opaquelocktoken:asdf',
+ 'validToken' => false,
+ ]
+ ],
+ ]
+ ],
+ [
+ [
+ 'tokens' => [
+ [
+ 'token' => 'aToken',
+ 'validToken' => false,
+ ],
+ [],
+ [
+ 'token' => 'opaquelocktoken:asdf',
+ 'validToken' => true,
+ ]
+ ],
+ ]
+ ],
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider tokenDataProvider
+ * @param array $input
+ * @param array $expected
+ */
+ public function testValidateTokens(array $input, array $expected) {
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $this->fakeLockerPlugin->validateTokens($request, $input);
+ $this->assertSame($expected, $input);
+ }
+
+ public function testFakeLockProvider() {
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $response = new Response();
+ $server = $this->getMock('\Sabre\DAV\Server');
+ $this->fakeLockerPlugin->initialize($server);
+
+ $request->expects($this->exactly(2))
+ ->method('getPath')
+ ->will($this->returnValue('MyPath'));
+
+ $this->assertSame(false, $this->fakeLockerPlugin->fakeLockProvider($request, $response));
+
+ $expectedXml = '<?xml version="1.0" encoding="utf-8"?><d:prop xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:lockdiscovery><d:activelock><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:lockroot><d:href>MyPath</d:href></d:lockroot><d:depth>infinity</d:depth><d:timeout>Second-1800</d:timeout><d:locktoken><d:href>opaquelocktoken:fe4f7f2437b151fbcb4e9f5c8118c6b1</d:href></d:locktoken><d:owner/></d:activelock></d:lockdiscovery></d:prop>';
+
+ $this->assertXmlStringEqualsXmlString($expectedXml, $response->getBody());
+ }
+
+ public function testFakeUnlockProvider() {
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $response = $this->getMock('\Sabre\HTTP\ResponseInterface');
+
+ $response->expects($this->once())
+ ->method('setStatus')
+ ->with('204');
+ $response->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Length', '0');
+
+ $this->assertSame(false, $this->fakeLockerPlugin->fakeUnlockProvider($request, $response));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php
new file mode 100644
index 00000000000..0f404a6a16c
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php
@@ -0,0 +1,987 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OC\Files\Storage\Local;
+use OCP\Files\ForbiddenException;
+use Test\HookHelper;
+use OC\Files\Filesystem;
+use OCP\Lock\ILockingProvider;
+
+/**
+ * Class File
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class FileTest extends \Test\TestCase {
+
+ /**
+ * @var string
+ */
+ private $user;
+
+ 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);
+ }
+
+ public function tearDown() {
+ $userManager = \OC::$server->getUserManager();
+ $userManager->get($this->user)->delete();
+ unset($_SERVER['HTTP_OC_CHUNKED']);
+
+ parent::tearDown();
+ }
+
+ private function getMockStorage() {
+ $storage = $this->getMock('\OCP\Files\Storage');
+ $storage->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('home::someuser'));
+ return $storage;
+ }
+
+ /**
+ * @param string $string
+ */
+ private function getStream($string) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $string);
+ fseek($stream, 0);
+ return $stream;
+ }
+
+
+ public function fopenFailuresProvider() {
+ return [
+ [
+ // return false
+ null,
+ '\Sabre\Dav\Exception',
+ false
+ ],
+ [
+ new \OCP\Files\NotPermittedException(),
+ 'Sabre\DAV\Exception\Forbidden'
+ ],
+ [
+ new \OCP\Files\EntityTooLargeException(),
+ 'OCA\DAV\Connector\Sabre\Exception\EntityTooLarge'
+ ],
+ [
+ new \OCP\Files\InvalidContentException(),
+ 'OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType'
+ ],
+ [
+ new \OCP\Files\InvalidPathException(),
+ 'Sabre\DAV\Exception\Forbidden'
+ ],
+ [
+ new \OCP\Files\ForbiddenException('', true),
+ 'OCA\DAV\Connector\Sabre\Exception\Forbidden'
+ ],
+ [
+ new \OCP\Files\LockNotAcquiredException('/test.txt', 1),
+ 'OCA\DAV\Connector\Sabre\Exception\FileLocked'
+ ],
+ [
+ new \OCP\Lock\LockedException('/test.txt'),
+ 'OCA\DAV\Connector\Sabre\Exception\FileLocked'
+ ],
+ [
+ new \OCP\Encryption\Exceptions\GenericEncryptionException(),
+ 'Sabre\DAV\Exception\ServiceUnavailable'
+ ],
+ [
+ new \OCP\Files\StorageNotAvailableException(),
+ 'Sabre\DAV\Exception\ServiceUnavailable'
+ ],
+ [
+ new \Sabre\DAV\Exception('Generic sabre exception'),
+ 'Sabre\DAV\Exception',
+ false
+ ],
+ [
+ new \Exception('Generic exception'),
+ 'Sabre\DAV\Exception'
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider fopenFailuresProvider
+ */
+ public function testSimplePutFails($thrownException, $expectedException, $checkPreviousClass = true) {
+ // setup
+ $storage = $this->getMock(
+ '\OC\Files\Storage\Local',
+ ['fopen'],
+ [['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]
+ );
+ \OC\Files\Filesystem::mount($storage, [], $this->user . '/');
+ $view = $this->getMock('\OC\Files\View', array('getRelativePath', 'resolvePath'), array());
+ $view->expects($this->atLeastOnce())
+ ->method('resolvePath')
+ ->will($this->returnCallback(
+ function ($path) use ($storage) {
+ return [$storage, $path];
+ }
+ ));
+
+ if ($thrownException !== null) {
+ $storage->expects($this->once())
+ ->method('fopen')
+ ->will($this->throwException($thrownException));
+ } else {
+ $storage->expects($this->once())
+ ->method('fopen')
+ ->will($this->returnValue(false));
+ }
+
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $caughtException = null;
+ try {
+ $file->put('test data');
+ } catch (\Exception $e) {
+ $caughtException = $e;
+ }
+
+ $this->assertInstanceOf($expectedException, $caughtException);
+ if ($checkPreviousClass) {
+ $this->assertInstanceOf(get_class($thrownException), $caughtException->getPrevious());
+ }
+
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * Test putting a file using chunking
+ *
+ * @dataProvider fopenFailuresProvider
+ */
+ public function testChunkedPutFails($thrownException, $expectedException, $checkPreviousClass = false) {
+ // setup
+ $storage = $this->getMock(
+ '\OC\Files\Storage\Local',
+ ['fopen'],
+ [['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]
+ );
+ \OC\Files\Filesystem::mount($storage, [], $this->user . '/');
+ $view = $this->getMock('\OC\Files\View', ['getRelativePath', 'resolvePath'], []);
+ $view->expects($this->atLeastOnce())
+ ->method('resolvePath')
+ ->will($this->returnCallback(
+ function ($path) use ($storage) {
+ return [$storage, $path];
+ }
+ ));
+
+ if ($thrownException !== null) {
+ $storage->expects($this->once())
+ ->method('fopen')
+ ->will($this->throwException($thrownException));
+ } else {
+ $storage->expects($this->once())
+ ->method('fopen')
+ ->will($this->returnValue(false));
+ }
+
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+
+ $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ], null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // put first chunk
+ $file->acquireLock(ILockingProvider::LOCK_SHARED);
+ $this->assertNull($file->put('test data one'));
+ $file->releaseLock(ILockingProvider::LOCK_SHARED);
+
+ $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ], null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $caughtException = null;
+ try {
+ // last chunk
+ $file->acquireLock(ILockingProvider::LOCK_SHARED);
+ $file->put('test data two');
+ $file->releaseLock(ILockingProvider::LOCK_SHARED);
+ } catch (\Exception $e) {
+ $caughtException = $e;
+ }
+
+ $this->assertInstanceOf($expectedException, $caughtException);
+ if ($checkPreviousClass) {
+ $this->assertInstanceOf(get_class($thrownException), $caughtException->getPrevious());
+ }
+
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * 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, $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 \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // beforeMethod locks
+ $view->lockFile($path, ILockingProvider::LOCK_SHARED);
+
+ $result = $file->put($this->getStream('test data'));
+
+ // afterMethod unlocks
+ $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
+
+ return $result;
+ }
+
+ /**
+ * Test putting a single file
+ */
+ public function testPutSingleFile() {
+ $this->assertNotEmpty($this->doPut('/foo.txt'));
+ }
+
+ /**
+ * Test putting a file using chunking
+ */
+ public function testChunkedPut() {
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+ $this->assertNull($this->doPut('/test.txt-chunking-12345-2-0'));
+ $this->assertNotEmpty($this->doPut('/test.txt-chunking-12345-2-1'));
+ }
+
+ /**
+ * Test that putting a file triggers create hooks
+ */
+ public function testPutSingleFileTriggersHooks() {
+ HookHelper::setUpHooks();
+
+ $this->assertNotEmpty($this->doPut('/foo.txt'));
+
+ $this->assertCount(4, HookHelper::$hookCalls);
+ $this->assertHookCall(
+ HookHelper::$hookCalls[0],
+ Filesystem::signal_create,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[1],
+ Filesystem::signal_write,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[2],
+ Filesystem::signal_post_create,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[3],
+ Filesystem::signal_post_write,
+ '/foo.txt'
+ );
+ }
+
+ /**
+ * Test that putting a file triggers update hooks
+ */
+ public function testPutOverwriteFileTriggersHooks() {
+ $view = \OC\Files\Filesystem::getView();
+ $view->file_put_contents('/foo.txt', 'some content that will be replaced');
+
+ HookHelper::setUpHooks();
+
+ $this->assertNotEmpty($this->doPut('/foo.txt'));
+
+ $this->assertCount(4, HookHelper::$hookCalls);
+ $this->assertHookCall(
+ HookHelper::$hookCalls[0],
+ Filesystem::signal_update,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[1],
+ Filesystem::signal_write,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[2],
+ Filesystem::signal_post_update,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[3],
+ Filesystem::signal_post_write,
+ '/foo.txt'
+ );
+ }
+
+ /**
+ * Test that putting a file triggers hooks with the correct path
+ * if the passed view was chrooted (can happen with public webdav
+ * where the root is the share root)
+ */
+ public function testPutSingleFileTriggersHooksDifferentRoot() {
+ $view = \OC\Files\Filesystem::getView();
+ $view->mkdir('noderoot');
+
+ HookHelper::setUpHooks();
+
+ // happens with public webdav where the view root is the share root
+ $this->assertNotEmpty($this->doPut('/foo.txt', '/' . $this->user . '/files/noderoot'));
+
+ $this->assertCount(4, HookHelper::$hookCalls);
+ $this->assertHookCall(
+ HookHelper::$hookCalls[0],
+ Filesystem::signal_create,
+ '/noderoot/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[1],
+ Filesystem::signal_write,
+ '/noderoot/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[2],
+ Filesystem::signal_post_create,
+ '/noderoot/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[3],
+ Filesystem::signal_post_write,
+ '/noderoot/foo.txt'
+ );
+ }
+
+ /**
+ * Test that putting a file with chunks triggers create hooks
+ */
+ public function testPutChunkedFileTriggersHooks() {
+ HookHelper::setUpHooks();
+
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+ $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0'));
+ $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1'));
+
+ $this->assertCount(4, HookHelper::$hookCalls);
+ $this->assertHookCall(
+ HookHelper::$hookCalls[0],
+ Filesystem::signal_create,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[1],
+ Filesystem::signal_write,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[2],
+ Filesystem::signal_post_create,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[3],
+ Filesystem::signal_post_write,
+ '/foo.txt'
+ );
+ }
+
+ /**
+ * Test that putting a chunked file triggers update hooks
+ */
+ public function testPutOverwriteChunkedFileTriggersHooks() {
+ $view = \OC\Files\Filesystem::getView();
+ $view->file_put_contents('/foo.txt', 'some content that will be replaced');
+
+ HookHelper::setUpHooks();
+
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+ $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0'));
+ $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1'));
+
+ $this->assertCount(4, HookHelper::$hookCalls);
+ $this->assertHookCall(
+ HookHelper::$hookCalls[0],
+ Filesystem::signal_update,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[1],
+ Filesystem::signal_write,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[2],
+ Filesystem::signal_post_update,
+ '/foo.txt'
+ );
+ $this->assertHookCall(
+ HookHelper::$hookCalls[3],
+ Filesystem::signal_post_write,
+ '/foo.txt'
+ );
+ }
+
+ public static function cancellingHook($params) {
+ self::$hookCalls[] = array(
+ 'signal' => Filesystem::signal_post_create,
+ 'params' => $params
+ );
+ }
+
+ /**
+ * Test put file with cancelled hook
+ */
+ public function testPutSingleFileCancelPreHook() {
+ \OCP\Util::connectHook(
+ Filesystem::CLASSNAME,
+ Filesystem::signal_create,
+ '\Test\HookHelper',
+ 'cancellingCallback'
+ );
+
+ // action
+ $thrown = false;
+ try {
+ $this->doPut('/foo.txt');
+ } catch (\Sabre\DAV\Exception $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles(), 'No stray part files');
+ }
+
+ /**
+ * Test exception when the uploaded size did not match
+ */
+ public function testSimplePutFailsSizeCheck() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array('rename', 'getRelativePath', 'filesize'));
+ $view->expects($this->any())
+ ->method('rename')
+ ->withAnyParameters()
+ ->will($this->returnValue(false));
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+
+ $view->expects($this->any())
+ ->method('filesize')
+ ->will($this->returnValue(123456));
+
+ $_SERVER['CONTENT_LENGTH'] = 123456;
+ $_SERVER['REQUEST_METHOD'] = 'PUT';
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $thrown = false;
+ try {
+ // beforeMethod locks
+ $file->acquireLock(ILockingProvider::LOCK_SHARED);
+
+ $file->put($this->getStream('test data'));
+
+ // afterMethod unlocks
+ $file->releaseLock(ILockingProvider::LOCK_SHARED);
+ } catch (\Sabre\DAV\Exception\BadRequest $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * Test exception during final rename in simple upload mode
+ */
+ public function testSimplePutFailsMoveFromStorage() {
+ $view = new \OC\Files\View('/' . $this->user . '/files');
+
+ // simulate situation where the target file is locked
+ $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
+
+ $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $thrown = false;
+ try {
+ // beforeMethod locks
+ $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+
+ $file->put($this->getStream('test data'));
+
+ // afterMethod unlocks
+ $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+ } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * Test exception during final rename in chunk upload mode
+ */
+ public function testChunkedPutFailsFinalRename() {
+ $view = new \OC\Files\View('/' . $this->user . '/files');
+
+ // simulate situation where the target file is locked
+ $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
+
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+
+ $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ], null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $file->acquireLock(ILockingProvider::LOCK_SHARED);
+ $this->assertNull($file->put('test data one'));
+ $file->releaseLock(ILockingProvider::LOCK_SHARED);
+
+ $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ], null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $thrown = false;
+ try {
+ $file->acquireLock(ILockingProvider::LOCK_SHARED);
+ $file->put($this->getStream('test data'));
+ $file->releaseLock(ILockingProvider::LOCK_SHARED);
+ } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * Test put file with invalid chars
+ */
+ public function testSimplePutInvalidChars() {
+ // setup
+ $view = $this->getMock('\OC\Files\View', array('getRelativePath'));
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+
+ $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $thrown = false;
+ try {
+ // beforeMethod locks
+ $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+
+ $file->put($this->getStream('test data'));
+
+ // afterMethod unlocks
+ $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+ } catch (\OCA\DAV\Connector\Sabre\Exception\InvalidPath $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ * Test setting name with setName() with invalid chars
+ *
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\InvalidPath
+ */
+ public function testSetNameInvalidChars() {
+ // setup
+ $view = $this->getMock('\OC\Files\View', array('getRelativePath'));
+
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+
+ $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $file->setName('/super*star.txt');
+ }
+
+ /**
+ */
+ public function testUploadAbort() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array('rename', 'getRelativePath', 'filesize'));
+ $view->expects($this->any())
+ ->method('rename')
+ ->withAnyParameters()
+ ->will($this->returnValue(false));
+ $view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnArgument(0));
+ $view->expects($this->any())
+ ->method('filesize')
+ ->will($this->returnValue(123456));
+
+ $_SERVER['CONTENT_LENGTH'] = 12345;
+ $_SERVER['REQUEST_METHOD'] = 'PUT';
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $thrown = false;
+ try {
+ // beforeMethod locks
+ $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+
+ $file->put($this->getStream('test data'));
+
+ // afterMethod unlocks
+ $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
+ } catch (\Sabre\DAV\Exception\BadRequest $e) {
+ $thrown = true;
+ }
+
+ $this->assertTrue($thrown);
+ $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
+ }
+
+ /**
+ *
+ */
+ public function testDeleteWhenAllowed() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array());
+
+ $view->expects($this->once())
+ ->method('unlink')
+ ->will($this->returnValue(true));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $file->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteThrowsWhenDeletionNotAllowed() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array());
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => 0
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $file->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteThrowsWhenDeletionFailed() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array());
+
+ // but fails
+ $view->expects($this->once())
+ ->method('unlink')
+ ->will($this->returnValue(false));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $file->delete();
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
+ */
+ public function testDeleteThrowsWhenDeletionThrows() {
+ // setup
+ $view = $this->getMock('\OC\Files\View',
+ array());
+
+ // but fails
+ $view->expects($this->once())
+ ->method('unlink')
+ ->willThrowException(new ForbiddenException('', true));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ // action
+ $file->delete();
+ }
+
+ /**
+ * Asserts hook call
+ *
+ * @param array $callData hook call data to check
+ * @param string $signal signal name
+ * @param string $hookPath hook path
+ */
+ protected function assertHookCall($callData, $signal, $hookPath) {
+ $this->assertEquals($signal, $callData['signal']);
+ $params = $callData['params'];
+ $this->assertEquals(
+ $hookPath,
+ $params[Filesystem::signal_param_path]
+ );
+ }
+
+ /**
+ * Test whether locks are set before and after the operation
+ */
+ public function testPutLocking() {
+ $view = new \OC\Files\View('/' . $this->user . '/files/');
+
+ $path = 'test-locking.txt';
+ $info = new \OC\Files\FileInfo(
+ '/' . $this->user . '/files/' . $path,
+ $this->getMockStorage(),
+ null,
+ ['permissions' => \OCP\Constants::PERMISSION_ALL],
+ null
+ );
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED),
+ 'File unlocked before put'
+ );
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE),
+ 'File unlocked before put'
+ );
+
+ $wasLockedPre = false;
+ $wasLockedPost = false;
+ $eventHandler = $this->getMockBuilder('\stdclass')
+ ->setMethods(['writeCallback', 'postWriteCallback'])
+ ->getMock();
+
+ // both pre and post hooks might need access to the file,
+ // so only shared lock is acceptable
+ $eventHandler->expects($this->once())
+ ->method('writeCallback')
+ ->will($this->returnCallback(
+ function () use ($view, $path, &$wasLockedPre) {
+ $wasLockedPre = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED);
+ $wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE);
+ }
+ ));
+ $eventHandler->expects($this->once())
+ ->method('postWriteCallback')
+ ->will($this->returnCallback(
+ function () use ($view, $path, &$wasLockedPost) {
+ $wasLockedPost = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED);
+ $wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE);
+ }
+ ));
+
+ \OCP\Util::connectHook(
+ Filesystem::CLASSNAME,
+ Filesystem::signal_write,
+ $eventHandler,
+ 'writeCallback'
+ );
+ \OCP\Util::connectHook(
+ Filesystem::CLASSNAME,
+ Filesystem::signal_post_write,
+ $eventHandler,
+ 'postWriteCallback'
+ );
+
+ // beforeMethod locks
+ $view->lockFile($path, ILockingProvider::LOCK_SHARED);
+
+ $this->assertNotEmpty($file->put($this->getStream('test data')));
+
+ // afterMethod unlocks
+ $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
+
+ $this->assertTrue($wasLockedPre, 'File was locked during pre-hooks');
+ $this->assertTrue($wasLockedPost, 'File was locked during post-hooks');
+
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED),
+ 'File unlocked after put'
+ );
+ $this->assertFalse(
+ $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE),
+ 'File unlocked after put'
+ );
+ }
+
+ /**
+ * Returns part files in the given path
+ *
+ * @param \OC\Files\View view which root is the current user's "files" folder
+ * @param string $path path for which to list part files
+ *
+ * @return array list of part files
+ */
+ private function listPartFiles(\OC\Files\View $userView = null, $path = '') {
+ if ($userView === null) {
+ $userView = \OC\Files\Filesystem::getView();
+ }
+ $files = [];
+ list($storage, $internalPath) = $userView->resolvePath($path);
+ if($storage instanceof Local) {
+ $realPath = $storage->getSourcePath($internalPath);
+ $dh = opendir($realPath);
+ while (($file = readdir($dh)) !== false) {
+ if (substr($file, strlen($file) - 5, 5) === '.part') {
+ $files[] = $file;
+ }
+ }
+ closedir($dh);
+ }
+ return $files;
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ServiceUnavailable
+ */
+ public function testGetFopenFails() {
+ $view = $this->getMock('\OC\Files\View', ['fopen'], array());
+ $view->expects($this->atLeastOnce())
+ ->method('fopen')
+ ->will($this->returnValue(false));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ $file->get();
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\Forbidden
+ */
+ public function testGetFopenThrows() {
+ $view = $this->getMock('\OC\Files\View', ['fopen'], array());
+ $view->expects($this->atLeastOnce())
+ ->method('fopen')
+ ->willThrowException(new ForbiddenException('', true));
+
+ $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
+ 'permissions' => \OCP\Constants::PERMISSION_ALL
+ ), null);
+
+ $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+ $file->get();
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
new file mode 100644
index 00000000000..5a944e74fca
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
@@ -0,0 +1,469 @@
+<?php
+/**
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCP\Files\StorageNotAvailableException;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
+use Test\TestCase;
+
+/**
+ * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class FilesPluginTest extends TestCase {
+ const GETETAG_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::GETETAG_PROPERTYNAME;
+ const FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::FILEID_PROPERTYNAME;
+ const INTERNAL_FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::INTERNAL_FILEID_PROPERTYNAME;
+ const SIZE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::SIZE_PROPERTYNAME;
+ const PERMISSIONS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::PERMISSIONS_PROPERTYNAME;
+ const LASTMODIFIED_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::LASTMODIFIED_PROPERTYNAME;
+ const DOWNLOADURL_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME;
+ const OWNER_ID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_ID_PROPERTYNAME;
+ const OWNER_DISPLAY_NAME_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME;
+ const DATA_FINGERPRINT_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME;
+
+ /**
+ * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $server;
+
+ /**
+ * @var \Sabre\DAV\Tree | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $tree;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\FilesPlugin
+ */
+ private $plugin;
+
+ /**
+ * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $view;
+
+ /**
+ * @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $config;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->view = $this->getMockBuilder('\OC\Files\View')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->config = $this->getMock('\OCP\IConfig');
+ $this->config->method('getSystemValue')
+ ->with($this->equalTo('data-fingerprint'), $this->equalTo(''))
+ ->willReturn('my_fingerprint');
+
+ $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin(
+ $this->tree,
+ $this->view,
+ $this->config
+ );
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @param string $class
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ private function createTestNode($class) {
+ $node = $this->getMockBuilder($class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ $node->expects($this->any())
+ ->method('getFileId')
+ ->will($this->returnValue('00000123instanceid'));
+ $node->expects($this->any())
+ ->method('getInternalFileId')
+ ->will($this->returnValue('123'));
+ $node->expects($this->any())
+ ->method('getEtag')
+ ->will($this->returnValue('"abc"'));
+ $node->expects($this->any())
+ ->method('getDavPermissions')
+ ->will($this->returnValue('DWCKMSR'));
+
+ return $node;
+ }
+
+ public function testGetPropertiesForFile() {
+ /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+
+ $propFind = new PropFind(
+ '/dummyPath',
+ array(
+ self::GETETAG_PROPERTYNAME,
+ self::FILEID_PROPERTYNAME,
+ self::INTERNAL_FILEID_PROPERTYNAME,
+ self::SIZE_PROPERTYNAME,
+ self::PERMISSIONS_PROPERTYNAME,
+ self::DOWNLOADURL_PROPERTYNAME,
+ self::OWNER_ID_PROPERTYNAME,
+ self::OWNER_DISPLAY_NAME_PROPERTYNAME,
+ self::DATA_FINGERPRINT_PROPERTYNAME,
+ ),
+ 0
+ );
+
+ $user = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $user
+ ->expects($this->once())
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $user
+ ->expects($this->once())
+ ->method('getDisplayName')
+ ->will($this->returnValue('M. Foo'));
+
+ $node->expects($this->once())
+ ->method('getDirectDownload')
+ ->will($this->returnValue(array('url' => 'http://example.com/')));
+ $node->expects($this->exactly(2))
+ ->method('getOwner')
+ ->will($this->returnValue($user));
+ $node->expects($this->never())
+ ->method('getSize');
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME));
+ $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME));
+ $this->assertEquals('123', $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME));
+ $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ $this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
+ $this->assertEquals('foo', $propFind->get(self::OWNER_ID_PROPERTYNAME));
+ $this->assertEquals('M. Foo', $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME));
+ $this->assertEquals([self::SIZE_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties());
+ }
+
+ public function testGetPropertiesForFileHome() {
+ /** @var \OCA\DAV\Files\FilesHome | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->getMockBuilder('\OCA\DAV\Files\FilesHome')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propFind = new PropFind(
+ '/dummyPath',
+ array(
+ self::GETETAG_PROPERTYNAME,
+ self::FILEID_PROPERTYNAME,
+ self::INTERNAL_FILEID_PROPERTYNAME,
+ self::SIZE_PROPERTYNAME,
+ self::PERMISSIONS_PROPERTYNAME,
+ self::DOWNLOADURL_PROPERTYNAME,
+ self::OWNER_ID_PROPERTYNAME,
+ self::OWNER_DISPLAY_NAME_PROPERTYNAME,
+ self::DATA_FINGERPRINT_PROPERTYNAME,
+ ),
+ 0
+ );
+
+ $user = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $user->expects($this->never())->method('getUID');
+ $user->expects($this->never())->method('getDisplayName');
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals(null, $propFind->get(self::GETETAG_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::FILEID_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::OWNER_ID_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME));
+ $this->assertEquals(['{DAV:}getetag',
+ '{http://owncloud.org/ns}id',
+ '{http://owncloud.org/ns}fileid',
+ '{http://owncloud.org/ns}size',
+ '{http://owncloud.org/ns}permissions',
+ '{http://owncloud.org/ns}downloadURL',
+ '{http://owncloud.org/ns}owner-id',
+ '{http://owncloud.org/ns}owner-display-name'
+ ], $propFind->get404Properties());
+ $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME));
+ }
+
+ public function testGetPropertiesStorageNotAvailable() {
+ /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+
+ $propFind = new PropFind(
+ '/dummyPath',
+ array(
+ self::DOWNLOADURL_PROPERTYNAME,
+ ),
+ 0
+ );
+
+ $node->expects($this->once())
+ ->method('getDirectDownload')
+ ->will($this->throwException(new StorageNotAvailableException()));
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
+ }
+
+ public function testGetPublicPermissions() {
+ $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin(
+ $this->tree,
+ $this->view,
+ $this->config,
+ true);
+ $this->plugin->initialize($this->server);
+
+ $propFind = new PropFind(
+ '/dummyPath',
+ [
+ self::PERMISSIONS_PROPERTYNAME,
+ ],
+ 0
+ );
+
+ /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ $node->expects($this->any())
+ ->method('getDavPermissions')
+ ->will($this->returnValue('DWCKMSR'));
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals('DWCKR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ }
+
+ public function testGetPropertiesForDirectory() {
+ /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory');
+
+ $propFind = new PropFind(
+ '/dummyPath',
+ array(
+ self::GETETAG_PROPERTYNAME,
+ self::FILEID_PROPERTYNAME,
+ self::SIZE_PROPERTYNAME,
+ self::PERMISSIONS_PROPERTYNAME,
+ self::DOWNLOADURL_PROPERTYNAME,
+ self::DATA_FINGERPRINT_PROPERTYNAME,
+ ),
+ 0
+ );
+
+ $node->expects($this->once())
+ ->method('getSize')
+ ->will($this->returnValue(1025));
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME));
+ $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME));
+ $this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME));
+ $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME));
+ $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
+ $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties());
+ }
+
+ public function testGetPropertiesForRootDirectory() {
+ /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->method('getPath')->willReturn('/');
+
+ $propFind = new PropFind(
+ '/',
+ [
+ self::DATA_FINGERPRINT_PROPERTYNAME,
+ ],
+ 0
+ );
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME));
+ }
+
+ public function testUpdateProps() {
+ $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+
+ $testDate = 'Fri, 13 Feb 2015 00:01:02 GMT';
+
+ $node->expects($this->once())
+ ->method('touch')
+ ->with($testDate);
+
+ $node->expects($this->once())
+ ->method('setEtag')
+ ->with('newetag')
+ ->will($this->returnValue(true));
+
+ // properties to set
+ $propPatch = new PropPatch(array(
+ self::GETETAG_PROPERTYNAME => 'newetag',
+ self::LASTMODIFIED_PROPERTYNAME => $testDate
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(200, $result[self::LASTMODIFIED_PROPERTYNAME]);
+ $this->assertEquals(200, $result[self::GETETAG_PROPERTYNAME]);
+ }
+
+ public function testUpdatePropsForbidden() {
+ $propPatch = new PropPatch(array(
+ self::OWNER_ID_PROPERTYNAME => 'user2',
+ self::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two',
+ self::FILEID_PROPERTYNAME => 12345,
+ self::PERMISSIONS_PROPERTYNAME => 'C',
+ self::SIZE_PROPERTYNAME => 123,
+ self::DOWNLOADURL_PROPERTYNAME => 'http://example.com/',
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(403, $result[self::OWNER_ID_PROPERTYNAME]);
+ $this->assertEquals(403, $result[self::OWNER_DISPLAY_NAME_PROPERTYNAME]);
+ $this->assertEquals(403, $result[self::FILEID_PROPERTYNAME]);
+ $this->assertEquals(403, $result[self::PERMISSIONS_PROPERTYNAME]);
+ $this->assertEquals(403, $result[self::SIZE_PROPERTYNAME]);
+ $this->assertEquals(403, $result[self::DOWNLOADURL_PROPERTYNAME]);
+ }
+
+ /**
+ * Testcase from https://github.com/owncloud/core/issues/5251
+ *
+ * |-FolderA
+ * |-text.txt
+ * |-test.txt
+ *
+ * FolderA is an incoming shared folder and there are no delete permissions.
+ * Thus moving /FolderA/test.txt to /test.txt should fail already on that check
+ *
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ * @expectedExceptionMessage FolderA/test.txt cannot be deleted
+ */
+ public function testMoveSrcNotDeletable() {
+ $fileInfoFolderATestTXT = $this->getMockBuilder('\OCP\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $fileInfoFolderATestTXT->expects($this->once())
+ ->method('isDeletable')
+ ->willReturn(false);
+
+ $this->view->expects($this->once())
+ ->method('getFileInfo')
+ ->with('FolderA/test.txt')
+ ->willReturn($fileInfoFolderATestTXT);
+
+ $this->plugin->checkMove('FolderA/test.txt', 'test.txt');
+ }
+
+ public function testMoveSrcDeletable() {
+ $fileInfoFolderATestTXT = $this->getMockBuilder('\OCP\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $fileInfoFolderATestTXT->expects($this->once())
+ ->method('isDeletable')
+ ->willReturn(true);
+
+ $this->view->expects($this->once())
+ ->method('getFileInfo')
+ ->with('FolderA/test.txt')
+ ->willReturn($fileInfoFolderATestTXT);
+
+ $this->plugin->checkMove('FolderA/test.txt', 'test.txt');
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\NotFound
+ * @expectedExceptionMessage FolderA/test.txt does not exist
+ */
+ public function testMoveSrcNotExist() {
+ $this->view->expects($this->once())
+ ->method('getFileInfo')
+ ->with('FolderA/test.txt')
+ ->willReturn(false);
+
+ $this->plugin->checkMove('FolderA/test.txt', 'test.txt');
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
new file mode 100644
index 00000000000..d5bbbbcc99d
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
@@ -0,0 +1,611 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation;
+use Sabre\DAV\Exception\NotFound;
+use OCP\SystemTag\ISystemTagObjectMapper;
+use OC\Files\View;
+use OCP\Files\Folder;
+use OCP\IGroupManager;
+use OCP\SystemTag\ISystemTagManager;
+
+class FilesReportPluginTest extends \Test\TestCase {
+ /** @var \Sabre\DAV\Server|\PHPUnit_Framework_MockObject_MockObject */
+ private $server;
+
+ /** @var \Sabre\DAV\Tree|\PHPUnit_Framework_MockObject_MockObject */
+ private $tree;
+
+ /** @var ISystemTagObjectMapper|\PHPUnit_Framework_MockObject_MockObject */
+ private $tagMapper;
+
+ /** @var ISystemTagManager|\PHPUnit_Framework_MockObject_MockObject */
+ private $tagManager;
+
+ /** @var \OCP\IUserSession */
+ private $userSession;
+
+ /** @var FilesReportPluginImplementation */
+ private $plugin;
+
+ /** @var View|\PHPUnit_Framework_MockObject_MockObject **/
+ private $view;
+
+ /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject **/
+ private $groupManager;
+
+ /** @var Folder|\PHPUnit_Framework_MockObject_MockObject **/
+ private $userFolder;
+
+ public function setUp() {
+ parent::setUp();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->view = $this->getMockBuilder('\OC\Files\View')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->setConstructorArgs([$this->tree])
+ ->setMethods(['getRequestUri'])
+ ->getMock();
+
+ $this->groupManager = $this->getMockBuilder('\OCP\IGroupManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->userFolder = $this->getMockBuilder('\OCP\Files\Folder')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tagManager = $this->getMock('\OCP\SystemTag\ISystemTagManager');
+ $this->tagMapper = $this->getMock('\OCP\SystemTag\ISystemTagObjectMapper');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
+
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('testuser'));
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->plugin = new FilesReportPluginImplementation(
+ $this->tree,
+ $this->view,
+ $this->tagManager,
+ $this->tagMapper,
+ $this->userSession,
+ $this->groupManager,
+ $this->userFolder
+ );
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ReportNotSupported
+ */
+ public function testOnReportInvalidNode() {
+ $path = 'totally/unrelated/13';
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/' . $path)
+ ->will($this->returnValue($this->getMock('\Sabre\DAV\INode')));
+
+ $this->server->expects($this->any())
+ ->method('getRequestUri')
+ ->will($this->returnValue($path));
+ $this->plugin->initialize($this->server);
+
+ $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ReportNotSupported
+ */
+ public function testOnReportInvalidReportName() {
+ $path = 'test';
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/' . $path)
+ ->will($this->returnValue($this->getMock('\Sabre\DAV\INode')));
+
+ $this->server->expects($this->any())
+ ->method('getRequestUri')
+ ->will($this->returnValue($path));
+ $this->plugin->initialize($this->server);
+
+ $this->plugin->onReport('{whoever}whatever', [], '/' . $path);
+ }
+
+ public function testOnReport() {
+ $path = 'test';
+
+ $parameters = [
+ [
+ 'name' => '{DAV:}prop',
+ 'value' => [
+ ['name' => '{DAV:}getcontentlength', 'value' => ''],
+ ['name' => '{http://owncloud.org/ns}size', 'value' => ''],
+ ],
+ ],
+ [
+ 'name' => '{http://owncloud.org/ns}filter-rules',
+ 'value' => [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ],
+ ],
+ ];
+
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->at(0))
+ ->method('getObjectIdsForTags')
+ ->with('123', 'files')
+ ->will($this->returnValue(['111', '222']));
+ $this->tagMapper->expects($this->at(1))
+ ->method('getObjectIdsForTags')
+ ->with('456', 'files')
+ ->will($this->returnValue(['111', '222', '333']));
+
+ $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $response->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Type', 'application/xml; charset=utf-8');
+
+ $response->expects($this->once())
+ ->method('setStatus')
+ ->with(207);
+
+ $response->expects($this->once())
+ ->method('setBody');
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/' . $path)
+ ->will($this->returnValue($reportTargetNode));
+
+ $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $filesNode2 = $this->getMockBuilder('\OCP\Files\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->userFolder->expects($this->at(0))
+ ->method('getById')
+ ->with('111')
+ ->will($this->returnValue([$filesNode1]));
+ $this->userFolder->expects($this->at(1))
+ ->method('getById')
+ ->with('222')
+ ->will($this->returnValue([$filesNode2]));
+
+ $this->server->expects($this->any())
+ ->method('getRequestUri')
+ ->will($this->returnValue($path));
+ $this->server->httpResponse = $response;
+ $this->plugin->initialize($this->server);
+
+ $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path);
+ }
+
+ public function testFindNodesByFileIdsRoot() {
+ $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $filesNode1->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('first node'));
+
+ $filesNode2 = $this->getMockBuilder('\OCP\Files\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $filesNode2->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('second node'));
+
+ $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $reportTargetNode->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue('/'));
+
+ $this->userFolder->expects($this->at(0))
+ ->method('getById')
+ ->with('111')
+ ->will($this->returnValue([$filesNode1]));
+ $this->userFolder->expects($this->at(1))
+ ->method('getById')
+ ->with('222')
+ ->will($this->returnValue([$filesNode2]));
+
+ /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */
+ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
+
+ $this->assertCount(2, $result);
+ $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]);
+ $this->assertEquals('first node', $result[0]->getName());
+ $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]);
+ $this->assertEquals('second node', $result[1]->getName());
+ }
+
+ public function testFindNodesByFileIdsSubDir() {
+ $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $filesNode1->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('first node'));
+
+ $filesNode2 = $this->getMockBuilder('\OCP\Files\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $filesNode2->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('second node'));
+
+ $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $reportTargetNode->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue('/sub1/sub2'));
+
+
+ $subNode = $this->getMockBuilder('\OCP\Files\Folder')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->userFolder->expects($this->at(0))
+ ->method('get')
+ ->with('/sub1/sub2')
+ ->will($this->returnValue($subNode));
+
+ $subNode->expects($this->at(0))
+ ->method('getById')
+ ->with('111')
+ ->will($this->returnValue([$filesNode1]));
+ $subNode->expects($this->at(1))
+ ->method('getById')
+ ->with('222')
+ ->will($this->returnValue([$filesNode2]));
+
+ /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */
+ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
+
+ $this->assertCount(2, $result);
+ $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]);
+ $this->assertEquals('first node', $result[0]->getName());
+ $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]);
+ $this->assertEquals('second node', $result[1]->getName());
+ }
+
+ public function testPrepareResponses() {
+ $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype'];
+
+ $node1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $node1->expects($this->once())
+ ->method('getInternalFileId')
+ ->will($this->returnValue('111'));
+ $node2->expects($this->once())
+ ->method('getInternalFileId')
+ ->will($this->returnValue('222'));
+ $node2->expects($this->once())
+ ->method('getSize')
+ ->will($this->returnValue(1024));
+
+ $config = $this->getMock('\OCP\IConfig');
+
+ $this->server->addPlugin(
+ new \OCA\DAV\Connector\Sabre\FilesPlugin(
+ $this->tree,
+ $this->view,
+ $config
+ )
+ );
+ $this->plugin->initialize($this->server);
+ $responses = $this->plugin->prepareResponses($requestedProps, [$node1, $node2]);
+
+ $this->assertCount(2, $responses);
+
+ $this->assertEquals(200, $responses[0]->getHttpStatus());
+ $this->assertEquals(200, $responses[1]->getHttpStatus());
+
+ $props1 = $responses[0]->getResponseProperties();
+ $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']);
+ $this->assertNull($props1[404]['{DAV:}getcontentlength']);
+ $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']);
+ $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue();
+ $this->assertEquals('{DAV:}collection', $resourceType1[0]);
+
+ $props2 = $responses[1]->getResponseProperties();
+ $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']);
+ $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']);
+ $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']);
+ $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue());
+ }
+
+ public function testProcessFilterRulesSingle() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(1))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', 0, '', ['111', '222']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ];
+
+ $this->assertEquals(['111', '222'], $this->invokePrivate($this->plugin, 'processFilterRules', [$rules]));
+ }
+
+ public function testProcessFilterRulesAndCondition() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(2))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', 0, '', ['111', '222']],
+ ['456', 'files', 0, '', ['222', '333']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+
+ public function testProcessFilterRulesAndConditionWithOneEmptyResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(2))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', 0, '', ['111', '222']],
+ ['456', 'files', 0, '', []],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+
+ public function testProcessFilterRulesAndConditionWithFirstEmptyResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(1))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', 0, '', []],
+ ['456', 'files', 0, '', ['111', '222']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+
+ public function testProcessFilterRulesAndConditionWithEmptyMidResult() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $this->tagMapper->expects($this->exactly(2))
+ ->method('getObjectIdsForTags')
+ ->withConsecutive(
+ ['123', 'files'],
+ ['456', 'files'],
+ ['789', 'files']
+ )
+ ->willReturnMap([
+ ['123', 'files', 0, '', ['111', '222']],
+ ['456', 'files', 0, '', ['333']],
+ ['789', 'files', 0, '', ['111', '222']],
+ ]);
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'],
+ ];
+
+ $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+
+ public function testProcessFilterRulesInvisibleTagAsAdmin() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(true));
+
+ $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag1->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(true));
+
+ $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag2->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(false));
+
+ // no need to fetch tags to check permissions
+ $this->tagManager->expects($this->never())
+ ->method('getTagsByIds');
+
+ $this->tagMapper->expects($this->at(0))
+ ->method('getObjectIdsForTags')
+ ->with('123')
+ ->will($this->returnValue(['111', '222']));
+ $this->tagMapper->expects($this->at(1))
+ ->method('getObjectIdsForTags')
+ ->with('456')
+ ->will($this->returnValue(['222', '333']));
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testProcessFilterRulesInvisibleTagAsUser() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(false));
+
+ $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag1->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(true));
+
+ $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag2->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(false)); // invisible
+
+ $this->tagManager->expects($this->once())
+ ->method('getTagsByIds')
+ ->with(['123', '456'])
+ ->will($this->returnValue([$tag1, $tag2]));
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->invokePrivate($this->plugin, 'processFilterRules', [$rules]);
+ }
+
+ public function testProcessFilterRulesVisibleTagAsUser() {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(false));
+
+ $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag1->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(true));
+
+ $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag');
+ $tag2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('123'));
+ $tag2->expects($this->any())
+ ->method('isUserVisible')
+ ->will($this->returnValue(true));
+
+ $this->tagManager->expects($this->once())
+ ->method('getTagsByIds')
+ ->with(['123', '456'])
+ ->will($this->returnValue([$tag1, $tag2]));
+
+ $this->tagMapper->expects($this->at(0))
+ ->method('getObjectIdsForTags')
+ ->with('123')
+ ->will($this->returnValue(['111', '222']));
+ $this->tagMapper->expects($this->at(1))
+ ->method('getObjectIdsForTags')
+ ->with('456')
+ ->will($this->returnValue(['222', '333']));
+
+ $rules = [
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
+ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
+ ];
+
+ $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php b/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php
new file mode 100644
index 00000000000..a95dcd69f44
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\MaintenancePlugin;
+use Test\TestCase;
+use OCP\IConfig;
+
+/**
+ * Class MaintenancePluginTest
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class MaintenancePluginTest extends TestCase {
+ /** @var IConfig */
+ private $config;
+ /** @var MaintenancePlugin */
+ private $maintenancePlugin;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->config = $this->getMock('\OCP\IConfig');
+ $this->maintenancePlugin = new MaintenancePlugin($this->config);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ServiceUnavailable
+ * @expectedExceptionMessage System in single user mode.
+ */
+ public function testSingleUserMode() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('singleuser', false)
+ ->will($this->returnValue(true));
+
+ $this->maintenancePlugin->checkMaintenanceMode();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\ServiceUnavailable
+ * @expectedExceptionMessage System in single user mode.
+ */
+ public function testMaintenanceMode() {
+ $this->config
+ ->expects($this->exactly(1))
+ ->method('getSystemValue')
+ ->will($this->onConsecutiveCalls([false, true]));
+
+ $this->maintenancePlugin->checkMaintenanceMode();
+ }
+
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
new file mode 100644
index 00000000000..deb6ecf7f70
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+/**
+ * Class NodeTest
+ *
+ * @group DB
+ * @package OCA\DAV\Tests\unit\Connector\Sabre
+ */
+class NodeTest extends \Test\TestCase {
+ public function davPermissionsProvider() {
+ return array(
+ array(\OCP\Constants::PERMISSION_ALL, 'file', false, false, 'RDNVW'),
+ array(\OCP\Constants::PERMISSION_ALL, 'dir', false, false, 'RDNVCK'),
+ array(\OCP\Constants::PERMISSION_ALL, 'file', true, false, 'SRDNVW'),
+ array(\OCP\Constants::PERMISSION_ALL, 'file', true, true, 'SRMDNVW'),
+ array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE, 'file', true, false, 'SDNVW'),
+ array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_UPDATE, 'file', false, false, 'RD'),
+ array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_DELETE, 'file', false, false, 'RNVW'),
+ array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'file', false, false, 'RDNVW'),
+ array(\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'dir', false, false, 'RDNV'),
+ );
+ }
+
+ /**
+ * @dataProvider davPermissionsProvider
+ */
+ public function testDavPermissions($permissions, $type, $shared, $mounted, $expected) {
+ $info = $this->getMockBuilder('\OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getPermissions', 'isShared', 'isMounted', 'getType'))
+ ->getMock();
+ $info->expects($this->any())
+ ->method('getPermissions')
+ ->will($this->returnValue($permissions));
+ $info->expects($this->any())
+ ->method('isShared')
+ ->will($this->returnValue($shared));
+ $info->expects($this->any())
+ ->method('isMounted')
+ ->will($this->returnValue($mounted));
+ $info->expects($this->any())
+ ->method('getType')
+ ->will($this->returnValue($type));
+ $view = $this->getMock('\OC\Files\View');
+
+ $node = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $this->assertEquals($expected, $node->getDavPermissions());
+ }
+
+ public function sharePermissionsProvider() {
+ return [
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 1, 1],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 3, 3],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 5, 1],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 7, 3],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 9, 1],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 11, 3],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 13, 1],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 15, 3],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 17, 17],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 19, 19],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 21, 17],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 23, 19],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 25, 17],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 27, 19],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 29, 17],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 30, 18],
+ [\OCP\Files\FileInfo::TYPE_FILE, null, 31, 19],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 1, 1],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 3, 3],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 5, 5],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 7, 7],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 9, 9],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 11, 11],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 13, 13],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 15, 15],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 17, 17],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 19, 19],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 21, 21],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 23, 23],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 25, 25],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 27, 27],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 29, 29],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 30, 30],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, null, 31, 31],
+ [\OCP\Files\FileInfo::TYPE_FOLDER, 'shareToken', 7, 7],
+ ];
+ }
+
+ /**
+ * @dataProvider sharePermissionsProvider
+ */
+ public function testSharePermissions($type, $user, $permissions, $expected) {
+ $storage = $this->getMock('\OCP\Files\Storage');
+ $storage->method('getPermissions')->willReturn($permissions);
+
+ $mountpoint = $this->getMock('\OCP\Files\Mount\IMountPoint');
+ $mountpoint->method('getMountPoint')->willReturn('myPath');
+ $shareManager = $this->getMockBuilder('OCP\Share\IManager')->disableOriginalConstructor()->getMock();
+ $share = $this->getMockBuilder('OCP\Share\IShare')->disableOriginalConstructor()->getMock();
+
+ if ($user === null) {
+ $shareManager->expects($this->never())->method('getShareByToken');
+ $share->expects($this->never())->method('getPermissions');
+ } else {
+ $shareManager->expects($this->once())->method('getShareByToken')->with($user)
+ ->willReturn($share);
+ $share->expects($this->once())->method('getPermissions')->willReturn($permissions);
+ }
+
+ $info = $this->getMockBuilder('\OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->setMethods(['getStorage', 'getType', 'getMountPoint'])
+ ->getMock();
+
+ $info->method('getStorage')->willReturn($storage);
+ $info->method('getType')->willReturn($type);
+ $info->method('getMountPoint')->willReturn($mountpoint);
+
+ $view = $this->getMock('\OC\Files\View');
+
+ $node = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $this->invokePrivate($node, 'shareManager', [$shareManager]);
+ $this->assertEquals($expected, $node->getSharePermissions($user));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php
new file mode 100644
index 00000000000..4a5e43376c0
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php
@@ -0,0 +1,355 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+
+use OC\Files\FileInfo;
+use OC\Files\Storage\Temporary;
+
+class TestDoubleFileView extends \OC\Files\View {
+
+ public function __construct($updatables, $deletables, $canRename = true) {
+ $this->updatables = $updatables;
+ $this->deletables = $deletables;
+ $this->canRename = $canRename;
+ }
+
+ public function isUpdatable($path) {
+ return $this->updatables[$path];
+ }
+
+ public function isCreatable($path) {
+ return $this->updatables[$path];
+ }
+
+ public function isDeletable($path) {
+ return $this->deletables[$path];
+ }
+
+ public function rename($path1, $path2) {
+ return $this->canRename;
+ }
+
+ public function getRelativePath($path) {
+ return $path;
+ }
+}
+
+/**
+ * Class ObjectTreeTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\Unit\Connector\Sabre
+ */
+class ObjectTreeTest extends \Test\TestCase {
+
+ /**
+ * @dataProvider moveFailedProvider
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testMoveFailed($source, $destination, $updatables, $deletables) {
+ $this->moveTest($source, $destination, $updatables, $deletables);
+ }
+
+ /**
+ * @dataProvider moveSuccessProvider
+ */
+ public function testMoveSuccess($source, $destination, $updatables, $deletables) {
+ $this->moveTest($source, $destination, $updatables, $deletables);
+ $this->assertTrue(true);
+ }
+
+ /**
+ * @dataProvider moveFailedInvalidCharsProvider
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\InvalidPath
+ */
+ public function testMoveFailedInvalidChars($source, $destination, $updatables, $deletables) {
+ $this->moveTest($source, $destination, $updatables, $deletables);
+ }
+
+ function moveFailedInvalidCharsProvider() {
+ return array(
+ array('a/b', 'a/*', array('a' => true, 'a/b' => true, 'a/c*' => false), array()),
+ );
+ }
+
+ function moveFailedProvider() {
+ return array(
+ array('a/b', 'a/c', array('a' => false, 'a/b' => false, 'a/c' => false), array()),
+ array('a/b', 'b/b', array('a' => false, 'a/b' => false, 'b' => false, 'b/b' => false), array()),
+ array('a/b', 'b/b', array('a' => false, 'a/b' => true, 'b' => false, 'b/b' => false), array()),
+ array('a/b', 'b/b', array('a' => true, 'a/b' => true, 'b' => false, 'b/b' => false), array()),
+ array('a/b', 'b/b', array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false), array('a/b' => false)),
+ array('a/b', 'a/c', array('a' => false, 'a/b' => true, 'a/c' => false), array()),
+ );
+ }
+
+ function moveSuccessProvider() {
+ return array(
+ array('a/b', 'b/b', array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false), array('a/b' => true)),
+ // older files with special chars can still be renamed to valid names
+ array('a/b*', 'b/b', array('a' => true, 'a/b*' => true, 'b' => true, 'b/b' => false), array('a/b*' => true)),
+ );
+ }
+
+ /**
+ * @param $source
+ * @param $destination
+ * @param $updatables
+ */
+ private function moveTest($source, $destination, $updatables, $deletables) {
+ $view = new TestDoubleFileView($updatables, $deletables);
+
+ $info = new FileInfo('', null, null, array(), null);
+
+ $rootDir = new \OCA\DAV\Connector\Sabre\Directory($view, $info);
+ $objectTree = $this->getMock('\OCA\DAV\Connector\Sabre\ObjectTree',
+ array('nodeExists', 'getNodeForPath'),
+ array($rootDir, $view));
+
+ $objectTree->expects($this->once())
+ ->method('getNodeForPath')
+ ->with($this->identicalTo($source))
+ ->will($this->returnValue(false));
+
+ /** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */
+ $mountManager = \OC\Files\Filesystem::getMountManager();
+ $objectTree->init($rootDir, $view, $mountManager);
+ $objectTree->move($source, $destination);
+ }
+
+ /**
+ * @dataProvider nodeForPathProvider
+ */
+ public function testGetNodeForPath(
+ $inputFileName,
+ $fileInfoQueryPath,
+ $outputFileName,
+ $type,
+ $enableChunkingHeader
+ ) {
+
+ if ($enableChunkingHeader) {
+ $_SERVER['HTTP_OC_CHUNKED'] = true;
+ }
+
+ $rootNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mountManager = $this->getMock('\OC\Files\Mount\Manager');
+ $view = $this->getMock('\OC\Files\View');
+ $fileInfo = $this->getMock('\OCP\Files\FileInfo');
+ $fileInfo->expects($this->once())
+ ->method('getType')
+ ->will($this->returnValue($type));
+ $fileInfo->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue($outputFileName));
+
+ $view->expects($this->once())
+ ->method('getFileInfo')
+ ->with($fileInfoQueryPath)
+ ->will($this->returnValue($fileInfo));
+
+ $tree = new \OCA\DAV\Connector\Sabre\ObjectTree();
+ $tree->init($rootNode, $view, $mountManager);
+
+ $node = $tree->getNodeForPath($inputFileName);
+
+ $this->assertNotNull($node);
+ $this->assertEquals($outputFileName, $node->getName());
+
+ if ($type === 'file') {
+ $this->assertTrue($node instanceof \OCA\DAV\Connector\Sabre\File);
+ } else {
+ $this->assertTrue($node instanceof \OCA\DAV\Connector\Sabre\Directory);
+ }
+
+ unset($_SERVER['HTTP_OC_CHUNKED']);
+ }
+
+ function nodeForPathProvider() {
+ return array(
+ // regular file
+ array(
+ 'regularfile.txt',
+ 'regularfile.txt',
+ 'regularfile.txt',
+ 'file',
+ false
+ ),
+ // regular directory
+ array(
+ 'regulardir',
+ 'regulardir',
+ 'regulardir',
+ 'dir',
+ false
+ ),
+ // regular file with chunking
+ array(
+ 'regularfile.txt',
+ 'regularfile.txt',
+ 'regularfile.txt',
+ 'file',
+ true
+ ),
+ // regular directory with chunking
+ array(
+ 'regulardir',
+ 'regulardir',
+ 'regulardir',
+ 'dir',
+ true
+ ),
+ // file with chunky file name
+ array(
+ 'regularfile.txt-chunking-123566789-10-1',
+ 'regularfile.txt',
+ 'regularfile.txt',
+ 'file',
+ true
+ ),
+ // regular file in subdir
+ array(
+ 'subdir/regularfile.txt',
+ 'subdir/regularfile.txt',
+ 'regularfile.txt',
+ 'file',
+ false
+ ),
+ // regular directory in subdir
+ array(
+ 'subdir/regulardir',
+ 'subdir/regulardir',
+ 'regulardir',
+ 'dir',
+ false
+ ),
+ // file with chunky file name in subdir
+ array(
+ 'subdir/regularfile.txt-chunking-123566789-10-1',
+ 'subdir/regularfile.txt',
+ 'regularfile.txt',
+ 'file',
+ true
+ ),
+ );
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\InvalidPath
+ */
+ public function testGetNodeForPathInvalidPath() {
+ $path = '/foo\bar';
+
+
+ $storage = new Temporary([]);
+
+ $view = $this->getMock('\OC\Files\View', ['resolvePath']);
+ $view->expects($this->once())
+ ->method('resolvePath')
+ ->will($this->returnCallback(function($path) use ($storage){
+ return [$storage, ltrim($path, '/')];
+ }));
+
+ $rootNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mountManager = $this->getMock('\OC\Files\Mount\Manager');
+
+ $tree = new \OCA\DAV\Connector\Sabre\ObjectTree();
+ $tree->init($rootNode, $view, $mountManager);
+
+ $tree->getNodeForPath($path);
+ }
+
+ public function testGetNodeForPathRoot() {
+ $path = '/';
+
+
+ $storage = new Temporary([]);
+
+ $view = $this->getMock('\OC\Files\View', ['resolvePath']);
+ $view->expects($this->any())
+ ->method('resolvePath')
+ ->will($this->returnCallback(function ($path) use ($storage) {
+ return [$storage, ltrim($path, '/')];
+ }));
+
+ $rootNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mountManager = $this->getMock('\OC\Files\Mount\Manager');
+
+ $tree = new \OCA\DAV\Connector\Sabre\ObjectTree();
+ $tree->init($rootNode, $view, $mountManager);
+
+ $this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path));
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ * @expectedExceptionMessage Could not copy directory nameOfSourceNode, target exists
+ */
+ public function testFailingMove() {
+ $source = 'a/b';
+ $destination = 'b/b';
+ $updatables = array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false);
+ $deletables = array('a/b' => true);
+
+ $view = new TestDoubleFileView($updatables, $deletables);
+
+ $info = new FileInfo('', null, null, array(), null);
+
+ $rootDir = new \OCA\DAV\Connector\Sabre\Directory($view, $info);
+ $objectTree = $this->getMock('\OCA\DAV\Connector\Sabre\ObjectTree',
+ array('nodeExists', 'getNodeForPath'),
+ array($rootDir, $view));
+
+ $sourceNode = $this->getMockBuilder('\Sabre\DAV\ICollection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sourceNode->expects($this->once())
+ ->method('getName')
+ ->will($this->returnValue('nameOfSourceNode'));
+
+ $objectTree->expects($this->once())
+ ->method('nodeExists')
+ ->with($this->identicalTo($destination))
+ ->will($this->returnValue(true));
+ $objectTree->expects($this->once())
+ ->method('getNodeForPath')
+ ->with($this->identicalTo($source))
+ ->will($this->returnValue($sourceNode));
+
+ /** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */
+ $mountManager = \OC\Files\Filesystem::getMountManager();
+ $objectTree->init($rootDir, $view, $mountManager);
+ $objectTree->move($source, $destination);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php
new file mode 100644
index 00000000000..63717713a7c
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php
@@ -0,0 +1,273 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+use OCP\IGroupManager;
+use \Sabre\DAV\PropPatch;
+use OCP\IUserManager;
+use Test\TestCase;
+
+class PrincipalTest extends TestCase {
+ /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+ private $userManager;
+ /** @var \OCA\DAV\Connector\Sabre\Principal */
+ private $connector;
+ /** @var IGroupManager | \PHPUnit_Framework_MockObject_MockObject */
+ private $groupManager;
+
+ public function setUp() {
+ $this->userManager = $this->getMockBuilder('\OCP\IUserManager')
+ ->disableOriginalConstructor()->getMock();
+ $this->groupManager = $this->getMockBuilder('\OCP\IGroupManager')
+ ->disableOriginalConstructor()->getMock();
+
+ $this->connector = new \OCA\DAV\Connector\Sabre\Principal(
+ $this->userManager,
+ $this->groupManager);
+ parent::setUp();
+ }
+
+ public function testGetPrincipalsByPrefixWithoutPrefix() {
+ $response = $this->connector->getPrincipalsByPrefix('');
+ $this->assertSame([], $response);
+ }
+
+ public function testGetPrincipalsByPrefixWithUsers() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getDisplayName')
+ ->will($this->returnValue('Dr. Foo-Bar'));
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getEMailAddress')
+ ->will($this->returnValue(''));
+ $barUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $barUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('bar'));
+ $barUser
+ ->expects($this->exactly(1))
+ ->method('getEMailAddress')
+ ->will($this->returnValue('bar@owncloud.org'));
+ $this->userManager
+ ->expects($this->once())
+ ->method('search')
+ ->with('')
+ ->will($this->returnValue([$fooUser, $barUser]));
+
+ $expectedResponse = [
+ 0 => [
+ 'uri' => 'principals/users/foo',
+ '{DAV:}displayname' => 'Dr. Foo-Bar'
+ ],
+ 1 => [
+ 'uri' => 'principals/users/bar',
+ '{DAV:}displayname' => 'bar',
+ '{http://sabredav.org/ns}email-address' => 'bar@owncloud.org'
+ ]
+ ];
+ $response = $this->connector->getPrincipalsByPrefix('principals/users');
+ $this->assertSame($expectedResponse, $response);
+ }
+
+ public function testGetPrincipalsByPrefixEmpty() {
+ $this->userManager
+ ->expects($this->once())
+ ->method('search')
+ ->with('')
+ ->will($this->returnValue([]));
+
+ $response = $this->connector->getPrincipalsByPrefix('principals/users');
+ $this->assertSame([], $response);
+ }
+
+ public function testGetPrincipalsByPathWithoutMail() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue($fooUser));
+
+ $expectedResponse = [
+ 'uri' => 'principals/users/foo',
+ '{DAV:}displayname' => 'foo'
+ ];
+ $response = $this->connector->getPrincipalByPath('principals/users/foo');
+ $this->assertSame($expectedResponse, $response);
+ }
+
+ public function testGetPrincipalsByPathWithMail() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getEMailAddress')
+ ->will($this->returnValue('foo@owncloud.org'));
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue($fooUser));
+
+ $expectedResponse = [
+ 'uri' => 'principals/users/foo',
+ '{DAV:}displayname' => 'foo',
+ '{http://sabredav.org/ns}email-address' => 'foo@owncloud.org'
+ ];
+ $response = $this->connector->getPrincipalByPath('principals/users/foo');
+ $this->assertSame($expectedResponse, $response);
+ }
+
+ public function testGetPrincipalsByPathEmpty() {
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue(null));
+
+ $response = $this->connector->getPrincipalByPath('principals/users/foo');
+ $this->assertSame(null, $response);
+ }
+
+ public function testGetGroupMemberSet() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue($fooUser));
+
+ $response = $this->connector->getGroupMemberSet('principals/users/foo');
+ $this->assertSame(['principals/users/foo'], $response);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception
+ * @expectedExceptionMessage Principal not found
+ */
+ public function testGetGroupMemberSetEmpty() {
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue(null));
+
+ $this->connector->getGroupMemberSet('principals/users/foo');
+ }
+
+ public function testGetGroupMembership() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $group = $this->getMockBuilder('\OCP\IGroup')
+ ->disableOriginalConstructor()->getMock();
+ $group->expects($this->once())
+ ->method('getGID')
+ ->willReturn('group1');
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->willReturn($fooUser);
+ $this->groupManager
+ ->expects($this->once())
+ ->method('getUserGroups')
+ ->willReturn([
+ $group
+ ]);
+
+ $expectedResponse = [
+ 'principals/groups/group1'
+ ];
+ $response = $this->connector->getGroupMembership('principals/users/foo');
+ $this->assertSame($expectedResponse, $response);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception
+ * @expectedExceptionMessage Principal not found
+ */
+ public function testGetGroupMembershipEmpty() {
+ $this->userManager
+ ->expects($this->once())
+ ->method('get')
+ ->with('foo')
+ ->will($this->returnValue(null));
+
+ $this->connector->getGroupMembership('principals/users/foo');
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception
+ * @expectedExceptionMessage Setting members of the group is not supported yet
+ */
+ public function testSetGroupMembership() {
+ $this->connector->setGroupMemberSet('principals/users/foo', ['foo']);
+ }
+
+ public function testUpdatePrincipal() {
+ $this->assertSame(0, $this->connector->updatePrincipal('foo', new PropPatch(array())));
+ }
+
+ public function testSearchPrincipals() {
+ $this->assertSame([], $this->connector->searchPrincipals('principals/users', []));
+ }
+
+ public function testFindByUri() {
+ $fooUser = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $fooUser
+ ->expects($this->exactly(1))
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+
+ $this->userManager->expects($this->once())->method('getByEmail')->willReturn([
+ $fooUser
+ ]);
+ $ret = $this->connector->findByUri('mailto:foo@bar.net', 'principals/users');
+ $this->assertSame('principals/users/foo', $ret);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php
new file mode 100644
index 00000000000..45fb1743d15
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+/**
+ * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class QuotaPluginTest extends \Test\TestCase {
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\QuotaPlugin
+ */
+ private $plugin;
+
+ private function init($quota, $checkedPath = '') {
+ $view = $this->buildFileViewMock($quota, $checkedPath);
+ $this->server = new \Sabre\DAV\Server();
+ $this->plugin = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\QuotaPlugin')
+ ->setConstructorArgs([$view])
+ ->setMethods(['getFileChunking'])
+ ->getMock();
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider lengthProvider
+ */
+ public function testLength($expected, $headers) {
+ $this->init(0);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $length = $this->plugin->getLength();
+ $this->assertEquals($expected, $length);
+ }
+
+ /**
+ * @dataProvider quotaOkayProvider
+ */
+ public function testCheckQuota($quota, $headers) {
+ $this->init($quota);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
+
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $result = $this->plugin->checkQuota('');
+ $this->assertTrue($result);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\InsufficientStorage
+ * @dataProvider quotaExceededProvider
+ */
+ public function testCheckExceededQuota($quota, $headers) {
+ $this->init($quota);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
+
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $this->plugin->checkQuota('');
+ }
+
+ /**
+ * @dataProvider quotaOkayProvider
+ */
+ public function testCheckQuotaOnPath($quota, $headers) {
+ $this->init($quota, 'sub/test.txt');
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
+
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $result = $this->plugin->checkQuota('/sub/test.txt');
+ $this->assertTrue($result);
+ }
+
+ public function quotaOkayProvider() {
+ return array(
+ array(1024, array()),
+ array(1024, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(1024, array('CONTENT-LENGTH' => '512')),
+ array(1024, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ // \OCP\Files\FileInfo::SPACE-UNKNOWN = -2
+ array(-2, array()),
+ array(-2, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(-2, array('CONTENT-LENGTH' => '512')),
+ array(-2, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ );
+ }
+
+ public function quotaExceededProvider() {
+ return array(
+ array(1023, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(511, array('CONTENT-LENGTH' => '512')),
+ array(2047, array('OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024')),
+ );
+ }
+
+ public function lengthProvider() {
+ return array(
+ array(null, array()),
+ array(1024, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(512, array('CONTENT-LENGTH' => '512')),
+ array(2048, array('OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024')),
+ array(4096, array('OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => '4096')),
+ );
+ }
+
+ public function quotaChunkedOkProvider() {
+ return array(
+ array(1024, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(1024, 0, array('CONTENT-LENGTH' => '512')),
+ array(1024, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ // with existing chunks (allowed size = total length - chunk total size)
+ array(400, 128, array('X-EXPECTED-ENTITY-LENGTH' => '512')),
+ array(400, 128, array('CONTENT-LENGTH' => '512')),
+ array(400, 128, array('OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500')),
+ // \OCP\Files\FileInfo::SPACE-UNKNOWN = -2
+ array(-2, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(-2, 0, array('CONTENT-LENGTH' => '512')),
+ array(-2, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ array(-2, 128, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(-2, 128, array('CONTENT-LENGTH' => '512')),
+ array(-2, 128, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ );
+ }
+
+ /**
+ * @dataProvider quotaChunkedOkProvider
+ */
+ public function testCheckQuotaChunkedOk($quota, $chunkTotalSize, $headers) {
+ $this->init($quota, 'sub/test.txt');
+
+ $mockChunking = $this->getMockBuilder('\OC_FileChunking')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockChunking->expects($this->once())
+ ->method('getCurrentSize')
+ ->will($this->returnValue($chunkTotalSize));
+
+ $this->plugin->expects($this->once())
+ ->method('getFileChunking')
+ ->will($this->returnValue($mockChunking));
+
+ $headers['OC-CHUNKED'] = 1;
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $result = $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1');
+ $this->assertTrue($result);
+ }
+
+ public function quotaChunkedFailProvider() {
+ return array(
+ array(400, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(400, 0, array('CONTENT-LENGTH' => '512')),
+ array(400, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ // with existing chunks (allowed size = total length - chunk total size)
+ array(380, 128, array('X-EXPECTED-ENTITY-LENGTH' => '512')),
+ array(380, 128, array('CONTENT-LENGTH' => '512')),
+ array(380, 128, array('OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500')),
+ );
+ }
+
+ /**
+ * @dataProvider quotaChunkedFailProvider
+ * @expectedException \Sabre\DAV\Exception\InsufficientStorage
+ */
+ public function testCheckQuotaChunkedFail($quota, $chunkTotalSize, $headers) {
+ $this->init($quota, 'sub/test.txt');
+
+ $mockChunking = $this->getMockBuilder('\OC_FileChunking')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockChunking->expects($this->once())
+ ->method('getCurrentSize')
+ ->will($this->returnValue($chunkTotalSize));
+
+ $this->plugin->expects($this->once())
+ ->method('getFileChunking')
+ ->will($this->returnValue($mockChunking));
+
+ $headers['OC-CHUNKED'] = 1;
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1');
+ }
+
+ private function buildFileViewMock($quota, $checkedPath) {
+ // mock filesysten
+ $view = $this->getMock('\OC\Files\View', array('free_space'), array(), '', false);
+ $view->expects($this->any())
+ ->method('free_space')
+ ->with($this->identicalTo($checkedPath))
+ ->will($this->returnValue($quota));
+
+ return $view;
+ }
+
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php
new file mode 100644
index 00000000000..a8fcf552849
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use Sabre\DAV\Auth\Backend\BackendInterface;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+class Auth implements BackendInterface {
+ /**
+ * @var string
+ */
+ private $user;
+
+ /**
+ * @var string
+ */
+ private $password;
+
+ /**
+ * Auth constructor.
+ *
+ * @param string $user
+ * @param string $password
+ */
+ public function __construct($user, $password) {
+ $this->user = $user;
+ $this->password = $password;
+ }
+
+ /**
+ * When this method is called, the backend must check if authentication was
+ * successful.
+ *
+ * The returned value must be one of the following
+ *
+ * [true, "principals/username"]
+ * [false, "reason for failure"]
+ *
+ * If authentication was successful, it's expected that the authentication
+ * backend returns a so-called principal url.
+ *
+ * Examples of a principal url:
+ *
+ * principals/admin
+ * principals/user1
+ * principals/users/joe
+ * principals/uid/123457
+ *
+ * If you don't use WebDAV ACL (RFC3744) we recommend that you simply
+ * return a string such as:
+ *
+ * principals/users/[username]
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return array
+ */
+ function check(RequestInterface $request, ResponseInterface $response) {
+ $userSession = \OC::$server->getUserSession();
+ $result = $userSession->login($this->user, $this->password);
+ if ($result) {
+ //we need to pass the user name, which may differ from login name
+ $user = $userSession->getUser()->getUID();
+ \OC_Util::setupFS($user);
+ //trigger creation of user home and /files folder
+ \OC::$server->getUserFolder($user);
+ return [true, "principals/$user"];
+ }
+ return [false, "login failed"];
+ }
+
+ /**
+ * This method is called when a user could not be authenticated, and
+ * authentication was required for the current request.
+ *
+ * This gives you the opportunity to set authentication headers. The 401
+ * status code will already be set.
+ *
+ * In this case of Basic Auth, this would for example mean that the
+ * following header needs to be set:
+ *
+ * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
+ *
+ * Keep in mind that in the case of multiple authentication backends, other
+ * WWW-Authenticate headers may already have been set, and you'll want to
+ * append your own WWW-Authenticate header instead of overwriting the
+ * existing one.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function challenge(RequestInterface $request, ResponseInterface $response) {
+ // TODO: Implement challenge() method.
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php
new file mode 100644
index 00000000000..1b296aaa243
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use OCP\AppFramework\Http;
+use OCP\Lock\ILockingProvider;
+
+/**
+ * Class DownloadTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest
+ */
+class DownloadTest extends RequestTest {
+ public function testDownload() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+
+ $response = $this->request($view, $user, 'pass', 'GET', '/foo.txt');
+ $this->assertEquals(Http::STATUS_OK, $response->getStatus());
+ $this->assertEquals(stream_get_contents($response->getBody()), 'bar');
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\FileLocked
+ */
+ public function testDownloadWriteLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_EXCLUSIVE);
+
+ $this->request($view, $user, 'pass', 'GET', '/foo.txt', 'asd');
+ }
+
+ public function testDownloadReadLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_SHARED);
+
+ $response = $this->request($view, $user, 'pass', 'GET', '/foo.txt', 'asd');
+ $this->assertEquals(Http::STATUS_OK, $response->getStatus());
+ $this->assertEquals(stream_get_contents($response->getBody()), 'bar');
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php
new file mode 100644
index 00000000000..1a593cb8d76
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use OC\Files\View;
+use Test\Traits\EncryptionTrait;
+
+/**
+ * Class EncryptionUploadTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\Unit\Connector\Sabre\RequestTest
+ */
+class EncryptionUploadTest extends UploadTest {
+ use EncryptionTrait;
+
+ protected function setupUser($name, $password) {
+ $this->createUser($name, $password);
+ $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder();
+ $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]);
+ $this->setupForUser($name, $password);
+ $this->loginWithEncryption($name);
+ return new View('/' . $name . '/files');
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php
new file mode 100644
index 00000000000..42b8ef927f0
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use Sabre\DAV\Exception;
+
+class ExceptionPlugin extends \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin {
+ /**
+ * @var \Exception[]
+ */
+ protected $exceptions = [];
+
+ public function logException(\Exception $ex) {
+ $exceptionClass = get_class($ex);
+ if (!isset($this->nonFatalExceptions[$exceptionClass])) {
+ $this->exceptions[] = $ex;
+ }
+ }
+
+ /**
+ * @return \Exception[]
+ */
+ public function getExceptions() {
+ return $this->exceptions;
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php
new file mode 100644
index 00000000000..2986db4a7f1
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use OC\Files\View;
+use Test\Traits\EncryptionTrait;
+
+/**
+ * Class PartFileInRootUploadTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest
+ */
+class PartFileInRootUploadTest extends UploadTest {
+ protected function setUp() {
+ $config = \OC::$server->getConfig();
+ $mockConfig = $this->getMock('\OCP\IConfig');
+ $mockConfig->expects($this->any())
+ ->method('getSystemValue')
+ ->will($this->returnCallback(function ($key, $default) use ($config) {
+ if ($key === 'part_file_in_storage') {
+ return false;
+ } else {
+ return $config->getSystemValue($key, $default);
+ }
+ }));
+ $this->overwriteService('AllConfig', $mockConfig);
+ parent::setUp();
+ }
+
+ protected function tearDown() {
+ $this->restoreService('AllConfig');
+ return parent::tearDown();
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTest.php
new file mode 100644
index 00000000000..1c951a2fbd0
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTest.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use OCA\DAV\Connector\Sabre\Server;
+use OCA\DAV\Connector\Sabre\ServerFactory;
+use OC\Files\Mount\MountPoint;
+use OC\Files\Storage\StorageFactory;
+use OC\Files\Storage\Temporary;
+use OC\Files\View;
+use OCP\IUser;
+use Sabre\HTTP\Request;
+use Test\TestCase;
+use Test\Traits\MountProviderTrait;
+use Test\Traits\UserTrait;
+
+abstract class RequestTest extends TestCase {
+ use UserTrait;
+ use MountProviderTrait;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\ServerFactory
+ */
+ protected $serverFactory;
+
+ protected function getStream($string) {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, $string);
+ fseek($stream, 0);
+ return $stream;
+ }
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->serverFactory = new ServerFactory(
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger(),
+ \OC::$server->getDatabaseConnection(),
+ \OC::$server->getUserSession(),
+ \OC::$server->getMountManager(),
+ \OC::$server->getTagManager(),
+ $this->getMock('\OCP\IRequest')
+ );
+ }
+
+ protected function setupUser($name, $password) {
+ $this->createUser($name, $password);
+ $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder();
+ $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]);
+ $this->loginAsUser($name);
+ return new View('/' . $name . '/files');
+ }
+
+ /**
+ * @param \OC\Files\View $view the view to run the webdav server against
+ * @param string $user
+ * @param string $password
+ * @param string $method
+ * @param string $url
+ * @param resource|string|null $body
+ * @param array|null $headers
+ * @return \Sabre\HTTP\Response
+ * @throws \Exception
+ */
+ protected function request($view, $user, $password, $method, $url, $body = null, $headers = null) {
+ if (is_string($body)) {
+ $body = $this->getStream($body);
+ }
+ $this->logout();
+ $exceptionPlugin = new ExceptionPlugin('webdav', null);
+ $server = $this->getSabreServer($view, $user, $password, $exceptionPlugin);
+ $request = new Request($method, $url, $headers, $body);
+
+ // since sabre catches all exceptions we need to save them and throw them from outside the sabre server
+
+ $originalServer = $_SERVER;
+
+ if (is_array($headers)) {
+ foreach ($headers as $header => $value) {
+ $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value;
+ }
+ }
+
+ $result = $this->makeRequest($server, $request);
+
+ foreach ($exceptionPlugin->getExceptions() as $exception) {
+ throw $exception;
+ }
+ $_SERVER = $originalServer;
+ return $result;
+ }
+
+ /**
+ * @param Server $server
+ * @param Request $request
+ * @return \Sabre\HTTP\Response
+ */
+ protected function makeRequest(Server $server, Request $request) {
+ $sapi = new Sapi($request);
+ $server->sapi = $sapi;
+ $server->httpRequest = $request;
+ $server->exec();
+ return $sapi->getResponse();
+ }
+
+ /**
+ * @param View $view
+ * @param string $user
+ * @param string $password
+ * @param ExceptionPlugin $exceptionPlugin
+ * @return Server
+ */
+ protected function getSabreServer(View $view, $user, $password, ExceptionPlugin $exceptionPlugin) {
+ $authBackend = new Auth($user, $password);
+
+ $server = $this->serverFactory->createServer('/', 'dummy', $authBackend, function () use ($view) {
+ return $view;
+ });
+ $server->addPlugin($exceptionPlugin);
+
+ return $server;
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php
new file mode 100644
index 00000000000..0f11ded89e0
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use Sabre\HTTP\Request;
+use Sabre\HTTP\Response;
+
+class Sapi {
+ /**
+ * @var \Sabre\HTTP\Request
+ */
+ private $request;
+
+ /**
+ * @var \Sabre\HTTP\Response
+ */
+ private $response;
+
+ /**
+ * This static method will create a new Request object, based on the
+ * current PHP request.
+ *
+ * @return \Sabre\HTTP\Request
+ */
+ public function getRequest() {
+ return $this->request;
+ }
+
+ public function __construct(Request $request) {
+ $this->request = $request;
+ }
+
+ /**
+ * @param \Sabre\HTTP\Response $response
+ * @return void
+ */
+ public function sendResponse(Response $response) {
+ // we need to copy the body since we close the source stream
+ $copyStream = fopen('php://temp', 'r+');
+ if (is_string($response->getBody())) {
+ fwrite($copyStream, $response->getBody());
+ } else if (is_resource($response->getBody())) {
+ stream_copy_to_stream($response->getBody(), $copyStream);
+ }
+ rewind($copyStream);
+ $this->response = new Response($response->getStatus(), $response->getHeaders(), $copyStream);
+ }
+
+ /**
+ * @return \Sabre\HTTP\Response
+ */
+ public function getResponse() {
+ return $this->response;
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php
new file mode 100644
index 00000000000..efb499b69c3
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre\RequestTest;
+
+use OC\Connector\Sabre\Exception\FileLocked;
+use OCP\AppFramework\Http;
+use OCP\Lock\ILockingProvider;
+
+/**
+ * Class UploadTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest
+ */
+class UploadTest extends RequestTest {
+ public function testBasicUpload() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $this->assertFalse($view->file_exists('foo.txt'));
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd');
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertTrue($view->file_exists('foo.txt'));
+ $this->assertEquals('asd', $view->file_get_contents('foo.txt'));
+
+ $info = $view->getFileInfo('foo.txt');
+ $this->assertInstanceOf('\OC\Files\FileInfo', $info);
+ $this->assertEquals(3, $info->getSize());
+ }
+
+ public function testUploadOverWrite() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'foobar');
+
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd');
+
+ $this->assertEquals(Http::STATUS_NO_CONTENT, $response->getStatus());
+ $this->assertEquals('asd', $view->file_get_contents('foo.txt'));
+
+ $info = $view->getFileInfo('foo.txt');
+ $this->assertInstanceOf('\OC\Files\FileInfo', $info);
+ $this->assertEquals(3, $info->getSize());
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\FileLocked
+ */
+ public function testUploadOverWriteReadLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_SHARED);
+
+ $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd');
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\FileLocked
+ */
+ public function testUploadOverWriteWriteLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_EXCLUSIVE);
+
+ $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd');
+ }
+
+ public function testChunkedUpload() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $this->assertFalse($view->file_exists('foo.txt'));
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(201, $response->getStatus());
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertTrue($view->file_exists('foo.txt'));
+
+ $this->assertEquals('asdbar', $view->file_get_contents('foo.txt'));
+
+ $info = $view->getFileInfo('foo.txt');
+ $this->assertInstanceOf('\OC\Files\FileInfo', $info);
+ $this->assertEquals(6, $info->getSize());
+ }
+
+ public function testChunkedUploadOverWrite() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'bar');
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertEquals('bar', $view->file_get_contents('foo.txt'));
+
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+
+ $this->assertEquals('asdbar', $view->file_get_contents('foo.txt'));
+
+ $info = $view->getFileInfo('foo.txt');
+ $this->assertInstanceOf('\OC\Files\FileInfo', $info);
+ $this->assertEquals(6, $info->getSize());
+ }
+
+ public function testChunkedUploadOutOfOrder() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $this->assertFalse($view->file_exists('foo.txt'));
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']);
+
+ $this->assertEquals(201, $response->getStatus());
+ $this->assertTrue($view->file_exists('foo.txt'));
+
+ $this->assertEquals('asdbar', $view->file_get_contents('foo.txt'));
+
+ $info = $view->getFileInfo('foo.txt');
+ $this->assertInstanceOf('\OC\Files\FileInfo', $info);
+ $this->assertEquals(6, $info->getSize());
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\FileLocked
+ */
+ public function testChunkedUploadOutOfOrderReadLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_SHARED);
+
+ try {
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']);
+ } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) {
+ $this->fail('Didn\'t expect locked error for the first chunk on read lock');
+ return;
+ }
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ // last chunk should trigger the locked error since it tries to assemble
+ $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']);
+ }
+
+ /**
+ * @expectedException \OCA\DAV\Connector\Sabre\Exception\FileLocked
+ */
+ public function testChunkedUploadOutOfOrderWriteLocked() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ $view->lockFile('/foo.txt', ILockingProvider::LOCK_EXCLUSIVE);
+
+ try {
+ $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']);
+ } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) {
+ $this->fail('Didn\'t expect locked error for the first chunk on write lock'); // maybe forbid this in the future for write locks only?
+ return;
+ }
+
+ $this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
+ $this->assertFalse($view->file_exists('foo.txt'));
+
+ // last chunk should trigger the locked error since it tries to assemble
+ $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']);
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php
new file mode 100644
index 00000000000..ff1f59e7851
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php
@@ -0,0 +1,259 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+class SharesPluginTest extends \Test\TestCase {
+
+ const SHARETYPES_PROPERTYNAME = \OCA\DAV\Connector\Sabre\SharesPlugin::SHARETYPES_PROPERTYNAME;
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \Sabre\DAV\Tree
+ */
+ private $tree;
+
+ /**
+ * @var \OCP\Share\IManager
+ */
+ private $shareManager;
+
+ /**
+ * @var \OCP\Files\Folder
+ */
+ private $userFolder;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\SharesPlugin
+ */
+ private $plugin;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = new \Sabre\DAV\Server();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->shareManager = $this->getMock('\OCP\Share\IManager');
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->once())
+ ->method('getUID')
+ ->will($this->returnValue('user1'));
+ $userSession = $this->getMock('\OCP\IUserSession');
+ $userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->userFolder = $this->getMock('\OCP\Files\Folder');
+
+ $this->plugin = new \OCA\DAV\Connector\Sabre\SharesPlugin(
+ $this->tree,
+ $userSession,
+ $this->userFolder,
+ $this->shareManager
+ );
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider sharesGetPropertiesDataProvider
+ */
+ public function testGetProperties($shareTypes) {
+ $sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sabreNode->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+ $sabreNode->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue('/subdir'));
+
+ // node API nodes
+ $node = $this->getMock('\OCP\Files\Folder');
+
+ $this->userFolder->expects($this->once())
+ ->method('get')
+ ->with('/subdir')
+ ->will($this->returnValue($node));
+
+ $this->shareManager->expects($this->any())
+ ->method('getSharesBy')
+ ->with(
+ $this->equalTo('user1'),
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo(false),
+ $this->equalTo(1)
+ )
+ ->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){
+ if (in_array($requestedShareType, $shareTypes)) {
+ return ['dummyshare'];
+ }
+ return [];
+ }));
+
+ $propFind = new \Sabre\DAV\PropFind(
+ '/dummyPath',
+ [self::SHARETYPES_PROPERTYNAME],
+ 0
+ );
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $sabreNode
+ );
+
+ $result = $propFind->getResultForMultiStatus();
+
+ $this->assertEmpty($result[404]);
+ unset($result[404]);
+ $this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes());
+ }
+
+ /**
+ * @dataProvider sharesGetPropertiesDataProvider
+ */
+ public function testPreloadThenGetProperties($shareTypes) {
+ $sabreNode1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sabreNode1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(111));
+ $sabreNode1->expects($this->never())
+ ->method('getPath');
+ $sabreNode2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sabreNode2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(222));
+ $sabreNode2->expects($this->never())
+ ->method('getPath');
+
+ $sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $sabreNode->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+ // never, because we use getDirectoryListing from the Node API instead
+ $sabreNode->expects($this->never())
+ ->method('getChildren');
+ $sabreNode->expects($this->any())
+ ->method('getPath')
+ ->will($this->returnValue('/subdir'));
+
+ // node API nodes
+ $node = $this->getMock('\OCP\Files\Folder');
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+ $node1 = $this->getMock('\OCP\Files\File');
+ $node1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(111));
+ $node2 = $this->getMock('\OCP\Files\File');
+ $node2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(222));
+ $node->expects($this->once())
+ ->method('getDirectoryListing')
+ ->will($this->returnValue([$node1, $node2]));
+
+ $this->userFolder->expects($this->once())
+ ->method('get')
+ ->with('/subdir')
+ ->will($this->returnValue($node));
+
+ $this->shareManager->expects($this->any())
+ ->method('getSharesBy')
+ ->with(
+ $this->equalTo('user1'),
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo(false),
+ $this->equalTo(1)
+ )
+ ->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){
+ if ($node->getId() === 111 && in_array($requestedShareType, $shareTypes)) {
+ return ['dummyshare'];
+ }
+
+ return [];
+ }));
+
+ // simulate sabre recursive PROPFIND traversal
+ $propFindRoot = new \Sabre\DAV\PropFind(
+ '/subdir',
+ [self::SHARETYPES_PROPERTYNAME],
+ 1
+ );
+ $propFind1 = new \Sabre\DAV\PropFind(
+ '/subdir/test.txt',
+ [self::SHARETYPES_PROPERTYNAME],
+ 0
+ );
+ $propFind2 = new \Sabre\DAV\PropFind(
+ '/subdir/test2.txt',
+ [self::SHARETYPES_PROPERTYNAME],
+ 0
+ );
+
+ $this->plugin->handleGetProperties(
+ $propFindRoot,
+ $sabreNode
+ );
+ $this->plugin->handleGetProperties(
+ $propFind1,
+ $sabreNode1
+ );
+ $this->plugin->handleGetProperties(
+ $propFind2,
+ $sabreNode2
+ );
+
+ $result = $propFind1->getResultForMultiStatus();
+
+ $this->assertEmpty($result[404]);
+ unset($result[404]);
+ $this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes());
+ }
+
+ function sharesGetPropertiesDataProvider() {
+ return [
+ [[]],
+ [[\OCP\Share::SHARE_TYPE_USER]],
+ [[\OCP\Share::SHARE_TYPE_GROUP]],
+ [[\OCP\Share::SHARE_TYPE_LINK]],
+ [[\OCP\Share::SHARE_TYPE_REMOTE]],
+ [[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]],
+ [[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]],
+ [[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_LINK]],
+ [[\OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]],
+ [[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_REMOTE]],
+ ];
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php
new file mode 100644
index 00000000000..e48d9b3aa5f
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php
@@ -0,0 +1,417 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Connector\Sabre;
+
+/**
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class TagsPluginTest extends \Test\TestCase {
+
+ const TAGS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\TagsPlugin::TAGS_PROPERTYNAME;
+ const FAVORITE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\TagsPlugin::FAVORITE_PROPERTYNAME;
+ const TAG_FAVORITE = \OCA\DAV\Connector\Sabre\TagsPlugin::TAG_FAVORITE;
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \Sabre\DAV\Tree
+ */
+ private $tree;
+
+ /**
+ * @var \OCP\ITagManager
+ */
+ private $tagManager;
+
+ /**
+ * @var \OCP\ITags
+ */
+ private $tagger;
+
+ /**
+ * @var \OCA\DAV\Connector\Sabre\TagsPlugin
+ */
+ private $plugin;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = new \Sabre\DAV\Server();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->tagger = $this->getMock('\OCP\ITags');
+ $this->tagManager = $this->getMock('\OCP\ITagManager');
+ $this->tagManager->expects($this->any())
+ ->method('load')
+ ->with('files')
+ ->will($this->returnValue($this->tagger));
+ $this->plugin = new \OCA\DAV\Connector\Sabre\TagsPlugin($this->tree, $this->tagManager);
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider tagsGetPropertiesDataProvider
+ */
+ public function testGetProperties($tags, $requestedProperties, $expectedProperties) {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $expectedCallCount = 0;
+ if (count($requestedProperties) > 0) {
+ $expectedCallCount = 1;
+ }
+
+ $this->tagger->expects($this->exactly($expectedCallCount))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123)))
+ ->will($this->returnValue(array(123 => $tags)));
+
+ $propFind = new \Sabre\DAV\PropFind(
+ '/dummyPath',
+ $requestedProperties,
+ 0
+ );
+
+ $this->plugin->handleGetProperties(
+ $propFind,
+ $node
+ );
+
+ $result = $propFind->getResultForMultiStatus();
+
+ $this->assertEmpty($result[404]);
+ unset($result[404]);
+ $this->assertEquals($expectedProperties, $result);
+ }
+
+ /**
+ * @dataProvider tagsGetPropertiesDataProvider
+ */
+ public function testPreloadThenGetProperties($tags, $requestedProperties, $expectedProperties) {
+ $node1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(111));
+ $node2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(222));
+
+ $expectedCallCount = 0;
+ if (count($requestedProperties) > 0) {
+ // this guarantees that getTagsForObjects
+ // is only called once and then the tags
+ // are cached
+ $expectedCallCount = 1;
+ }
+
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+ $node->expects($this->exactly($expectedCallCount))
+ ->method('getChildren')
+ ->will($this->returnValue(array($node1, $node2)));
+
+ $this->tagger->expects($this->exactly($expectedCallCount))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123, 111, 222)))
+ ->will($this->returnValue(
+ array(
+ 111 => $tags,
+ 123 => $tags
+ )
+ ));
+
+ // simulate sabre recursive PROPFIND traversal
+ $propFindRoot = new \Sabre\DAV\PropFind(
+ '/subdir',
+ $requestedProperties,
+ 1
+ );
+ $propFind1 = new \Sabre\DAV\PropFind(
+ '/subdir/test.txt',
+ $requestedProperties,
+ 0
+ );
+ $propFind2 = new \Sabre\DAV\PropFind(
+ '/subdir/test2.txt',
+ $requestedProperties,
+ 0
+ );
+
+ $this->plugin->handleGetProperties(
+ $propFindRoot,
+ $node
+ );
+ $this->plugin->handleGetProperties(
+ $propFind1,
+ $node1
+ );
+ $this->plugin->handleGetProperties(
+ $propFind2,
+ $node2
+ );
+
+ $result = $propFind1->getResultForMultiStatus();
+
+ $this->assertEmpty($result[404]);
+ unset($result[404]);
+ $this->assertEquals($expectedProperties, $result);
+ }
+
+ function tagsGetPropertiesDataProvider() {
+ return array(
+ // request both, receive both
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(array('tag1', 'tag2')),
+ self::FAVORITE_PROPERTYNAME => true,
+ )
+ )
+ ),
+ // request tags alone
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::TAGS_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(array('tag1', 'tag2')),
+ )
+ )
+ ),
+ // request fav alone
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::FAVORITE_PROPERTYNAME => true,
+ )
+ )
+ ),
+ // request none
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(),
+ array(
+ 200 => array()
+ ),
+ ),
+ // request both with none set, receive both
+ array(
+ array(),
+ array(self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(array()),
+ self::FAVORITE_PROPERTYNAME => false,
+ )
+ )
+ ),
+ );
+ }
+
+ public function testUpdateTags() {
+ // this test will replace the existing tags "tagremove" with "tag1" and "tag2"
+ // and keep "tagkeep"
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ $this->tagger->expects($this->at(0))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123)))
+ ->will($this->returnValue(array(123 => array('tagkeep', 'tagremove', self::TAG_FAVORITE))));
+
+ // then tag as tag1 and tag2
+ $this->tagger->expects($this->at(1))
+ ->method('tagAs')
+ ->with(123, 'tag1');
+ $this->tagger->expects($this->at(2))
+ ->method('tagAs')
+ ->with(123, 'tag2');
+
+ // it will untag tag3
+ $this->tagger->expects($this->at(3))
+ ->method('unTag')
+ ->with(123, 'tagremove');
+
+ // properties to set
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(array('tag1', 'tag2', 'tagkeep'))
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(200, $result[self::TAGS_PROPERTYNAME]);
+ $this->assertFalse(isset($result[self::FAVORITE_PROPERTYNAME]));
+ }
+
+ public function testUpdateTagsFromScratch() {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ $this->tagger->expects($this->at(0))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123)))
+ ->will($this->returnValue(array()));
+
+ // then tag as tag1 and tag2
+ $this->tagger->expects($this->at(1))
+ ->method('tagAs')
+ ->with(123, 'tag1');
+ $this->tagger->expects($this->at(2))
+ ->method('tagAs')
+ ->with(123, 'tag2');
+
+ // properties to set
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(array('tag1', 'tag2', 'tagkeep'))
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertEquals(200, $result[self::TAGS_PROPERTYNAME]);
+ $this->assertFalse(false, isset($result[self::FAVORITE_PROPERTYNAME]));
+ }
+
+ public function testUpdateFav() {
+ // this test will replace the existing tags "tagremove" with "tag1" and "tag2"
+ // and keep "tagkeep"
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('/dummypath')
+ ->will($this->returnValue($node));
+
+ // set favorite tag
+ $this->tagger->expects($this->once())
+ ->method('tagAs')
+ ->with(123, self::TAG_FAVORITE);
+
+ // properties to set
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ self::FAVORITE_PROPERTYNAME => true
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertFalse(false, isset($result[self::TAGS_PROPERTYNAME]));
+ $this->assertEquals(200, isset($result[self::FAVORITE_PROPERTYNAME]));
+
+ // unfavorite now
+ // set favorite tag
+ $this->tagger->expects($this->once())
+ ->method('unTag')
+ ->with(123, self::TAG_FAVORITE);
+
+ // properties to set
+ $propPatch = new \Sabre\DAV\PropPatch(array(
+ self::FAVORITE_PROPERTYNAME => false
+ ));
+
+ $this->plugin->handleUpdateProperties(
+ '/dummypath',
+ $propPatch
+ );
+
+ $propPatch->commit();
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($propPatch->getRemainingMutations());
+
+ $result = $propPatch->getResult();
+ $this->assertFalse(false, isset($result[self::TAGS_PROPERTYNAME]));
+ $this->assertEquals(200, isset($result[self::FAVORITE_PROPERTYNAME]));
+ }
+
+}