Files
opencloud/tests/acceptance/features/bootstrap/TrashbinContext.php
Karun Atreya 46fde6240a [tests-only][full-ci] Backport refactoring webdav context removing setresponse() in given/then step (#7480)
* refactoring webdav context (setting response in when step only)

refactoring the given steps in the webdav context

refactored remaining given/when/then steps

return file id

trim trailing from the folder path

add missing when step and make other minor corrections

set responseXml and correct status code check

correct the naming of steps and helper functions

* setting response
2023-10-13 16:50:56 +05:45

1140 lines
34 KiB
PHP

<?php declare(strict_types=1);
/**
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @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/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\HttpRequestHelper;
use TestHelpers\WebDavHelper;
require_once 'bootstrap.php';
/**
* Trashbin context
*/
class TrashbinContext implements Context {
private FeatureContext $featureContext;
/**
* @param string|null $user user
*
* @return ResponseInterface
*/
public function emptyTrashbin(?string $user):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$davPathVersion = $this->featureContext->getDavPathVersion();
return WebDavHelper::makeDavRequest(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
'DELETE',
null,
[],
$this->featureContext->getStepLineRef(),
null,
$davPathVersion,
'trash-bin'
);
}
/**
* @When user :user empties the trashbin using the trashbin API
*
* @param string $user user
*
* @return void
*/
public function userEmptiesTrashbin(string $user): void {
$this->featureContext->setResponse($this->emptyTrashbin($user));
}
/**
* @Given user :user has emptied the trashbin
*
* @param string $user user
*
* @return void
*/
public function userHasEmptiedTrashbin(string $user):void {
$response = $this->emptyTrashbin($user);
$this->featureContext->theHTTPStatusCodeShouldBe(204, '', $response);
}
/**
* Get files list from the response from trashbin api
*
* @param SimpleXMLElement|null $responseXml
*
* @return array
*/
public function getTrashbinContentFromResponseXml(?SimpleXMLElement $responseXml): array {
$xmlElements = $responseXml->xpath('//d:response');
$files = \array_map(
static function (SimpleXMLElement $element) {
$href = $element->xpath('./d:href')[0];
$propStats = $element->xpath('./d:propstat');
$successPropStat = \array_filter(
$propStats,
static function (SimpleXMLElement $propStat) {
$status = $propStat->xpath('./d:status');
return (string) $status[0] === 'HTTP/1.1 200 OK';
}
);
if (isset($successPropStat[0])) {
$successPropStat = $successPropStat[0];
$name = $successPropStat->xpath('./d:prop/oc:trashbin-original-filename');
$mtime = $successPropStat->xpath('./d:prop/oc:trashbin-delete-timestamp');
$resourcetype = $successPropStat->xpath('./d:prop/d:resourcetype');
if (\array_key_exists(0, $resourcetype) && ($resourcetype[0]->asXML() === "<d:resourcetype><d:collection/></d:resourcetype>")) {
$collection[0] = true;
} else {
$collection[0] = false;
}
$originalLocation = $successPropStat->xpath('./d:prop/oc:trashbin-original-location');
} else {
$name = [];
$mtime = [];
$collection = [];
$originalLocation = [];
}
return [
'href' => (string) $href,
'name' => isset($name[0]) ? (string) $name[0] : null,
'mtime' => isset($mtime[0]) ? (string) $mtime[0] : null,
'collection' => isset($collection[0]) ? $collection[0] : false,
'original-location' => isset($originalLocation[0]) ? (string) $originalLocation[0] : null
];
},
$xmlElements
);
return $files;
}
/**
* List the top of the trashbin folder for a user
*
* @param string|null $user user
* @param string $depth
*
* @return array response
* @throws Exception
*/
public function listTopOfTrashbinFolder(?string $user, string $depth = "1"):array {
$password = $this->featureContext->getPasswordForUser($user);
$davPathVersion = $this->featureContext->getDavPathVersion();
$response = WebDavHelper::listFolder(
$this->featureContext->getBaseUrl(),
$user,
$password,
"",
$depth,
$this->featureContext->getStepLineRef(),
[
'oc:trashbin-original-filename',
'oc:trashbin-original-location',
'oc:trashbin-delete-timestamp',
'd:getlastmodified'
],
'trash-bin',
$davPathVersion
);
$this->featureContext->setResponse($response);
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$this->featureContext->setResponseXmlObject($responseXml);
$files = $this->getTrashbinContentFromResponseXml($responseXml);
// filter root element
$files = \array_filter(
$files,
static function ($element) use ($user) {
return ($element['href'] !== "/remote.php/dav/trash-bin/$user/");
}
);
return $files;
}
/**
* List trashbin folder
*
* @param string|null $user user
* @param string $depth
*
* @return array of all the items in the trashbin of the user
* @throws Exception
*/
public function listTrashbinFolder(?string $user, string $depth = "1"):array {
return $this->listTrashbinFolderCollection(
$user,
"",
$depth
);
}
/**
* List a collection in the trashbin
*
* @param string|null $user user
* @param string|null $collectionPath the string of ids of the folder and sub-folders
* @param string $depth
* @param int $level
*
* @return array response
* @throws Exception
*/
public function listTrashbinFolderCollection(?string $user, ?string $collectionPath = "", string $depth = "1", int $level = 1):array {
// $collectionPath should be some list of file-ids like 2147497661/2147497662
// or the empty string, which will list the whole trashbin from the top.
$collectionPath = \trim($collectionPath, "/");
$password = $this->featureContext->getPasswordForUser($user);
$davPathVersion = $this->featureContext->getDavPathVersion();
$response = WebDavHelper::listFolder(
$this->featureContext->getBaseUrl(),
$user,
$password,
$collectionPath,
$depth,
$this->featureContext->getStepLineRef(),
[
'oc:trashbin-original-filename',
'oc:trashbin-original-location',
'oc:trashbin-delete-timestamp',
'd:resourcetype',
'd:getlastmodified'
],
'trash-bin',
$davPathVersion
);
$response->getBody()->rewind();
$statusCode = $response->getStatusCode();
$respBody = $response->getBody()->getContents();
Assert::assertEquals("207", $statusCode, "Expected status code to be '207' but got $statusCode \nResponse\n$respBody");
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__ . " $collectionPath"
);
$files = $this->getTrashbinContentFromResponseXml($responseXml);
// set endpoint according to webdav request (2 = new, 3 = spaces)
$endpoint = "/remote.php/dav/trash-bin/$user";
if ($davPathVersion === 3) {
$space_id = (WebDavHelper::$SPACE_ID_FROM_OCIS) ?: WebDavHelper::getPersonalSpaceIdForUser(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef()
);
$endpoint = "/remote.php/dav/spaces/trash-bin/$space_id";
}
// filter out the collection itself, we only want to return the members
$files = \array_filter(
$files,
static function ($element) use ($endpoint, $collectionPath) {
$path = $collectionPath;
if ($path !== "") {
$path = $path . "/";
}
return ($element['href'] !== "$endpoint/$path");
}
);
foreach ($files as $file) {
// check for unexpected/invalid href values and fail early in order to
// avoid "common" situations that could cause infinite recursion.
$trashbinRef = $file["href"];
$trimmedTrashbinRef = \trim($trashbinRef, "/");
if ($davPathVersion === WebDavHelper::DAV_VERSION_SPACES) {
$expectedStart = "remote.php/dav/spaces/trash-bin";
} else {
$expectedStart = "remote.php/dav/trash-bin/$user";
}
$expectedStartLength = \strlen($expectedStart);
if ((\substr($trimmedTrashbinRef, 0, $expectedStartLength) !== $expectedStart)
|| (\strlen($trimmedTrashbinRef) === $expectedStartLength)
) {
// A top href (maybe without even the username) has been returned
// in the response. That should never happen, or have been filtered out
// by code above.
throw new Exception(
__METHOD__ . " Error: unexpected href in trashbin propfind at level $level: '$trashbinRef'"
);
}
if ($file["collection"]) {
$trimmedHref = \trim($trashbinRef, "/");
$explodedHref = \explode("/", $trimmedHref);
$trashbinId = $collectionPath . "/" . end($explodedHref);
$nextFiles = $this->listTrashbinFolderCollection(
$user,
$trashbinId,
$depth,
$level + 1
);
// filter the collection element. We only want the members.
$nextFiles = \array_filter(
$nextFiles,
static function ($element) use ($user, $trashbinRef) {
return ($element['href'] !== $trashbinRef);
}
);
\array_push($files, ...$nextFiles);
}
}
return $files;
}
/**
* @When user :user lists the resources in the trashbin with depth :depth using the WebDAV API
*
* @param string $user
* @param string $depth
*
* @return void
* @throws Exception
*/
public function userGetsFilesInTheTrashbinWithDepthUsingTheWebdavApi(string $user, string $depth):void {
$this->listTopOfTrashbinFolder($user, $depth);
}
/**
* @Then the trashbin DAV response should not contain these nodes
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theTrashbinDavResponseShouldNotContainTheseNodes(TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ['name']);
$responseXml = $this->featureContext->getResponseXmlObject();
$files = $this->getTrashbinContentFromResponseXml($responseXml);
foreach ($table->getHash() as $row) {
$path = trim((string)$row['name'], "/");
foreach ($files as $file) {
if (trim((string)$file['original-location'], "/") === $path) {
throw new Exception("file $path was not expected in trashbin response but was found");
}
}
}
}
/**
* @Then the trashbin DAV response should contain these nodes
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theTrashbinDavResponseShouldContainTheseNodes(TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ['name']);
$responseXml = $this->featureContext->getResponseXmlObject();
$files = $this->getTrashbinContentFromResponseXml($responseXml);
foreach ($table->getHash() as $row) {
$path = trim($row['name'], "/");
$found = false;
foreach ($files as $file) {
if (trim((string)$file['original-location'], "/") === $path) {
$found = true;
break;
}
}
if (!$found) {
throw new Exception("file $path was expected in trashbin response but was not found");
}
}
}
/**
* Send a webdav request to list the trashbin content
*
* @param string $user user
* @param string|null $asUser - To send request as another user
* @param string|null $password
*
* @return void
* @throws Exception
*/
public function sendTrashbinListRequest(string $user, ?string $asUser = null, ?string $password = null):void {
$asUser = $asUser ?? $user;
$password = $password ?? $this->featureContext->getPasswordForUser($asUser);
$davPathVersion = $this->featureContext->getDavPathVersion();
$response = WebDavHelper::propfind(
$this->featureContext->getBaseUrl(),
$asUser,
$password,
null,
[
'oc:trashbin-original-filename',
'oc:trashbin-original-location',
'oc:trashbin-delete-timestamp',
'd:getlastmodified'
],
$this->featureContext->getStepLineRef(),
'1',
'trash-bin',
$davPathVersion,
$user
);
$this->featureContext->setResponse($response);
try {
$responseXmlObject = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$this->featureContext->setResponseXmlObject($responseXmlObject);
} catch (Exception $e) {
$this->featureContext->clearResponseXmlObject();
}
}
/**
* @When user :asUser tries to list the trashbin content for user :user
*
* @param string $asUser
* @param string $user
*
* @return void
* @throws Exception
*/
public function userTriesToListTheTrashbinContentForUser(string $asUser, string $user) {
$user = $this->featureContext->getActualUsername($user);
$asUser = $this->featureContext->getActualUsername($asUser);
$this->sendTrashbinListRequest($user, $asUser);
}
/**
* @When user :asUser tries to list the trashbin content for user :user using password :password
*
* @param string $asUser
* @param string $user
* @param string $password
*
* @return void
* @throws Exception
*/
public function userTriesToListTheTrashbinContentForUserUsingPassword(string $asUser, string $user, string $password):void {
$this->sendTrashbinListRequest($user, $asUser, $password);
}
/**
* @Then the last webdav response should contain the following elements
*
* @param TableNode $elements
*
* @return void
*/
public function theLastWebdavResponseShouldContainFollowingElements(TableNode $elements):void {
$files = $this->getTrashbinContentFromResponseXml($this->featureContext->getResponseXmlObject());
$elementRows = $elements->getHash();
foreach ($elementRows as $expectedElement) {
$found = false;
$expectedPath = $expectedElement['path'];
foreach ($files as $file) {
if (\ltrim($expectedPath, "/") === \ltrim($file['original-location'], "/")) {
$found = true;
break;
}
}
Assert::assertTrue($found, "$expectedPath expected to be listed in response but not found");
}
}
/**
* @Then the last webdav response should not contain the following elements
*
* @param TableNode $elements
*
* @return void
* @throws Exception
*/
public function theLastWebdavResponseShouldNotContainFollowingElements(TableNode $elements):void {
$files = $this->getTrashbinContentFromResponseXml($this->featureContext->getResponseXmlObject());
// 'user' is also allowed in the table even though it is not used anywhere
// This for better readability in feature files
$this->featureContext->verifyTableNodeColumns($elements, ['path'], ['path', 'user']);
$elementRows = $elements->getHash();
foreach ($elementRows as $expectedElement) {
$notFound = true;
$expectedPath = "/" . \ltrim($expectedElement['path'], "/");
foreach ($files as $file) {
// Allow the table of expected elements to have entries that do
// not have to specify the "implied" leading slash, or have multiple
// leading slashes, to make scenario outlines more flexible
if ($expectedPath === $file['original-location']) {
$notFound = false;
}
}
Assert::assertTrue($notFound, "$expectedPath expected not to be listed in response but found");
}
}
/**
* @When user :asUser tries to delete the file with original path :path from the trashbin of user :user using the trashbin API
*
* @param string $asUser
* @param string $path
* @param string $user
*
* @return void
* @throws Exception
*/
public function userTriesToDeleteFromTrashbinOfUser(string $asUser, string $path, string $user):void {
$numItemsDeleted = $this->tryToDeleteFileFromTrashbin($user, $path, $asUser);
}
/**
* @When user :asUser tries to delete the file with original path :path from the trashbin of user :user using the password :password and the trashbin API
*
* @param string|null $asUser
* @param string|null $path
* @param string|null $user
* @param string|null $password
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function userTriesToDeleteFromTrashbinOfUserUsingPassword(?string $asUser, ?string $path, ?string $user, ?string $password):void {
$user = $this->featureContext->getActualUsername($user);
$asUser = $this->featureContext->getActualUsername($asUser);
$this->tryToDeleteFileFromTrashbin($user, $path, $asUser, $password);
}
/**
* @When user :asUser tries to restore the file with original path :path from the trashbin of user :user using the trashbin API
*
* @param string|null $asUser
* @param string|null $path
* @param string|null $user
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function userTriesToRestoreFromTrashbinOfUser(?string $asUser, ?string $path, ?string $user):void {
$user = $this->featureContext->getActualUsername($user);
$asUser = $this->featureContext->getActualUsername($asUser);
$response = $this->restoreElement($user, $path, null, true, $asUser);
$this->featureContext->setResponse($response);
}
/**
* @When user :asUser tries to restore the file with original path :path from the trashbin of user :user using the password :password and the trashbin API
*
* @param string|null $asUser
* @param string|null $path
* @param string|null $user
* @param string|null $password
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function userTriesToRestoreFromTrashbinOfUserUsingPassword(?string $asUser, ?string $path, ?string $user, ?string $password):void {
$asUser = $this->featureContext->getActualUsername($asUser);
$user = $this->featureContext->getActualUsername($user);
$response = $this->restoreElement($user, $path, null, true, $asUser, $password);
$this->featureContext->setResponse($response);
}
/**
* converts the trashItemHRef from /<base>/remote.php/dav/trash-bin/<user>/<item_id>/ to /trash-bin/<user>/<item_id>
*
* @param string $href
*
* @return string
*/
private function convertTrashbinHref(string $href):string {
$trashItemHRef = \trim($href, '/');
$trashItemHRef = \strstr($trashItemHRef, '/trash-bin');
$trashItemHRef = \trim($trashItemHRef, '/');
$parts = \explode('/', $trashItemHRef);
$decodedParts = \array_slice($parts, 2);
return '/' . \join('/', $decodedParts);
}
/**
* @When /^user "([^"]*)" tries to delete the (?:file|folder|entry) with original path "([^"]*)" from the trashbin using the trashbin API$/
*
* @param string|null $user
* @param string|null $originalPath
* @param string|null $asUser
* @param string|null $password
*
* @return int the number of items that were matched and requested for delete
* @throws JsonException
* @throws Exception
*/
public function tryToDeleteFileFromTrashbin(?string $user, ?string $originalPath, ?string $asUser = null, ?string $password = null):int {
$user = $this->featureContext->getActualUsername($user);
$asUser = $asUser ?? $user;
$listing = $this->listTrashbinFolder($user);
$originalPath = \trim($originalPath, '/');
$numItemsDeleted = 0;
foreach ($listing as $entry) {
// The entry for the trashbin root can have original-location null.
// That is reasonable, because the trashbin root is not something that can be restored.
$originalLocation = $entry['original-location'] ?? '';
if (\trim($originalLocation, '/') === $originalPath) {
$trashItemHRef = $this->convertTrashbinHref($entry['href']);
$response = $this->featureContext->makeDavRequest(
$asUser,
'DELETE',
$trashItemHRef,
[],
null,
'trash-bin',
null,
false,
$password,
[],
$user
);
$this->featureContext->setResponse($response);
$numItemsDeleted++;
}
}
return $numItemsDeleted;
}
/**
* @When /^user "([^"]*)" deletes the (?:file|folder|entry) with original path "([^"]*)" from the trashbin using the trashbin API$/
*
* @param string $user
* @param string $originalPath
*
* @return void
* @throws Exception
*/
public function deleteFileFromTrashbin(string $user, string $originalPath):void {
$numItemsDeleted = $this->tryToDeleteFileFromTrashbin($user, $originalPath);
Assert::assertEquals(
1,
$numItemsDeleted,
"Expected to delete exactly one item from the trashbin but $numItemsDeleted were deleted"
);
$this->featureContext->pushToLastStatusCodesArrays();
}
/**
* @When /^user "([^"]*)" deletes the following (?:files|folders|entries) with original path from the trashbin$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function deleteFollowingFilesFromTrashbin(string $user, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $path) {
$this->deleteFileFromTrashbin($user, $path["path"]);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @Then /^as "([^"]*)" (?:file|folder|entry) "([^"]*)" should exist in the trashbin$/
*
* @param string|null $user
* @param string|null $path
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function asFileOrFolderExistsInTrash(?string $user, ?string $path):void {
$user = $this->featureContext->getActualUsername($user);
$path = \trim($path, '/');
$sections = \explode('/', $path, 2);
$firstEntry = $this->findFirstTrashedEntry($user, \trim($sections[0], '/'));
Assert::assertNotNull(
$firstEntry,
"The first trash entry was not found while looking for trashbin entry '$path' of user '$user'"
);
if (\count($sections) !== 1) {
// TODO: handle deeper structures
$listing = $this->listTrashbinFolderCollection($user, \basename(\rtrim($firstEntry['href'], '/')));
} else {
$listing = [];
}
// query was on the main element ?
if (\count($sections) === 1) {
// already found, return
return;
}
$checkedName = \basename($path);
$found = false;
foreach ($listing as $entry) {
if ($entry['name'] === $checkedName) {
$found = true;
break;
}
}
Assert::assertTrue(
$found,
__METHOD__
. " Could not find expected resource '$path' in the trash"
);
}
/**
* Function to check if an element is in the trashbin
*
* @param string|null $user
* @param string|null $originalPath
*
* @return bool
* @throws Exception
*/
private function isInTrash(?string $user, ?string $originalPath):bool {
$listing = $this->listTrashbinFolder($user);
// we don't care if the test step writes a leading "/" or not
$originalPath = \ltrim($originalPath, '/');
foreach ($listing as $entry) {
if ($entry['original-location'] !== null && \ltrim($entry['original-location'], '/') === $originalPath) {
return true;
}
}
return false;
}
/**
* @param string $user
* @param string $trashItemHRef
* @param string $destinationPath
* @param string|null $asUser - To send request as another user
* @param string|null $password
*
* @return ResponseInterface
* @throws JsonException
* @throws GuzzleException
*/
private function sendUndeleteRequest(string $user, string $trashItemHRef, string $destinationPath, ?string $asUser = null, ?string $password = null):ResponseInterface {
$asUser = $asUser ?? $user;
$destinationPath = \trim($destinationPath, '/');
$destinationValue = $this->featureContext->getBaseUrl() . "/remote.php/dav/files/$user/$destinationPath";
$trashItemHRef = $this->convertTrashbinHref($trashItemHRef);
$headers['Destination'] = $destinationValue;
return $this->featureContext->makeDavRequest(
$asUser,
'MOVE',
$trashItemHRef,
$headers,
null,
'trash-bin',
'2',
false,
$password,
[],
$user
);
}
/**
* @param string $user
* @param string $originalPath
* @param string|null $destinationPath
* @param bool $throwExceptionIfNotFound
* @param string|null $asUser - To send request as another user
* @param string|null $password
*
* @return ResponseInterface
* @throws JsonException
* @throws GuzzleException
*/
private function restoreElement(string $user, string $originalPath, ?string $destinationPath = null, bool $throwExceptionIfNotFound = true, ?string $asUser = null, ?string $password = null):ResponseInterface {
$asUser = $asUser ?? $user;
$listing = $this->listTrashbinFolder($user);
$originalPath = \trim($originalPath, '/');
if ($destinationPath === null) {
$destinationPath = $originalPath;
}
foreach ($listing as $entry) {
if ($entry['original-location'] === $originalPath) {
return $this->sendUndeleteRequest(
$user,
$entry['href'],
$destinationPath,
$asUser,
$password
);
}
}
// The requested element to restore was not even in the trashbin.
// Throw an exception, because there was not any API call, and so there
// is also no up-to-date response to examine in later test steps.
if ($throwExceptionIfNotFound) {
throw new \Exception(
__METHOD__
. " cannot restore from trashbin because no element was found for user $user at original path $originalPath"
);
}
}
/**
* @When user :user restores the folder with original path :originalPath without specifying the destination using the trashbin API
*
* @param $user string
* @param $originalPath string
*
* @return ResponseInterface
* @throws Exception
*/
public function restoreFileWithoutDestination(string $user, string $originalPath):ResponseInterface {
$asUser = $asUser ?? $user;
$listing = $this->listTrashbinFolder($user);
$originalPath = \trim($originalPath, '/');
foreach ($listing as $entry) {
if ($entry['original-location'] === $originalPath) {
$trashItemHRef = $this->convertTrashbinHref($entry['href']);
$response = $this->featureContext->makeDavRequest(
$asUser,
'MOVE',
$trashItemHRef,
[],
null,
'trash-bin'
);
$this->featureContext->setResponse($response);
// this gives empty response in ocis
try {
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$this->featureContext->setResponseXmlObject($responseXml);
} catch (Exception $e) {
}
return $response;
}
}
throw new \Exception(
__METHOD__
. " cannot restore from trashbin because no element was found for user $user at original path $originalPath"
);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" if the file is also in the trashbin should be "([^"]*)" otherwise "([^"]*)"$/
*
* Note: this is a special step for an unusual bug combination.
* Delete it when the bug is fixed and the step is no longer needed.
*
* @param string|null $fileName
* @param string|null $user
* @param string|null $content
* @param string|null $alternativeContent
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function contentOfFileForUserIfAlsoInTrashShouldBeOtherwise(
?string $fileName,
?string $user,
?string $content,
?string $alternativeContent
):void {
$isInTrash = $this->isInTrash($user, $fileName);
$user = $this->featureContext->getActualUsername($user);
$response = $this->featureContext->downloadFileAsUserUsingPassword($user, $fileName);
if ($isInTrash) {
$this->featureContext->checkDownloadedContentMatches($content, '', $response);
} else {
$this->featureContext->checkDownloadedContentMatches($alternativeContent, '', $response);
}
}
/**
* @When /^user "([^"]*)" tries to restore the (?:file|folder|entry) with original path "([^"]*)" using the trashbin API$/
*
* @param string $user
* @param string $originalPath
*
* @return void
* @throws Exception
*/
public function userTriesToRestoreElementInTrash(string $user, string $originalPath):void {
$response = $this->restoreElement($user, $originalPath, null, false);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" restores the (?:file|folder|entry) with original path "([^"]*)" using the trashbin API$/
*
* @param string|null $user
* @param string $originalPath
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function elementInTrashIsRestored(?string $user, string $originalPath):void {
$user = $this->featureContext->getActualUsername($user);
$this->featureContext->setResponse($this->restoreElement($user, $originalPath));
}
/**
* @When /^user "([^"]*)" restores the following (?:files|folders|entries) with original path$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRestoresFollowingFiles(string $user, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $originalPath) {
$this->elementInTrashIsRestored($user, $originalPath["path"]);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @Given /^user "([^"]*)" has restored the (?:file|folder|entry) with original path "([^"]*)"$/
*
* @param string $user
* @param string $originalPath
*
* @return void
* @throws Exception
*/
public function elementInTrashHasBeenRestored(string $user, string $originalPath):void {
$response = $this->restoreElement($user, $originalPath);
$this->featureContext->theHTTPStatusCodeShouldBe(201, "", $response);
if ($this->isInTrash($user, $originalPath)) {
throw new Exception("File previously located at $originalPath is still in the trashbin");
}
}
/**
* @When /^user "([^"]*)" restores the (?:file|folder|entry) with original path "([^"]*)" to "([^"]*)" using the trashbin API$/
*
* @param string|null $user
* @param string|null $originalPath
* @param string|null $destinationPath
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function userRestoresTheFileWithOriginalPathToUsingTheTrashbinApi(
?string $user,
?string $originalPath,
?string $destinationPath
):void {
$user = $this->featureContext->getActualUsername($user);
$this->featureContext->setResponse($this->restoreElement($user, $originalPath, $destinationPath));
}
/**
* @Then /^as "([^"]*)" the (?:file|folder|entry) with original path "([^"]*)" should exist in the trashbin$/
*
* @param string|null $user
* @param string|null $originalPath
*
* @return void
* @throws JsonException
* @throws Exception
*/
public function elementIsInTrashCheckingOriginalPath(
?string $user,
?string $originalPath
):void {
$user = $this->featureContext->getActualUsername($user);
Assert::assertTrue(
$this->isInTrash($user, $originalPath),
"File previously located at $originalPath wasn't found in the trashbin of user $user"
);
}
/**
* @Then /^as "([^"]*)" the (?:file|folder|entry) with original path "([^"]*)" should not exist in the trashbin/
*
* @param string|null $user
* @param string $originalPath
*
* @return void
* @throws Exception
*/
public function elementIsNotInTrashCheckingOriginalPath(
?string $user,
string $originalPath
):void {
$user = $this->featureContext->getActualUsername($user);
Assert::assertFalse(
$this->isInTrash($user, $originalPath),
"File previously located at $originalPath was found in the trashbin of user $user"
);
}
/**
* @Then /^as "([^"]*)" the (?:files|folders|entries) with following original paths should not exist in the trashbin$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function followingElementsAreNotInTrashCheckingOriginalPath(
string $user,
TableNode $table
):void {
$this->featureContext->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $originalPath) {
$this->elementIsNotInTrashCheckingOriginalPath($user, $originalPath["path"]);
}
}
/**
* @Then /^as "([^"]*)" the (?:files|folders|entries) with following original paths should exist in the trashbin$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function followingElementsAreInTrashCheckingOriginalPath(
string $user,
TableNode $table
):void {
$this->featureContext->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $originalPath) {
$this->elementIsInTrashCheckingOriginalPath($user, $originalPath["path"]);
}
}
/**
* Finds the first trashed entry matching the given name
*
* @param string $user
* @param string $name
*
* @return array|null real entry name with timestamp suffix or null if not found
* @throws Exception
*/
private function findFirstTrashedEntry(string $user, string $name):?array {
$listing = $this->listTrashbinFolder($user);
foreach ($listing as $entry) {
if ($entry['name'] === $name) {
return $entry;
}
}
return null;
}
/**
* 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');
}
/**
* @Then /^the deleted (?:file|folder) "([^"]*)" should have the correct deletion mtime in the response$/
*
* @param string $resource file or folder in trashbin
*
* @return void
*/
public function theDeletedFileFolderShouldHaveCorrectDeletionMtimeInTheResponse(string $resource):void {
$files = $this->getTrashbinContentFromResponseXml(
$this->featureContext->getResponseXmlObject()
);
$found = false;
$expectedMtime = $this->featureContext->getLastUploadDeleteTime();
$responseMtime = '';
foreach ($files as $file) {
if (\ltrim($resource, "/") === \ltrim((string)$file['original-location'], "/")) {
$responseMtime = $file['mtime'];
$mtime_difference = \abs((int)\trim((string)$expectedMtime) - (int)\trim($responseMtime));
if ($mtime_difference <= 2) {
$found = true;
break;
}
}
}
Assert::assertTrue(
$found,
"$resource expected to be listed in response with mtime '$expectedMtime' but found '$responseMtime'"
);
}
}