fix: get test suites for core api tests

remove oc10 specific test suites

provide behat config with make command

fix typo

add missing helpers
This commit is contained in:
Saw-jan
2023-01-05 09:23:30 +05:45
committed by Phil Davis
parent 048557712e
commit 42fb4a68e4
168 changed files with 2058 additions and 21135 deletions
@@ -0,0 +1,617 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use TestHelpers\LoggingHelper;
use TestHelpers\OcisHelper;
use TestHelpers\SetupHelper;
require_once 'bootstrap.php';
/**
* Context to make the Logging steps available
*/
class LoggingContext implements Context {
/**
* @var FeatureContext
*/
private $featureContext;
private $oldLogLevel = null;
private $oldLogBackend = null;
private $oldLogTimezone = null;
/**
* checks for specific rows in the log file.
* order of the table has to be the same as in the log file
* empty cells in the table will not be checked!
*
* @Then /^the last lines of the log file should contain log-entries (with|containing|matching) these attributes:$/
*
* @param string $comparingMode
* @param int $ignoredLines
* @param TableNode|null $expectedLogEntries table with headings that correspond
* to the json keys in the log entry
* e.g.
* |user|app|method|message|
*
* @return void
* @throws Exception
*/
public function theLastLinesOfTheLogFileShouldContainEntriesWithTheseAttributes(
string $comparingMode,
int $ignoredLines = 0,
?TableNode $expectedLogEntries = null
):void {
if (OcisHelper::isTestingOnOcisOrReva()) {
// Currently we don't interact with the log file on reva or OCIS
// So skip processing this test step.
return;
}
$ignoredLines = (int) $ignoredLines;
//-1 because getRows gives also the header
$linesToRead = \count($expectedLogEntries->getRows()) - 1 + $ignoredLines;
$logLines = LoggingHelper::getLogFileContent(
$this->featureContext->getBaseUrl(),
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getStepLineRef(),
$linesToRead
);
$lineNo = 0;
foreach ($expectedLogEntries as $expectedLogEntry) {
$logEntry = \json_decode($logLines[$lineNo], true);
if ($logEntry === null) {
throw new Exception("the log line :\n{$logLines[$lineNo]} is not valid JSON");
}
foreach (\array_keys($expectedLogEntry) as $attribute) {
if ($comparingMode === 'matching') {
$expectedLogEntry[$attribute]
= $this->featureContext->substituteInLineCodes(
$expectedLogEntry[$attribute],
null,
['preg_quote' => ['/']]
);
} else {
$expectedLogEntry[$attribute]
= $this->featureContext->substituteInLineCodes(
$expectedLogEntry[$attribute]
);
}
if ($expectedLogEntry[$attribute] !== "") {
Assert::assertArrayHasKey(
$attribute,
$logEntry,
"could not find attribute: '$attribute' in log entry: '{$logLines[$lineNo]}'"
);
$message = "log entry:\n{$logLines[$lineNo]}\n";
if (!\is_string($logEntry[$attribute])) {
$logEntry[$attribute] = \json_encode(
$logEntry[$attribute],
JSON_UNESCAPED_SLASHES
);
}
if ($comparingMode === 'with') {
Assert::assertEquals(
$expectedLogEntry[$attribute],
$logEntry[$attribute],
$message
);
} elseif ($comparingMode === 'containing') {
Assert::assertStringContainsString(
$expectedLogEntry[$attribute],
$logEntry[$attribute],
$message
);
} elseif ($comparingMode === 'matching') {
Assert::assertMatchesRegularExpression(
$expectedLogEntry[$attribute],
$logEntry[$attribute],
$message
);
} else {
throw new \InvalidArgumentException(
"$comparingMode is not a valid mode"
);
}
}
}
$lineNo++;
if (($lineNo + $ignoredLines) >= $linesToRead) {
break;
}
}
}
/**
* alternative wording theLastLinesOfTheLogFileShouldContainEntriesWithTheseAttributes()
*
* @Then /^the last lines of the log file, ignoring the last (\d+) lines, should contain log-entries (with|containing|matching) these attributes:$/
*
* @param int $ignoredLines
* @param string $comparingMode
* @param TableNode $expectedLogEntries
*
* @return void
* @throws Exception
*/
public function theLastLinesOfTheLogFileIgnoringSomeShouldContainEntries(
int $ignoredLines,
string $comparingMode,
TableNode $expectedLogEntries
):void {
$this->theLastLinesOfTheLogFileShouldContainEntriesWithTheseAttributes(
$comparingMode,
$ignoredLines,
$expectedLogEntries
);
}
/**
* alternative wording theLastLinesOfTheLogFileShouldContainEntriesWithTheseAttributes()
*
* @Then /^the last lines of the log file, ignoring the last line, should contain log-entries (with|containing|matching) these attributes:$/
*
* @param string $comparingMode
* @param TableNode $expectedLogEntries
*
* @return void
* @throws Exception
*/
public function theLastLinesOfTheLogFileIgnoringLastShouldContainEntries(
string $comparingMode,
TableNode $expectedLogEntries
):void {
$this->theLastLinesOfTheLogFileShouldContainEntriesWithTheseAttributes(
$comparingMode,
1,
$expectedLogEntries
);
}
/**
* wrapper around assertLogFileContainsAtLeastOneEntryMatchingTable()
*
* @Then the log file should contain at least one entry matching each of these lines:
*
* @param TableNode $expectedLogEntries table with headings that correspond
* to the json keys in the log entry
* e.g.
* |user|app|method|message|
*
* @return void
* @throws Exception
* @see assertLogFileContainsAtLeastOneEntryMatchingTable()
*/
public function logFileShouldContainEntriesMatching(
TableNode $expectedLogEntries
):void {
$this->assertLogFileContainsAtLeastOneEntryMatchingTable(
true,
$expectedLogEntries
);
}
/**
* wrapper around assertLogFileContainsAtLeastOneEntryMatchingTable()
*
* @Then the log file should contain at least one entry matching the regular expressions in each of these lines:
*
* @param TableNode $expectedLogEntries
*
* @return void
* @throws Exception
* @see assertLogFileContainsAtLeastOneEntryMatchingTable()
*/
public function logFileShouldContainEntriesMatchingRegularExpression(
TableNode $expectedLogEntries
):void {
$this->assertLogFileContainsAtLeastOneEntryMatchingTable(
true,
$expectedLogEntries,
true
);
}
/**
* @Then the log file should not contain any entry matching the regular expressions in each of these lines:
*
* @param TableNode $expectedLogEntries
*
* @return void
* @throws Exception
*/
public function logFileShouldNotContainAnyTheEntriesMatchingTheRegularExpression(
TableNode $expectedLogEntries
):void {
$this->assertLogFileContainsAtLeastOneEntryMatchingTable(
false,
$expectedLogEntries,
true
);
}
/**
* checks that every line in the table has at least one
* corresponding line in the log file
* empty cells in the table will not be checked!
*
* @param boolean $shouldOrNot if true the table entries are expected to match
* at least one entry in the log
* if false the table entries are expected not
* to match any log in the log file
* @param TableNode $expectedLogEntries table with headings that correspond
* to the json keys in the log entry
* e.g.
* |user|app|method|message|
* @param boolean $regexCompare if true the table entries are expected
* to be regular expressions
*
* @return void
* @throws Exception
*/
private function assertLogFileContainsAtLeastOneEntryMatchingTable(
bool $shouldOrNot,
TableNode $expectedLogEntries,
bool $regexCompare = false
):void {
if (OcisHelper::isTestingOnOcisOrReva()) {
// Currently we don't interact with the log file on reva or OCIS
// So skip processing this test step.
return;
}
$logLines = LoggingHelper::getLogFileContent(
$this->featureContext->getBaseUrl(),
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getStepLineRef()
);
$expectedLogEntries = $expectedLogEntries->getHash();
foreach ($logLines as $logLine) {
$logEntry = \json_decode($logLine, true);
if ($logEntry === null) {
throw new Exception("the log line :\n{$logLine} is not valid JSON");
}
//reindex the array, we might have deleted entries
$expectedLogEntries = \array_values($expectedLogEntries);
for ($entryNo = 0; $entryNo < \count($expectedLogEntries); $entryNo++) {
$count = 0;
$expectedLogEntry = $expectedLogEntries[$entryNo];
$foundLine = true;
foreach (\array_keys($expectedLogEntry) as $attribute) {
if ($expectedLogEntry[$attribute] === "") {
//don't check empty table entries
continue;
}
if (!\array_key_exists($attribute, $logEntry)) {
//this line does not have the attribute we are looking for
$foundLine = false;
break;
}
if (!\is_string($logEntry[$attribute])) {
$logEntry[$attribute] = \json_encode(
$logEntry[$attribute],
JSON_UNESCAPED_SLASHES
);
}
if ($regexCompare === true) {
$expectedLogEntry[$attribute]
= $this->featureContext->substituteInLineCodes(
$expectedLogEntry[$attribute],
null,
['preg_quote' => ['/']]
);
$matchAttribute = \preg_match(
$expectedLogEntry[$attribute],
$logEntry[$attribute]
);
} else {
$expectedLogEntry[$attribute]
= $this->featureContext->substituteInLineCodes(
$expectedLogEntry[$attribute]
);
$matchAttribute
= ($expectedLogEntry[$attribute] === $logEntry[$attribute]);
}
if (!$matchAttribute) {
$foundLine = false;
break;
}
if ($matchAttribute and !$shouldOrNot) {
$count += 1;
Assert::assertNotEquals(
$count,
\count($expectedLogEntry),
"The entry matches"
);
}
}
if ($foundLine === true) {
unset($expectedLogEntries[$entryNo]);
}
}
}
$notFoundLines = \print_r($expectedLogEntries, true);
if ($shouldOrNot) {
Assert::assertEmpty(
$expectedLogEntries,
"could not find these expected line(s):\n $notFoundLines"
);
}
}
/**
* fails if there is at least one line in the log file that matches all
* given attributes
* attributes in the table that are empty will match any value in the
* corresponding attribute in the log file
*
* @Then /^the log file should not contain any log-entries (with|containing) these attributes:$/
*
* @param string $withOrContaining
* @param TableNode $logEntriesExpectedNotToExist table with headings that
* correspond to the json
* keys in the log entry
* e.g.
* |user|app|method|message|
*
* @return void
* @throws Exception
*/
public function theLogFileShouldNotContainAnyLogEntriesWithTheseAttributes(
$withOrContaining,
TableNode $logEntriesExpectedNotToExist
):void {
if (OcisHelper::isTestingOnOcisOrReva()) {
// Currently we don't interact with the log file on reva or OCIS
// So skip processing this test step.
return;
}
$logLines = LoggingHelper::getLogFileContent(
$this->featureContext->getBaseUrl(),
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getStepLineRef()
);
foreach ($logLines as $logLine) {
$logEntry = \json_decode($logLine, true);
if ($logEntry === null) {
throw new Exception("the log line :\n$logLine is not valid JSON");
}
foreach ($logEntriesExpectedNotToExist as $logEntryExpectedNotToExist) {
$match = true; // start by assuming the worst, we match the unwanted log entry
foreach (\array_keys($logEntryExpectedNotToExist) as $attribute) {
$logEntryExpectedNotToExist[$attribute]
= $this->featureContext->substituteInLineCodes(
$logEntryExpectedNotToExist[$attribute]
);
if (isset($logEntry[$attribute]) && ($logEntryExpectedNotToExist[$attribute] !== "")) {
if ($withOrContaining === 'with') {
$match = ($logEntryExpectedNotToExist[$attribute] === $logEntry[$attribute]);
} else {
$match = (\strpos($logEntry[$attribute], $logEntryExpectedNotToExist[$attribute]) !== false);
}
}
if (!isset($logEntry[$attribute])) {
$match = false;
}
if (!$match) {
break;
}
}
}
Assert::assertFalse(
$match,
"found a log entry that should not be there\n$logLine\n"
);
}
}
/**
* @When the owncloud log level is set to :logLevel
*
* @param string $logLevel (debug|info|warning|error|fatal)
*
* @return void
* @throws Exception
*/
public function owncloudLogLevelIsSetTo(string $logLevel):void {
LoggingHelper::setLogLevel(
$logLevel,
$this->featureContext->getStepLineRef()
);
}
/**
* @Given the owncloud log level has been set to :logLevel
*
* @param string $logLevel (debug|info|warning|error|fatal)
*
* @return void
* @throws Exception
*/
public function owncloudLogLevelHasBeenSetTo(string $logLevel):void {
$this->owncloudLogLevelIsSetTo($logLevel);
$logLevelArray = LoggingHelper::LOG_LEVEL_ARRAY;
$logLevelExpected = \array_search($logLevel, $logLevelArray);
$logLevelActual = \array_search(
LoggingHelper::getLogLevel(
$this->featureContext->getStepLineRef()
),
$logLevelArray
);
Assert::assertEquals(
$logLevelExpected,
$logLevelActual,
"The expected log level is {$logLevelExpected} but the log level has been set to {$logLevelActual}"
);
}
/**
* @When the owncloud log backend is set to :backend
*
* @param string $backend (owncloud|syslog|errorlog)
*
* @return void
* @throws Exception
*/
public function owncloudLogBackendIsSetTo(string $backend):void {
LoggingHelper::setLogBackend(
$backend,
$this->featureContext->getStepLineRef()
);
}
/**
* @Given the owncloud log backend has been set to :backend
*
* @param string $expectedBackend (owncloud|syslog|errorlog)
*
* @return void
* @throws Exception
*/
public function owncloudLogBackendHasBeenSetTo(string $expectedBackend):void {
$this->owncloudLogBackendIsSetTo($expectedBackend);
$currentBackend = LoggingHelper::getLogBackend(
$this->featureContext->getStepLineRef()
);
Assert::assertEquals(
$expectedBackend,
$currentBackend,
"The owncloud log backend was expected to be set to {$expectedBackend} but got {$currentBackend}"
);
}
/**
* @When the owncloud log timezone is set to :timezone
*
* @param string $timezone
*
* @return void
* @throws Exception
*/
public function owncloudLogTimezoneIsSetTo(string $timezone):void {
LoggingHelper::setLogTimezone(
$timezone,
$this->featureContext->getStepLineRef()
);
}
/**
* @Given the owncloud log timezone has been set to :timezone
*
* @param string $expectedTimezone
*
* @return void
* @throws Exception
*/
public function owncloudLogTimezoneHasBeenSetTo(string $expectedTimezone):void {
$this->owncloudLogTimezoneIsSetTo($expectedTimezone);
$currentTimezone = LoggingHelper::getLogTimezone(
$this->featureContext->getStepLineRef()
);
Assert::assertEquals(
$expectedTimezone,
$currentTimezone,
"The owncloud log timezone was expected to be set to {$expectedTimezone}, but got {$currentTimezone}"
);
}
/**
* @When the owncloud log is cleared
* @Given the owncloud log has been cleared
*
* checks for the httpRequest is done inside clearLogFile function
*
* @return void
* @throws Exception
*/
public function theOwncloudLogIsCleared():void {
LoggingHelper::clearLogFile(
$this->featureContext->getBaseUrl(),
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getStepLineRef()
);
}
/**
* After Scenario for logging. Sets back old log settings
*
* @AfterScenario
*
* @return void
* @throws Exception
*/
public function tearDownScenarioLogging():void {
LoggingHelper::restoreLoggingStatus(
$this->oldLogLevel,
$this->oldLogBackend,
$this->oldLogTimezone,
$this->featureContext->getStepLineRef()
);
}
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function setUpScenario(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
SetupHelper::init(
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getBaseUrl(),
$this->featureContext->getOcPath()
);
}
/**
* Before Scenario for logging. Saves current log settings
*
* @BeforeScenario
*
* @return void
* @throws Exception
*/
public function setUpScenarioLogging():void {
$logging = LoggingHelper::getLogInfo(
$this->featureContext->getStepLineRef()
);
$this->oldLogLevel = $logging["level"];
$this->oldLogBackend = $logging["backend"];
$this->oldLogTimezone = $logging["timezone"];
}
}
@@ -0,0 +1,231 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Joas Schilling <coding@schilljs.com>
* @author Sergio Bertolin <sbertolin@owncloud.com>
* @author Phillip Davis <phil@jankaritech.com>
* @copyright Copyright (c) 2018, ownCloud GmbH
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use Psr\Http\Message\ResponseInterface;
use PHPUnit\Framework\Assert;
require_once 'bootstrap.php';
/**
* Sharees context.
*/
class ShareesContext implements Context {
/**
*
* @var FeatureContext
*/
private $featureContext;
/**
*
* @var OCSContext
*/
private $ocsContext;
/**
* @When /^the user gets the sharees using the sharing API with parameters$/
*
* @param TableNode $body
*
* @return void
*/
public function theUserGetsTheShareesWithParameters(TableNode $body):void {
$this->userGetsTheShareesWithParameters(
$this->featureContext->getCurrentUser(),
$body
);
}
/**
* @When /^user "([^"]*)" gets the sharees using the sharing API with parameters$/
*
* @param string $user
* @param TableNode $body
*
* @return void
* @throws Exception
*/
public function userGetsTheShareesWithParameters(string $user, TableNode $body):void {
$user = $this->featureContext->getActualUsername($user);
$url = '/apps/files_sharing/api/v1/sharees';
$this->featureContext->verifyTableNodeColumnsCount($body, 2);
if ($body instanceof TableNode) {
$parameters = [];
foreach ($body->getRowsHash() as $key => $value) {
$parameters[] = "$key=$value";
}
if (!empty($parameters)) {
$url .= '?' . \implode('&', $parameters);
}
}
$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
$user,
'GET',
$url,
null
);
}
/**
* @Then /^the "([^"]*)" sharees returned should be$/
*
* @param string $shareeType
* @param TableNode $shareesList
*
* @return void
* @throws Exception
*/
public function theShareesReturnedShouldBe(string $shareeType, TableNode $shareesList):void {
$this->featureContext->verifyTableNodeColumnsCount($shareesList, 3);
$sharees = $shareesList->getRows();
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
Assert::assertEquals(
$sharees,
$respondedArray,
"Returned sharees do not match the expected ones. See the differences below."
);
}
/**
* @Then /^the "([^"]*)" sharees returned should include$/
*
* @param string $shareeType
* @param TableNode $shareesList
*
* @return void
* @throws Exception
*/
public function theShareesReturnedShouldInclude(string $shareeType, TableNode $shareesList):void {
$this->featureContext->verifyTableNodeColumnsCount($shareesList, 3);
$sharees = $shareesList->getRows();
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
foreach ($sharees as $sharee) {
Assert::assertContains(
$sharee,
$respondedArray,
"Returned sharees do not match the expected ones. See the differences below."
);
}
}
/**
* @Then /^the "([^"]*)" sharees returned should be empty$/
*
* @param string $shareeType
*
* @return void
*/
public function theShareesReturnedShouldBeEmpty(string $shareeType):void {
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
if (isset($respondedArray[0])) {
// [0] is display name and [2] is user or group id
$firstEntry = $respondedArray[0][0] . " (" . $respondedArray[0][2] . ")";
} else {
$firstEntry = "";
}
Assert::assertEmpty(
$respondedArray,
"'$shareeType' array should be empty, but it starts with $firstEntry"
);
}
/**
* @param ResponseInterface $response
* @param string $shareeType
*
* @return array
* @throws Exception
*/
public function getArrayOfShareesResponded(
ResponseInterface $response,
string $shareeType
):array {
$elements = $this->featureContext->getResponseXml($response, __METHOD__)->data;
$elements = \json_decode(\json_encode($elements), true);
if (\strpos($shareeType, 'exact ') === 0) {
$elements = $elements['exact'];
$shareeType = \substr($shareeType, 6);
}
Assert::assertArrayHasKey(
$shareeType,
$elements,
__METHOD__ . " The sharees response does not have key '$shareeType'"
);
$sharees = [];
foreach ($elements[$shareeType] as $element) {
if (\is_int(\key($element))) {
// this is a list of elements instead of just one item,
// so return the list
foreach ($element as $innerItem) {
$sharees[] = [
$innerItem['label'],
$innerItem['value']['shareType'],
$innerItem['value']['shareWith']
];
}
} else {
$sharees[] = [
$element['label'],
$element['value']['shareType'],
$element['value']['shareWith']
];
}
}
return $sharees;
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->ocsContext = $environment->getContext('OCSContext');
}
}
@@ -0,0 +1,700 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use PHPUnit\Framework\Assert;
use TestHelpers\HttpRequestHelper;
use TestHelpers\OcsApiHelper;
use TestHelpers\WebDavHelper;
require_once 'bootstrap.php';
/**
* context containing API steps needed for the locking mechanism of webdav
*/
class WebDavLockingContext implements Context {
/**
*
* @var FeatureContext
*/
private $featureContext;
/**
*
* @var PublicWebDavContext
*/
private $publicWebDavContext;
/**
*
* @var string[][]
*/
private $tokenOfLastLock = [];
/**
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
* @param boolean $public if the file is in a public share or not
* @param boolean $expectToSucceed
*
* @return void
*/
private function lockFile(
$user,
$file,
TableNode $properties,
$public = false,
$expectToSucceed = true
) {
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
if ($public === true) {
$type = "public-files";
$password = null;
} else {
$type = "files";
$password = $this->featureContext->getPasswordForUser($user);
}
$body
= "<?xml version='1.0' encoding='UTF-8'?>" .
"<d:lockinfo xmlns:d='DAV:'> ";
$headers = [];
$this->featureContext->verifyTableNodeRows($properties, [], ['lockscope', 'depth', 'timeout']);
$propertiesRows = $properties->getRowsHash();
foreach ($propertiesRows as $property => $value) {
if ($property === "depth" || $property === "timeout") {
//properties that are set in the header not in the xml
$headers[$property] = $value;
} else {
$body .= "<d:$property><d:$value/></d:$property>";
}
}
$body .= "</d:lockinfo>";
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"LOCK",
$file,
$headers,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion(),
$type
);
$this->featureContext->setResponse($response);
$responseXml = $this->featureContext->getResponseXml(null, __METHOD__);
$this->featureContext->setResponseXmlObject($responseXml);
$xmlPart = $responseXml->xpath("//d:locktoken/d:href");
if (isset($xmlPart[0])) {
$this->tokenOfLastLock[$user][$file] = (string) $xmlPart[0];
} else {
if ($expectToSucceed === true) {
Assert::fail("could not find lock token after trying to lock '$file'");
}
}
}
/**
* @When user :user locks file/folder :file using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function lockFileUsingWebDavAPI($user, $file, TableNode $properties) {
$this->lockFile($user, $file, $properties, false, false);
}
/**
* @Given user :user has locked file/folder :file setting the following properties
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userHasLockedFile($user, $file, TableNode $properties) {
$this->lockFile($user, $file, $properties, false, true);
}
/**
* @Given the public has locked the last public link shared file/folder setting the following properties
*
* @param TableNode $properties
*
* @return void
*/
public function publicHasLockedLastSharedFile(TableNode $properties) {
$this->lockFile(
$this->featureContext->getLastPublicShareToken(),
"/",
$properties,
true
);
}
/**
* @When the public locks the last public link shared file/folder using the WebDAV API setting the following properties
*
* @param TableNode $properties
*
* @return void
*/
public function publicLocksLastSharedFile(TableNode $properties) {
$this->lockFile(
$this->featureContext->getLastPublicShareToken(),
"/",
$properties,
true,
false
);
}
/**
* @Given the public has locked :file in the last public link shared folder setting the following properties
*
* @param string $file
* @param TableNode $properties
*
* @return void
*/
public function publicHasLockedFileLastSharedFolder(
$file,
TableNode $properties
) {
$this->lockFile(
$this->featureContext->getLastPublicShareToken(),
$file,
$properties,
true
);
}
/**
* @When /^the public locks "([^"]*)" in the last public link shared folder using the (old|new) public WebDAV API setting the following properties$/
*
* @param string $file
* @param string $publicWebDAVAPIVersion
* @param TableNode $properties
*
* @return void
*/
public function publicLocksFileLastSharedFolder(
$file,
$publicWebDAVAPIVersion,
TableNode $properties
) {
$this->lockFile(
$this->featureContext->getLastPublicShareToken(),
$file,
$properties,
true,
false
);
}
/**
* @When user :user unlocks the last created lock of file/folder :file using the WebDAV API
*
* @param string $user
* @param string $file
*
* @return void
*/
public function unlockLastLockUsingWebDavAPI($user, $file) {
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$file,
$user,
$file
);
}
/**
* @When user :user unlocks file/folder :itemToUnlock with the last created lock of file/folder :itemToUseLockOf using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemWithLastLockOfOtherItemUsingWebDavAPI(
$user,
$itemToUnlock,
$itemToUseLockOf
) {
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$user,
$itemToUseLockOf
);
}
/**
* @When user :user unlocks file/folder :itemToUnlock with the last created public lock of file/folder :itemToUseLockOf using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemWithLastPublicLockOfOtherItemUsingWebDavAPI(
$user,
$itemToUnlock,
$itemToUseLockOf
) {
$lockOwner = $this->featureContext->getLastPublicShareToken();
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf
);
}
/**
*
* @param string $user
* @param string $itemToUnlock
*
* @return int|void
*
* @throws Exception|GuzzleException
*/
private function countLockOfResources(
string $user,
string $itemToUnlock
) {
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
$body
= "<?xml version='1.0' encoding='UTF-8'?>" .
"<d:propfind xmlns:d='DAV:'> " .
"<d:prop><d:lockdiscovery/></d:prop>" .
"</d:propfind>";
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"PROPFIND",
$itemToUnlock,
null,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion()
);
$responseXml = $this->featureContext->getResponseXml($response, __METHOD__);
$xmlPart = $responseXml->xpath("//d:response//d:lockdiscovery/d:activelock");
if (\is_array($xmlPart)) {
return \count($xmlPart);
} else {
throw new Exception("xmlPart for 'd:activelock' was expected to be array but found: $xmlPart");
}
}
/**
* @Given user :user has unlocked file/folder :itemToUnlock with the last created lock of file/folder :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
* @param boolean $public
*
* @return void
* @throws Exception|GuzzleException
*/
public function hasUnlockItemWithTheLastCreatedLock(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf,
$public = false
) {
$lockCount = $this->countLockOfResources($user, $itemToUnlock);
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf,
$public
);
$this->featureContext->theHTTPStatusCodeShouldBe(204);
$this->numberOfLockShouldBeReported($lockCount - 1, $itemToUnlock, $user);
}
/**
* @When user :user unlocks file/folder :itemToUnlock with the last created lock of file/folder :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
* @param boolean $public
*
* @return void
*/
public function unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
string $user,
string $itemToUnlock,
string $lockOwner,
string $itemToUseLockOf,
bool $public = false
) {
$user = $this->featureContext->getActualUsername($user);
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
if ($public === true) {
$type = "public-files";
$password = null;
} else {
$type = "files";
$password = $this->featureContext->getPasswordForUser($user);
}
$baseUrl = $this->featureContext->getBaseUrl();
if (!isset($this->tokenOfLastLock[$lockOwner][$itemToUseLockOf])) {
Assert::fail(
"could not find saved token of '$itemToUseLockOf' " .
"owned by user '$lockOwner'"
);
}
$headers = [
"Lock-Token" => $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf]
];
$this->featureContext->setResponse(
WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"UNLOCK",
$itemToUnlock,
$headers,
$this->featureContext->getStepLineRef(),
null,
$this->featureContext->getDavPathVersion(),
$type
)
);
$this->featureContext->pushToLastStatusCodesArrays();
}
/**
* @When the public unlocks file/folder :itemToUnlock with the last created lock of file/folder :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemAsPublicWithLastLockOfUserAndItemUsingWebDavAPI(
$itemToUnlock,
$lockOwner,
$itemToUseLockOf
) {
$user = $this->featureContext->getLastPublicShareToken();
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf,
true
);
}
/**
* @When the public unlocks file/folder :itemToUnlock using the WebDAV API
*
* @param string $itemToUnlock
*
* @return void
*/
public function unlockItemAsPublicUsingWebDavAPI($itemToUnlock) {
$user = $this->featureContext->getLastPublicShareToken();
$this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$user,
$itemToUnlock,
true
);
}
/**
* @When /^user "([^"]*)" moves (?:file|folder|entry) "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
* @param string $itemToUseLockOf
*
* @return void
*/
public function moveItemSendingLockToken(
$user,
$fileSource,
$fileDestination,
$itemToUseLockOf
) {
$this->moveItemSendingLockTokenOfUser(
$user,
$fileSource,
$fileDestination,
$itemToUseLockOf,
$user
);
}
/**
* @When /^user "([^"]*)" moves (?:file|folder|entry) "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" of user "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
* @param string $itemToUseLockOf
* @param string $lockOwner
*
* @return void
*/
public function moveItemSendingLockTokenOfUser(
$user,
$fileSource,
$fileDestination,
$itemToUseLockOf,
$lockOwner
) {
$user = $this->featureContext->getActualUsername($user);
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
$destination = $this->featureContext->destinationHeaderValue(
$user,
$fileDestination
);
$token = $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf];
$headers = [
"Destination" => $destination,
"If" => "(<$token>)"
];
try {
$response = $this->featureContext->makeDavRequest(
$user,
"MOVE",
$fileSource,
$headers
);
$this->featureContext->setResponse($response);
} catch (ConnectException $e) {
}
}
/**
* @When /^user "([^"]*)" uploads file with content "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $content
* @param string $destination
* @param string $itemToUseLockOf
*
* @return void
*/
public function userUploadsAFileWithContentTo(
$user,
$content,
$destination,
$itemToUseLockOf
) {
$user = $this->featureContext->getActualUsername($user);
$token = $this->tokenOfLastLock[$user][$itemToUseLockOf];
$this->featureContext->pauseUploadDelete();
$response = $this->featureContext->makeDavRequest(
$user,
"PUT",
$destination,
["If" => "(<$token>)"],
$content
);
$this->featureContext->setResponse($response);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @When /^the public uploads file "([^"]*)" with content "([^"]*)" sending the locktoken of file "([^"]*)" of user "([^"]*)" using the (old|new) public WebDAV API$/
*
* @param string $filename
* @param string $content
* @param string $itemToUseLockOf
* @param string $lockOwner
* @param string $publicWebDAVAPIVersion
*
* @return void
*
*/
public function publicUploadFileSendingLockTokenOfUser(
$filename,
$content,
$itemToUseLockOf,
$lockOwner,
$publicWebDAVAPIVersion
) {
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
$headers = [
"If" => "(<" . $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf] . ">)"
];
$this->publicWebDavContext->publicUploadContent(
$filename,
'',
$content,
false,
$headers,
$publicWebDAVAPIVersion
);
}
/**
* @When /^the public uploads file "([^"]*)" with content "([^"]*)" sending the locktoken of "([^"]*)" of the public using the (old|new) public WebDAV API$/
*
* @param string $filename
* @param string $content
* @param string $itemToUseLockOf
* @param string $publicWebDAVAPIVersion
*
* @return void
*/
public function publicUploadFileSendingLockTokenOfPublic(
$filename,
$content,
$itemToUseLockOf,
$publicWebDAVAPIVersion
) {
$lockOwner = $this->featureContext->getLastPublicShareToken();
$this->publicUploadFileSendingLockTokenOfUser(
$filename,
$content,
$itemToUseLockOf,
$lockOwner,
$publicWebDAVAPIVersion
);
}
/**
* @Then :count locks should be reported for file/folder :file of user :user by the WebDAV API
*
* @param int $count
* @param string $file
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function numberOfLockShouldBeReported($count, $file, $user) {
$lockCount = $this->countLockOfResources($user, $file);
Assert::assertEquals(
$count,
$lockCount,
"Expected $count lock(s) for '$file' but found '$lockCount'"
);
}
/**
* @Then group :expectedGroup should exist as a lock breaker group
*
* @param string $expectedGroup
*
* @return void
*
* @throws Exception
*/
public function groupShouldExistAsLockBreakerGroups($expectedGroup) {
$baseUrl = $this->featureContext->getBaseUrl();
$admin = $this->featureContext->getAdminUsername();
$password = $this->featureContext->getAdminPassword();
$ocsApiVersion = $this->featureContext->getOcsApiVersion();
$response = OcsApiHelper::sendRequest(
$baseUrl,
$admin,
$password,
'GET',
"/apps/testing/api/v1/app/core/lock-breaker-groups",
(string) $ocsApiVersion
);
$responseXml = HttpRequestHelper::getResponseXml($response, __METHOD__)->data->element;
$lockbreakergroup = trim(\json_decode(\json_encode($responseXml), true)['value'], '\'[]"');
$actualgroup = explode("\",\"", $lockbreakergroup);
if (!\in_array($expectedGroup, $actualgroup)) {
Assert::fail("could not find group '$expectedGroup' in lock breakers group");
}
}
/**
* @Then following groups should exist as lock breaker groups
*
* @param TableNode $table
*
* @return void
*
* @throws Exception
*/
public function followingGroupShouldExistAsLockBreakerGroups(TableNode $table) {
$this->featureContext->verifyTableNodeColumns($table, ["groups"]);
$paths = $table->getHash();
foreach ($paths as $group) {
$this->groupShouldExistAsLockBreakerGroups($group["groups"]);
}
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope) {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->publicWebDavContext = $environment->getContext('PublicWebDavContext');
}
}