aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergio Bertolín <sbertolin@solidgear.es>2016-07-25 12:06:28 +0200
committerThomas Müller <DeepDiver1975@users.noreply.github.com>2016-07-25 12:06:28 +0200
commit4ae337f827e328fa876563f295839528189c413e (patch)
treef49d299cc70340781d1a274d4b93b1153202d75a
parent3eaa45ddcc61686aa0a4a742c1a00f6ed855697d (diff)
downloadnextcloud-server-4ae337f827e328fa876563f295839528189c413e.tar.gz
nextcloud-server-4ae337f827e328fa876563f295839528189c413e.zip
Adapted integration tests for stable8.2 (#25456)
* Adapted integration tests for stable8.2 * Fixed provisioning-v1:279 * Removed provisioning-v1.feature:160 * Removed tests related with enabling or disabling users * Removed tests about permissions property added in 9.1 * Removed check of mimetype and permissions to expect 7 instead of 15 * Removed tests only suitable for 9.1 and modified expected fields which doesn't work on 8.2 * Removed test for allowing sharing of root folder * Fixed problem with shares involving a rename which calls setupFS and requires to check the parent id instead of the actual share id * Removed quota check fixed only in >9.0 * Removed test with undefined quota -3 only in >9.0 * Removed tests about new chunking (only >9.1) * removed all the field checks in fed sharing, as they appeared after 8.2 * Removed tests about CSRF only suitable for master * Removed tests about special characters in groups * Modified range to start on 0
-rw-r--r--build/integration/composer.json4
-rw-r--r--build/integration/config/behat.yml42
-rw-r--r--build/integration/data/textfile.txt3
-rw-r--r--build/integration/features/bootstrap/BasicStructure.php312
-rw-r--r--build/integration/features/bootstrap/FeatureContext.php244
-rw-r--r--build/integration/features/bootstrap/FederationContext.php54
-rw-r--r--build/integration/features/bootstrap/Provisioning.php656
-rw-r--r--build/integration/features/bootstrap/Sharing.php498
-rw-r--r--build/integration/features/bootstrap/WebDav.php508
-rw-r--r--build/integration/features/favorites.feature42
-rw-r--r--build/integration/features/provisioning-v1.feature394
-rw-r--r--build/integration/features/sharing-v1.feature569
-rw-r--r--build/integration/features/webdav-related.feature203
-rw-r--r--build/integration/federation_features/federated.feature156
-rwxr-xr-xbuild/integration/run.sh25
15 files changed, 3351 insertions, 359 deletions
diff --git a/build/integration/composer.json b/build/integration/composer.json
index 98b2f294c7a..29f73f2064a 100644
--- a/build/integration/composer.json
+++ b/build/integration/composer.json
@@ -1,7 +1,9 @@
{
"require-dev": {
"phpunit/phpunit": "~4.6",
+ "behat/behat": "^3.0",
"guzzlehttp/guzzle": "~5.0",
- "behat/behat": "2.4.*@stable"
+ "jarnaiz/behat-junit-formatter": "^1.3",
+ "sabre/dav": "3.0.9"
}
}
diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml
index 01ca0d18790..c898c285a31 100644
--- a/build/integration/config/behat.yml
+++ b/build/integration/config/behat.yml
@@ -1,17 +1,31 @@
default:
- paths:
- features: ../features
- bootstrap: %behat.paths.features%/bootstrap
+ autoload:
+ '': %paths.base%/../features/bootstrap
+ suites:
+ default:
+ paths:
+ - %paths.base%/../features
+ contexts:
+ - FeatureContext:
+ baseUrl: http://localhost:8080/ocs/
+ admin:
+ - admin
+ - admin
+ regular_user_password: 123456
+ federation:
+ paths:
+ - %paths.base%/../federation_features
+ contexts:
+ - FederationContext:
+ baseUrl: http://localhost:8080/ocs/
+ admin:
+ - admin
+ - admin
+ regular_user_password: 123456
- context:
- parameters:
- baseUrl: http://localhost:8080/ocs/
- admin:
- - admin
- - admin
-ci:
- formatter:
- name: pretty,junit
- parameters:
- output_path: null,./output
+
+ extensions:
+ jarnaiz\JUnitFormatter\JUnitFormatterExtension:
+ filename: report.xml
+ outputDir: %paths.base%/../output/
diff --git a/build/integration/data/textfile.txt b/build/integration/data/textfile.txt
new file mode 100644
index 00000000000..efffdeff159
--- /dev/null
+++ b/build/integration/data/textfile.txt
@@ -0,0 +1,3 @@
+This is a testfile.
+
+Cheers. \ No newline at end of file
diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php
new file mode 100644
index 00000000000..f693a242e17
--- /dev/null
+++ b/build/integration/features/bootstrap/BasicStructure.php
@@ -0,0 +1,312 @@
+<?php
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Message\ResponseInterface;
+
+require __DIR__ . '/../../vendor/autoload.php';
+
+trait BasicStructure {
+
+ /** @var string */
+ private $currentUser = '';
+
+ /** @var string */
+ private $currentServer = '';
+
+ /** @var string */
+ private $baseUrl = '';
+
+ /** @var int */
+ private $apiVersion = 1;
+
+ /** @var ResponseInterface */
+ private $response = null;
+
+ /** @var \GuzzleHttp\Cookie\CookieJar */
+ private $cookieJar;
+
+ /** @var string */
+ private $requestToken;
+
+ public function __construct($baseUrl, $admin, $regular_user_password) {
+
+ // Initialize your context here
+ $this->baseUrl = $baseUrl;
+ $this->adminUser = $admin;
+ $this->regularUser = $regular_user_password;
+ $this->localBaseUrl = $this->baseUrl;
+ $this->remoteBaseUrl = $this->baseUrl;
+ $this->currentServer = 'LOCAL';
+ $this->cookieJar = new \GuzzleHttp\Cookie\CookieJar();
+
+ // in case of ci deployment we take the server url from the environment
+ $testServerUrl = getenv('TEST_SERVER_URL');
+ if ($testServerUrl !== false) {
+ $this->baseUrl = $testServerUrl;
+ $this->localBaseUrl = $testServerUrl;
+ }
+
+ // federated server url from the environment
+ $testRemoteServerUrl = getenv('TEST_SERVER_FED_URL');
+ if ($testRemoteServerUrl !== false) {
+ $this->remoteBaseUrl = $testRemoteServerUrl;
+ }
+ }
+
+ /**
+ * @Given /^using api version "([^"]*)"$/
+ * @param string $version
+ */
+ public function usingApiVersion($version) {
+ $this->apiVersion = $version;
+ }
+
+ /**
+ * @Given /^As an "([^"]*)"$/
+ * @param string $user
+ */
+ public function asAn($user) {
+ $this->currentUser = $user;
+ }
+
+ /**
+ * @Given /^Using server "(LOCAL|REMOTE)"$/
+ * @param string $server
+ * @return string Previous used server
+ */
+ public function usingServer($server) {
+ $previousServer = $this->currentServer;
+ if ($server === 'LOCAL'){
+ $this->baseUrl = $this->localBaseUrl;
+ $this->currentServer = 'LOCAL';
+ return $previousServer;
+ } else {
+ $this->baseUrl = $this->remoteBaseUrl;
+ $this->currentServer = 'REMOTE';
+ return $previousServer;
+ }
+ }
+
+ /**
+ * @When /^sending "([^"]*)" to "([^"]*)"$/
+ * @param string $verb
+ * @param string $url
+ */
+ public function sendingTo($verb, $url) {
+ $this->sendingToWith($verb, $url, null);
+ }
+
+ /**
+ * Parses the xml answer to get ocs response which doesn't match with
+ * http one in v1 of the api.
+ * @param ResponseInterface $response
+ * @return string
+ */
+ public function getOCSResponse($response) {
+ return $response->xml()->meta[0]->statuscode;
+ }
+
+ /**
+ * This function is needed to use a vertical fashion in the gherkin tables.
+ * @param array $arrayOfArrays
+ * @return array
+ */
+ public function simplifyArray($arrayOfArrays){
+ $a = array_map(function($subArray) { return $subArray[0]; }, $arrayOfArrays);
+ return $a;
+ }
+
+ /**
+ * @When /^sending "([^"]*)" to "([^"]*)" with$/
+ * @param string $verb
+ * @param string $url
+ * @param \Behat\Gherkin\Node\TableNode $body
+ */
+ public function sendingToWith($verb, $url, $body) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url;
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$this->currentUser, $this->regularUser];
+ }
+ if ($body instanceof \Behat\Gherkin\Node\TableNode) {
+ $fd = $body->getRowsHash();
+ $options['body'] = $fd;
+ }
+
+ try {
+ $this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ }
+ }
+
+ public function isExpectedUrl($possibleUrl, $finalPart){
+ $baseUrlChopped = substr($this->baseUrl, 0, -4);
+ $endCharacter = strlen($baseUrlChopped) + strlen($finalPart);
+ return (substr($possibleUrl,0,$endCharacter) == "$baseUrlChopped" . "$finalPart");
+ }
+
+ /**
+ * @Then /^the OCS status code should be "([^"]*)"$/
+ * @param int $statusCode
+ */
+ public function theOCSStatusCodeShouldBe($statusCode) {
+ PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response));
+ }
+
+ /**
+ * @Then /^the HTTP status code should be "([^"]*)"$/
+ * @param int $statusCode
+ */
+ public function theHTTPStatusCodeShouldBe($statusCode) {
+ PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode());
+ }
+
+ /**
+ * @param ResponseInterface $response
+ */
+ private function extracRequestTokenFromResponse(ResponseInterface $response) {
+ $this->requestToken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89);
+ }
+
+ /**
+ * @Given Logging in using web as :user
+ * @param string $user
+ */
+ public function loggingInUsingWebAs($user) {
+ $loginUrl = substr($this->baseUrl, 0, -5) . '/login';
+ // Request a new session and extract CSRF token
+ $client = new Client();
+ $response = $client->get(
+ $loginUrl,
+ [
+ 'cookies' => $this->cookieJar,
+ ]
+ );
+ $this->extracRequestTokenFromResponse($response);
+
+ // Login and extract new token
+ $password = ($user === 'admin') ? 'admin' : '123456';
+ $client = new Client();
+ $response = $client->post(
+ $loginUrl,
+ [
+ 'body' => [
+ 'user' => $user,
+ 'password' => $password,
+ 'requesttoken' => $this->requestToken,
+ ],
+ 'cookies' => $this->cookieJar,
+ ]
+ );
+ $this->extracRequestTokenFromResponse($response);
+ }
+
+ /**
+ * @When Sending a :method to :url with requesttoken
+ * @param string $method
+ * @param string $url
+ */
+ public function sendingAToWithRequesttoken($method, $url) {
+ $baseUrl = substr($this->baseUrl, 0, -5);
+
+ $client = new Client();
+ $request = $client->createRequest(
+ $method,
+ $baseUrl . $url,
+ [
+ 'cookies' => $this->cookieJar,
+ ]
+ );
+ $request->addHeader('requesttoken', $this->requestToken);
+ try {
+ $this->response = $client->send($request);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @When Sending a :method to :url without requesttoken
+ * @param string $method
+ * @param string $url
+ */
+ public function sendingAToWithoutRequesttoken($method, $url) {
+ $baseUrl = substr($this->baseUrl, 0, -5);
+
+ $client = new Client();
+ $request = $client->createRequest(
+ $method,
+ $baseUrl . $url,
+ [
+ 'cookies' => $this->cookieJar,
+ ]
+ );
+ try {
+ $this->response = $client->send($request);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->response = $e->getResponse();
+ }
+ }
+
+ public static function removeFile($path, $filename){
+ if (file_exists("$path" . "$filename")) {
+ unlink("$path" . "$filename");
+ }
+ }
+
+ /**
+ * @Given User :user modifies text of :filename with text :text
+ * @param string $user
+ * @param string $filename
+ * @param string $text
+ */
+ public function modifyTextOfFile($user, $filename, $text) {
+ self::removeFile("../../data/$user/files", "$filename");
+ file_put_contents("../../data/$user/files" . "$filename", "$text");
+ }
+
+ /**
+ * @BeforeSuite
+ */
+ public static function addFilesToSkeleton(){
+ for ($i=0; $i<5; $i++){
+ file_put_contents("../../core/skeleton/" . "textfile" . "$i" . ".txt", "ownCloud test text file\n");
+ }
+ if (!file_exists("../../core/skeleton/FOLDER")) {
+ mkdir("../../core/skeleton/FOLDER", 0777, true);
+ }
+ if (!file_exists("../../core/skeleton/PARENT")) {
+ mkdir("../../core/skeleton/PARENT", 0777, true);
+ }
+ file_put_contents("../../core/skeleton/PARENT/" . "parent.txt", "ownCloud test text file\n");
+ if (!file_exists("../../core/skeleton/PARENT/CHILD")) {
+ mkdir("../../core/skeleton/PARENT/CHILD", 0777, true);
+ }
+ file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "ownCloud test text file\n");
+ }
+
+ /**
+ * @AfterSuite
+ */
+ public static function removeFilesFromSkeleton(){
+ for ($i=0; $i<5; $i++){
+ self::removeFile("../../core/skeleton/", "textfile" . "$i" . ".txt");
+ }
+ if (is_dir("../../core/skeleton/FOLDER")) {
+ rmdir("../../core/skeleton/FOLDER");
+ }
+ self::removeFile("../../core/skeleton/PARENT/CHILD/", "child.txt");
+ if (is_dir("../../core/skeleton/PARENT/CHILD")) {
+ rmdir("../../core/skeleton/PARENT/CHILD");
+ }
+ self::removeFile("../../core/skeleton/PARENT/", "parent.txt");
+ if (is_dir("../../core/skeleton/PARENT")) {
+ rmdir("../../core/skeleton/PARENT");
+ }
+ }
+}
+
diff --git a/build/integration/features/bootstrap/FeatureContext.php b/build/integration/features/bootstrap/FeatureContext.php
index caff517a16d..21ca8d87295 100644
--- a/build/integration/features/bootstrap/FeatureContext.php
+++ b/build/integration/features/bootstrap/FeatureContext.php
@@ -1,248 +1,14 @@
<?php
-use Behat\Behat\Context\BehatContext;
-use GuzzleHttp\Client;
-use GuzzleHttp\Message\ResponseInterface;
+use Behat\Behat\Context\Context;
+use Behat\Behat\Context\SnippetAcceptingContext;
require __DIR__ . '/../../vendor/autoload.php';
+
/**
* Features context.
*/
-class FeatureContext extends BehatContext {
-
- /** @var string */
- private $baseUrl = '';
-
- /** @var ResponseInterface */
- private $response = null;
-
- /** @var string */
- private $currentUser = '';
-
- /** @var int */
- private $apiVersion = 1;
-
- /**
- * Initializes context.
- * Every scenario gets it's own context object.
- *
- * @param array $parameters context parameters (set them up through behat.yml)
- */
- public function __construct(array $parameters) {
-
- // Initialize your context here
- $this->baseUrl = $parameters['baseUrl'];
- $this->adminUser = $parameters['admin'];
-
- // in case of ci deployment we take the server url from the environment
- $testServerUrl = getenv('TEST_SERVER_URL');
- if ($testServerUrl !== false) {
- $this->baseUrl = $testServerUrl;
- }
- }
-
- /**
- * @When /^sending "([^"]*)" to "([^"]*)"$/
- */
- public function sendingTo($verb, $url) {
- $this->sendingToWith($verb, $url, null);
- }
-
- /**
- * Parses the xml answer to get ocs response which doesn't match with
- * http one in v1 of the api.
- */
- public function getOCSResponse($response) {
- return $response->xml()->meta[0]->statuscode;
- }
-
- /**
- * Parses the xml answer to get the array of users returned.
- */
- public function getArrayOfUsersResponded($resp) {
- $listCheckedElements = $resp->xml()->data[0]->users[0]->element;
- $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
- return $extractedElementsArray;
- }
-
- /**
- * Parses the xml answer to get the array of groups returned.
- */
- public function getArrayOfGroupsResponded($resp) {
- $listCheckedElements = $resp->xml()->data[0]->groups[0]->element;
- $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
- return $extractedElementsArray;
- }
-
- /**
- * @Then /^users returned are$/
- * @param \Behat\Gherkin\Node\TableNode|null $formData
- */
- public function theUsersShouldBe($usersList) {
- if ($usersList instanceof \Behat\Gherkin\Node\TableNode) {
- $users = $usersList->getRows()[0];
- $respondedArray = $this->getArrayOfUsersResponded($this->response);
- PHPUnit_Framework_Assert::assertEquals(asort($users), asort($respondedArray));
- }
-
- }
-
- /**
- * @Then /^groups returned are$/
- * @param \Behat\Gherkin\Node\TableNode|null $formData
- */
- public function theGroupsShouldBe($groupsList) {
- if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) {
- $groups = $groupsList->getRows()[0];
- $respondedArray = $this->getArrayOfGroupsResponded($this->response);
- PHPUnit_Framework_Assert::assertEquals(asort($groups), asort($respondedArray));
- }
-
- }
-
- /**
- * @Then /^the OCS status code should be "([^"]*)"$/
- */
- public function theOCSStatusCodeShouldBe($statusCode) {
- PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response));
- }
-
- /**
- * @Then /^the HTTP status code should be "([^"]*)"$/
- */
- public function theHTTPStatusCodeShouldBe($statusCode) {
- PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode());
- }
-
- /**
- * @Given /^As an "([^"]*)"$/
- */
- public function asAn($user) {
- $this->currentUser = $user;
- }
-
- /**
- * @Given /^using api version "([^"]*)"$/
- */
- public function usingApiVersion($version) {
- $this->apiVersion = $version;
- }
-
- /**
- * @Given /^user "([^"]*)" exists$/
- */
- public function userExists($user) {
- $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user";
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
-
- $this->response = $client->get($fullUrl, $options);
- PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
- }
-
- /**
- * @Given /^user "([^"]*)" does not exist$/
- */
- public function userDoesNotExist($user) {
- try {
- $this->userExists($user);
- PHPUnit_Framework_Assert::fail('The user "' . $user . '" exists');
- } catch (\GuzzleHttp\Exception\ClientException $ex) {
- $this->response = $ex->getResponse();
- PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
- }
- }
-
- /**
- * @When /^creating the user "([^"]*)r"$/
- */
- public function creatingTheUser($user) {
- $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
-
- $this->response = $client->post($fullUrl, [
- 'form_params' => [
- 'userid' => $user,
- 'password' => '123456'
- ]
- ]);
-
- }
-
- /**
- * @When /^creating the group "([^"]*)r"$/
- */
- public function creatingTheGroup($group) {
- $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups/addgroup";
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
-
- $this->response = $client->post($fullUrl, [
- 'form_params' => [
- 'groupid' => $user
- ]
- ]);
- }
-
- /**
- * @Given /^group "([^"]*)" exists$/
- */
- public function groupExists($group) {
- $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group";
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
-
- $this->response = $client->get($fullUrl, $options);
- PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
- }
-
- /**
- * @Given /^group "([^"]*)" does not exist$/
- */
- public function groupDoesNotExist($group) {
- try {
- $this->groupExists($group);
- PHPUnit_Framework_Assert::fail('The group "' . $group . '" exists');
- } catch (\GuzzleHttp\Exception\ClientException $ex) {
- $this->response = $ex->getResponse();
- PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
- }
- }
-
- /**
- * @When /^sending "([^"]*)" to "([^"]*)" with$/
- * @param \Behat\Gherkin\Node\TableNode|null $formData
- */
- public function sendingToWith($verb, $url, $body) {
- $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url;
- $client = new Client();
- $options = [];
- if ($this->currentUser === 'admin') {
- $options['auth'] = $this->adminUser;
- }
- if ($body instanceof \Behat\Gherkin\Node\TableNode) {
- $fd = $body->getRowsHash();
- $options['body'] = $fd;
- }
-
- try {
- $this->response = $client->send($client->createRequest($verb, $fullUrl, $options));
- } catch (\GuzzleHttp\Exception\ClientException $ex) {
- $this->response = $ex->getResponse();
- }
- }
+class FeatureContext implements Context, SnippetAcceptingContext {
+ use WebDav;
}
diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php
new file mode 100644
index 00000000000..55f3a55da0d
--- /dev/null
+++ b/build/integration/features/bootstrap/FederationContext.php
@@ -0,0 +1,54 @@
+<?php
+
+use Behat\Behat\Context\Context;
+use Behat\Behat\Context\SnippetAcceptingContext;
+use GuzzleHttp\Client;
+use GuzzleHttp\Message\ResponseInterface;
+
+require __DIR__ . '/../../vendor/autoload.php';
+
+/**
+ * Federation context.
+ */
+class FederationContext implements Context, SnippetAcceptingContext {
+
+ use WebDav;
+
+ /**
+ * @Given /^User "([^"]*)" from server "(LOCAL|REMOTE)" shares "([^"]*)" with user "([^"]*)" from server "(LOCAL|REMOTE)"$/
+ *
+ * @param string $sharerUser
+ * @param string $sharerServer "LOCAL" or "REMOTE"
+ * @param string $sharerPath
+ * @param string $shareeUser
+ * @param string $shareeServer "LOCAL" or "REMOTE"
+ */
+ public function federateSharing($sharerUser, $sharerServer, $sharerPath, $shareeUser, $shareeServer){
+ if ($shareeServer == "REMOTE"){
+ $shareWith = "$shareeUser@" . substr($this->remoteBaseUrl, 0, -4);
+ } else {
+ $shareWith = "$shareeUser@" . substr($this->localBaseUrl, 0, -4);
+ }
+ $previous = $this->usingServer($sharerServer);
+ $this->createShare($sharerUser, $sharerPath, 6, $shareWith, null, null, null);
+ $this->usingServer($previous);
+ }
+
+ /**
+ * @When /^User "([^"]*)" from server "(LOCAL|REMOTE)" accepts last pending share$/
+ * @param string $user
+ * @param string $server
+ */
+ public function acceptLastPendingShare($user, $server){
+ $previous = $this->usingServer($server);
+ $this->asAn($user);
+ $this->sendingToWith('GET', "/apps/files_sharing/api/v1/remote_shares/pending", null);
+ $this->theHTTPStatusCodeShouldBe('200');
+ $this->theOCSStatusCodeShouldBe('100');
+ $share_id = $this->response->xml()->data[0]->element[0]->id;
+ $this->sendingToWith('POST', "/apps/files_sharing/api/v1/remote_shares/pending/{$share_id}", null);
+ $this->theHTTPStatusCodeShouldBe('200');
+ $this->theOCSStatusCodeShouldBe('100');
+ $this->usingServer($previous);
+ }
+}
diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php
new file mode 100644
index 00000000000..6cf57514483
--- /dev/null
+++ b/build/integration/features/bootstrap/Provisioning.php
@@ -0,0 +1,656 @@
+<?php
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Message\ResponseInterface;
+
+require __DIR__ . '/../../vendor/autoload.php';
+
+trait Provisioning {
+ use BasicStructure;
+
+ /** @var array */
+ private $createdUsers = [];
+
+ /** @var array */
+ private $createdRemoteUsers = [];
+
+ /** @var array */
+ private $createdRemoteGroups = [];
+
+ /** @var array */
+ private $createdGroups = [];
+
+ /**
+ * @Given /^user "([^"]*)" exists$/
+ * @param string $user
+ */
+ public function assureUserExists($user) {
+ try {
+ $this->userExists($user);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->creatingTheUser($user);
+ $this->currentUser = $previous_user;
+ }
+ $this->userExists($user);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Given /^user "([^"]*)" does not exist$/
+ * @param string $user
+ */
+ public function userDoesNotExist($user) {
+ try {
+ $this->userExists($user);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
+ return;
+ }
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->deletingTheUser($user);
+ $this->currentUser = $previous_user;
+ try {
+ $this->userExists($user);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
+ }
+ }
+
+ public function creatingTheUser($user) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $options['body'] = [
+ 'userid' => $user,
+ 'password' => '123456'
+ ];
+
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ if ($this->currentServer === 'LOCAL'){
+ $this->createdUsers[$user] = $user;
+ } elseif ($this->currentServer === 'REMOTE') {
+ $this->createdRemoteUsers[$user] = $user;
+ }
+
+ //Quick hack to login once with the current user
+ $options2 = [
+ 'auth' => [$user, '123456'],
+ ];
+ $url = $fullUrl.'/'.$user;
+ $client->send($client->createRequest('GET', $url, $options2));
+ }
+
+ public function createUser($user) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->creatingTheUser($user);
+ $this->userExists($user);
+ $this->currentUser = $previous_user;
+ }
+
+ public function deleteUser($user) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->deletingTheUser($user);
+ $this->userDoesNotExist($user);
+ $this->currentUser = $previous_user;
+ }
+
+ public function createGroup($group) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->creatingTheGroup($group);
+ $this->groupExists($group);
+ $this->currentUser = $previous_user;
+ }
+
+ public function deleteGroup($group) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->deletingTheGroup($group);
+ $this->groupDoesNotExist($group);
+ $this->currentUser = $previous_user;
+ }
+
+ public function userExists($user){
+ $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user";
+ $client = new Client();
+ $options = [];
+ $options['auth'] = $this->adminUser;
+
+ $this->response = $client->get($fullUrl, $options);
+ }
+
+ /**
+ * @Then /^check that user "([^"]*)" belongs to group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function checkThatUserBelongsToGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfGroupsResponded($this->response);
+ sort($respondedArray);
+ PHPUnit_Framework_Assert::assertContains($group, $respondedArray);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ public function userBelongsToGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfGroupsResponded($this->response);
+
+ if (array_key_exists($group, $respondedArray)) {
+ return True;
+ } else{
+ return False;
+ }
+ }
+
+ /**
+ * @Given /^user "([^"]*)" belongs to group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function assureUserBelongsToGroup($user, $group){
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+
+ if (!$this->userBelongsToGroup($user, $group)){
+ $this->addingUserToGroup($user, $group);
+ }
+
+ $this->checkThatUserBelongsToGroup($user, $group);
+ $this->currentUser = $previous_user;
+ }
+
+ /**
+ * @Given /^user "([^"]*)" does not belong to group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function userDoesNotBelongToGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $groups = array($group);
+ $respondedArray = $this->getArrayOfGroupsResponded($this->response);
+ PHPUnit_Framework_Assert::assertNotEquals($groups, $respondedArray, "", 0.0, 10, true);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @When /^creating the group "([^"]*)"$/
+ * @param string $group
+ */
+ public function creatingTheGroup($group) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $options['body'] = [
+ 'groupid' => $group,
+ ];
+
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ if ($this->currentServer === 'LOCAL'){
+ $this->createdGroups[$group] = $group;
+ } elseif ($this->currentServer === 'REMOTE') {
+ $this->createdRemoteGroups[$group] = $group;
+ }
+ }
+
+ /**
+ * @When /^assure user "([^"]*)" is disabled$/
+ */
+ public function assureUserIsDisabled($user) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/disable";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options));
+ }
+
+ /**
+ * @When /^Deleting the user "([^"]*)"$/
+ * @param string $user
+ */
+ public function deletingTheUser($user) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->send($client->createRequest("DELETE", $fullUrl, $options));
+ }
+
+ /**
+ * @When /^Deleting the group "([^"]*)"$/
+ * @param string $group
+ */
+ public function deletingTheGroup($group) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups/$group";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->send($client->createRequest("DELETE", $fullUrl, $options));
+ }
+
+ /**
+ * @Given /^Add user "([^"]*)" to the group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function addUserToGroup($user, $group) {
+ $this->userExists($user);
+ $this->groupExists($group);
+ $this->addingUserToGroup($user, $group);
+
+ }
+
+ /**
+ * @When /^User "([^"]*)" is added to the group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function addingUserToGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/groups";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $options['body'] = [
+ 'groupid' => $group,
+ ];
+
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ }
+
+
+ public function groupExists($group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group";
+ $client = new Client();
+ $options = [];
+ $options['auth'] = $this->adminUser;
+
+ $this->response = $client->get($fullUrl, $options);
+ }
+
+ /**
+ * @Given /^group "([^"]*)" exists$/
+ * @param string $group
+ */
+ public function assureGroupExists($group) {
+ try {
+ $this->groupExists($group);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->creatingTheGroup($group);
+ $this->currentUser = $previous_user;
+ }
+ $this->groupExists($group);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Given /^group "([^"]*)" does not exist$/
+ * @param string $group
+ */
+ public function groupDoesNotExist($group) {
+ try {
+ $this->groupExists($group);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
+ return;
+ }
+ $previous_user = $this->currentUser;
+ $this->currentUser = "admin";
+ $this->deletingTheGroup($group);
+ $this->currentUser = $previous_user;
+ try {
+ $this->groupExists($group);
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode());
+ }
+ }
+
+ /**
+ * @Given /^user "([^"]*)" is subadmin of group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function userIsSubadminOfGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group/subadmins";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfSubadminsResponded($this->response);
+ sort($respondedArray);
+ PHPUnit_Framework_Assert::assertContains($user, $respondedArray);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Given /^Assure user "([^"]*)" is subadmin of group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function assureUserIsSubadminOfGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/subadmins";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+ $options['body'] = [
+ 'groupid' => $group
+ ];
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Given /^user "([^"]*)" is not a subadmin of group "([^"]*)"$/
+ * @param string $user
+ * @param string $group
+ */
+ public function userIsNotSubadminOfGroup($user, $group) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group/subadmins";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfSubadminsResponded($this->response);
+ sort($respondedArray);
+ PHPUnit_Framework_Assert::assertNotContains($user, $respondedArray);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Then /^users returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $usersList
+ */
+ public function theUsersShouldBe($usersList) {
+ if ($usersList instanceof \Behat\Gherkin\Node\TableNode) {
+ $users = $usersList->getRows();
+ $usersSimplified = $this->simplifyArray($users);
+ $respondedArray = $this->getArrayOfUsersResponded($this->response);
+ PHPUnit_Framework_Assert::assertEquals($usersSimplified, $respondedArray, "", 0.0, 10, true);
+ }
+
+ }
+
+ /**
+ * @Then /^groups returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $groupsList
+ */
+ public function theGroupsShouldBe($groupsList) {
+ if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) {
+ $groups = $groupsList->getRows();
+ $groupsSimplified = $this->simplifyArray($groups);
+ $respondedArray = $this->getArrayOfGroupsResponded($this->response);
+ PHPUnit_Framework_Assert::assertEquals($groupsSimplified, $respondedArray, "", 0.0, 10, true);
+ }
+
+ }
+
+ /**
+ * @Then /^subadmin groups returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $groupsList
+ */
+ public function theSubadminGroupsShouldBe($groupsList) {
+ if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) {
+ $groups = $groupsList->getRows();
+ $groupsSimplified = $this->simplifyArray($groups);
+ $respondedArray = $this->getArrayOfSubadminsResponded($this->response);
+ PHPUnit_Framework_Assert::assertEquals($groupsSimplified, $respondedArray, "", 0.0, 10, true);
+ }
+
+ }
+
+ /**
+ * @Then /^apps returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $appList
+ */
+ public function theAppsShouldBe($appList) {
+ if ($appList instanceof \Behat\Gherkin\Node\TableNode) {
+ $apps = $appList->getRows();
+ $appsSimplified = $this->simplifyArray($apps);
+ $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ PHPUnit_Framework_Assert::assertEquals($appsSimplified, $respondedArray, "", 0.0, 10, true);
+ }
+
+ }
+
+ /**
+ * @Then /^subadmin users returned are$/
+ * @param \Behat\Gherkin\Node\TableNode|null $groupsList
+ */
+ public function theSubadminUsersShouldBe($groupsList) {
+ $this->theSubadminGroupsShouldBe($groupsList);
+ }
+
+ /**
+ * Parses the xml answer to get the array of users returned.
+ * @param ResponseInterface $resp
+ * @return array
+ */
+ public function getArrayOfUsersResponded($resp) {
+ $listCheckedElements = $resp->xml()->data[0]->users[0]->element;
+ $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
+ return $extractedElementsArray;
+ }
+
+ /**
+ * Parses the xml answer to get the array of groups returned.
+ * @param ResponseInterface $resp
+ * @return array
+ */
+ public function getArrayOfGroupsResponded($resp) {
+ $listCheckedElements = $resp->xml()->data[0]->groups[0]->element;
+ $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
+ return $extractedElementsArray;
+ }
+
+ /**
+ * Parses the xml answer to get the array of apps returned.
+ * @param ResponseInterface $resp
+ * @return array
+ */
+ public function getArrayOfAppsResponded($resp) {
+ $listCheckedElements = $resp->xml()->data[0]->apps[0]->element;
+ $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
+ return $extractedElementsArray;
+ }
+
+ /**
+ * Parses the xml answer to get the array of subadmins returned.
+ * @param ResponseInterface $resp
+ * @return array
+ */
+ public function getArrayOfSubadminsResponded($resp) {
+ $listCheckedElements = $resp->xml()->data[0]->element;
+ $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1);
+ return $extractedElementsArray;
+ }
+
+
+ /**
+ * @Given /^app "([^"]*)" is disabled$/
+ * @param string $app
+ */
+ public function appIsDisabled($app) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=disabled";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ PHPUnit_Framework_Assert::assertContains($app, $respondedArray);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Given /^app "([^"]*)" is enabled$/
+ * @param string $app
+ */
+ public function appIsEnabled($app) {
+ $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=enabled";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ $respondedArray = $this->getArrayOfAppsResponded($this->response);
+ PHPUnit_Framework_Assert::assertContains($app, $respondedArray);
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @Then /^user "([^"]*)" is disabled$/
+ * @param string $user
+ */
+ public function userIsDisabled($user) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ PHPUnit_Framework_Assert::assertEquals("false", $this->response->xml()->data[0]->enabled);
+ }
+
+ /**
+ * @Then /^user "([^"]*)" is enabled$/
+ * @param string $user
+ */
+ public function userIsEnabled($user) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ }
+
+ $this->response = $client->get($fullUrl, $options);
+ PHPUnit_Framework_Assert::assertEquals("true", $this->response->xml()->data[0]->enabled);
+ }
+
+ /**
+ * @Given user :user has a quota of :quota
+ * @param string $user
+ * @param string $quota
+ */
+ public function userHasAQuotaOf($user, $quota)
+ {
+ $body = new \Behat\Gherkin\Node\TableNode([
+ 0 => ['key', 'quota'],
+ 1 => ['value', $quota],
+ ]);
+
+ // method used from BasicStructure trait
+ $this->sendingToWith("PUT", "/cloud/users/" . $user, $body);
+ }
+
+ /**
+ * @Given user :user has unlimited quota
+ * @param string $user
+ */
+ public function userHasUnlimitedQuota($user)
+ {
+ $this->userHasAQuotaOf($user, 'none');
+ }
+
+ /**
+ * @BeforeScenario
+ * @AfterScenario
+ */
+ public function cleanupUsers()
+ {
+ $previousServer = $this->currentServer;
+ $this->usingServer('LOCAL');
+ foreach($this->createdUsers as $user) {
+ $this->deleteUser($user);
+ }
+ $this->usingServer('REMOTE');
+ foreach($this->createdRemoteUsers as $remoteUser) {
+ $this->deleteUser($remoteUser);
+ }
+ $this->usingServer($previousServer);
+ }
+
+ /**
+ * @BeforeScenario
+ * @AfterScenario
+ */
+ public function cleanupGroups()
+ {
+ $previousServer = $this->currentServer;
+ $this->usingServer('LOCAL');
+ foreach($this->createdGroups as $group) {
+ $this->deleteGroup($group);
+ }
+ $this->usingServer('REMOTE');
+ foreach($this->createdRemoteGroups as $remoteGroup) {
+ $this->deleteUser($remoteGroup);
+ }
+ $this->usingServer($previousServer);
+ }
+
+}
diff --git a/build/integration/features/bootstrap/Sharing.php b/build/integration/features/bootstrap/Sharing.php
new file mode 100644
index 00000000000..39ec4397bab
--- /dev/null
+++ b/build/integration/features/bootstrap/Sharing.php
@@ -0,0 +1,498 @@
+<?php
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Message\ResponseInterface;
+
+require __DIR__ . '/../../vendor/autoload.php';
+
+
+
+trait Sharing {
+ use Provisioning;
+
+ /** @var int */
+ private $sharingApiVersion = 1;
+
+ /** @var SimpleXMLElement */
+ private $lastShareData = null;
+
+ /** @var int */
+ private $savedShareId = null;
+
+ /**
+ * @Given /^as "([^"]*)" creating a share with$/
+ * @param string $user
+ * @param \Behat\Gherkin\Node\TableNode|null $body
+ */
+ public function asCreatingAShareWith($user, $body) {
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares";
+ $client = new Client();
+ $options = [];
+ if ($user === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$user, $this->regularUser];
+ }
+
+ if ($body instanceof \Behat\Gherkin\Node\TableNode) {
+ $fd = $body->getRowsHash();
+ if (array_key_exists('expireDate', $fd)){
+ $dateModification = $fd['expireDate'];
+ $fd['expireDate'] = date('Y-m-d', strtotime($dateModification));
+ }
+ $options['body'] = $fd;
+ }
+
+ try {
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ }
+
+ $this->lastShareData = $this->response->xml();
+ }
+
+ /**
+ * @When /^creating a share with$/
+ * @param \Behat\Gherkin\Node\TableNode|null $body
+ */
+ public function creatingShare($body) {
+ $this->asCreatingAShareWith($this->currentUser, $body);
+ }
+
+ /**
+ * @Then /^Public shared file "([^"]*)" can be downloaded$/
+ */
+ public function checkPublicSharedFile($filename) {
+ $client = new Client();
+ $options = [];
+ if (count($this->lastShareData->data->element) > 0){
+ $url = $this->lastShareData->data[0]->url;
+ }
+ else{
+ $url = $this->lastShareData->data->url;
+ }
+ $fullUrl = $url . "/download";
+ $options['save_to'] = "./$filename";
+ $this->response = $client->get($fullUrl, $options);
+ $finfo = new finfo;
+ $fileinfo = $finfo->file("./$filename", FILEINFO_MIME_TYPE);
+ PHPUnit_Framework_Assert::assertEquals($fileinfo, "text/plain");
+ if (file_exists("./$filename")) {
+ unlink("./$filename");
+ }
+ }
+
+ /**
+ * @Then /^Public shared file "([^"]*)" with password "([^"]*)" can be downloaded$/
+ */
+ public function checkPublicSharedFileWithPassword($filename, $password) {
+ $client = new Client();
+ $options = [];
+ if (count($this->lastShareData->data->element) > 0){
+ $token = $this->lastShareData->data[0]->token;
+ }
+ else{
+ $token = $this->lastShareData->data->token;
+ }
+
+ $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav";
+ $options['auth'] = [$token, $password];
+ $options['save_to'] = "./$filename";
+ $this->response = $client->get($fullUrl, $options);
+ $finfo = new finfo;
+ $fileinfo = $finfo->file("./$filename", FILEINFO_MIME_TYPE);
+ PHPUnit_Framework_Assert::assertEquals($fileinfo, "text/plain");
+ if (file_exists("./$filename")) {
+ unlink("./$filename");
+ }
+ }
+
+ /**
+ * @When /^Adding expiration date to last share$/
+ */
+ public function addingExpirationDate() {
+ $share_id = (string) $this->lastShareData->data[0]->id;
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$this->currentUser, $this->regularUser];
+ }
+ $date = date('Y-m-d', strtotime("+3 days"));
+ $options['body'] = ['expireDate' => $date];
+ $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options));
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ /**
+ * @When /^Updating last share with$/
+ * @param \Behat\Gherkin\Node\TableNode|null $body
+ */
+ public function updatingLastShare($body) {
+ $share_id = (string) $this->lastShareData->data[0]->id;
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id";
+ $client = new Client();
+ $options = [];
+ if ($this->currentUser === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$this->currentUser, $this->regularUser];
+ }
+
+ if ($body instanceof \Behat\Gherkin\Node\TableNode) {
+ $fd = $body->getRowsHash();
+ if (array_key_exists('expireDate', $fd)){
+ $dateModification = $fd['expireDate'];
+ $fd['expireDate'] = date('Y-m-d', strtotime($dateModification));
+ }
+ $options['body'] = $fd;
+ }
+
+ try {
+ $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options));
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ }
+
+ PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode());
+ }
+
+ public function createShare($user,
+ $path = null,
+ $shareType = null,
+ $shareWith = null,
+ $publicUpload = null,
+ $password = null,
+ $permissions = null){
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares";
+ $client = new Client();
+ $options = [];
+
+ if ($user === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$user, $this->regularUser];
+ }
+ $fd = [];
+ if (!is_null($path)){
+ $fd['path'] = $path;
+ }
+ if (!is_null($shareType)){
+ $fd['shareType'] = $shareType;
+ }
+ if (!is_null($shareWith)){
+ $fd['shareWith'] = $shareWith;
+ }
+ if (!is_null($publicUpload)){
+ $fd['publicUpload'] = $publicUpload;
+ }
+ if (!is_null($password)){
+ $fd['password'] = $password;
+ }
+ if (!is_null($permissions)){
+ $fd['permissions'] = $permissions;
+ }
+
+ $options['body'] = $fd;
+
+ try {
+ $this->response = $client->send($client->createRequest("POST", $fullUrl, $options));
+ $this->lastShareData = $this->response->xml();
+ } catch (\GuzzleHttp\Exception\ClientException $ex) {
+ $this->response = $ex->getResponse();
+ }
+ }
+
+ public function isFieldInResponse($field, $contentExpected){
+ $data = $this->response->xml()->data[0];
+ if ((string)$field == 'expiration'){
+ $contentExpected = date('Y-m-d', strtotime($contentExpected)) . " 00:00:00";
+ }
+
+ if (count($data->element) > 0){
+ foreach($data as $element) {
+ if ($contentExpected == "A_TOKEN"){
+ return (strlen((string)$element->$field) == 15);
+ }
+ elseif ($contentExpected == "A_NUMBER"){
+ return is_numeric((string)$element->$field);
+ }
+ elseif($contentExpected == "AN_URL"){
+ return $this->isExpectedUrl((string)$element->$field, "index.php/s/");
+ }
+ elseif ((string)$element->$field == $contentExpected){
+ return True;
+ }
+ else{
+ print($element->$field);
+ }
+ }
+
+ return False;
+ } else {
+ if ($contentExpected == "A_TOKEN"){
+ return (strlen((string)$data->$field) == 15);
+ }
+ elseif ($contentExpected == "A_NUMBER"){
+ return is_numeric((string)$data->$field);
+ }
+ elseif($contentExpected == "AN_URL"){
+ return $this->isExpectedUrl((string)$data->$field, "index.php/s/");
+ }
+ elseif ($data->$field == $contentExpected){
+ return True;
+ }
+ return False;
+ }
+ }
+
+ /**
+ * @Then /^File "([^"]*)" should be included in the response$/
+ *
+ * @param string $filename
+ */
+ public function checkSharedFileInResponse($filename){
+ PHPUnit_Framework_Assert::assertEquals(True, $this->isFieldInResponse('file_target', "/$filename"));
+ }
+
+ /**
+ * @Then /^File "([^"]*)" should not be included in the response$/
+ *
+ * @param string $filename
+ */
+ public function checkSharedFileNotInResponse($filename){
+ PHPUnit_Framework_Assert::assertEquals(False, $this->isFieldInResponse('file_target', "/$filename"));
+ }
+
+ /**
+ * @Then /^User "([^"]*)" should be included in the response$/
+ *
+ * @param string $user
+ */
+ public function checkSharedUserInResponse($user){
+ PHPUnit_Framework_Assert::assertEquals(True, $this->isFieldInResponse('share_with', "$user"));
+ }
+
+ /**
+ * @Then /^User "([^"]*)" should not be included in the response$/
+ *
+ * @param string $user
+ */
+ public function checkSharedUserNotInResponse($user){
+ PHPUnit_Framework_Assert::assertEquals(False, $this->isFieldInResponse('share_with', "$user"));
+ }
+
+ public function isUserOrGroupInSharedData($userOrGroup){
+ $data = $this->response->xml()->data[0];
+ foreach($data as $element) {
+ if ($element->share_with == $userOrGroup){
+ return True;
+ }
+ }
+ return False;
+ }
+
+ /**
+ * @Given /^file "([^"]*)" of user "([^"]*)" is shared with user "([^"]*)"$/
+ *
+ * @param string $filepath
+ * @param string $user1
+ * @param string $user2
+ */
+ public function assureFileIsShared($filepath, $user1, $user2){
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath";
+ $client = new Client();
+ $options = [];
+ if ($user1 === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$user1, $this->regularUser];
+ }
+ $this->response = $client->get($fullUrl, $options);
+ if ($this->isUserOrGroupInSharedData($user2)){
+ return;
+ } else {
+ $this->createShare($user1, $filepath, 0, $user2, null, null, null);
+ }
+ $this->response = $client->get($fullUrl, $options);
+ PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($user2));
+ }
+
+ /**
+ * @Given /^file "([^"]*)" of user "([^"]*)" is shared with group "([^"]*)"$/
+ *
+ * @param string $filepath
+ * @param string $user
+ * @param string $group
+ */
+ public function assureFileIsSharedWithGroup($filepath, $user, $group){
+ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath";
+ $client = new Client();
+ $options = [];
+ if ($user === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$user, $this->regularUser];
+ }
+ $this->response = $client->get($fullUrl, $options);
+ if ($this->isUserOrGroupInSharedData($group)){
+ return;
+ } else {
+ $this->createShare($user, $filepath, 1, $group, null, null, null);
+ }
+ $this->response = $client->get($fullUrl, $options);
+ PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($group));
+ }
+
+ /**
+ * @When /^Deleting last share$/
+ */
+ public function deletingLastShare(){
+ $share_id = $this->lastShareData->data[0]->id;
+ $url = "/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id";
+ $this->sendingToWith("DELETE", $url, null);
+ }
+
+ /**
+ * @When /^Getting info of last share$/
+ */
+ public function gettingInfoOfLastShare(){
+ $share_id = $this->lastShareData->data[0]->id;
+ $url = "/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id";
+ $this->sendingToWith("GET", $url, null);
+ }
+
+ /**
+ * @Then /^last share_id is included in the answer$/
+ */
+ public function checkingLastShareIDIsIncluded(){
+ $share_id = $this->lastShareData->data[0]->id;
+ if (!$this->isFieldInResponse('id', $share_id)){
+ PHPUnit_Framework_Assert::fail("Share id $share_id not found in response");
+ }
+ }
+
+ /**
+ * @Then /^last share_id is included in the answer as parent$/
+ */
+ public function checkingLastShareIDIsIncludedAsParent(){
+ $share_id = $this->lastShareData->data[0]->id;
+ //This is a problem before 9.0, setupFS is called with every api call so a new share id is created
+ //we just need to check the parent id to match with the returned id.
+ if (!$this->isFieldInResponse('parent', $share_id)){
+ PHPUnit_Framework_Assert::fail("Share id $share_id not found in response as parent");
+ }
+ }
+
+ /**
+ * @Then /^last share_id is not included in the answer$/
+ */
+ public function checkingLastShareIDIsNotIncluded(){
+ $share_id = $this->lastShareData->data[0]->id;
+ if ($this->isFieldInResponse('id', $share_id)){
+ PHPUnit_Framework_Assert::fail("Share id $share_id has been found in response");
+ }
+ }
+
+ /**
+ * @Then /^last share_id is not included in the answer as parent$/
+ */
+ public function checkingLastShareIDIsNotIncludedAsParent(){
+ $share_id = $this->lastShareData->data[0]->id;
+ //This is a problem before 9.0, setupFS is called with every api call so a new share id is created
+ //we just need to check the parent id to match with the returned id.
+ if ($this->isFieldInResponse('parent', $share_id)){
+ PHPUnit_Framework_Assert::fail("Share id $share_id has been found in response as parent");
+ }
+ }
+
+ /**
+ * @Then /^Share fields of last share match with$/
+ * @param \Behat\Gherkin\Node\TableNode|null $body
+ */
+ public function checkShareFields($body){
+ if ($body instanceof \Behat\Gherkin\Node\TableNode) {
+ $fd = $body->getRowsHash();
+
+ foreach($fd as $field => $value) {
+ if (substr($field, 0, 10 ) === "share_with"){
+ $value = str_replace("REMOTE", substr($this->remoteBaseUrl, 0, -5), $value);
+ $value = str_replace("LOCAL", substr($this->localBaseUrl, 0, -5), $value);
+ }
+ if (substr($field, 0, 6 ) === "remote"){
+ $value = str_replace("REMOTE", substr($this->remoteBaseUrl, 0, -4), $value);
+ $value = str_replace("LOCAL", substr($this->localBaseUrl, 0, -4), $value);
+ }
+ if (!$this->isFieldInResponse($field, $value)){
+ PHPUnit_Framework_Assert::fail("$field" . " doesn't have value " . "$value");
+ }
+ }
+ }
+ }
+
+ /**
+ * @Then As :user remove all shares from the file named :fileName
+ */
+ public function asRemoveAllSharesFromTheFileNamed($user, $fileName) {
+ $url = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares?format=json";
+ $client = new \GuzzleHttp\Client();
+ $res = $client->get(
+ $url,
+ [
+ 'auth' => [
+ $user,
+ '123456',
+ ],
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ ],
+ ]
+ );
+ $json = json_decode($res->getBody()->getContents(), true);
+ $deleted = false;
+ foreach($json['ocs']['data'] as $data) {
+ if (stripslashes($data['path']) === $fileName) {
+ $id = $data['id'];
+ $client->delete(
+ $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/{$id}",
+ [
+ 'auth' => [
+ $user,
+ '123456',
+ ],
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ ],
+ ]
+ );
+ $deleted = true;
+ }
+ }
+
+ if($deleted === false) {
+ throw new \Exception("Could not delete file $fileName");
+ }
+ }
+
+ /**
+ * @When save last share id
+ */
+ public function saveLastShareId()
+ {
+ $this->savedShareId = $this->lastShareData['data']['id'];
+ }
+
+ /**
+ * @Then share ids should match
+ */
+ public function shareIdsShouldMatch()
+ {
+ if ($this->savedShareId !== $this->lastShareData['data']['id']) {
+ throw new \Exception('Expected the same link share to be returned');
+ }
+ }
+}
+
diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php
new file mode 100644
index 00000000000..3df37db72bb
--- /dev/null
+++ b/build/integration/features/bootstrap/WebDav.php
@@ -0,0 +1,508 @@
+<?php
+
+use GuzzleHttp\Client as GClient;
+use GuzzleHttp\Message\ResponseInterface;
+use Sabre\DAV\Client as SClient;
+
+require __DIR__ . '/../../vendor/autoload.php';
+
+
+trait WebDav {
+ use Sharing;
+
+ /** @var string*/
+ private $davPath = "remote.php/webdav";
+ /** @var ResponseInterface */
+ private $response;
+
+ /**
+ * @Given /^using dav path "([^"]*)"$/
+ */
+ public function usingDavPath($davPath) {
+ $this->davPath = $davPath;
+ }
+
+ public function makeDavRequest($user, $method, $path, $headers, $body = null){
+ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath . "$path";
+ $client = new GClient();
+ $options = [];
+ if ($user === 'admin') {
+ $options['auth'] = $this->adminUser;
+ } else {
+ $options['auth'] = [$user, $this->regularUser];
+ }
+ $request = $client->createRequest($method, $fullUrl, $options);
+ if (!is_null($headers)){
+ foreach ($headers as $key => $value) {
+ $request->addHeader($key, $value);
+ }
+ }
+
+ if (!is_null($body)) {
+ $request->setBody($body);
+ }
+
+ return $client->send($request);
+ }
+
+ /**
+ * @Given /^User "([^"]*)" moved file "([^"]*)" to "([^"]*)"$/
+ * @param string $user
+ * @param string $fileSource
+ * @param string $fileDestination
+ */
+ public function userMovedFile($user, $fileSource, $fileDestination){
+ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath;
+ $headers['Destination'] = $fullUrl . $fileDestination;
+ $this->response = $this->makeDavRequest($user, "MOVE", $fileSource, $headers);
+ PHPUnit_Framework_Assert::assertEquals(201, $this->response->getStatusCode());
+ }
+
+ /**
+ * @When /^User "([^"]*)" moves file "([^"]*)" to "([^"]*)"$/
+ * @param string $user
+ * @param string $fileSource
+ * @param string $fileDestination
+ */
+ public function userMovesFile($user, $fileSource, $fileDestination){
+ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath;
+ $headers['Destination'] = $fullUrl . $fileDestination;
+ try {
+ $this->response = $this->makeDavRequest($user, "MOVE", $fileSource, $headers);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @When /^User "([^"]*)" copies file "([^"]*)" to "([^"]*)"$/
+ * @param string $user
+ * @param string $fileSource
+ * @param string $fileDestination
+ */
+ public function userCopiesFile($user, $fileSource, $fileDestination){
+ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath;
+ $headers['Destination'] = $fullUrl . $fileDestination;
+ try {
+ $this->response = $this->makeDavRequest($user, "COPY", $fileSource, $headers);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @When /^Downloading file "([^"]*)" with range "([^"]*)"$/
+ * @param string $fileSource
+ * @param string $range
+ */
+ public function downloadFileWithRange($fileSource, $range){
+ $headers['Range'] = $range;
+ $this->response = $this->makeDavRequest($this->currentUser, "GET", $fileSource, $headers);
+ }
+
+ /**
+ * @When /^Downloading last public shared file with range "([^"]*)"$/
+ * @param string $range
+ */
+ public function downloadPublicFileWithRange($range){
+ $token = $this->lastShareData->data->token;
+ $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav";
+ $headers['Range'] = $range;
+
+ $client = new GClient();
+ $options = [];
+ $options['auth'] = [$token, ""];
+
+ $request = $client->createRequest("GET", $fullUrl, $options);
+ $request->addHeader('Range', $range);
+
+ $this->response = $client->send($request);
+ }
+
+ /**
+ * @When /^Downloading last public shared file inside a folder "([^"]*)" with range "([^"]*)"$/
+ * @param string $range
+ */
+ public function downloadPublicFileInsideAFolderWithRange($path, $range){
+ $token = $this->lastShareData->data->token;
+ $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav" . "$path";
+ $headers['Range'] = $range;
+
+ $client = new GClient();
+ $options = [];
+ $options['auth'] = [$token, ""];
+
+ $request = $client->createRequest("GET", $fullUrl, $options);
+ $request->addHeader('Range', $range);
+
+ $this->response = $client->send($request);
+ }
+
+ /**
+ * @Then /^Downloaded content should be "([^"]*)"$/
+ * @param string $content
+ */
+ public function downloadedContentShouldBe($content){
+ PHPUnit_Framework_Assert::assertEquals($content, (string)$this->response->getBody());
+ }
+
+ /**
+ * @Then /^Downloaded content when downloading file "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/
+ * @param string $fileSource
+ * @param string $range
+ * @param string $content
+ */
+ public function downloadedContentWhenDownloadindShouldBe($fileSource, $range, $content){
+ $this->downloadFileWithRange($fileSource, $range);
+ $this->downloadedContentShouldBe($content);
+ }
+
+ /**
+ * @When Downloading file :fileName
+ * @param string $fileName
+ */
+ public function downloadingFile($fileName) {
+ try {
+ $this->response = $this->makeDavRequest($this->currentUser, 'GET', $fileName, []);
+ } catch (\GuzzleHttp\Exception\ClientException $e) {
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @Then The following headers should be set
+ * @param \Behat\Gherkin\Node\TableNode $table
+ * @throws \Exception
+ */
+ public function theFollowingHeadersShouldBeSet(\Behat\Gherkin\Node\TableNode $table) {
+ foreach($table->getTable() as $header) {
+ $headerName = $header[0];
+ $expectedHeaderValue = $header[1];
+ $returnedHeader = $this->response->getHeader($headerName);
+ if($returnedHeader !== $expectedHeaderValue) {
+ throw new \Exception(
+ sprintf(
+ "Expected value '%s' for header '%s', got '%s'",
+ $expectedHeaderValue,
+ $headerName,
+ $returnedHeader
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * @Then Downloaded content should start with :start
+ * @param int $start
+ * @throws \Exception
+ */
+ public function downloadedContentShouldStartWith($start) {
+ if(strpos($this->response->getBody()->getContents(), $start) !== 0) {
+ throw new \Exception(
+ sprintf(
+ "Expected '%s', got '%s'",
+ $start,
+ $this->response->getBody()->getContents()
+ )
+ );
+ }
+ }
+
+ /**
+ * @Then /^as "([^"]*)" gets properties of folder "([^"]*)" with$/
+ * @param string $user
+ * @param string $path
+ * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable
+ */
+ public function asGetsPropertiesOfFolderWith($user, $path, $propertiesTable) {
+ $properties = null;
+ if ($propertiesTable instanceof \Behat\Gherkin\Node\TableNode) {
+ foreach ($propertiesTable->getRows() as $row) {
+ $properties[] = $row[0];
+ }
+ }
+ $this->response = $this->listFolder($user, $path, 0, $properties);
+ }
+
+ /**
+ * @Then the single response should contain a property :key with value :value
+ * @param string $key
+ * @param string $expectedValue
+ * @throws \Exception
+ */
+ public function theSingleResponseShouldContainAPropertyWithValue($key, $expectedValue) {
+ $keys = $this->response;
+ if (!array_key_exists($key, $keys)) {
+ throw new \Exception("Cannot find property \"$key\" with \"$expectedValue\"");
+ }
+
+ $value = $keys[$key];
+ if ($value != $expectedValue) {
+ throw new \Exception("Property \"$key\" found with value \"$value\", expected \"$expectedValue\"");
+ }
+ }
+
+ /**
+ * @Then the response should contain a share-types property with
+ */
+ public function theResponseShouldContainAShareTypesPropertyWith($table)
+ {
+ $keys = $this->response;
+ if (!array_key_exists('{http://owncloud.org/ns}share-types', $keys)) {
+ throw new \Exception("Cannot find property \"{http://owncloud.org/ns}share-types\"");
+ }
+
+ $foundTypes = [];
+ $data = $keys['{http://owncloud.org/ns}share-types'];
+ foreach ($data as $item) {
+ if ($item['name'] !== '{http://owncloud.org/ns}share-type') {
+ throw new \Exception('Invalid property found: "' . $item['name'] . '"');
+ }
+
+ $foundTypes[] = $item['value'];
+ }
+
+ foreach ($table->getRows() as $row) {
+ $key = array_search($row[0], $foundTypes);
+ if ($key === false) {
+ throw new \Exception('Expected type ' . $row[0] . ' not found');
+ }
+
+ unset($foundTypes[$key]);
+ }
+
+ if ($foundTypes !== []) {
+ throw new \Exception('Found more share types then specified: ' . $foundTypes);
+ }
+ }
+
+ /**
+ * @Then the response should contain an empty property :property
+ * @param string $property
+ * @throws \Exception
+ */
+ public function theResponseShouldContainAnEmptyProperty($property) {
+ $properties = $this->response;
+ if (!array_key_exists($property, $properties)) {
+ throw new \Exception("Cannot find property \"$property\"");
+ }
+
+ if ($properties[$property] !== null) {
+ throw new \Exception("Property \"$property\" is not empty");
+ }
+ }
+
+
+ /*Returns the elements of a propfind, $folderDepth requires 1 to see elements without children*/
+ public function listFolder($user, $path, $folderDepth, $properties = null){
+ $fullUrl = substr($this->baseUrl, 0, -4);
+
+ $settings = array(
+ 'baseUri' => $fullUrl,
+ 'userName' => $user,
+ );
+
+ if ($user === 'admin') {
+ $settings['password'] = $this->adminUser[1];
+ } else {
+ $settings['password'] = $this->regularUser;
+ }
+
+ $client = new SClient($settings);
+
+ if (!$properties) {
+ $properties = [
+ '{DAV:}getetag'
+ ];
+ }
+
+ $response = $client->propfind($this->davPath . '/' . ltrim($path, '/'), $properties, $folderDepth);
+
+ return $response;
+ }
+
+ /**
+ * @Then /^user "([^"]*)" should see following elements$/
+ * @param string $user
+ * @param \Behat\Gherkin\Node\TableNode|null $expectedElements
+ */
+ public function checkElementList($user, $expectedElements){
+ $elementList = $this->listFolder($user, '/', 3);
+ if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) {
+ $elementRows = $expectedElements->getRows();
+ $elementsSimplified = $this->simplifyArray($elementRows);
+ foreach($elementsSimplified as $expectedElement) {
+ $webdavPath = "/" . $this->davPath . $expectedElement;
+ if (!array_key_exists($webdavPath,$elementList)){
+ PHPUnit_Framework_Assert::fail("$webdavPath" . " is not in propfind answer");
+ }
+ }
+ }
+ }
+
+ /**
+ * @When User :user uploads file :source to :destination
+ * @param string $user
+ * @param string $source
+ * @param string $destination
+ */
+ public function userUploadsAFileTo($user, $source, $destination)
+ {
+ $file = \GuzzleHttp\Stream\Stream::factory(fopen($source, 'r'));
+ try {
+ $this->response = $this->makeDavRequest($user, "PUT", $destination, [], $file);
+ } catch (\GuzzleHttp\Exception\ServerException $e) {
+ // 4xx and 5xx responses cause an exception
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @When User :user uploads file with content :content to :destination
+ */
+ public function userUploadsAFileWithContentTo($user, $content, $destination)
+ {
+ $file = \GuzzleHttp\Stream\Stream::factory($content);
+ try {
+ $this->response = $this->makeDavRequest($user, "PUT", $destination, [], $file);
+ } catch (\GuzzleHttp\Exception\ServerException $e) {
+ // 4xx and 5xx responses cause an exception
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @When User :user deletes file :file
+ * @param string $user
+ * @param string $file
+ */
+ public function userDeletesFile($user, $file) {
+ try {
+ $this->response = $this->makeDavRequest($user, 'DELETE', $file, []);
+ } catch (\GuzzleHttp\Exception\ServerException $e) {
+ // 4xx and 5xx responses cause an exception
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @Given User :user created a folder :destination
+ * @param string $user
+ * @param string $destination
+ */
+ public function userCreatedAFolder($user, $destination){
+ try {
+ $this->response = $this->makeDavRequest($user, "MKCOL", $destination, []);
+ } catch (\GuzzleHttp\Exception\ServerException $e) {
+ // 4xx and 5xx responses cause an exception
+ $this->response = $e->getResponse();
+ }
+ }
+
+ /**
+ * @Given user :user uploads chunk file :num of :total with :data to :destination
+ * @param string $user
+ * @param int $num
+ * @param int $total
+ * @param string $data
+ * @param string $destination
+ */
+ public function userUploadsChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination)
+ {
+ $num -= 1;
+ $data = \GuzzleHttp\Stream\Stream::factory($data);
+ $file = $destination . '-chunking-42-'.$total.'-'.$num;
+ $this->makeDavRequest($user, 'PUT', $file, ['OC-Chunked' => '1'], $data);
+ }
+
+ /**
+ * @Given user :user creates a new chunking upload with id :id
+ */
+ public function userCreatesANewChunkingUploadWithId($user, $id)
+ {
+ $destination = '/uploads/'.$user.'/'.$id;
+ $this->makeDavRequest($user, 'MKCOL', $destination, []);
+ }
+
+ /**
+ * @Given user :user uploads new chunk file :num with :data to id :id
+ */
+ public function userUploadsNewChunkFileOfWithToId($user, $num, $data, $id)
+ {
+ $data = \GuzzleHttp\Stream\Stream::factory($data);
+ $destination = '/uploads/'.$user.'/'.$id.'/'.$num;
+ $this->makeDavRequest($user, 'PUT', $destination, [], $data);
+ }
+
+ /**
+ * @Given user :user moves new chunk file with id :id to :dest
+ */
+ public function userMovesNewChunkFileWithIdToMychunkedfile($user, $id, $dest)
+ {
+ $source = '/uploads/'.$user.'/'.$id.'/.file';
+ $destination = substr($this->baseUrl, 0, -4) . $this->davPath . '/files/'.$user.$dest;
+ $this->makeDavRequest($user, 'MOVE', $source, [
+ 'Destination' => $destination
+ ]);
+ }
+
+
+ /**
+ * @Given /^Downloading file "([^"]*)" as "([^"]*)"$/
+ */
+ public function downloadingFileAs($fileName, $user) {
+ try {
+ $this->response = $this->makeDavRequest($user, 'GET', $fileName, []);
+ } catch (\GuzzleHttp\Exception\ServerException $ex) {
+ $this->response = $ex->getResponse();
+ }
+ }
+
+ /**
+ * @When user :user favorites element :path
+ */
+ public function userFavoritesElement($user, $path){
+ $this->response = $this->changeFavStateOfAnElement($user, $path, 1, 0, null);
+ }
+
+ /**
+ * @When user :user unfavorites element :path
+ */
+ public function userUnfavoritesElement($user, $path){
+ $this->response = $this->changeFavStateOfAnElement($user, $path, 0, 0, null);
+ }
+
+ /*Set the elements of a proppatch, $folderDepth requires 1 to see elements without children*/
+ public function changeFavStateOfAnElement($user, $path, $favOrUnfav, $folderDepth, $properties = null){
+ $fullUrl = substr($this->baseUrl, 0, -4);
+ $settings = array(
+ 'baseUri' => $fullUrl,
+ 'userName' => $user,
+ );
+ if ($user === 'admin') {
+ $settings['password'] = $this->adminUser[1];
+ } else {
+ $settings['password'] = $this->regularUser;
+ }
+ $client = new SClient($settings);
+ if (!$properties) {
+ $properties = [
+ '{http://owncloud.org/ns}favorite' => $favOrUnfav
+ ];
+ }
+
+ $response = $client->proppatch($this->davPath . '/' . ltrim($path, '/'), $properties, $folderDepth);
+ return $response;
+ }
+
+ /**
+ * @Then /^as "([^"]*)" gets properties of file "([^"]*)" with$/
+ * @param string $user
+ * @param string $path
+ * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable
+ */
+ public function asGetsPropertiesOfFileWith($user, $path, $propertiesTable) {
+ $this->asGetsPropertiesOfFolderWith($user, $path, $propertiesTable);
+ }
+}
diff --git a/build/integration/features/favorites.feature b/build/integration/features/favorites.feature
new file mode 100644
index 00000000000..86643fdd1e2
--- /dev/null
+++ b/build/integration/features/favorites.feature
@@ -0,0 +1,42 @@
+Feature: favorite
+ Background:
+ Given using api version "1"
+
+ Scenario: Favorite a folder
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ When user "user0" favorites element "/FOLDER"
+ Then as "user0" gets properties of folder "/FOLDER" with
+ |{http://owncloud.org/ns}favorite|
+ And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1"
+
+ Scenario: Favorite and unfavorite a folder
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ When user "user0" favorites element "/FOLDER"
+ And user "user0" unfavorites element "/FOLDER"
+ Then as "user0" gets properties of folder "/FOLDER" with
+ |{http://owncloud.org/ns}favorite|
+ And the single response should contain a property "{http://owncloud.org/ns}favorite" with value ""
+
+ Scenario: Favorite a file
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ When user "user0" favorites element "/textfile0.txt"
+ Then as "user0" gets properties of file "/textfile0.txt" with
+ |{http://owncloud.org/ns}favorite|
+ And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1"
+
+ Scenario: Favorite and unfavorite a file
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ When user "user0" favorites element "/textfile0.txt"
+ And user "user0" unfavorites element "/textfile0.txt"
+ Then as "user0" gets properties of file "/textfile0.txt" with
+ |{http://owncloud.org/ns}favorite|
+ And the single response should contain a property "{http://owncloud.org/ns}favorite" with value ""
+
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index d865ee687ed..5357769cdbe 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -1,106 +1,296 @@
Feature: provisioning
- Background:
- Given using api version "1"
-
- Scenario: Getting an not existing user
- Given As an "admin"
- When sending "GET" to "/cloud/users/test"
- Then the OCS status code should be "998"
- And the HTTP status code should be "200"
-
- Scenario: Listing all users
- Given As an "admin"
- When sending "GET" to "/cloud/users"
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
-
- Scenario: Create a user
- Given As an "admin"
- And user "brand-new-user" does not exist
- When sending "POST" to "/cloud/users" with
- | userid | brand-new-user |
- | password | 123456 |
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
- And user "brand-new-user" exists
-
- Scenario: Create an existing user
- Given As an "admin"
- And user "brand-new-user" exists
- When sending "POST" to "/cloud/users" with
- | userid | brand-new-user |
- | password | 123456 |
- Then the OCS status code should be "102"
- And the HTTP status code should be "200"
-
- Scenario: Get an existing user
- Given As an "admin"
- When sending "GET" to "/cloud/users/brand-new-user"
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
-
-
- Scenario: Getting all users
- Given As an "admin"
- And user "brand-new-user" exists
- And user "admin" exists
- When sending "GET" to "/cloud/users"
- And users returned are
- | brand-new-user |
- | admin |
-
-
- Scenario: Edit a user
- Given As an "admin"
- And user "brand-new-user" exists
- When sending "PUT" to "/cloud/users/brand-new-user" with
- | key | quota |
- | value | 12MB |
- | key | email |
- | value | brand-new-user@gmail.com |
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
- And user "brand-new-user" exists
-
-
- Scenario: Delete a user
- Given As an "admin"
- And user "brand-new-user" exists
- When sending "DELETE" to "/cloud/users/brand-new-user"
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
- And user "brand-new-user" does not exist
-
-
- Scenario: Create a group
- Given As an "admin"
- And group "new-group" does not exist
- When sending "POST" to "/cloud/groups" with
- | groupid | new-group |
- | password | 123456 |
-
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
- And group "new-group" exists
-
-
- Scenario: Getting all groups
- Given As an "admin"
- And group "new-group" exists
- And group "admin" exists
- When sending "GET" to "/cloud/groups"
- And groups returned are
- | admin |
- | new-group |
-
-
- Scenario: Delete a group
- Given As an "admin"
- And group "new-group" exists
- When sending "DELETE" to "/cloud/groups/new-group"
- Then the OCS status code should be "100"
- And the HTTP status code should be "200"
- And group "new-group" does not exist
+ Background:
+ Given using api version "1"
+ Scenario: Getting an not existing user
+ Given As an "admin"
+ When sending "GET" to "/cloud/users/test"
+ Then the OCS status code should be "998"
+ And the HTTP status code should be "200"
+ Scenario: Listing all users
+ Given As an "admin"
+ When sending "GET" to "/cloud/users"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Create a user
+ Given As an "admin"
+ And user "brand-new-user" does not exist
+ When sending "POST" to "/cloud/users" with
+ | userid | brand-new-user |
+ | password | 123456 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And user "brand-new-user" exists
+
+ Scenario: Create an existing user
+ Given As an "admin"
+ And user "brand-new-user" exists
+ When sending "POST" to "/cloud/users" with
+ | userid | brand-new-user |
+ | password | 123456 |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+
+ Scenario: Get an existing user
+ Given As an "admin"
+ When sending "GET" to "/cloud/users/brand-new-user"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting all users
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And user "admin" exists
+ When sending "GET" to "/cloud/users"
+ Then users returned are
+ | brand-new-user |
+ | admin |
+
+ Scenario: Edit a user
+ Given As an "admin"
+ And user "brand-new-user" exists
+ When sending "PUT" to "/cloud/users/brand-new-user" with
+ | key | quota |
+ | value | 12MB |
+ | key | email |
+ | value | brand-new-user@gmail.com |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And user "brand-new-user" exists
+
+ Scenario: Create a group
+ Given As an "admin"
+ And group "new-group" does not exist
+ When sending "POST" to "/cloud/groups" with
+ | groupid | new-group |
+ | password | 123456 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And group "new-group" exists
+
+ Scenario: adding user to a group without sending the group
+ Given As an "admin"
+ And user "brand-new-user" exists
+ When sending "POST" to "/cloud/users/brand-new-user/groups" with
+ | groupid | |
+ Then the OCS status code should be "101"
+ And the HTTP status code should be "200"
+
+ Scenario: adding user to a group which doesn't exist
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "not-group" does not exist
+ When sending "POST" to "/cloud/users/brand-new-user/groups" with
+ | groupid | not-group |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+
+ Scenario: adding user to a group without privileges
+ Given As an "brand-new-user"
+ When sending "POST" to "/cloud/users/brand-new-user/groups" with
+ | groupid | new-group |
+ Then the OCS status code should be "997"
+ And the HTTP status code should be "401"
+
+ Scenario: adding user to a group
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ When sending "POST" to "/cloud/users/brand-new-user/groups" with
+ | groupid | new-group |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: getting groups of an user
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ When sending "GET" to "/cloud/users/brand-new-user/groups"
+ Then groups returned are
+ | new-group |
+ And the OCS status code should be "100"
+
+ Scenario: adding a user which doesn't exist to a group
+ Given As an "admin"
+ And user "not-user" does not exist
+ And group "new-group" exists
+ When sending "POST" to "/cloud/users/not-user/groups" with
+ | groupid | new-group |
+ Then the OCS status code should be "103"
+ And the HTTP status code should be "200"
+
+ Scenario: getting a group
+ Given As an "admin"
+ And group "new-group" exists
+ When sending "GET" to "/cloud/groups/new-group"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting all groups
+ Given As an "admin"
+ And group "new-group" exists
+ And group "admin" exists
+ When sending "GET" to "/cloud/groups"
+ Then groups returned are
+ | admin |
+ | new-group |
+
+ Scenario: create a subadmin
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ When sending "POST" to "/cloud/users/brand-new-user/subadmins" with
+ | groupid | new-group |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: removing a user from a group which doesn't exists
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "not-group" does not exist
+ When sending "DELETE" to "/cloud/users/brand-new-user/groups" with
+ | groupid | not-group |
+ Then the OCS status code should be "102"
+
+ Scenario: removing a user from a group
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ And user "brand-new-user" belongs to group "new-group"
+ When sending "DELETE" to "/cloud/users/brand-new-user/groups" with
+ | groupid | new-group |
+ Then the OCS status code should be "100"
+ And user "brand-new-user" does not belong to group "new-group"
+
+ Scenario: create a subadmin using a user which not exist
+ Given As an "admin"
+ And user "not-user" does not exist
+ And group "new-group" exists
+ When sending "POST" to "/cloud/users/not-user/subadmins" with
+ | groupid | new-group |
+ Then the OCS status code should be "101"
+ And the HTTP status code should be "200"
+
+ Scenario: create a subadmin using a group which not exist
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "not-group" does not exist
+ When sending "POST" to "/cloud/users/brand-new-user/subadmins" with
+ | groupid | not-group |
+ Then the OCS status code should be "102"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting subadmin groups
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ When sending "GET" to "/cloud/users/brand-new-user/subadmins"
+ Then subadmin groups returned are
+ | new-group |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting subadmin groups of a user which not exist
+ Given As an "admin"
+ And user "not-user" does not exist
+ And group "new-group" exists
+ When sending "GET" to "/cloud/users/not-user/subadmins"
+ Then the OCS status code should be "101"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting subadmin users of a group
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ When sending "GET" to "/cloud/groups/new-group/subadmins"
+ Then subadmin users returned are
+ | brand-new-user |
+ And the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Getting subadmin users of a group which doesn't exist
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "not-group" does not exist
+ When sending "GET" to "/cloud/groups/not-group/subadmins"
+ Then the OCS status code should be "101"
+ And the HTTP status code should be "200"
+
+ Scenario: Removing subadmin from a group
+ Given As an "admin"
+ And user "brand-new-user" exists
+ And group "new-group" exists
+ And user "brand-new-user" is subadmin of group "new-group"
+ When sending "DELETE" to "/cloud/users/brand-new-user/subadmins" with
+ | groupid | new-group |
+ And the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Delete a user
+ Given As an "admin"
+ And user "brand-new-user" exists
+ When sending "DELETE" to "/cloud/users/brand-new-user"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And user "brand-new-user" does not exist
+
+ Scenario: Delete a group
+ Given As an "admin"
+ And group "new-group" exists
+ When sending "DELETE" to "/cloud/groups/new-group"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And group "new-group" does not exist
+
+ Scenario: get enabled apps
+ Given As an "admin"
+ When sending "GET" to "/cloud/apps?filter=enabled"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And apps returned are
+ | files |
+ | files_sharing |
+ | files_trashbin |
+ | files_versions |
+ | provisioning_api |
+
+ Scenario: get app info
+ Given As an "admin"
+ When sending "GET" to "/cloud/apps/files"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: enable an app
+ Given As an "admin"
+ And app "files_external" is disabled
+ When sending "POST" to "/cloud/apps/files_external"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And app "files_external" is enabled
+
+ Scenario: disable an app
+ Given As an "admin"
+ And app "files_external" is enabled
+ When sending "DELETE" to "/cloud/apps/files_external"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And app "files_external" is disabled
+
+ Scenario: Making a web request with an enabled user
+ Given As an "admin"
+ And user "user0" exists
+ And As an "user0"
+ When sending "GET" to "/index.php/apps/files"
+ Then the HTTP status code should be "200"
+
+ Scenario: Making a web request with a disabled user
+ Given As an "admin"
+ And user "user0" exists
+ And assure user "user0" is disabled
+ And As an "user0"
+ When sending "GET" to "/index.php/apps/files"
+ Then the OCS status code should be "999"
+ And the HTTP status code should be "200"
diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature
new file mode 100644
index 00000000000..cb1c55fe267
--- /dev/null
+++ b/build/integration/features/sharing-v1.feature
@@ -0,0 +1,569 @@
+Feature: sharing
+ Background:
+ Given using api version "1"
+ Given using dav path "remote.php/webdav"
+
+ Scenario: Creating a new share with user
+ Given user "user0" exists
+ And user "user1" exists
+ And As an "user0"
+ When sending "POST" to "/apps/files_sharing/api/v1/shares" with
+ | path | welcome.txt |
+ | shareWith | user1 |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Creating a share with a group
+ Given user "user0" exists
+ And user "user1" exists
+ And group "sharing-group" exists
+ And As an "user0"
+ When sending "POST" to "/apps/files_sharing/api/v1/shares" with
+ | path | welcome.txt |
+ | shareWith | sharing-group |
+ | shareType | 1 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Creating a new share with user who already received a share through their group
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And group "sharing-group" exists
+ And user "user1" belongs to group "sharing-group"
+ And file "welcome.txt" of user "user0" is shared with group "sharing-group"
+ And As an "user0"
+ Then sending "POST" to "/apps/files_sharing/api/v1/shares" with
+ | path | welcome.txt |
+ | shareWith | user1 |
+ | shareType | 0 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Creating a new public share
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Public shared file "welcome.txt" can be downloaded
+
+ Scenario: Creating a new public share with password
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ | password | publicpw |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Public shared file "welcome.txt" with password "publicpw" can be downloaded
+
+ Scenario: Creating a new public share of a folder
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | FOLDER |
+ | shareType | 3 |
+ | password | publicpw |
+ | expireDate | +3 days |
+ | publicUpload | true |
+ | permissions | 7 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | permissions | 7 |
+ | expiration | +3 days |
+ | url | AN_URL |
+ | token | A_TOKEN |
+
+ Scenario: Creating a new public share with password and adding an expiration date
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ | password | publicpw |
+ And Updating last share with
+ | expireDate | +3 days |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Public shared file "welcome.txt" with password "publicpw" can be downloaded
+
+ Scenario: Creating a new public share, updating its expiration date and getting its info
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | FOLDER |
+ | shareType | 3 |
+ And Updating last share with
+ | expireDate | +3 days |
+ And Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | folder |
+ | item_source | A_NUMBER |
+ | share_type | 3 |
+ | file_source | A_NUMBER |
+ | file_target | /FOLDER |
+ | permissions | 1 |
+ | stime | A_NUMBER |
+ | expiration | +3 days |
+ | token | A_TOKEN |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | displayname_owner | user0 |
+ | url | AN_URL |
+
+ Scenario: Creating a new public share, updating its password and getting its info
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | FOLDER |
+ | shareType | 3 |
+ And Updating last share with
+ | password | publicpw |
+ And Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | folder |
+ | item_source | A_NUMBER |
+ | share_type | 3 |
+ | file_source | A_NUMBER |
+ | file_target | /FOLDER |
+ | permissions | 1 |
+ | stime | A_NUMBER |
+ | token | A_TOKEN |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | displayname_owner | user0 |
+ | url | AN_URL |
+
+ Scenario: Creating a new public share, updating its permissions and getting its info
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | FOLDER |
+ | shareType | 3 |
+ And Updating last share with
+ | permissions | 7 |
+ And Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | folder |
+ | item_source | A_NUMBER |
+ | share_type | 3 |
+ | file_source | A_NUMBER |
+ | file_target | /FOLDER |
+ | permissions | 7 |
+ | stime | A_NUMBER |
+ | token | A_TOKEN |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | displayname_owner | user0 |
+ | url | AN_URL |
+
+ Scenario: Creating a new public share, updating publicUpload option and getting its info
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | FOLDER |
+ | shareType | 3 |
+ And Updating last share with
+ | publicUpload | true |
+ And Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | folder |
+ | item_source | A_NUMBER |
+ | share_type | 3 |
+ | file_source | A_NUMBER |
+ | file_target | /FOLDER |
+ | permissions | 7 |
+ | stime | A_NUMBER |
+ | token | A_TOKEN |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | displayname_owner | user0 |
+ | url | AN_URL |
+
+ Scenario: getting all shares of a user using that user
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "user0"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And File "textfile0 (2).txt" should be included in the response
+
+ Scenario: getting all shares of a user using another user
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "admin"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And File "textfile0.txt" should not be included in the response
+
+ Scenario: getting all shares of a file
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And user "user3" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile0.txt" of user "user0" is shared with user "user2"
+ And As an "user0"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?path=textfile0.txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And User "user1" should be included in the response
+ And User "user2" should be included in the response
+ And User "user3" should not be included in the response
+
+ Scenario: getting all shares of a file with reshares
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And user "user3" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile0 (2).txt" of user "user1" is shared with user "user2"
+ And As an "user0"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And User "user1" should be included in the response
+ And User "user2" should be included in the response
+ And User "user3" should not be included in the response
+
+ Scenario: getting share info of a share
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "user0"
+ When Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | file |
+ | item_source | A_NUMBER |
+ | share_type | 0 |
+ | share_with | user1 |
+ | file_source | A_NUMBER |
+ | file_target | /textfile0 (2).txt |
+ | path | /textfile0.txt |
+ | permissions | 23 |
+ | stime | A_NUMBER |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | share_with_displayname | user1 |
+ | displayname_owner | user0 |
+
+ Scenario: keep group permissions in sync
+ Given As an "admin"
+ Given user "user0" exists
+ And user "user1" exists
+ And group "group1" exists
+ And user "user1" belongs to group "group1"
+ And file "textfile0.txt" of user "user0" is shared with group "group1"
+ And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt"
+ And As an "user0"
+ When Updating last share with
+ | permissions | 1 |
+ And Getting info of last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | item_type | file |
+ | item_source | A_NUMBER |
+ | share_type | 1 |
+ | file_source | A_NUMBER |
+ | file_target | /textfile0.txt |
+ | permissions | 1 |
+ | stime | A_NUMBER |
+ | storage | A_NUMBER |
+ | mail_send | 0 |
+ | uid_owner | user0 |
+ | storage_id | home::user0 |
+ | file_parent | A_NUMBER |
+ | displayname_owner | user0 |
+
+ Scenario: Sharee can see the share
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is included in the answer
+
+ Scenario: Sharee can see the filtered share
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile1.txt" of user "user0" is shared with user "user1"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile1 (2).txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is included in the answer
+
+ Scenario: Sharee can't see the share that is filtered out
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile1.txt" of user "user0" is shared with user "user1"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile0 (2).txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is not included in the answer as parent
+
+ Scenario: Sharee can see the group share
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And group "group0" exists
+ And user "user1" belongs to group "group0"
+ And file "textfile0.txt" of user "user0" is shared with group "group0"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is included in the answer as parent
+
+ Scenario: User is not allowed to reshare file
+ As an "admin"
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And As an "user0"
+ And creating a share with
+ | path | /textfile0.txt |
+ | shareType | 0 |
+ | shareWith | user1 |
+ | permissions | 8 |
+ And As an "user1"
+ When creating a share with
+ | path | /textfile0 (2).txt |
+ | shareType | 0 |
+ | shareWith | user2 |
+ | permissions | 31 |
+ Then the OCS status code should be "404"
+ And the HTTP status code should be "200"
+
+ Scenario: User is not allowed to reshare file with more permissions
+ As an "admin"
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And As an "user0"
+ And creating a share with
+ | path | /textfile0.txt |
+ | shareType | 0 |
+ | shareWith | user1 |
+ | permissions | 16 |
+ And As an "user1"
+ When creating a share with
+ | path | /textfile0 (2).txt |
+ | shareType | 0 |
+ | shareWith | user2 |
+ | permissions | 31 |
+ Then the OCS status code should be "404"
+ And the HTTP status code should be "200"
+
+ Scenario: Get a share with a user which didn't received the share
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "user2"
+ When Getting info of last share
+ Then the OCS status code should be "404"
+ And the HTTP status code should be "200"
+
+ Scenario: Share of folder and sub-folder to same user - core#20645
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And group "group0" exists
+ And user "user1" belongs to group "group0"
+ And file "/PARENT" of user "user0" is shared with user "user1"
+ When file "/PARENT/CHILD" of user "user0" is shared with group "group0"
+ Then user "user1" should see following elements
+ | /FOLDER/ |
+ | /PARENT/ |
+ | /CHILD/ |
+ | /PARENT/parent.txt |
+ | /CHILD/child.txt |
+ And the HTTP status code should be "200"
+
+ Scenario: Share a file by multiple channels
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And group "group0" exists
+ And user "user1" belongs to group "group0"
+ And user "user2" belongs to group "group0"
+ And user "user0" created a folder "/common"
+ And user "user0" created a folder "/common/sub"
+ And file "common" of user "user0" is shared with group "group0"
+ And file "textfile0.txt" of user "user1" is shared with user "user2"
+ And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt"
+ And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt"
+ And As an "user2"
+ When Downloading file "/common/sub/textfile0.txt" with range "bytes=9-17"
+ Then Downloaded content should be "test text"
+ And Downloaded content when downloading file "/textfile0.txt" with range "bytes=9-17" should be "test text"
+ And user "user2" should see following elements
+ | /common/sub/textfile0.txt |
+
+ Scenario: Share a file by multiple channels
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And group "group0" exists
+ And user "user1" belongs to group "group0"
+ And user "user2" belongs to group "group0"
+ And user "user0" created a folder "/common"
+ And user "user0" created a folder "/common/sub"
+ And file "common" of user "user0" is shared with group "group0"
+ And file "textfile0.txt" of user "user1" is shared with user "user2"
+ And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt"
+ And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt"
+ And As an "user2"
+ When Downloading file "/textfile0.txt" with range "bytes=9-17"
+ Then Downloaded content should be "test text"
+ And user "user2" should see following elements
+ | /common/sub/textfile0.txt |
+
+ Scenario: Delete all group shares
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And group "group1" exists
+ And user "user1" belongs to group "group1"
+ And file "textfile0.txt" of user "user0" is shared with group "group1"
+ And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt"
+ And As an "user0"
+ And Deleting last share
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is not included in the answer
+
+ Scenario: delete a share
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And As an "user0"
+ When Deleting last share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Keep usergroup shares (#22143)
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And group "group" exists
+ And user "user1" belongs to group "group"
+ And user "user2" belongs to group "group"
+ And user "user0" created a folder "/TMP"
+ And file "TMP" of user "user0" is shared with group "group"
+ And user "user1" created a folder "/myFOLDER"
+ And User "user1" moves file "/TMP" to "/myFOLDER/myTMP"
+ And user "user2" does not exist
+ And user "user1" should see following elements
+ | /myFOLDER/myTMP/ |
+
+ Scenario: Allow modification of reshare
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And user "user0" created a folder "/TMP"
+ And file "TMP" of user "user0" is shared with user "user1"
+ And file "TMP" of user "user1" is shared with user "user2"
+ And As an "user1"
+ When Updating last share with
+ | permissions | 1 |
+ Then the OCS status code should be "100"
+
+ Scenario: Do not allow reshare to exceed permissions
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And user "user0" created a folder "/TMP"
+ And As an "user0"
+ And creating a share with
+ | path | /TMP |
+ | shareType | 0 |
+ | shareWith | user1 |
+ | permissions | 21 |
+ And As an "user1"
+ And creating a share with
+ | path | /TMP |
+ | shareType | 0 |
+ | shareWith | user2 |
+ | permissions | 21 |
+ When Updating last share with
+ | permissions | 31 |
+ Then the OCS status code should be "404"
+
+ Scenario: Only allow 1 link share per file/folder
+ Given user "user0" exists
+ And As an "user0"
+ And creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ When save last share id
+ And creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ Then share ids should match
+
+ Scenario: unique target names for incoming shares
+ Given user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And user "user0" created a folder "/foo"
+ And user "user1" created a folder "/foo"
+ When file "/foo" of user "user0" is shared with user "user2"
+ And file "/foo" of user "user1" is shared with user "user2"
+ Then user "user2" should see following elements
+ | /foo/ |
+ | /foo%20(2)/ |
diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature
new file mode 100644
index 00000000000..a0d469f4805
--- /dev/null
+++ b/build/integration/features/webdav-related.feature
@@ -0,0 +1,203 @@
+Feature: webdav-related
+ Background:
+ Given using api version "1"
+
+ Scenario: Moving a file
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And As an "user0"
+ When User "user0" moves file "/welcome.txt" to "/FOLDER/welcome.txt"
+ Then the HTTP status code should be "201"
+ And Downloaded content when downloading file "/FOLDER/welcome.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: Moving and overwriting a file old way
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And As an "user0"
+ When User "user0" moves file "/welcome.txt" to "/textfile0.txt"
+ Then the HTTP status code should be "204"
+ And Downloaded content when downloading file "/textfile0.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: Moving a file to a folder with no permissions
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And As an "user1"
+ And user "user1" created a folder "/testshare"
+ And as "user1" creating a share with
+ | path | testshare |
+ | shareType | 0 |
+ | permissions | 1 |
+ | shareWith | user0 |
+ And As an "user0"
+ And User "user0" moves file "/textfile0.txt" to "/testshare/textfile0.txt"
+ And the HTTP status code should be "403"
+ When Downloading file "/testshare/textfile0.txt"
+ Then the HTTP status code should be "404"
+
+ Scenario: Moving a file to overwrite a file in a folder with no permissions
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And As an "user1"
+ And user "user1" created a folder "/testshare"
+ And as "user1" creating a share with
+ | path | testshare |
+ | shareType | 0 |
+ | permissions | 1 |
+ | shareWith | user0 |
+ And User "user1" copies file "/welcome.txt" to "/testshare/overwritethis.txt"
+ And As an "user0"
+ When User "user0" moves file "/textfile0.txt" to "/testshare/overwritethis.txt"
+ Then the HTTP status code should be "403"
+ And Downloaded content when downloading file "/testshare/overwritethis.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: Copying a file
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And As an "user0"
+ When User "user0" copies file "/welcome.txt" to "/FOLDER/welcome.txt"
+ Then the HTTP status code should be "201"
+ And Downloaded content when downloading file "/FOLDER/welcome.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: Copying and overwriting a file
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And As an "user0"
+ When User "user0" copies file "/welcome.txt" to "/textfile1.txt"
+ Then the HTTP status code should be "204"
+ And Downloaded content when downloading file "/textfile1.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: Copying a file to a folder with no permissions
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And As an "user1"
+ And user "user1" created a folder "/testshare"
+ And as "user1" creating a share with
+ | path | testshare |
+ | shareType | 0 |
+ | permissions | 1 |
+ | shareWith | user0 |
+ And As an "user0"
+ When User "user0" copies file "/textfile0.txt" to "/testshare/textfile0.txt"
+ Then the HTTP status code should be "403"
+ And Downloading file "/testshare/textfile0.txt"
+ And the HTTP status code should be "404"
+
+ Scenario: Copying a file to overwrite a file into a folder with no permissions
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And As an "user1"
+ And user "user1" created a folder "/testshare"
+ And as "user1" creating a share with
+ | path | testshare |
+ | shareType | 0 |
+ | permissions | 1 |
+ | shareWith | user0 |
+ And User "user1" copies file "/welcome.txt" to "/testshare/overwritethis.txt"
+ And As an "user0"
+ When User "user0" copies file "/textfile0.txt" to "/testshare/overwritethis.txt"
+ Then the HTTP status code should be "403"
+ And Downloaded content when downloading file "/testshare/overwritethis.txt" with range "bytes=0-6" should be "Welcome"
+
+ Scenario: download a file with range
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ When Downloading file "/welcome.txt" with range "bytes=51-77"
+ Then Downloaded content should be "example file for developers"
+
+ Scenario: Upload forbidden if quota is 0
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user0" has a quota of "0"
+ When User "user0" uploads file "data/textfile.txt" to "/asdf.txt"
+ Then the HTTP status code should be "507"
+
+ Scenario: Retrieving folder quota when quota is set
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ When user "user0" has a quota of "10 MB"
+ Then as "user0" gets properties of folder "/" with
+ |{DAV:}quota-available-bytes|
+ And the single response should contain a property "{DAV:}quota-available-bytes" with value "10485429"
+
+ Scenario: Retrieving folder quota of shared folder with quota when no quota is set for recipient
+ Given using dav path "remote.php/webdav"
+ And As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And user "user0" has unlimited quota
+ And user "user1" has a quota of "10 MB"
+ And As an "user1"
+ And user "user1" created a folder "/testquota"
+ And as "user1" creating a share with
+ | path | testquota |
+ | shareType | 0 |
+ | permissions | 31 |
+ | shareWith | user0 |
+ Then as "user0" gets properties of folder "/testquota" with
+ |{DAV:}quota-available-bytes|
+ And the single response should contain a property "{DAV:}quota-available-bytes" with value "10485429"
+
+ Scenario: download a public shared file with range
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | welcome.txt |
+ | shareType | 3 |
+ And Downloading last public shared file with range "bytes=51-77"
+ Then Downloaded content should be "example file for developers"
+
+ Scenario: download a public shared file inside a folder with range
+ Given user "user0" exists
+ And As an "user0"
+ When creating a share with
+ | path | PARENT |
+ | shareType | 3 |
+ And Downloading last public shared file inside a folder "/parent.txt" with range "bytes=1-7"
+ Then Downloaded content should be "wnCloud"
+
+ Scenario: Doing a GET with a web login should work without CSRF token on the old backend
+ Given Logging in using web as "admin"
+ When Sending a "GET" to "/remote.php/webdav/welcome.txt" without requesttoken
+ Then Downloaded content should start with "Welcome to your ownCloud account!"
+ And the HTTP status code should be "200"
+
+ Scenario: Upload chunked file asc
+ Given user "user0" exists
+ And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt"
+ When As an "user0"
+ And Downloading file "/myChunkedFile.txt"
+ Then Downloaded content should be "AAAAABBBBBCCCCC"
+
+ Scenario: Upload chunked file desc
+ Given user "user0" exists
+ And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt"
+ When As an "user0"
+ And Downloading file "/myChunkedFile.txt"
+ Then Downloaded content should be "AAAAABBBBBCCCCC"
+
+ Scenario: Upload chunked file random
+ Given user "user0" exists
+ And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt"
+ And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt"
+ When As an "user0"
+ And Downloading file "/myChunkedFile.txt"
+ Then Downloaded content should be "AAAAABBBBBCCCCC"
diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature
new file mode 100644
index 00000000000..e822de94c85
--- /dev/null
+++ b/build/integration/federation_features/federated.feature
@@ -0,0 +1,156 @@
+Feature: federated
+ Background:
+ Given using api version "1"
+
+ Scenario: Federate share a file with another server
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ When User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | permissions | 23 |
+
+ Scenario: Federate share a file with local server
+ Given Using server "LOCAL"
+ And user "user0" exists
+ And Using server "REMOTE"
+ And user "user1" exists
+ When User "user1" from server "REMOTE" shares "/textfile0.txt" with user "user0" from server "LOCAL"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | permissions | 23 |
+
+
+ Scenario: Remote sharee can see the pending share
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And Using server "REMOTE"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | remote | LOCAL |
+ | remote_id | A_NUMBER |
+ | share_token | A_TOKEN |
+ | name | /textfile0.txt |
+ | owner | user0 |
+ | user | user1 |
+ | mountpoint | {{TemporaryMountPointName#/textfile0.txt}} |
+ | accepted | 0 |
+
+ Scenario: accept a pending remote share
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ When User "user1" from server "REMOTE" accepts last pending share
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+
+ Scenario: Reshare a federated shared file
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ When creating a share with
+ | path | /textfile0 (2).txt |
+ | shareType | 0 |
+ | shareWith | user2 |
+ | permissions | 19 |
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And Share fields of last share match with
+ | id | A_NUMBER |
+ | permissions | 19 |
+
+ Scenario: Overwrite a federated shared file as recipient
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
+ When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt"
+ And Downloading file "/textfile0 (2).txt" with range "bytes=0-8"
+ Then Downloaded content should be "BLABLABLA"
+
+ Scenario: Overwrite a federated shared folder as recipient
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
+ When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt"
+ And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8"
+ Then Downloaded content should be "BLABLABLA"
+
+ Scenario: Overwrite a federated shared file as recipient using old chunking
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/textfile0 (2).txt"
+ And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/textfile0 (2).txt"
+ And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/textfile0 (2).txt"
+ When Downloading file "/textfile0 (2).txt" with range "bytes=0-4"
+ Then Downloaded content should be "AAAAA"
+
+ Scenario: Overwrite a federated shared folder as recipient using old chunking
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/PARENT (2)/textfile0.txt"
+ And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/PARENT (2)/textfile0.txt"
+ And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/PARENT (2)/textfile0.txt"
+ When Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-11"
+ Then Downloaded content should be "AAAAABBBBBCC"
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/integration/run.sh b/build/integration/run.sh
index 08f10b86c5f..5a222bda3e3 100755
--- a/build/integration/run.sh
+++ b/build/integration/run.sh
@@ -2,18 +2,37 @@
composer install
-# TODO: avoid port collision on jenkins - use $EXECUTOR_NUMBER
+SCENARIO_TO_RUN=$1
+HIDE_OC_LOGS=$2
+
+# avoid port collision on jenkins - use $EXECUTOR_NUMBER
if [ -z "$EXECUTOR_NUMBER" ]; then
EXECUTOR_NUMBER=0
fi
PORT=$((8080 + $EXECUTOR_NUMBER))
-#PORT=8080
echo $PORT
php -S localhost:$PORT -t ../.. &
PHPPID=$!
echo $PHPPID
+PORT_FED=$((8180 + $EXECUTOR_NUMBER))
+echo $PORT_FED
+php -S localhost:$PORT_FED -t ../.. &
+PHPPID_FED=$!
+echo $PHPPID_FED
+
export TEST_SERVER_URL="http://localhost:$PORT/ocs/"
-vendor/bin/behat --profile ci
+export TEST_SERVER_FED_URL="http://localhost:$PORT_FED/ocs/"
+
+vendor/bin/behat -f junit -f pretty $SCENARIO_TO_RUN
+RESULT=$?
kill $PHPPID
+kill $PHPPID_FED
+
+if [ -z $HIDE_OC_LOGS ]; then
+ tail "../../data/owncloud.log"
+fi
+
+exit $RESULT
+