* Add transfer ownership integration tests * Added more transfer ownership tests and OCC checks Signed-off-by: Lukas Reschke <lukas@statuscode.ch>tags/v12.0.0beta1
baseUrl: http://localhost:8080 | baseUrl: http://localhost:8080 | ||||
- ChecksumsContext: | - ChecksumsContext: | ||||
baseUrl: http://localhost:8080 | baseUrl: http://localhost:8080 | ||||
- CommandLineContext: | |||||
baseUrl: http://localhost:8080 | |||||
ocPath: ../../ | |||||
federation: | federation: | ||||
paths: | paths: | ||||
- %paths.base%/../federation_features | - %paths.base%/../federation_features | ||||
- admin | - admin | ||||
regular_user_password: 123456 | regular_user_password: 123456 | ||||
extensions: | extensions: | ||||
jarnaiz\JUnitFormatter\JUnitFormatterExtension: | jarnaiz\JUnitFormatter\JUnitFormatterExtension: | ||||
filename: report.xml | filename: report.xml |
<?php | |||||
/** | |||||
* @author Vincent Petry <pvince81@owncloud.com> | |||||
* | |||||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||||
* @license AGPL-3.0 | |||||
* | |||||
* This code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License, version 3, | |||||
* as published by the Free Software Foundation. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License, version 3, | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||||
* | |||||
*/ | |||||
require __DIR__ . '/../../vendor/autoload.php'; | |||||
trait CommandLine { | |||||
/** @var int return code of last command */ | |||||
private $lastCode; | |||||
/** @var string stdout of last command */ | |||||
private $lastStdOut; | |||||
/** @var string stderr of last command */ | |||||
private $lastStdErr; | |||||
/** @var string */ | |||||
protected $ocPath = '../..'; | |||||
/** | |||||
* Invokes an OCC command | |||||
* | |||||
* @param string OCC command, the part behind "occ". For example: "files:transfer-ownership" | |||||
* @return int exit code | |||||
*/ | |||||
public function runOcc($args = []) { | |||||
$args = array_map(function($arg) { | |||||
return escapeshellarg($arg); | |||||
}, $args); | |||||
$args[] = '--no-ansi'; | |||||
$args = implode(' ', $args); | |||||
$descriptor = [ | |||||
0 => ['pipe', 'r'], | |||||
1 => ['pipe', 'w'], | |||||
2 => ['pipe', 'w'], | |||||
]; | |||||
$process = proc_open('php console.php ' . $args, $descriptor, $pipes, $this->ocPath); | |||||
$this->lastStdOut = stream_get_contents($pipes[1]); | |||||
$this->lastStdErr = stream_get_contents($pipes[2]); | |||||
$this->lastCode = proc_close($process); | |||||
return $this->lastCode; | |||||
} | |||||
/** | |||||
* @Given /^invoking occ with "([^"]*)"$/ | |||||
*/ | |||||
public function invokingTheCommand($cmd) { | |||||
$args = explode(' ', $cmd); | |||||
$this->runOcc($args); | |||||
} | |||||
/** | |||||
* Find exception texts in stderr | |||||
*/ | |||||
public function findExceptions() { | |||||
$exceptions = []; | |||||
$captureNext = false; | |||||
// the exception text usually appears after an "[Exception"] row | |||||
foreach (explode("\n", $this->lastStdErr) as $line) { | |||||
if (preg_match('/\[Exception\]/', $line)) { | |||||
$captureNext = true; | |||||
continue; | |||||
} | |||||
if ($captureNext) { | |||||
$exceptions[] = trim($line); | |||||
$captureNext = false; | |||||
} | |||||
} | |||||
return $exceptions; | |||||
} | |||||
/** | |||||
* Finds all lines containing the given text | |||||
* | |||||
* @param string $input stdout or stderr output | |||||
* @param string $text text to search for | |||||
* @return array array of lines that matched | |||||
*/ | |||||
public function findLines($input, $text) { | |||||
$results = []; | |||||
// the exception text usually appears after an "[Exception"] row | |||||
foreach (explode("\n", $input) as $line) { | |||||
if (strpos($line, $text) >= 0) { | |||||
$results[] = $line; | |||||
} | |||||
} | |||||
return $results; | |||||
} | |||||
/** | |||||
* @Then /^the command was successful$/ | |||||
*/ | |||||
public function theCommandWasSuccessful() { | |||||
$exceptions = $this->findExceptions(); | |||||
if ($this->lastCode !== 0) { | |||||
$msg = 'The command was not successful, exit code was ' . $this->lastCode . '.'; | |||||
if (!empty($exceptions)) { | |||||
$msg .= ' Exceptions: ' . implode(', ', $exceptions); | |||||
} | |||||
throw new \Exception($msg); | |||||
} else if (!empty($exceptions)) { | |||||
$msg = 'The command was successful but triggered exceptions: ' . implode(', ', $exceptions); | |||||
throw new \Exception($msg); | |||||
} | |||||
} | |||||
/** | |||||
* @Then /^the command failed with exit code ([0-9]+)$/ | |||||
*/ | |||||
public function theCommandFailedWithExitCode($exitCode) { | |||||
if ($this->lastCode !== (int)$exitCode) { | |||||
throw new \Exception('The command was expected to fail with exit code ' . $exitCode . ' but got ' . $this->lastCode); | |||||
} | |||||
} | |||||
/** | |||||
* @Then /^the command failed with exception text "([^"]*)"$/ | |||||
*/ | |||||
public function theCommandFailedWithException($exceptionText) { | |||||
$exceptions = $this->findExceptions(); | |||||
if (empty($exceptions)) { | |||||
throw new \Exception('The command did not throw any exceptions'); | |||||
} | |||||
if (!in_array($exceptionText, $exceptions)) { | |||||
throw new \Exception('The command did not throw any exception with the text "' . $exceptionText . '"'); | |||||
} | |||||
} | |||||
/** | |||||
* @Then /^the command output contains the text "([^"]*)"$/ | |||||
*/ | |||||
public function theCommandOutputContainsTheText($text) { | |||||
$lines = $this->findLines($this->lastStdOut, $text); | |||||
if (empty($lines)) { | |||||
throw new \Exception('The command did not output the expected text on stdout "' . $exceptionText . '"'); | |||||
} | |||||
} | |||||
/** | |||||
* @Then /^the command error output contains the text "([^"]*)"$/ | |||||
*/ | |||||
public function theCommandErrorOutputContainsTheText($text) { | |||||
$lines = $this->findLines($this->lastStdErr, $text); | |||||
if (empty($lines)) { | |||||
throw new \Exception('The command did not output the expected text on stderr "' . $exceptionText . '"'); | |||||
} | |||||
} | |||||
} |
<?php | |||||
/** | |||||
* @author Vincent Petry <pvince81@owncloud.com> | |||||
* | |||||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||||
* @license AGPL-3.0 | |||||
* | |||||
* This code is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License, version 3, | |||||
* as published by the Free Software Foundation. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License, version 3, | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||||
* | |||||
*/ | |||||
require __DIR__ . '/../../vendor/autoload.php'; | |||||
use Behat\Behat\Hook\Scope\BeforeScenarioScope; | |||||
class CommandLineContext implements \Behat\Behat\Context\Context { | |||||
use CommandLine; | |||||
private $lastTransferPath; | |||||
private $featureContext; | |||||
public function __construct($ocPath, $baseUrl) { | |||||
$this->ocPath = rtrim($ocPath, '/') . '/'; | |||||
$this->localBaseUrl = $baseUrl; | |||||
$this->remoteBaseUrl = $baseUrl; | |||||
} | |||||
/** @BeforeScenario */ | |||||
public function gatherContexts(BeforeScenarioScope $scope) { | |||||
$environment = $scope->getEnvironment(); | |||||
// this should really be "WebDavContext" ... | |||||
$this->featureContext = $environment->getContext('FeatureContext'); | |||||
} | |||||
private function findLastTransferFolderForUser($sourceUser, $targetUser) { | |||||
$foundPaths = []; | |||||
$results = $this->featureContext->listFolder($targetUser, '', 1); | |||||
foreach ($results as $path => $data) { | |||||
$path = rawurldecode($path); | |||||
$parts = explode(' ', $path); | |||||
if (basename($parts[0]) !== 'transferred') { | |||||
continue; | |||||
} | |||||
if (isset($parts[2]) && $parts[2] === $sourceUser) { | |||||
// store timestamp as key | |||||
$foundPaths[] = [ | |||||
'date' => strtotime(dirname($parts[4])), | |||||
'path' => $path, | |||||
]; | |||||
} | |||||
} | |||||
if (empty($foundPaths)) { | |||||
return null; | |||||
} | |||||
usort($foundPaths, function($a, $b) { | |||||
return $a['date'] - $b['date']; | |||||
}); | |||||
$davPath = rtrim($this->featureContext->getDavFilesPath($targetUser), '/'); | |||||
$foundPath = end($foundPaths)['path']; | |||||
// strip dav path | |||||
return substr($foundPath, strlen($davPath) + 1); | |||||
} | |||||
/** | |||||
* @When /^transfering ownership from "([^"]+)" to "([^"]+)"/ | |||||
*/ | |||||
public function transferingOwnership($user1, $user2) { | |||||
if ($this->runOcc(['files:transfer-ownership', $user1, $user2]) === 0) { | |||||
$this->lastTransferPath = $this->findLastTransferFolderForUser($user1, $user2); | |||||
} else { | |||||
// failure | |||||
$this->lastTransferPath = null; | |||||
} | |||||
} | |||||
/** | |||||
* @When /^using received transfer folder of "([^"]+)" as dav path$/ | |||||
*/ | |||||
public function usingTransferFolderAsDavPath($user) { | |||||
$davPath = $this->featureContext->getDavFilesPath($user); | |||||
$davPath = rtrim($davPath, '/') . $this->lastTransferPath; | |||||
$this->featureContext->usingDavPath($davPath); | |||||
} | |||||
} |
*/ | */ | ||||
public function userCreatedAFolder($user, $destination) { | public function userCreatedAFolder($user, $destination) { | ||||
try { | try { | ||||
$destination = '/' . ltrim($destination, '/'); | |||||
$this->response = $this->makeDavRequest($user, "MKCOL", $destination, []); | $this->response = $this->makeDavRequest($user, "MKCOL", $destination, []); | ||||
} catch (\GuzzleHttp\Exception\ServerException $e) { | } catch (\GuzzleHttp\Exception\ServerException $e) { | ||||
// 4xx and 5xx responses cause an exception | // 4xx and 5xx responses cause an exception |
Feature: transfer-ownership | |||||
Scenario: transfering ownership of a file | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And User "user0" uploads file "data/textfile.txt" to "/somefile.txt" | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user1" | |||||
And using received transfer folder of "user1" as dav path | |||||
Then Downloaded content when downloading file "/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership of a folder | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And User "user0" created a folder "/test" | |||||
And User "user0" uploads file "data/textfile.txt" to "/test/somefile.txt" | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user1" | |||||
And using received transfer folder of "user1" as dav path | |||||
Then Downloaded content when downloading file "/test/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership of file shares | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And User "user0" uploads file "data/textfile.txt" to "/somefile.txt" | |||||
And file "/somefile.txt" of user "user0" is shared with user "user2" with permissions 19 | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user2" | |||||
Then Downloaded content when downloading file "/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership of folder shared with third user | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And User "user0" created a folder "/test" | |||||
And User "user0" uploads file "data/textfile.txt" to "/test/somefile.txt" | |||||
And folder "/test" of user "user0" is shared with user "user2" with permissions 31 | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user2" | |||||
Then Downloaded content when downloading file "/test/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership of folder shared with transfer recipient | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And User "user0" created a folder "/test" | |||||
And User "user0" uploads file "data/textfile.txt" to "/test/somefile.txt" | |||||
And folder "/test" of user "user0" is shared with user "user1" with permissions 31 | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user1" | |||||
Then as "user1" the folder "/test" does not exist | |||||
And using received transfer folder of "user1" as dav path | |||||
And Downloaded content when downloading file "/test/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership of folder doubly shared with third user | |||||
Given group "group1" exists | |||||
And user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And user "user2" belongs to group "group1" | |||||
And User "user0" created a folder "/test" | |||||
And User "user0" uploads file "data/textfile.txt" to "/test/somefile.txt" | |||||
And folder "/test" of user "user0" is shared with group "group1" with permissions 31 | |||||
And folder "/test" of user "user0" is shared with user "user2" with permissions 31 | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user2" | |||||
Then Downloaded content when downloading file "/test/somefile.txt" with range "bytes=0-6" should be "This is" | |||||
Scenario: transfering ownership does not transfer received shares | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And User "user2" created a folder "/test" | |||||
And folder "/test" of user "user2" is shared with user "user0" with permissions 31 | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user1" | |||||
And using received transfer folder of "user1" as dav path | |||||
Then as "user1" the folder "/test" does not exist | |||||
@local_storage | |||||
Scenario: transfering ownership does not transfer external storage | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
When transfering ownership from "user0" to "user1" | |||||
And the command was successful | |||||
And As an "user1" | |||||
And using received transfer folder of "user1" as dav path | |||||
Then as "user1" the folder "/local_storage" does not exist | |||||
Scenario: transfering ownership does not fail with shared trashed files | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And User "user0" created a folder "/sub" | |||||
And User "user0" created a folder "/sub/test" | |||||
And folder "/sub/test" of user "user0" is shared with user "user2" with permissions 31 | |||||
And User "user0" deletes folder "/sub" | |||||
When transfering ownership from "user0" to "user1" | |||||
Then the command was successful | |||||
Scenario: transfering ownership fails with invalid source user | |||||
Given user "user0" exists | |||||
When transfering ownership from "invalid_user" to "user0" | |||||
Then the command error output contains the text "Unknown source user" | |||||
And the command failed with exit code 1 | |||||
Scenario: transfering ownership fails with invalid target user | |||||
Given user "user0" exists | |||||
When transfering ownership from "user0" to "invalid_user" | |||||
Then the command error output contains the text "Unknown target user" | |||||
And the command failed with exit code 1 | |||||
#!/usr/bin/env bash | #!/usr/bin/env bash | ||||
COMPOSER=$(which composer) | |||||
composer install | |||||
OC_PATH=../../ | |||||
OCC=${OC_PATH}occ | |||||
SCENARIO_TO_RUN=$1 | SCENARIO_TO_RUN=$1 | ||||
HIDE_OC_LOGS=$2 | HIDE_OC_LOGS=$2 | ||||
if [ -x "$COMPOSER" ]; then | |||||
echo "Using composer executable $COMPOSER" | |||||
else | |||||
echo "Could not find composer executable" >&2 | |||||
exit 1 | |||||
fi | |||||
INSTALLED=$(../../occ status | grep installed: | cut -d " " -f 5) | |||||
if [ "$INSTALLED" == "true" ]; then | |||||
# Disable bruteforce protection because the integration tests do trigger them | |||||
../../occ config:system:set auth.bruteforce.protection.enabled --value false --type bool | |||||
else | |||||
if [ "$SCENARIO_TO_RUN" != "setup_features/setup.feature" ]; then | |||||
echo "Nextcloud instance needs to be installed" >&2 | |||||
exit 1 | |||||
fi | |||||
fi | |||||
composer install | |||||
# avoid port collision on jenkins - use $EXECUTOR_NUMBER | # avoid port collision on jenkins - use $EXECUTOR_NUMBER | ||||
if [ -z "$EXECUTOR_NUMBER" ]; then | if [ -z "$EXECUTOR_NUMBER" ]; then | ||||
EXECUTOR_NUMBER=0 | EXECUTOR_NUMBER=0 | ||||
export TEST_SERVER_URL="http://localhost:$PORT/ocs/" | export TEST_SERVER_URL="http://localhost:$PORT/ocs/" | ||||
export TEST_SERVER_FED_URL="http://localhost:$PORT_FED/ocs/" | export TEST_SERVER_FED_URL="http://localhost:$PORT_FED/ocs/" | ||||
if [ "$INSTALLED" == "true" ]; then | |||||
#Enable external storage app | |||||
../../occ app:enable files_external | |||||
#Enable external storage app | |||||
$OCC app:enable files_external | |||||
mkdir -p work/local_storage | |||||
OUTPUT_CREATE_STORAGE=`../../occ files_external:create local_storage local null::null -c datadir=./build/integration/work/local_storage` | |||||
mkdir -p work/local_storage | |||||
OUTPUT_CREATE_STORAGE=`$OCC files_external:create local_storage local null::null -c datadir=./build/integration/work/local_storage` | |||||
ID_STORAGE=`echo $OUTPUT_CREATE_STORAGE | awk {'print $5'}` | |||||
ID_STORAGE=`echo $OUTPUT_CREATE_STORAGE | awk {'print $5'}` | |||||
../../occ files_external:option $ID_STORAGE enable_sharing true | |||||
fi | |||||
$OCC files_external:option $ID_STORAGE enable_sharing true | |||||
vendor/bin/behat -f junit -f pretty $SCENARIO_TO_RUN | |||||
vendor/bin/behat --strict -f junit -f pretty $SCENARIO_TO_RUN | |||||
RESULT=$? | RESULT=$? | ||||
kill $PHPPID | kill $PHPPID | ||||
kill $PHPPID_FED | kill $PHPPID_FED | ||||
if [ "$INSTALLED" -eq "true" ]; then | |||||
../../occ files_external:delete -y $ID_STORAGE | |||||
$OCC files_external:delete -y $ID_STORAGE | |||||
#Disable external storage app | |||||
../../occ app:disable files_external | |||||
fi | |||||
#Disable external storage app | |||||
$OCC app:disable files_external | |||||
if [ -z $HIDE_OC_LOGS ]; then | if [ -z $HIDE_OC_LOGS ]; then | ||||
tail "../../data/nextcloud.log" | |||||
tail "${OC_PATH}/data/owncloud.log" | |||||
fi | fi | ||||
echo "runsh: Exit code: $RESULT" | |||||
exit $RESULT | exit $RESULT | ||||