add test framework for doing full request webdav tests

This commit is contained in:
Robin Appelman 2015-08-07 16:04:27 +02:00
parent 6e4a79f852
commit 8c5302847b
7 changed files with 418 additions and 2 deletions

View File

@ -28,7 +28,7 @@ use Sabre\DAV\Exception;
use Sabre\HTTP\Response;
class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
private $nonFatalExceptions = array(
protected $nonFatalExceptions = array(
'Sabre\DAV\Exception\NotAuthenticated' => true,
// the sync client uses this to find out whether files exist,
// so it is not always an error, log it as debug

View File

@ -332,7 +332,7 @@ class File extends Node implements IFile {
$info = \OC_FileChunking::decodeName($name);
if (empty($info)) {
throw new NotImplemented();
throw new NotImplemented('Invalid chunk name');
}
$chunk_handler = new \OC_FileChunking($info);
$bytesWritten = $chunk_handler->store($info['index'], $data);

View File

@ -0,0 +1,69 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\Connector\Sabre\RequestTest;
use Sabre\DAV\Auth\Backend\BackendInterface;
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;
}
/**
* Authenticates the user based on the current request.
*
* If authentication is successful, true must be returned.
* If authentication fails, an exception must be thrown.
*
* @param \Sabre\DAV\Server $server
* @param string $realm
* @return bool
*/
function authenticate(\Sabre\DAV\Server $server, $realm) {
$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 $result;
}
/**
* Returns information about the currently logged in username.
*
* If nobody is currently logged in, this method should return null.
*
* @return string|null
*/
function getCurrentUser() {
return $this->user;
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\Connector\Sabre\RequestTest;
use Sabre\DAV\Exception;
class ExceptionPlugin extends \OC\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;
}
}

View File

@ -0,0 +1,174 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\Connector\Sabre\RequestTest;
use OC\Connector\Sabre\Server;
use OC\Files\Mount\MountPoint;
use OC\Files\Storage\Temporary;
use OC\Files\View;
use OCP\IUser;
use Sabre\HTTP\Request;
use Test\TestCase;
abstract class RequestTest extends TestCase {
/**
* @var \OC_User_Dummy
*/
protected $userBackend;
/**
* @var \OCP\Files\Config\IMountProvider[]
*/
protected $mountProviders;
protected function getStream($string) {
$stream = fopen('php://temp', 'r+');
fwrite($stream, $string);
fseek($stream, 0);
return $stream;
}
/**
* @param $userId
* @param $storages
* @return \OCP\Files\Config\IMountProvider
*/
protected function getMountProvider($userId, $storages) {
$mounts = [];
foreach ($storages as $mountPoint => $storage) {
$mounts[] = new MountPoint($storage, $mountPoint);
}
$provider = $this->getMock('\OCP\Files\Config\IMountProvider');
$provider->expects($this->any())
->method('getMountsForUser')
->will($this->returnCallback(function (IUser $user) use ($userId, $mounts) {
if ($user->getUID() === $userId) {
return $mounts;
} else {
return [];
}
}));
return $provider;
}
protected function setUp() {
$this->userBackend = new \OC_User_Dummy();
\OC::$server->getUserManager()->registerBackend($this->userBackend);
}
protected function tearDown() {
\OC::$server->getUserManager()->removeBackend($this->userBackend);
}
protected function setupUser($name, $password) {
$this->userBackend->createUser($name, $password);
\OC::$server->getMountProviderCollection()->registerProvider($this->getMountProvider($name, [
'/' . $name => new Temporary()
]));
$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
*/
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);
$objectTree = new \OC\Connector\Sabre\ObjectTree();
$server = new \OC\Connector\Sabre\Server($objectTree);
$server->setBaseUri('/');
// Load plugins
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'oc-test'));
$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin());
$server->addPlugin(new \OC\Connector\Sabre\FilesPlugin($objectTree));
$server->addPlugin($exceptionPlugin);
// wait with registering these until auth is handled and the filesystem is setup
$server->on('beforeMethod', function () use ($server, $objectTree, $view) {
$rootInfo = $view->getFileInfo('');
// Create ownCloud Dir
$mountManager = \OC\Files\Filesystem::getMountManager();
$rootDir = new \OC\Connector\Sabre\Directory($view, $rootInfo);
$objectTree->init($rootDir, $view, $mountManager);
$server->addPlugin(new \OC\Connector\Sabre\QuotaPlugin($view));
// custom properties plugin must be the last one
$server->addPlugin(
new \Sabre\DAV\PropertyStorage\Plugin(
new \OC\Connector\Sabre\CustomPropertiesBackend(
$objectTree,
\OC::$server->getDatabaseConnection(),
\OC::$server->getUserSession()->getUser()
)
)
);
$server->addPlugin(new \OC\Connector\Sabre\CopyEtagHeaderPlugin());
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
return $server;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\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) {
$this->response = $response;
}
/**
* @return \Sabre\HTTP\Response
*/
public function getResponse() {
return $this->response;
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\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(201, $response->getStatus());
$this->assertTrue($view->file_exists('foo.txt'));
$this->assertEquals('asd', $view->file_get_contents('foo.txt'));
}
public function testUploadOverWrite() {
$user = $this->getUniqueID();
$view = $this->setupUser($user, 'pass');
$view->file_put_contents('foo.txt', 'bar');
$response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd');
$this->assertEquals(204, $response->getStatus());
$this->assertEquals('asd', $view->file_get_contents('foo.txt'));
}
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(201, $response->getStatus());
$this->assertTrue($view->file_exists('foo.txt'));
$this->assertEquals('asdbar', $view->file_get_contents('foo.txt'));
}
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(201, $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(201, $response->getStatus());
$this->assertEquals('asdbar', $view->file_get_contents('foo.txt'));
}
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(201, $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'));
}
}