Files
opencloud/tests/acceptance/features/bootstrap/WebDav.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

5921 lines
157 KiB
PHP

<?php declare(strict_types=1);
/**
* @author Sergio Bertolin <sbertolin@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\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Ring\Exception\ConnectException;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Stream\StreamInterface;
use TestHelpers\UploadHelper;
use TestHelpers\WebDavHelper;
use TestHelpers\HttpRequestHelper;
use TestHelpers\Asserts\WebDav as WebDavAssert;
use TestHelpers\GraphHelper;
/**
* WebDav functions
*/
trait WebDav {
private string $davPath = "remote.php/webdav";
private bool $usingOldDavPath = true;
private bool $usingSpacesDavPath = false;
/**
* @var ResponseInterface[]
*/
private array $uploadResponses;
/**
* @var int|string|null
*/
private $storedFileID = null;
private ?int $lastUploadDeleteTime = null;
/**
* a variable that contains the DAV path without "remote.php/(web)dav"
* when setting $this->davPath directly by usingDavPath()
*/
private ?string $customDavPath = null;
/**
* response content parsed from XML to an array
*/
private array $responseXml = [];
/**
* add resource created by admin in an array
* This array is used while cleaning up the resource created by admin during test run
* As of now it tracks only for (files|folder) creation
* This can be expanded and modified to track other actions like (upload, deleted ..)
*/
private array $adminResources = [];
/**
* response content parsed into a SimpleXMLElement
*/
private ?SimpleXMLElement $responseXmlObject = null;
private int $httpRequestTimeout = 0;
private ?int $chunkingToUse = null;
/**
* The ability to do requests with depth infinity is disabled by default.
* This remembers when the setting dav.propfind.depth_infinity has been
* enabled, so that test code can make use of it as appropriate.
*/
private bool $davPropfindDepthInfinityEnabled = false;
/**
* @return void
*/
public function davPropfindDepthInfinityEnabled():void {
$this->davPropfindDepthInfinityEnabled = true;
}
/**
* @return void
*/
public function davPropfindDepthInfinityDisabled():void {
$this->davPropfindDepthInfinityEnabled = false;
}
/**
* @return bool
*/
public function davPropfindDepthInfinityIsEnabled():bool {
return $this->davPropfindDepthInfinityEnabled;
}
/**
* @param int $lastUploadDeleteTime
*
* @return void
*/
public function setLastUploadDeleteTime(int $lastUploadDeleteTime):void {
$this->lastUploadDeleteTime = $lastUploadDeleteTime;
}
/**
* @return number
*/
public function getLastUploadDeleteTime():int {
return $this->lastUploadDeleteTime;
}
/**
* @return SimpleXMLElement|null
*/
public function getResponseXmlObject():?SimpleXMLElement {
return $this->responseXmlObject;
}
/**
* @param SimpleXMLElement $responseXmlObject
*
* @return void
*/
public function setResponseXmlObject(SimpleXMLElement $responseXmlObject):void {
$this->responseXmlObject = $responseXmlObject;
}
/**
* @return void
*/
public function clearResponseXmlObject():void {
$this->responseXmlObject = null;
}
/**
* @param SimpleXMLElement|null $xmlObject
*
* @return string the etag or an empty string if the getetag property does not exist
*/
public function getEtagFromResponseXmlObject(?SimpleXMLElement $xmlObject = null): string {
$xmlObject = $xmlObject ?? $this->getResponseXml();
$xmlPart = $xmlObject->xpath("//d:prop/d:getetag");
if (!\is_array($xmlPart) || (\count($xmlPart) === 0)) {
return '';
}
return $xmlPart[0]->__toString();
}
/**
*
* @param string $eTag
*
* @return boolean
*/
public function isEtagValid(string $eTag): bool {
if (\preg_match("/^\"[a-f0-9:\.]{1,32}\"$/", $eTag)
) {
return true;
} else {
return false;
}
}
/**
* @param array $responseXml
*
* @return void
*/
public function setResponseXml(array $responseXml):void {
$this->responseXml = $responseXml;
}
/**
* @param ResponseInterface[] $uploadResponses
*
* @return void
*/
public function setUploadResponses(array $uploadResponses):void {
$this->uploadResponses = $uploadResponses;
}
/**
* @Given /^using DAV path "([^"]*)"$/
*
* @param string $davPath
*
* @return void
*/
public function usingDavPath(string $davPath):void {
$this->davPath = $davPath;
$this->customDavPath = \preg_replace(
"/remote\.php\/(web)?dav\//",
"",
$davPath
);
}
/**
* @return string
*/
public function getOldDavPath():string {
return "remote.php/webdav";
}
/**
* @return string
*/
public function getNewDavPath():string {
return "remote.php/dav";
}
/**
* @return string
*/
public function getSpacesDavPath():string {
return "dav/spaces";
}
/**
* @Given /^using (old|new|spaces) (?:dav|DAV) path$/
*
* @param string $davChoice
*
* @return void
*/
public function usingOldOrNewDavPath(string $davChoice):void {
if ($davChoice === 'old') {
$this->usingOldDavPath();
} elseif ($davChoice === 'new') {
$this->usingNewDavPath();
} else {
$this->usingSpacesDavPath();
}
}
/**
* Select the old DAV path as the default for later scenario steps
*
* @return void
*/
public function usingOldDavPath():void {
$this->davPath = $this->getOldDavPath();
$this->usingOldDavPath = true;
$this->customDavPath = null;
$this->usingSpacesDavPath = false;
}
/**
* Select the new DAV path as the default for later scenario steps
*
* @return void
*/
public function usingNewDavPath():void {
$this->davPath = $this->getNewDavPath();
$this->usingOldDavPath = false;
$this->customDavPath = null;
$this->usingSpacesDavPath = false;
}
/**
* Select the spaces dav path as the default for later scenario steps
*
* @return void
*/
public function usingSpacesDavPath():void {
$this->davPath = $this->getSpacesDavPath();
$this->usingOldDavPath = false;
$this->customDavPath = null;
$this->usingSpacesDavPath = true;
}
/**
* gives the DAV path of a file including the subfolder of the webserver
* e.g. when the server runs in `http://localhost/owncloud/`
* this function will return `owncloud/remote.php/webdav/prueba.txt`
*
* @param string $user
*
* @return string
* @throws GuzzleException
*/
public function getFullDavFilesPath(string $user):string {
$spaceId = null;
if ($this->getDavPathVersion() === WebDavHelper::DAV_VERSION_SPACES) {
$spaceId = (WebDavHelper::$SPACE_ID_FROM_OCIS) ?: WebDavHelper::getPersonalSpaceIdForUser(
$this->getBaseUrl(),
$user,
$this->getPasswordForUser($user),
$this->getStepLineRef()
);
}
$path = $this->getBasePath() . "/" .
WebDavHelper::getDavPath($user, $this->getDavPathVersion(), "files", $spaceId);
$path = WebDavHelper::sanitizeUrl($path);
return \ltrim($path, "/");
}
/**
* @param string $token
* @param string $type
*
* @return string
*/
public function getPublicLinkDavPath(string $token, string $type):string {
$path = $this->getBasePath() . "/" .
WebDavHelper::getDavPath($token, $this->getDavPathVersion(), $type);
$path = WebDavHelper::sanitizeUrl($path);
return \ltrim($path, "/");
}
/**
* Select a suitable DAV path version number.
* Some endpoints have only existed since a certain point in time, so for
* those make sure to return a DAV path version that works for that endpoint.
* Otherwise return the currently selected DAV path version.
*
* @param string|null $for the category of endpoint that the DAV path will be used for
*
* @return int DAV path version (1, 2 or 3) selected, or appropriate for the endpoint
*/
public function getDavPathVersion(?string $for = null):?int {
if ($this->usingSpacesDavPath) {
return WebDavHelper::DAV_VERSION_SPACES;
}
if ($for === 'systemtags') {
// systemtags only exists since DAV v2
return WebDavHelper::DAV_VERSION_NEW;
}
if ($for === 'file_versions') {
// file_versions only exists since DAV v2
return WebDavHelper::DAV_VERSION_NEW;
}
if ($this->usingOldDavPath === true) {
return WebDavHelper::DAV_VERSION_OLD;
} else {
return WebDavHelper::DAV_VERSION_NEW;
}
}
/**
* Select a suitable DAV path.
* Some endpoints have only existed since a certain point in time, so for
* those make sure to return a DAV path that works for that endpoint.
* Otherwise return the currently selected DAV path.
*
* @param string|null $for the category of endpoint that the DAV path will be used for
*
* @return string DAV path selected, or appropriate for the endpoint
*/
public function getDavPath(?string $for = null):string {
$davPathVersion = $this->getDavPathVersion($for);
if ($davPathVersion === WebDavHelper::DAV_VERSION_OLD) {
return $this->getOldDavPath();
}
if ($davPathVersion === WebDavHelper::DAV_VERSION_NEW) {
return $this->getNewDavPath();
}
return $this->getSpacesDavPath();
}
/**
* @param string|null $user
* @param string|null $method
* @param string|null $path
* @param array|null $headers
* @param StreamInterface|null $body
* @param string|null $type
* @param string|null $davPathVersion
* @param bool $stream Set to true to stream a response rather
* than download it all up-front.
* @param string|null $password
* @param array|null $urlParameter
* @param string|null $doDavRequestAsUser
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws JsonException
*/
public function makeDavRequest(
?string $user,
?string $method,
?string $path,
?array $headers,
$body = null,
?string $type = "files",
?string $davPathVersion = null,
bool $stream = false,
?string $password = null,
?array $urlParameter = [],
?string $doDavRequestAsUser = null,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
if ($this->customDavPath !== null) {
$path = $this->customDavPath . $path;
}
if ($davPathVersion === null) {
$davPathVersion = $this->getDavPathVersion();
} else {
$davPathVersion = (int) $davPathVersion;
}
if ($password === null) {
$password = $this->getPasswordForUser($user);
}
return WebDavHelper::makeDavRequest(
$this->getBaseUrl(),
$user,
$password,
$method,
$path,
$headers,
$this->getStepLineRef(),
$body,
$davPathVersion,
$type,
null,
"basic",
$stream,
$this->httpRequestTimeout,
null,
$urlParameter,
$doDavRequestAsUser,
$isGivenStep
);
}
/**
*
* @param string $user
* @param string $folder
* @param bool|null $isGivenStep
*
* @return ResponseInterface
* @throws JsonException | GuzzleException
* @throws GuzzleException | JsonException
*/
public function createFolder(string $user, string $folder, ?bool $isGivenStep = false): ResponseInterface {
$folder = '/' . \ltrim($folder, '/');
return $this->makeDavRequest(
$user,
"MKCOL",
$folder,
[],
null,
"files",
null,
false,
null,
[],
null,
$isGivenStep
);
}
/**
* @param string $user
* @param string|null $path
* @param string|null $doDavRequestAsUser
* @param string|null $width
* @param string|null $height
*
* @return ResponseInterface
*/
public function downloadPreviews(string $user, ?string $path, ?string $doDavRequestAsUser, ?string $width, ?string $height):ResponseInterface {
$user = $this->getActualUsername($user);
$doDavRequestAsUser = $this->getActualUsername($doDavRequestAsUser);
$urlParameter = [
'x' => $width,
'y' => $height,
'forceIcon' => '0',
'preview' => '1'
];
return $this->makeDavRequest(
$user,
"GET",
$path,
[],
null,
"files",
null,
false,
null,
$urlParameter,
$doDavRequestAsUser
);
}
/**
* @Then the number of versions should be :arg1
*
* @param int $number
*
* @return void
* @throws Exception
*/
public function theNumberOfVersionsShouldBe(int $number):void {
$resXml = $this->getResponseXmlObject();
if ($resXml === null) {
$resXml = HttpRequestHelper::getResponseXml(
$this->getResponse(),
__METHOD__
);
$this->setResponseXmlObject($resXml);
}
$xmlPart = $resXml->xpath("//d:getlastmodified");
$actualNumber = \count($xmlPart);
Assert::assertEquals(
$number,
$actualNumber,
"Expected number of versions was '$number', but got '$actualNumber'"
);
}
/**
* @Then the number of etag elements in the response should be :number
*
* @param int $number
*
* @return void
* @throws Exception
*/
public function theNumberOfEtagElementInTheResponseShouldBe(int $number):void {
$resXml = $this->getResponseXmlObject();
if ($resXml === null) {
$resXml = HttpRequestHelper::getResponseXml(
$this->getResponse(),
__METHOD__
);
}
$xmlPart = $resXml->xpath("//d:getetag");
$actualNumber = \count($xmlPart);
Assert::assertEquals(
$number,
$actualNumber,
"Expected number of etag elements was '$number', but got '$actualNumber'"
);
}
/**
* @Given the HTTP-Request-timeout is set to :seconds seconds
*
* @param int $timeout
*
* @return void
*/
public function setHttpTimeout(int $timeout):void {
$this->httpRequestTimeout = $timeout;
}
/**
* @param string $user
* @param string $fileDestination
*
* @return string
* @throws GuzzleException
*/
public function destinationHeaderValue(string $user, string $fileDestination):string {
$spaceId = $this->getPersonalSpaceIdForUser($user);
$fullUrl = $this->getBaseUrl() . '/' .
WebDavHelper::getDavPath($user, $this->getDavPathVersion(), "files", $spaceId);
return \rtrim($fullUrl, '/') . '/' . \ltrim($fileDestination, '/');
}
/**
* @Given /^user "([^"]*)" has moved (?:file|folder|entry) "([^"]*)" to "([^"]*)"$/
*
* @param string|null $user
* @param string|null $fileSource
* @param string|null $fileDestination
*
* @return void
*/
public function userHasMovedFile(
?string $user,
?string $fileSource,
?string $fileDestination
):void {
$user = $this->getActualUsername($user);
$headers['Destination'] = $this->destinationHeaderValue(
$user,
$fileDestination
);
$response = $this->makeDavRequest(
$user,
"MOVE",
$fileSource,
$headers
);
$actualStatusCode = $response->getStatusCode();
$this->theHTTPStatusCodeShouldBe(201, " Failed moving resource '$fileSource' to '$fileDestination'." . " Expected status code was 201 but got '$actualStatusCode' ", $response);
}
/**
* @Given /^the user has moved (?:file|folder|entry) "([^"]*)" to "([^"]*)"$/
*
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function theUserHasMovedFile(string $fileSource, string $fileDestination):void {
$this->userHasMovedFile($this->getCurrentUser(), $fileSource, $fileDestination);
}
/**
* @When /^user "([^"]*)" moves (file|folder|entry) "([^"]*)"\s?(asynchronously|) to these (?:filenames|foldernames|entries) using the webDAV API then the results should be as listed$/
*
* @param string $user
* @param string $entry
* @param string $fileSource
* @param string $type "asynchronously" or empty
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userMovesEntriesUsingTheAPI(
string $user,
string $entry,
string $fileSource,
string $type,
TableNode $table
):void {
$user = $this->getActualUsername($user);
foreach ($table->getHash() as $row) {
// Allow the "filename" column to be optionally be called "foldername"
// to help readability of scenarios that test moving folders
$targetName = $row['foldername'] ?? $row['filename'];
$this->userMovesFileUsingTheAPI(
$user,
$fileSource,
$type,
$targetName
);
$this->theHTTPStatusCodeShouldBe(
$row['http-code'],
"HTTP status code is not the expected value while trying to move " . $targetName
);
if ($row['exists'] === "yes") {
$this->asFileOrFolderShouldExist($user, $entry, $targetName);
// The move was successful.
// Move the file/folder back so the source file/folder exists for the next move
$this->userMovesFileUsingTheAPI(
$user,
$targetName,
'',
$fileSource
);
} else {
$this->asFileOrFolderShouldNotExist($user, $entry, $targetName);
}
}
}
/**
* @When /^user "([^"]*)" moves (?:file|folder|entry) "([^"]*)"\s?(asynchronously|) to "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $type "asynchronously" or empty
* @param string $fileDestination
*
* @return void
* @throws JsonException
* @throws GuzzleException
*/
public function userMovesFileUsingTheAPI(
string $user,
string $fileSource,
string $type,
string $fileDestination
):void {
$user = $this->getActualUsername($user);
$headers['Destination'] = $this->destinationHeaderValue(
$user,
$fileDestination
);
$stream = false;
if ($type === "asynchronously") {
$headers['OC-LazyOps'] = 'true';
if ($this->httpRequestTimeout > 0) {
//LazyOps is set and a request timeout, so we want to use stream
//to be able to read data from the request before its times out
//when doing LazyOps the server does not close the connection
//before its really finished
//but we want to read JobStatus-Location before the end of the job
//to see if it reports the correct values
$stream = true;
}
}
try {
$this->response = $this->makeDavRequest(
$user,
"MOVE",
$fileSource,
$headers,
null,
"files",
null,
$stream
);
$this->setResponseXml(
HttpRequestHelper::parseResponseAsXml($this->response)
);
$this->pushToLastHttpStatusCodesArray(
(string) $this->getResponse()->getStatusCode()
);
} catch (ConnectException $e) {
}
}
/**
* @When user :user moves the following file using the WebDAV API
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userMovesTheFollowingFileUsingTheWebdavApi(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["source", "destination"]);
$rows = $table->getHash();
foreach ($rows as $row) {
$this->userMovesFileUsingTheAPI($user, $row["source"], "", $row["destination"]);
}
}
/**
* @When /^user "([^"]*)" moves the following (?:files|folders|entries)\s?(asynchronously|) using the WebDAV API$/
*
* @param string $user
* @param string $type "asynchronously" or empty
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userMovesFollowingFileUsingTheAPI(
string $user,
string $type,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["from", "to"]);
$paths = $table->getHash();
foreach ($paths as $file) {
$this->userMovesFileUsingTheAPI($user, $file['from'], $type, $file['to']);
$this->pushToLastStatusCodesArrays();
}
}
/**
* @Then /^user "([^"]*)" should be able to rename (file|folder|entry) "([^"]*)" to "([^"]*)"$/
*
* @param string $user
* @param string $entry
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function theUserShouldBeAbleToRenameEntryTo(string $user, string $entry, string $source, string $destination):void {
$user = $this->getActualUsername($user);
$this->asFileOrFolderShouldExist($user, $entry, $source);
$this->userMovesFileUsingTheAPI($user, $source, "", $destination);
$this->asFileOrFolderShouldNotExist($user, $entry, $source);
$this->asFileOrFolderShouldExist($user, $entry, $destination);
}
/**
* @Then /^user "([^"]*)" should not be able to rename (file|folder|entry) "([^"]*)" to "([^"]*)"$/
*
* @param string $user
* @param string $entry
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function theUserShouldNotBeAbleToRenameEntryTo(string $user, string $entry, string $source, string $destination):void {
$this->asFileOrFolderShouldExist($user, $entry, $source);
$this->userMovesFileUsingTheAPI($user, $source, "", $destination);
$this->asFileOrFolderShouldExist($user, $entry, $source);
$this->asFileOrFolderShouldNotExist($user, $entry, $destination);
}
/**
* @When /^user "([^"]*)" on "(LOCAL|REMOTE)" moves (?:file|folder|entry) "([^"]*)" to "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $server
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function userOnMovesFileUsingTheAPI(
string $user,
string $server,
string $fileSource,
string $fileDestination
):void {
$previousServer = $this->usingServer($server);
$this->userMovesFileUsingTheAPI($user, $fileSource, "", $fileDestination);
$this->usingServer($previousServer);
}
/**
* @param string $user
* @param string $fileSource
* @param string $fileDestination
*
* @return ResponseInterface
*/
public function copyFile(
string $user,
string $fileSource,
string $fileDestination
):ResponseInterface {
$user = $this->getActualUsername($user);
$headers['Destination'] = $this->destinationHeaderValue(
$user,
$fileDestination
);
return $this->makeDavRequest(
$user,
"COPY",
$fileSource,
$headers
);
}
/**
* @When /^user "([^"]*)" copies (?:file|folder) "([^"]*)" to "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function userCopiesFileUsingTheAPI(
string $user,
string $fileSource,
string $fileDestination
):void {
$response = $this->copyFile($user, $fileSource, $fileDestination);
$this->setResponse($response);
$this->pushToLastHttpStatusCodesArray();
}
/**
* @Given /^user "([^"]*)" has copied file "([^"]*)" to "([^"]*)"$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function userHasCopiedFileUsingTheAPI(
string $user,
string $fileSource,
string $fileDestination
):void {
$response = $this->copyFile($user, $fileSource, $fileDestination);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to copy file '$fileSource' to '$fileDestination' for user '$user'",
$response
);
}
/**
* @When /^the user copies file "([^"]*)" to "([^"]*)" using the WebDAV API$/
*
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function theUserCopiesFileUsingTheAPI(string $fileSource, string $fileDestination):void {
$this->userCopiesFileUsingTheAPI($this->getCurrentUser(), $fileSource, $fileDestination);
}
/**
* @Given /^the user has copied file "([^"]*)" to "([^"]*)"$/
*
* @param string $fileSource
* @param string $fileDestination
*
* @return void
*/
public function theUserHasCopiedFileUsingTheAPI(string $fileSource, string $fileDestination):void {
$response = $this->copyFile($this->getCurrentUser(), $fileSource, $fileDestination);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to copy file '$fileSource' to '$fileDestination'",
$response
);
}
/**
* @When /^the user downloads file "([^"]*)" with range "([^"]*)" using the WebDAV API$/
*
* @param string $fileSource
* @param string $range
*
* @return void
*/
public function theUserDownloadsFileWithRange(string $fileSource, string $range):void {
$this->userDownloadsFileWithRangeUsingWebDavApi(
$this->currentUser,
$fileSource,
$range
);
}
/**
* @param string $user
* @param string $fileSource
* @param string $range
*
* @return ResponseInterface
*/
public function downloadFileWithRange(string $user, string $fileSource, string $range):ResponseInterface {
$user = $this->getActualUsername($user);
$headers['Range'] = $range;
return $this->makeDavRequest(
$user,
"GET",
$fileSource,
$headers
);
}
/**
* @When /^user "([^"]*)" downloads file "([^"]*)" with range "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $range
*
* @return void
*/
public function userDownloadsFileWithRangeUsingWebDavApi(string $user, string $fileSource, string $range):void {
$this->setResponse($this->downloadFileWithRange($user, $fileSource, $range));
}
/**
* @Then /^user "([^"]*)" using password "([^"]*)" should not be able to download file "([^"]*)"$/
*
* @param string $user
* @param string $password
* @param string $fileName
*
* @return void
*/
public function userUsingPasswordShouldNotBeAbleToDownloadFile(
string $user,
string $password,
string $fileName
):void {
$user = $this->getActualUsername($user);
$password = $this->getActualPassword($password);
$response = $this->downloadFileAsUserUsingPassword($user, $fileName, $password);
Assert::assertGreaterThanOrEqual(
400,
$response->getStatusCode(),
__METHOD__
. ' download must fail'
);
Assert::assertLessThanOrEqual(
499,
$response->getStatusCode(),
__METHOD__
. ' 4xx error expected but got status code "'
. $response->getStatusCode() . '"'
);
}
/**
* @Then /^user "([^"]*)" should not be able to download file "([^"]*)"$/
*
* @param string $user
* @param string $fileName
*
* @return void
* @throws JsonException
*/
public function userShouldNotBeAbleToDownloadFile(
string $user,
string $fileName
):void {
$user = $this->getActualUsername($user);
$password = $this->getPasswordForUser($user);
$response = $this->downloadFileAsUserUsingPassword($user, $fileName, $password);
Assert::assertGreaterThanOrEqual(
400,
$response->getStatusCode(),
__METHOD__
. ' download must fail'
);
Assert::assertLessThanOrEqual(
499,
$response->getStatusCode(),
__METHOD__
. ' 4xx error expected but got status code "'
. $response->getStatusCode() . '"'
);
}
/**
* @Then /^user "([^"]*)" should be able to access a skeleton file$/
*
* @param string $user
*
* @return void
*/
public function userShouldBeAbleToAccessASkeletonFile(string $user):void {
$this->contentOfFileForUserShouldBePlusEndOfLine(
"textfile0.txt",
$user,
"ownCloud test text file 0"
);
}
/**
* @Then the size of the downloaded file should be :size bytes
*
* @param string $size
*
* @return void
*/
public function sizeOfDownloadedFileShouldBe(string $size):void {
$actualSize = \strlen((string) $this->response->getBody());
Assert::assertEquals(
$size,
$actualSize,
"Expected size of the downloaded file was '$size' but got '$actualSize'"
);
}
/**
* @Then /^the downloaded content should end with "([^"]*)"$/
*
* @param string $content
*
* @return void
*/
public function downloadedContentShouldEndWith(string $content):void {
$actualContent = \substr((string) $this->response->getBody(), -\strlen($content));
Assert::assertEquals(
$content,
$actualContent,
"The downloaded content was expected to end with '$content', but actually ended with '$actualContent'."
);
}
/**
* @Then /^the downloaded content should be "([^"]*)"$/
*
* @param string $content
*
* @return void
*/
public function downloadedContentShouldBe(string $content):void {
$this->checkDownloadedContentMatches($content);
}
/**
* @param string $expectedContent
* @param string $extraErrorText
* @param ResponseInterface|null $response
*
* @return void
*/
public function checkDownloadedContentMatches(
string $expectedContent,
string $extraErrorText = "",
?ResponseInterface $response = null
):void {
$response = $response ?? $this->response;
$actualContent = (string) $response->getBody();
// For this test we really care about the content.
// A separate "Then" step can specifically check the HTTP status.
// But if the content is wrong (e.g. empty) then it is useful to
// report the HTTP status to give some clue what might be the problem.
$actualStatus = $response->getStatusCode();
if ($extraErrorText !== "") {
$extraErrorText .= "\n";
}
Assert::assertEquals(
$expectedContent,
$actualContent,
$extraErrorText . "The content was expected to be '$expectedContent', but actually is '$actualContent'. HTTP status was $actualStatus"
);
}
/**
* @Then the content in the response should match the following content:
*
* @param PyStringNode $content
*
* @return void
*/
public function theContentInTheResponseShouldMatchTheFollowingContent(PyStringNode $content): void {
$this->checkDownloadedContentMatches($content->getRaw());
}
/**
* @Then /^if the HTTP status code was "([^"]*)" then the downloaded content for multipart byterange should be:$/
*
* @param int $statusCode
* @param PyStringNode $content
*
* @return void
*
*/
public function theDownloadedContentForMultipartByteRangeShouldBe(int $statusCode, PyStringNode $content):void {
$actualStatusCode = $this->response->getStatusCode();
if ($actualStatusCode === $statusCode) {
$actualContent = (string) $this->response->getBody();
$pattern = ["/--\w*/", "/\s*/m"];
$actualContent = \preg_replace($pattern, "", $actualContent);
$content = \preg_replace("/\s*/m", '', $content->getRaw());
Assert::assertEquals(
$content,
$actualContent,
"The downloaded content was expected to be '$content', but actually is '$actualContent'. HTTP status was $actualStatusCode"
);
}
}
/**
* @Then /^if the HTTP status code was "([^"]*)" then the downloaded content should be "([^"]*)"$/
*
* @param int $statusCode
* @param string $content
*
* @return void
*/
public function checkStatusCodeForDownloadedContentShouldBe(int $statusCode, string $content):void {
$actualStatusCode = $this->response->getStatusCode();
if ($actualStatusCode === $statusCode) {
$this->downloadedContentShouldBe($content);
}
}
/**
* @Then /^the downloaded content should be "([^"]*)" plus end-of-line$/
*
* @param string $content
*
* @return void
*/
public function downloadedContentShouldBePlusEndOfLine(string $content):void {
$this->downloadedContentShouldBe("$content\n");
}
/**
* @Then /^the content of file "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileName
* @param string $content
*
* @return void
*/
public function contentOfFileShouldBe(string $fileName, string $content):void {
$response = $this->downloadFileAsUserUsingPassword($this->currentUser, $fileName);
$this->checkDownloadedContentMatches($content, '', $response);
}
/**
* @Then /^the content of file "([^"]*)" should be:$/
*
* @param string $fileName
* @param PyStringNode $content
*
* @return void
*/
public function contentOfFileShouldBePyString(
string $fileName,
PyStringNode $content
):void {
$this->contentOfFileShouldBe($fileName, $content->getRaw());
}
/**
* @Then /^the content of file "([^"]*)" should be "([^"]*)" plus end-of-line$/
*
* @param string $fileName
* @param string $content
*
* @return void
*/
public function contentOfFileShouldBePlusEndOfLine(string $fileName, string $content):void {
$this->theUserDownloadsTheFileUsingTheAPI($fileName);
$this->downloadedContentShouldBePlusEndOfLine($content);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileName
* @param string $user
* @param string $content
*
* @return void
*/
public function contentOfFileForUserShouldBe(string $fileName, string $user, string $content):void {
$user = $this->getActualUsername($user);
$response = $this->downloadFileAsUserUsingPassword($user, $fileName);
$actualStatus = $response->getStatusCode();
if ($actualStatus !== 200) {
throw new Exception(
"Expected status code to be '200', but got '$actualStatus'"
);
}
$this->checkDownloadedContentMatches($content, '', $response);
}
/**
* @Then /^the content of the following files for user "([^"]*)" should be "([^"]*)"$/
*
* @param string $user
* @param string $content
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function contentOfFollowingFilesShouldBe(string $user, string $content, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $file) {
$this->contentOfFileForUserShouldBe($file["path"], $user, $content);
}
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" on server "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileName
* @param string $user
* @param string $server
* @param string $content
*
* @return void
*/
public function theContentOfFileForUserOnServerShouldBe(
string $fileName,
string $user,
string $server,
string $content
):void {
$previousServer = $this->usingServer($server);
$this->contentOfFileForUserShouldBe($fileName, $user, $content);
$this->usingServer($previousServer);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" using password "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileName
* @param string $user
* @param string|null $password
* @param string $content
*
* @return void
*/
public function contentOfFileForUserUsingPasswordShouldBe(
string $fileName,
string $user,
?string $password,
string $content
):void {
$user = $this->getActualUsername($user);
$password = $this->getActualPassword($password);
$response = $this->downloadFileAsUserUsingPassword($user, $fileName, $password);
$this->checkDownloadedContentMatches($content, '', $response);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" should be:$/
*
* @param string $fileName
* @param string $user
* @param PyStringNode $content
*
* @return void
*/
public function contentOfFileForUserShouldBePyString(
string $fileName,
string $user,
PyStringNode $content
):void {
$this->contentOfFileForUserShouldBe($fileName, $user, $content->getRaw());
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" using password "([^"]*)" should be:$/
*
* @param string $fileName
* @param string $user
* @param string|null $password
* @param PyStringNode $content
*
* @return void
*/
public function contentOfFileForUserUsingPasswordShouldBePyString(
string $fileName,
string $user,
?string $password,
PyStringNode $content
):void {
$this->contentOfFileForUserUsingPasswordShouldBe(
$fileName,
$user,
$password,
$content->getRaw()
);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" should be "([^"]*)" plus end-of-line$/
*
* @param string $fileName
* @param string $user
* @param string $content
*
* @return void
*/
public function contentOfFileForUserShouldBePlusEndOfLine(string $fileName, string $user, string $content):void {
$this->contentOfFileForUserShouldBe(
$fileName,
$user,
"$content\n"
);
}
/**
* @Then the content of the following files for user :user should be the following plus end-of-line
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theContentOfTheFollowingFilesForUserShouldBeTheFollowingPlusEndOfLine(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["filename", "content"]);
$rows = $table->getHash();
foreach ($rows as $row) {
$this->contentOfFileForUserShouldBePlusEndOfLine($row["filename"], $user, $row["content"]);
}
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" on server "([^"]*)" should be "([^"]*)" plus end-of-line$/
*
* @param string $fileName
* @param string $user
* @param string $server
* @param string $content
*
* @return void
*/
public function theContentOfFileForUserOnServerShouldBePlusEndOfLine(
string $fileName,
string $user,
string $server,
string $content
):void {
$previousServer = $this->usingServer($server);
$this->contentOfFileForUserShouldBePlusEndOfLine($fileName, $user, $content);
$this->usingServer($previousServer);
}
/**
* @Then /^the content of file "([^"]*)" for user "([^"]*)" using password "([^"]*)" should be "([^"]*)" plus end-of-line$/
*
* @param string $fileName
* @param string $user
* @param string|null $password
* @param string $content
*
* @return void
*/
public function contentOfFileForUserUsingPasswordShouldBePlusEndOfLine(
string $fileName,
string $user,
?string $password,
string $content
):void {
$user = $this->getActualUsername($user);
$this->contentOfFileForUserUsingPasswordShouldBe(
$fileName,
$user,
$password,
"$content\n"
);
}
/**
* @Then /^the downloaded content when downloading file "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileSource
* @param string $range
* @param string $content
*
* @return void
*/
public function downloadedContentWhenDownloadingWithRangeShouldBe(
string $fileSource,
string $range,
string $content
):void {
$this->downloadFileWithRange($fileSource, $range);
$this->downloadedContentShouldBe($content);
}
/**
* @Then /^the downloaded content when downloading file "([^"]*)" for user "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/
*
* @param string $fileSource
* @param string $user
* @param string $range
* @param string $content
*
* @return void
*/
public function downloadedContentWhenDownloadingForUserWithRangeShouldBe(
string $fileSource,
string $user,
string $range,
string $content
):void {
$user = $this->getActualUsername($user);
$response = $this->downloadFileWithRange($user, $fileSource, $range);
$this->checkDownloadedContentMatches($content, '', $response);
}
/**
* @When the user downloads the file :fileName using the WebDAV API
*
* @param string $fileName
*
* @return void
*/
public function theUserDownloadsTheFileUsingTheAPI(string $fileName):void {
$this->setResponse($this->downloadFileAsUserUsingPassword($this->currentUser, $fileName));
}
/**
* @When user :user downloads file :fileName using the WebDAV API
*
* @param string $user
* @param string $fileName
*
* @return void
*/
public function userDownloadsFileUsingTheAPI(
string $user,
string $fileName
):void {
$this->setResponse($this->downloadFileAsUserUsingPassword($user, $fileName));
}
/**
* @When user :user using password :password downloads the file :fileName using the WebDAV API
*
* @param string $user
* @param string|null $password
* @param string $fileName
*
* @return void
*/
public function userUsingPasswordDownloadsTheFileUsingTheAPI(
string $user,
?string $password,
string $fileName
):void {
$this->setResponse($this->downloadFileAsUserUsingPassword($user, $fileName, $password));
}
/**
* @param string $user
* @param string $fileName
* @param string|null $password
* @param array|null $headers
*
* @return ResponseInterface
*/
public function downloadFileAsUserUsingPassword(
string $user,
string $fileName,
?string $password = null,
?array $headers = []
):ResponseInterface {
$user = $this->getActualUsername($user);
$password = $this->getActualPassword($password);
return $this->makeDavRequest(
$user,
'GET',
$fileName,
$headers,
null,
"files",
null,
false,
$password
);
}
/**
* @When the public gets the size of the last shared public link using the WebDAV API
*
* @return void
* @throws Exception
*/
public function publicGetsSizeOfLastSharedPublicLinkUsingTheWebdavApi():void {
$tokenArray = $this->getLastCreatedPublicShare()->token;
$token = (string)$tokenArray[0];
$url = $this->getBaseUrl() . "/remote.php/dav/public-files/$token";
$this->response = HttpRequestHelper::sendRequest(
$url,
$this->getStepLineRef(),
"PROPFIND"
);
}
/**
* @When user :user gets the size of file :resource using the WebDAV API
*
* @param string $user
* @param string $resource
*
* @return void
* @throws Exception
*/
public function userGetsSizeOfFileUsingTheWebdavApi(string $user, string $resource):void {
$user = $this->getActualUsername($user);
$password = $this->getPasswordForUser($user);
$this->response = WebDavHelper::propfind(
$this->getBaseUrl(),
$user,
$password,
$resource,
[],
$this->getStepLineRef(),
"0",
"files",
$this->getDavPathVersion()
);
}
/**
* @Then the size of the file should be :size
*
* @param string $size
*
* @return void
* @throws Exception
*/
public function theSizeOfTheFileShouldBe(string $size):void {
$responseXml = HttpRequestHelper::getResponseXml(
$this->response,
__METHOD__
);
$xmlPart = $responseXml->xpath("//d:prop/d:getcontentlength");
$actualSize = (string) $xmlPart[0];
Assert::assertEquals(
$size,
$actualSize,
__METHOD__
. " Expected size of the file was '$size', but got '$actualSize' instead."
);
}
/**
* @Then the following headers should be set
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theFollowingHeadersShouldBeSet(TableNode $table):void {
$this->verifyTableNodeColumns(
$table,
['header', 'value']
);
foreach ($table->getColumnsHash() as $header) {
$headerName = $header['header'];
$expectedHeaderValue = $header['value'];
$returnedHeader = $this->response->getHeader($headerName);
$expectedHeaderValue = $this->substituteInLineCodes($expectedHeaderValue);
if (empty($returnedHeader)) {
throw new Exception(
\sprintf(
"Missing expected header '%s'",
$headerName
)
);
}
$headerValue = $returnedHeader[0];
Assert::assertEquals(
$expectedHeaderValue,
$headerValue,
__METHOD__
. " Expected value for header '$headerName' was '$expectedHeaderValue', but got '$headerValue' instead."
);
}
}
/**
* @Then the downloaded content should start with :start
*
* @param string $start
*
* @return void
* @throws Exception
*/
public function downloadedContentShouldStartWith(string $start):void {
Assert::assertEquals(
0,
\strpos($this->response->getBody()->getContents(), $start),
__METHOD__
. " The downloaded content was expected to start with '$start', but actually started with '{$this->response->getBody()->getContents()}'"
);
}
/**
* @Then the oc job status values of last request for user :user should match these regular expressions
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function jobStatusValuesShouldMatchRegEx(string $user, TableNode $table):void {
$user = $this->getActualUsername($user);
$this->verifyTableNodeColumnsCount($table, 2);
$headerArray = $this->response->getHeader("OC-JobStatus-Location");
$url = $headerArray[0];
$url = $this->getBaseUrlWithoutPath() . $url;
$response = HttpRequestHelper::get(
$url,
$this->getStepLineRef(),
$user,
$this->getPasswordForUser($user)
);
$contents = $response->getBody()->getContents();
$result = \json_decode($contents, true);
PHPUnit\Framework\Assert::assertNotNull($result, "'$contents' is not valid JSON");
foreach ($table->getTable() as $row) {
$expectedKey = $row[0];
Assert::assertArrayHasKey(
$expectedKey,
$result,
"response does not have expected key '$expectedKey'"
);
$expectedValue = $this->substituteInLineCodes(
$row[1],
$user,
['preg_quote' => ['/']]
);
Assert::assertNotFalse(
(bool) \preg_match($expectedValue, (string)$result[$expectedKey]),
"'$expectedValue' does not match '$result[$expectedKey]'"
);
}
}
/**
* @Then /^as "([^"]*)" (file|folder|entry) "([^"]*)" should not exist$/
*
* @param string $user
* @param string $entry
* @param string|null $path
* @param string $type
*
* @return ResponseInterface
* @throws Exception
*/
public function asFileOrFolderShouldNotExist(
string $user,
string $entry = "file",
?string $path = null,
string $type = "files"
):ResponseInterface {
$user = $this->getActualUsername($user);
$path = $this->substituteInLineCodes($path);
$response = $this->listFolder(
$user,
$path,
'0',
null,
$type
);
$statusCode = $response->getStatusCode();
if ($statusCode < 401 || $statusCode > 404) {
try {
$responseXml = $this->featureContext->getResponseXml(
$response,
__METHOD__
);
} catch (Exception $e) {
Assert::fail(
"$entry '$path' should not exist. But API returned $statusCode without XML in the body"
);
}
Assert::assertTrue(
$this->isEtagValid($this->getEtagFromResponseXmlObject($responseXml)),
"$entry '$path' should not exist. But API returned $statusCode without an etag in the body"
);
$isCollection = $responseXml->xpath("//d:prop/d:resourcetype/d:collection");
if (\count($isCollection) === 0) {
$actualResourceType = "file";
} else {
$actualResourceType = "folder";
}
Assert::fail(
"$entry '$path' should not exist. But it does exist and is a $actualResourceType"
);
}
return $response;
}
/**
* @Then /^as "([^"]*)" the following (files|folders) should not exist$/
*
* @param string $user
* @param string $entry
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function followingFilesShouldNotExist(
string $user,
string $entry,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
$entry = \rtrim($entry, "s");
foreach ($paths as $file) {
$this->asFileOrFolderShouldNotExist($user, $entry, $file["path"]);
}
}
/**
* @Then /^as "([^"]*)" (file|folder|entry) "([^"]*)" should exist$/
*
* @param string $user
* @param string $entry
* @param string $path
* @param string $type
*
* @return void
* @throws Exception
*/
public function asFileOrFolderShouldExist(
string $user,
string $entry,
string $path,
string $type = "files"
):void {
$user = $this->getActualUsername($user);
$path = $this->substituteInLineCodes($path);
$responseXml = $this->listFolderAndReturnResponseXml(
$user,
$path,
'0',
null,
$type
);
Assert::assertTrue(
$this->isEtagValid($this->getEtagFromResponseXmlObject($responseXml)),
"$entry '$path' expected to exist for user $user but not found"
);
$isCollection = $responseXml->xpath("//d:prop/d:resourcetype/d:collection");
if ($entry === "folder") {
Assert::assertEquals(\count($isCollection), 1, "Unexpectedly, `$path` is not a folder");
} elseif ($entry === "file") {
Assert::assertEquals(\count($isCollection), 0, "Unexpectedly, `$path` is not a file");
}
}
/**
* @Then /^as "([^"]*)" the following (files|folders) should exist$/
*
* @param string $user
* @param string $entry
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function followingFilesOrFoldersShouldExist(
string $user,
string $entry,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
$entry = \rtrim($entry, "s");
foreach ($paths as $file) {
$this->asFileOrFolderShouldExist($user, $entry, $file["path"]);
}
}
/**
*
* @param string $user
* @param string $entry
* @param string $path
* @param string $type
*
* @return bool
*/
public function fileOrFolderExists(
string $user,
string $entry,
string $path,
string $type = "files"
):bool {
try {
$this->asFileOrFolderShouldExist($user, $entry, $path, $type);
return true;
} catch (Exception $e) {
return false;
}
}
/**
* @Then /^as "([^"]*)" exactly one of these (files|folders|entries) should exist$/
*
* @param string $user
* @param string $entries
* @param TableNode $table of file, folder or entry paths
*
* @return void
* @throws Exception
*/
public function asExactlyOneOfTheseFilesOrFoldersShouldExist(string $user, string $entries, TableNode $table):void {
$numEntriesThatExist = 0;
foreach ($table->getTable() as $row) {
$path = $this->substituteInLineCodes($row[0]);
$responseXml = $this->listFolderAndReturnResponseXml(
$user,
$path,
'0'
);
if ($this->isEtagValid($this->getEtagFromResponseXmlObject($responseXml))) {
$numEntriesThatExist = $numEntriesThatExist + 1;
}
}
Assert::assertEquals(
1,
$numEntriesThatExist,
"exactly one of these $entries should exist but found $numEntriesThatExist $entries"
);
}
/**
*
* @param string $user
* @param string $path
* @param string $folderDepth requires 1 to see elements without children
* @param array|null $properties
* @param string $type
*
* @return ResponseInterface
* @throws Exception
*/
public function listFolder(
string $user,
string $path,
string $folderDepth,
?array $properties = null,
string $type = "files"
):ResponseInterface {
if ($this->customDavPath !== null) {
$path = $this->customDavPath . $path;
}
return WebDavHelper::listFolder(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getPasswordForUser($user),
$path,
$folderDepth,
$this->getStepLineRef(),
$properties,
$type,
$this->getDavPathVersion()
);
}
/**
*
* @param string $user
* @param string $path
* @param string $folderDepth requires 1 to see elements without children
* @param array|null $properties
* @param string $type
*
* @return SimpleXMLElement
* @throws Exception
*/
public function listFolderAndReturnResponseXml(
string $user,
string $path,
string $folderDepth,
?array $properties = null,
string $type = "files"
):SimpleXMLElement {
return HttpRequestHelper::getResponseXml(
$this->listFolder(
$user,
$path,
$folderDepth,
$properties,
$type
),
__METHOD__
);
}
/**
* @Then /^user "([^"]*)" should (not|)\s?see the following elements$/
*
* @param string $user
* @param string $shouldOrNot
* @param TableNode $elements
*
* @return void
* @throws InvalidArgumentException|Exception
*
*/
public function userShouldSeeTheElements(string $user, string $shouldOrNot, TableNode $elements):void {
$should = ($shouldOrNot !== "not");
$this->checkElementList($user, $elements, $should);
}
/**
* @Then /^user "([^"]*)" should not see the following elements if the upper and lower case username are different/
*
* @param string $user
* @param TableNode $elements
*
* @return void
* @throws InvalidArgumentException|Exception
*
*/
public function userShouldNotSeeTheElementsIfUpperAndLowerCaseUsernameDifferent(string $user, TableNode $elements):void {
$effectiveUser = $this->getActualUsername($user);
if (\strtoupper($effectiveUser) === \strtolower($effectiveUser)) {
$expectedToBeListed = true;
} else {
$expectedToBeListed = false;
}
$this->checkElementList($user, $elements, $expectedToBeListed);
}
/**
* asserts that the user can or cannot see a list of files/folders by propfind
*
* @param string $user
* @param TableNode $elements
* @param boolean $expectedToBeListed
*
* @return void
* @throws InvalidArgumentException
* @throws Exception
*
*/
public function checkElementList(
string $user,
TableNode $elements,
bool $expectedToBeListed = true
):void {
$user = $this->getActualUsername($user);
$this->verifyTableNodeColumnsCount($elements, 1);
$elementRows = $elements->getRows();
$elementsSimplified = $this->simplifyArray($elementRows);
if ($this->davPropfindDepthInfinityIsEnabled()) {
// get a full "infinite" list of the user's root folder in one request
// and process that to check the elements (resources)
$responseXmlObject = $this->listFolderAndReturnResponseXml(
$user,
"/",
);
foreach ($elementsSimplified as $expectedElement) {
// 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
$expectedElement = $this->encodePath($expectedElement);
$expectedElement = "/" . \ltrim($expectedElement, "/");
$webdavPath = "/" . $this->getFullDavFilesPath($user) . $expectedElement;
$element = $responseXmlObject->xpath(
"//d:response/d:href[text() = \"$webdavPath\"]"
);
if ($expectedToBeListed
&& (!isset($element[0]) || urldecode($element[0]->__toString()) !== urldecode($webdavPath))
) {
Assert::fail(
"$webdavPath is not in propfind answer but should be"
);
} elseif (!$expectedToBeListed && isset($element[0])
) {
Assert::fail(
"$webdavPath is in propfind answer but should not be"
);
}
}
} else {
// do a PROPFIND for each element
foreach ($elementsSimplified as $elementToRequest) {
// 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
$elementToRequest = "/" . \ltrim($elementToRequest, "/");
// Note: in the request we ask to do a PROPFIND on a resource like:
// /some-folder with spaces/sub-folder
// but the response has encoded values for the special characters like:
// /some-folder%20with%20spaces/sub-folder
// So we need both $elementToRequest and $expectedElement
$expectedElement = $this->encodePath($elementToRequest);
$responseXmlObject = $this->listFolderAndReturnResponseXml(
$user,
$elementToRequest,
"1"
);
$webdavPath = "/" . $this->getFullDavFilesPath($user) . $expectedElement;
$element = $responseXmlObject->xpath(
"//d:response/d:href[text() = \"$webdavPath\"]"
);
if ($expectedToBeListed
&& (!isset($element[0]) || urldecode($element[0]->__toString()) !== urldecode($webdavPath))
) {
Assert::fail(
"$webdavPath is not in propfind answer but should be"
);
} elseif (!$expectedToBeListed && isset($element[0])
) {
Assert::fail(
"$webdavPath is in propfind answer but should not be"
);
}
}
}
}
/**
* @param string $user
* @param string $source
* @param string $destination
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
public function uploadFile(
string $user,
string $source,
string $destination,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
$file = \fopen($this->acceptanceTestsDirLocation() . $source, 'r');
$this->pauseUploadDelete();
$response = $this->makeDavRequest(
$user,
"PUT",
$destination,
[],
$file,
"files",
null,
false,
null,
[],
null,
$isGivenStep
);
$this->lastUploadDeleteTime = \time();
return $response;
}
/**
* @When user :user uploads file :source to :destination using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
*/
public function userUploadsAFileToUsingWebDavApi(
string $user,
string $source,
string $destination
):void {
$response = $this->uploadFile($user, $source, $destination);
$this->setResponse($response);
$this->setResponseXml(
HttpRequestHelper::parseResponseAsXml($response)
);
$this->pushToLastHttpStatusCodesArray(
(string) $this->getResponse()->getStatusCode()
);
}
/**
* @Given user :user has uploaded file :source to :destination
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
*/
public function userHasUploadedAFileTo(string $user, string $source, string $destination):void {
$response = $this->uploadFile($user, $source, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$source' to '$destination' for user '$user'",
$response
);
}
/**
* @When the user uploads file :source to :destination using the WebDAV API
*
* @param string $source
* @param string $destination
*
* @return void
*/
public function theUserUploadsAFileTo(
string $source,
string $destination
):void {
$this->userUploadsAFileToUsingWebDavApi($this->currentUser, $source, $destination);
}
/**
* @Given the user has uploaded file :source to :destination
*
* @param string $source
* @param string $destination
*
* @return void
*/
public function theUserHasUploadedFileTo(string $source, string $destination):void {
$response = $this->uploadFile($this->getCurrentUser(), $source, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$source' to '$destination'",
$response
);
}
/**
* @When /^user "([^"]*)" on "(LOCAL|REMOTE)" uploads file "([^"]*)" to "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $server
* @param string $source
* @param string $destination
*
* @return void
*/
public function userOnUploadsAFileTo(
string $user,
string $server,
string $source,
string $destination
):void {
$previousServer = $this->usingServer($server);
$this->userUploadsAFileToUsingWebDavApi($user, $source, $destination);
$this->usingServer($previousServer);
}
/**
* @Given /^user "([^"]*)" on "(LOCAL|REMOTE)" has uploaded file "([^"]*)" to "([^"]*)"$/
*
* @param string $user
* @param string $server
* @param string $source
* @param string $destination
*
* @return void
*/
public function userOnHasUploadedAFileTo(string $user, string $server, string $source, string $destination):void {
$this->userOnUploadsAFileTo($user, $server, $source, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$source' to '$destination' for user '$user' on server '$server'"
);
}
/**
* Upload file as a user with different headers
*
* @param string $user
* @param string $source
* @param string $destination
* @param array|null $headers
* @param int|null $noOfChunks Only use for chunked upload when $this->chunkingToUse is not null
*
* @return void
* @throws Exception
*/
public function uploadFileWithHeaders(
string $user,
string $source,
string $destination,
?array $headers = [],
?int $noOfChunks = 0
):void {
$chunkingVersion = $this->chunkingToUse;
if ($noOfChunks <= 0) {
$chunkingVersion = null;
}
try {
$this->responseXml = [];
$this->pauseUploadDelete();
$this->response = UploadHelper::upload(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getUserPassword($user),
$source,
$destination,
$this->getStepLineRef(),
$headers,
$this->getDavPathVersion(),
$chunkingVersion,
$noOfChunks
);
$this->lastUploadDeleteTime = \time();
} catch (BadResponseException $e) {
// 4xx and 5xx responses cause an exception
$this->response = $e->getResponse();
}
}
/**
* @When /^user "([^"]*)" uploads file "([^"]*)" to "([^"]*)" in (\d+) chunks (?:with (new|old|v1|v2) chunking and)?\s?using the WebDAV API$/
* @When user :user uploads file :source to :destination with chunks using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
* @param int $noOfChunks
* @param string|null $chunkingVersion old|v1|new|v2 null for autodetect
* @param bool $async use asynchronous move at the end or not
* @param array|null $headers
*
* @return void
* @throws Exception
*/
public function userUploadsAFileToWithChunks(
string $user,
string $source,
string $destination,
int $noOfChunks = 2,
?string $chunkingVersion = null,
bool $async = false,
?array $headers = []
):void {
$user = $this->getActualUsername($user);
Assert::assertGreaterThan(
0,
$noOfChunks,
"What does it mean to have $noOfChunks chunks?"
);
//use the chunking version that works with the set DAV version
if ($chunkingVersion === null) {
if ($this->usingOldDavPath || $this->usingSpacesDavPath) {
$chunkingVersion = "v1";
} else {
$chunkingVersion = "v2";
}
}
$this->useSpecificChunking($chunkingVersion);
Assert::assertTrue(
WebDavHelper::isValidDavChunkingCombination(
$this->getDavPathVersion(),
$this->chunkingToUse
),
"invalid chunking/webdav version combination"
);
if ($async === true) {
$headers['OC-LazyOps'] = 'true';
}
$this->uploadFileWithHeaders(
$user,
$this->acceptanceTestsDirLocation() . $source,
$destination,
$headers,
$noOfChunks
);
$this->pushToLastStatusCodesArrays();
}
/**
* @When /^user "([^"]*)" uploads file "([^"]*)" asynchronously to "([^"]*)" in (\d+) chunks (?:with (new|old|v1|v2) chunking and)?\s?using the WebDAV API$/
*
* @param string $user
* @param string $source
* @param string $destination
* @param int $noOfChunks
* @param string|null $chunkingVersion old|v1|new|v2 null for autodetect
*
* @return void
* @throws Exception
*/
public function userUploadsAFileAsyncToWithChunks(
string $user,
string $source,
string $destination,
int $noOfChunks = 2,
?string $chunkingVersion = null
):void {
$user = $this->getActualUsername($user);
$this->userUploadsAFileToWithChunks(
$user,
$source,
$destination,
$noOfChunks,
$chunkingVersion,
true
);
}
/**
* sets the chunking version from human-readable format
*
* @param string $version (no|v1|v2|new|old)
*
* @return void
*/
public function useSpecificChunking(string $version):void {
if ($version === "v1" || $version === "old") {
$this->chunkingToUse = 1;
} elseif ($version === "v2" || $version === "new") {
$this->chunkingToUse = 2;
} elseif ($version === "no") {
$this->chunkingToUse = null;
} else {
throw new InvalidArgumentException(
"cannot set chunking version to $version"
);
}
}
/**
* Uploading with old/new DAV and chunked/non-chunked.
*
* @When user :user uploads file :source to filenames based on :destination with all mechanisms using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userUploadsAFileToWithAllMechanisms(
string $user,
string $source,
string $destination
):void {
$user = $this->getActualUsername($user);
$this->uploadResponses = UploadHelper::uploadWithAllMechanisms(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getUserPassword($user),
$this->acceptanceTestsDirLocation() . $source,
$destination,
$this->getStepLineRef()
);
}
/**
* Uploading with old/new DAV and chunked/non-chunked.
* Except do not do the new-DAV-new-chunking combination. That is not being
* supported on all implementations.
*
* @When user :user uploads file :source to filenames based on :destination with all mechanisms except new chunking using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userUploadsAFileToWithAllMechanismsExceptNewChunking(
string $user,
string $source,
string $destination
):void {
$user = $this->getActualUsername($user);
$this->uploadResponses = UploadHelper::uploadWithAllMechanisms(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getUserPassword($user),
$this->acceptanceTestsDirLocation() . $source,
$destination,
$this->getStepLineRef(),
false,
'new'
);
}
/**
* Overwriting with old/new DAV and chunked/non-chunked.
*
* @When user :user overwrites from file :source to file :destination with all mechanisms using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userOverwritesAFileToWithAllMechanisms(
string $user,
string $source,
string $destination
):void {
$user = $this->getActualUsername($user);
$this->uploadResponses = UploadHelper::uploadWithAllMechanisms(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getUserPassword($user),
$this->acceptanceTestsDirLocation() . $source,
$destination,
$this->getStepLineRef(),
true
);
}
/**
* Overwriting with old/new DAV and chunked/non-chunked.
* Except do not do the new-DAV-new-chunking combination. That is not being
* supported on all implementations.
*
* @When user :user overwrites from file :source to file :destination with all mechanisms except new chunking using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userOverwritesAFileToWithAllMechanismsExceptNewChunking(
string $user,
string $source,
string $destination
):void {
$user = $this->getActualUsername($user);
$this->uploadResponses = UploadHelper::uploadWithAllMechanisms(
$this->getBaseUrl(),
$this->getActualUsername($user),
$this->getUserPassword($user),
$this->acceptanceTestsDirLocation() . $source,
$destination,
$this->getStepLineRef(),
true,
'new'
);
}
/**
* @Then /^the HTTP status code of all upload responses should be "([^"]*)"$/
*
* @param int $statusCode
*
* @return void
*/
public function theHTTPStatusCodeOfAllUploadResponsesShouldBe(int $statusCode):void {
foreach ($this->uploadResponses as $response) {
Assert::assertEquals(
$statusCode,
$response->getStatusCode(),
'Response did not return expected status code'
);
}
}
/**
* @Then the HTTP status code of responses on all endpoints should be :statusCode
*
* @param int $statusCode
*
* @return void
* @throws Exception
*/
public function theHTTPStatusCodeOfResponsesOnAllEndpointsShouldBe(int $statusCode):void {
$duplicateRemovedStatusCodes = \array_unique($this->lastHttpStatusCodesArray);
if (\count($duplicateRemovedStatusCodes) === 1) {
Assert::assertSame(
$statusCode,
\intval($duplicateRemovedStatusCodes[0]),
'Responses did not return expected http status code'
);
$this->emptyLastHTTPStatusCodesArray();
} else {
throw new Exception(
'Expected same but found different http status codes of last requested responses.' .
'Found status codes: ' . \implode(',', $this->lastHttpStatusCodesArray)
);
}
}
/**
* @Then the HTTP status code of responses on each endpoint should be :statusCode respectively
*
* @param string $statusCodes
*
* @return void
* @throws Exception
*/
public function theHTTPStatusCodeOfResponsesOnEachEndpointShouldBe(string $statusCodes):void {
$statusCodes = \explode(',', $statusCodes);
$count = \count($statusCodes);
if ($count === \count($this->lastHttpStatusCodesArray)) {
for ($i = 0; $i < $count; $i++) {
Assert::assertSame(
(int)\trim($statusCodes[$i]),
(int)$this->lastHttpStatusCodesArray[$i],
'Responses did not return expected HTTP status code'
);
}
$this->emptyLastHTTPStatusCodesArray();
} else {
throw new Exception(
'Expected HTTP status codes: "' . \implode(',', $statusCodes) .
'". Found HTTP status codes: "' . \implode(',', $this->lastHttpStatusCodesArray) . '"'
);
}
}
/**
* @Then the OCS status code of responses on each endpoint should be :statusCode respectively
*
* @param string $statusCodes
*
* @return void
* @throws Exception
*/
public function theOCStatusCodeOfResponsesOnEachEndpointShouldBe(string $statusCodes):void {
$statusCodes = \explode(',', $statusCodes);
$count = \count($statusCodes);
if ($count === \count($this->lastOCSStatusCodesArray)) {
for ($i = 0; $i < $count; $i++) {
Assert::assertSame(
(int)\trim($statusCodes[$i]),
(int)$this->lastOCSStatusCodesArray[$i],
'Responses did not return expected OCS status code'
);
}
} else {
throw new Exception(
'Expected OCS status codes: "' . \implode(',', $statusCodes) .
'". Found OCS status codes: "' . \implode(',', $this->lastOCSStatusCodesArray) . '"'
);
}
}
/**
* @Then the HTTP status code of responses on all endpoints should be :statusCode1 or :statusCode2
*
* @param string $statusCode1
* @param string $statusCode2
*
* @return void
* @throws Exception
*/
public function theHTTPStatusCodeOfResponsesOnAllEndpointsShouldBeOr(string $statusCode1, string $statusCode2):void {
$duplicateRemovedStatusCodes = \array_unique($this->lastHttpStatusCodesArray);
foreach ($duplicateRemovedStatusCodes as $status) {
$status = (string)$status;
if (($status != $statusCode1) && ($status != $statusCode2)) {
Assert::fail("Unexpected status code received " . $status);
}
}
}
/**
* @Then the OCS status code of responses on all endpoints should be :statusCode
*
* @param string $statusCode
*
* @return void
* @throws Exception
*/
public function theOCSStatusCodeOfResponsesOnAllEndpointsShouldBe(string $statusCode):void {
$duplicateRemovedStatusCodes = \array_unique($this->lastOCSStatusCodesArray);
if (\count($duplicateRemovedStatusCodes) === 1) {
Assert::assertSame(
\intval($statusCode),
\intval($duplicateRemovedStatusCodes[0]),
'Responses did not return expected ocs status code'
);
$this->emptyLastOCSStatusCodesArray();
} else {
throw new Exception(
'Expected same but found different ocs status codes of last requested responses.' .
'Found status codes: ' . \implode(',', $this->lastOCSStatusCodesArray)
);
}
}
/**
* @Then /^the HTTP reason phrase of all upload responses should be "([^"]*)"$/
*
* @param string $reasonPhrase
*
* @return void
*/
public function theHTTPReasonPhraseOfAllUploadResponsesShouldBe(string $reasonPhrase):void {
foreach ($this->uploadResponses as $response) {
Assert::assertEquals(
$reasonPhrase,
$response->getReasonPhrase(),
'Response did not return expected reason phrase'
);
}
}
/**
* @Then user :user should be able to upload file :source to :destination
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userShouldBeAbleToUploadFileTo(string $user, string $source, string $destination):void {
$user = $this->getActualUsername($user);
$response = $this->uploadFile($user, $source, $destination);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$destination'",
$response
);
$this->asFileOrFolderShouldExist($user, "file", $destination);
}
/**
* @Then the following users should be able to upload file :source to :destination
*
* @param string $source
* @param string $destination
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function usersShouldBeAbleToUploadFileTo(
string $source,
string $destination,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["username"]);
$usernames = $table->getHash();
foreach ($usernames as $username) {
$actualUser = $this->getActualUsername($username["username"]);
$this->uploadFile($actualUser, $source, $destination);
$this->asFileOrFolderShouldExist($actualUser, "file", $destination);
}
}
/**
* @Then user :user should not be able to upload file :source to :destination
*
* @param string $user
* @param string $source
* @param string $destination
*
* @return void
* @throws Exception
*/
public function theUserShouldNotBeAbleToUploadFileTo(string $user, string $source, string $destination):void {
$fileAlreadyExists = $this->fileOrFolderExists($user, "file", $destination);
if ($fileAlreadyExists) {
$response = $this->downloadFileAsUserUsingPassword($user, $destination);
$initialContent = (string) $response->getBody();
}
$response = $this->uploadFile($user, $source, $destination);
$this->theHTTPStatusCodeShouldBe(["403", "423"], "", $response);
if ($fileAlreadyExists) {
$response = $this->downloadFileAsUserUsingPassword($user, $destination);
$currentContent = (string) $response->getBody();
Assert::assertSame(
$initialContent,
$currentContent,
__METHOD__ . " user $user was unexpectedly able to upload $source to $destination - the content has changed:"
);
} else {
$this->asFileOrFolderShouldNotExist($user, "file", $destination);
}
}
/**
* @Then /^the HTTP status code of all upload responses should be between "(\d+)" and "(\d+)"$/
*
* @param int $minStatusCode
* @param int $maxStatusCode
*
* @return void
*/
public function theHTTPStatusCodeOfAllUploadResponsesShouldBeBetween(
int $minStatusCode,
int $maxStatusCode
):void {
foreach ($this->uploadResponses as $response) {
Assert::assertGreaterThanOrEqual(
$minStatusCode,
$response->getStatusCode(),
'Response did not return expected status code'
);
Assert::assertLessThanOrEqual(
$maxStatusCode,
$response->getStatusCode(),
'Response did not return expected status code'
);
}
}
/**
* Check that all the files uploaded with old/new DAV and chunked/non-chunked exist.
*
* @Then /^as "([^"]*)" the files uploaded to "([^"]*)" with all mechanisms should (not|)\s?exist$/
*
* @param string $user
* @param string $destination
* @param string $shouldOrNot
* @param string|null $exceptChunkingType empty string or "old" or "new"
*
* @return void
* @throws Exception
*/
public function filesUploadedToWithAllMechanismsShouldExist(
string $user,
string $destination,
string $shouldOrNot,
?string $exceptChunkingType = ''
):void {
switch ($exceptChunkingType) {
case 'old':
$exceptChunkingSuffix = 'olddav-oldchunking';
break;
case 'new':
$exceptChunkingSuffix = 'newdav-newchunking';
break;
default:
$exceptChunkingSuffix = '';
break;
}
if ($shouldOrNot !== "not") {
foreach (['old', 'new'] as $davVersion) {
foreach (["{$davVersion}dav-regular", "{$davVersion}dav-{$davVersion}chunking"] as $suffix) {
if ($suffix !== $exceptChunkingSuffix) {
$this->asFileOrFolderShouldExist(
$user,
'file',
"$destination-$suffix"
);
}
}
}
} else {
foreach (['old', 'new'] as $davVersion) {
foreach (["{$davVersion}dav-regular", "{$davVersion}dav-{$davVersion}chunking"] as $suffix) {
if ($suffix !== $exceptChunkingSuffix) {
$this->asFileOrFolderShouldNotExist(
$user,
'file',
"$destination-$suffix"
);
}
}
}
}
}
/**
* Check that all the files uploaded with old/new DAV and chunked/non-chunked exist.
* Except do not check the new-DAV-new-chunking combination. That is not being
* supported on all implementations.
*
* @Then /^as "([^"]*)" the files uploaded to "([^"]*)" with all mechanisms except new chunking should (not|)\s?exist$/
*
* @param string $user
* @param string $destination
* @param string $shouldOrNot
*
* @return void
* @throws Exception
*/
public function filesUploadedToWithAllMechanismsExceptNewChunkingShouldExist(
string $user,
string $destination,
string $shouldOrNot
):void {
$this->filesUploadedToWithAllMechanismsShouldExist(
$user,
$destination,
$shouldOrNot,
'new'
);
}
/**
* @Then /^as user "([^"]*)" on server "([^"]*)" the files uploaded to "([^"]*)" with all mechanisms should (not|)\s?exist$/
*
* @param string $user
* @param string $server
* @param string $destination
* @param string $shouldOrNot
*
* @return void
* @throws Exception
*/
public function asUserOnServerTheFilesUploadedToWithAllMechanismsShouldExit(
string $user,
string $server,
string $destination,
string $shouldOrNot
):void {
$previousServer = $this->usingServer($server);
$this->filesUploadedToWithAllMechanismsShouldExist($user, $destination, $shouldOrNot);
$this->usingServer($previousServer);
}
/**
* @Given user :user has uploaded file :destination of size :bytes bytes
*
* @param string $user
* @param string $destination
* @param string $bytes
*
* @return void
* @throws Exception
*/
public function userHasUploadedFileToOfSizeBytes(string $user, string $destination, string $bytes):void {
$user = $this->getActualUsername($user);
$this->userUploadsAFileToOfSizeBytes($user, $destination, $bytes, true);
$expectedElements = new TableNode([["$destination"]]);
$this->checkElementList($user, $expectedElements);
}
/**
* @When user :user uploads file :destination of size :bytes bytes
*
* @param string $user
* @param string $destination
* @param string $bytes
* @param bool|null $isGivenStep
*
* @return void
*/
public function userUploadsAFileToOfSizeBytes(
string $user,
string $destination,
string $bytes,
?bool $isGivenStep = false
):void {
$this->userUploadsAFileToEndingWithOfSizeBytes(
$user,
$destination,
'a',
$bytes,
$isGivenStep
);
}
/**
* @Given user :user has uploaded file :destination ending with :text of size :bytes bytes
*
* @param string $user
* @param string $destination
* @param string $text
* @param string $bytes
*
* @return void
* @throws Exception
*/
public function userHasUploadedFileToEndingWithOfSizeBytes(string $user, string $destination, string $text, string $bytes):void {
$this->userUploadsAFileToEndingWithOfSizeBytes($user, $destination, $text, $bytes, true);
$expectedElements = new TableNode([["$destination"]]);
$this->checkElementList($user, $expectedElements);
}
/**
* @When user :user uploads file :destination ending with :text of size :bytes bytes
*
* @param string $user
* @param string $destination
* @param string $text
* @param string $bytes
*
* @return void
*/
public function userUploadsAFileToEndingWithOfSizeBytes(
string $user,
string $destination,
string $text,
string $bytes
):void {
$filename = "filespecificSize.txt";
$this->createLocalFileOfSpecificSize($filename, $bytes, $text);
Assert::assertFileExists($this->workStorageDirLocation() . $filename);
$this->userUploadsAFileToUsingWebDavApi(
$user,
$this->temporaryStorageSubfolderName() . "/$filename",
$destination
);
$this->removeFile($this->workStorageDirLocation(), $filename);
}
/**
* @When user :user uploads to these filenames with content :content using the webDAV API then the results should be as listed
*
* @param string $user
* @param string $content
* @param TableNode $table
*
* @return void
* @throws Exception
* @throws GuzzleException
*/
public function userUploadsFilesWithContentTo(
string $user,
string $content,
TableNode $table
):void {
$user = $this->getActualUsername($user);
foreach ($table->getHash() as $row) {
$this->userUploadsAFileWithContentTo(
$user,
$content,
$row['filename']
);
$this->theHTTPStatusCodeShouldBe(
$row['http-code'],
"HTTP status code was not the expected value " . $row['http-code'] . " while trying to upload " . $row['filename']
);
if ($row['exists'] === "yes") {
$this->asFileOrFolderShouldExist($user, "file", $row['filename']);
} else {
$this->asFileOrFolderShouldNotExist($user, "file", $row['filename']);
}
}
}
/**
* @param string $user
* @param string|null $content
* @param string $destination
* @param bool|null $isGivenStep
*
* @return ResponseInterface
* @throws JsonException
* @throws GuzzleException
*/
public function uploadFileWithContent(
string $user,
?string $content,
string $destination,
?bool $isGivenStep = false
): ResponseInterface {
$user = $this->getActualUsername($user);
$this->pauseUploadDelete();
$response = $this->makeDavRequest(
$user,
"PUT",
$destination,
[],
$content,
"files",
null,
false,
null,
[],
null,
$isGivenStep
);
$this->lastUploadDeleteTime = \time();
return $response;
}
/**
* @When the administrator uploads file with content :content to :destination using the WebDAV API
*
* @param string|null $content
* @param string $destination
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function adminUploadsAFileWithContentTo(
?string $content,
string $destination
):void {
$response = $this->uploadFileWithContent($this->getAdminUsername(), $content, $destination);
$this->setResponse($response);
}
/**
* @Given the administrator has uploaded file with content :content to :destination
*
* @param string|null $content
* @param string $destination
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function adminHasUploadedAFileWithContentTo(
?string $content,
string $destination
):void {
$response = $this->uploadFileWithContent($this->getAdminUsername(), $content, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$destination'",
$response
);
}
/**
* @When user :user uploads file with content :content to :destination using the WebDAV API
*
* @param string $user
* @param string|null $content
* @param string $destination
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function userUploadsAFileWithContentTo(
string $user,
?string $content,
string $destination
):void {
$response = $this->uploadFileWithContent($user, $content, $destination);
$this->setResponse($response);
$this->pushToLastHttpStatusCodesArray();
}
/**
* @When /^user "([^"]*)" uploads the following files with content "([^"]*)"$/
*
* @param string $user
* @param string|null $content
* @param TableNode $table
*
* @return void
* @throws Exception|GuzzleException
*/
public function userUploadsFollowingFilesWithContentTo(
string $user,
?string $content,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $destination) {
$response = $this->uploadFileWithContent($user, $content, $destination["path"]);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user uploads file :source to :destination with mtime :mtime using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $mtime Time in human-readable format is taken as input which is converted into milliseconds that is used by API
* @param bool|null $isGivenStep
*
* @return void
* @throws Exception
*/
public function userUploadsFileToWithMtimeUsingTheWebdavApi(
string $user,
string $source,
string $destination,
string $mtime,
?bool $isGivenStep = false
):void {
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
$user = $this->getActualUsername($user);
$this->response = UploadHelper::upload(
$this->getBaseUrl(),
$user,
$this->getPasswordForUser($user),
$this->acceptanceTestsDirLocation() . $source,
$destination,
$this->getStepLineRef(),
["X-OC-Mtime" => $mtime],
$this->getDavPathVersion(),
null,
1,
$isGivenStep
);
}
/**
* @Given user :user has uploaded file :source to :destination with mtime :mtime using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $mtime Time in human-readable format is taken as input which is converted into milliseconds that is used by API
*
* @return void
* @throws Exception
*/
public function userHasUploadedFileToWithMtimeUsingTheWebdavApi(
string $user,
string $source,
string $destination,
string $mtime
):void {
$this->userUploadsFileToWithMtimeUsingTheWebdavApi(
$user,
$source,
$destination,
$mtime,
true
);
}
/**
* @Given user :user has uploaded file :filename with content :content and mtime of :days days ago using the WebDAV API
*
* @param string $user
* @param string $filename
* @param string $content
* @param string $days In days, e.g. "7"
*
* @return void
* @throws Exception
*/
public function userUploadsFileWithContentAndWithMtimeOfDaysAgoUsingWebdavApi(
string $user,
string $filename,
string $content,
string $days
): void {
$today = new DateTime();
$interval = new DateInterval('P' . $days . 'D');
$mtime = $today->sub($interval)->format('U');
$user = $this->getActualUsername($user);
$this->response = WebDavHelper::makeDavRequest(
$this->getBaseUrl(),
$user,
$this->getPasswordForUser($user),
"PUT",
$filename,
["X-OC-Mtime" => $mtime],
$this->getStepLineRef(),
$content,
WebDavHelper::DAV_VERSION_OLD,
"files",
null,
"basic",
false,
0,
null,
[],
null,
true
);
}
/**
* @Then as :user the mtime of the file :resource should be :mtime
*
* @param string $user
* @param string $resource
* @param string $mtime
*
* @return void
* @throws Exception
*/
public function theMtimeOfTheFileShouldBe(
string $user,
string $resource,
string $mtime
):void {
$user = $this->getActualUsername($user);
$password = $this->getPasswordForUser($user);
$baseUrl = $this->getBaseUrl();
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
Assert::assertEquals(
$mtime,
WebDavHelper::getMtimeOfResource(
$user,
$password,
$baseUrl,
$resource,
$this->getStepLineRef(),
$this->getDavPathVersion()
)
);
}
/**
* @Then as :user the mtime of the file :resource should not be :mtime
*
* @param string $user
* @param string $resource
* @param string $mtime
*
* @return void
* @throws Exception
*/
public function theMtimeOfTheFileShouldNotBe(
string $user,
string $resource,
string $mtime
):void {
$user = $this->getActualUsername($user);
$password = $this->getPasswordForUser($user);
$baseUrl = $this->getBaseUrl();
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
Assert::assertNotEquals(
$mtime,
WebDavHelper::getMtimeOfResource(
$user,
$password,
$baseUrl,
$resource,
$this->getStepLineRef(),
$this->getDavPathVersion()
)
);
}
/**
* @Given user :user has uploaded file with content :content to :destination
*
* @param string $user
* @param string|null $content
* @param string $destination
*
* @return array
* @throws GuzzleException
* @throws JsonException
*/
public function userHasUploadedAFileWithContentTo(
string $user,
?string $content,
string $destination
):array {
$user = $this->getActualUsername($user);
$response = $this->uploadFileWithContent($user, $content, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file '$destination' for user '$user'",
$response
);
return $response->getHeader('oc-fileid');
}
/**
* @Given /^user "([^"]*)" has uploaded the following files with content "([^"]*)"$/
*
* @param string $user
* @param string|null $content
* @param TableNode $table
*
* @return void
* @throws Exception|GuzzleException
*/
public function userHasUploadedFollowingFilesWithContent(
string $user,
?string $content,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["path"]);
$files = $table->getHash();
foreach ($files as $destination) {
$this->userHasUploadedAFileWithContentTo($user, $content, $destination["path"]);
}
}
/**
* @When /^user "([^"]*)" downloads the following files using the WebDAV API$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userDownloadsFollowingFiles(
string $user,
TableNode $table
):void {
$this->verifyTableNodeColumns($table, ["path"]);
$files = $table->getHash();
$this->emptyLastHTTPStatusCodesArray();
foreach ($files as $fileName) {
$response = $this->downloadFileAsUserUsingPassword($user, $fileName["path"]);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user uploads a file with content :content and mtime :mtime to :destination using the WebDAV API
*
* @param string $user
* @param string|null $content
* @param string $mtime
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userUploadsAFileWithContentAndMtimeTo(
string $user,
?string $content,
string $mtime,
string $destination
):void {
$user = $this->getActualUsername($user);
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
$response = $this->makeDavRequest(
$user,
"PUT",
$destination,
["X-OC-Mtime" => $mtime],
$content
);
$this->setResponse($response);
}
/**
* @param string $user
* @param string $checksum
* @param string|null $content
* @param string $destination
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
public function uploadFileWithChecksumAndContent(
string $user,
string $checksum,
?string $content,
string $destination,
?bool $isGivenStep = false
):ResponseInterface {
$this->pauseUploadDelete();
$response = $this->makeDavRequest(
$user,
"PUT",
$destination,
['OC-Checksum' => $checksum],
$content,
"files",
null,
false,
null,
[],
null,
$isGivenStep
);
$this->lastUploadDeleteTime = \time();
return $response;
}
/**
* @When user :user uploads file with checksum :checksum and content :content to :destination using the WebDAV API
*
* @param string $user
* @param string $checksum
* @param string $content
* @param string $destination
*
* @return void
*/
public function userUploadsAFileWithChecksumAndContentTo(
string $user,
string $checksum,
string $content,
string $destination
):void {
$response = $this->uploadFileWithChecksumAndContent($user, $checksum, $content, $destination);
$this->setResponse($response);
}
/**
* @Given user :user has uploaded file with checksum :checksum and content :content to :destination
*
* @param string $user
* @param string $checksum
* @param string|null $content
* @param string $destination
*
* @return void
*/
public function userHasUploadedAFileWithChecksumAndContentTo(
string $user,
string $checksum,
?string $content,
string $destination
):void {
$response = $this->uploadFileWithChecksumAndContent(
$user,
$checksum,
$content,
$destination,
true
);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload file with checksum '$checksum' to '$destination' for user '$user'",
$response
);
}
/**
* @Then /^user "([^"]*)" should be able to delete (file|folder|entry) "([^"]*)"$/
*
* @param string $user
* @param string $entry
* @param string $source
*
* @return void
* @throws Exception
*/
public function userShouldBeAbleToDeleteEntry(string $user, string $entry, string $source):void {
$user = $this->getActualUsername($user);
$this->asFileOrFolderShouldExist($user, $entry, $source);
$this->deleteFile($user, $source);
$this->asFileOrFolderShouldNotExist($user, $entry, $source);
}
/**
* @Then /^user "([^"]*)" should not be able to delete (file|folder|entry) "([^"]*)"$/
*
* @param string $user
* @param string $entry
* @param string $source
*
* @return void
* @throws Exception
*/
public function theUserShouldNotBeAbleToDeleteEntry(string $user, string $entry, string $source):void {
$this->asFileOrFolderShouldExist($user, $entry, $source);
$this->deleteFile($user, $source);
$this->asFileOrFolderShouldExist($user, $entry, $source);
}
/**
* @Given file :file has been deleted for user :user
*
* @param string $file
* @param string $user
*
* @return void
*/
public function fileHasBeenDeleted(string $file, string $user):void {
$this->userHasDeletedResource($user, "deleted", "file", $file);
}
/**
* @param string $user
* @param string $resource
*
* @return void
*/
public function deleteFile(string $user, string $resource):ResponseInterface {
$user = $this->getActualUsername($user);
$this->pauseUploadDelete();
$response = $this->makeDavRequest($user, 'DELETE', $resource, []);
$this->lastUploadDeleteTime = \time();
return $response;
}
/**
* @When user :user deletes file/folder :resource using the WebDAV API
*
* @param string $user
* @param string $resource
*
* @return void
*/
public function userDeletesFile(string $user, string $resource):void {
$response = $this->deleteFile($user, $resource);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
/**
* @Given /^user "([^"]*)" has deleted (?:file|folder|entity) "([^"]*)"$/
*
* @param string $user
* @param string $resource
*
* @return void
* @throws Exception
*/
public function userHasDeletedResource(string $user, string $resource):void {
$user = $this->getActualUsername($user);
$response = $this->deleteFile($user, $resource);
// If the file or folder was there and got deleted then we get a 204
// That is good and the expected status
// If the file or folder was already not there then we get a 404
// That is not expected. Scenarios that use "Given user has deleted..."
// should only be using such steps when it is a file that exists and needs
// to be deleted.
$this->theHTTPStatusCodeShouldBe(
["204"],
"HTTP status code was not 204 while trying to delete resource '$resource' for user '$user'",
$response
);
}
/**
* @Given /^user "([^"]*)" has deleted the following (?:files|folders|resources)$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userHasDeletedFollowingFiles(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $file) {
$this->userHasDeletedResource($user, $file["path"]);
}
}
/**
* @When /^user "([^"]*)" deletes the following (?:files|folders)$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userDeletesFollowingFiles(string $user, TableNode $table):void {
$user = $this->getActualUsername($user);
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $file) {
$this->userDeletesFile($user, $file["path"]);
$this->pushToLastStatusCodesArrays();
}
}
/**
* @When /^the user deletes (?:file|folder) "([^"]*)" using the WebDAV API$/
*
* @param string $file
*
* @return void
*/
public function theUserDeletesFile(string $file):void {
$response = $this->deleteFile($this->getCurrentUser(), $file);
$this->setResponse($response);
}
/**
* @Given /^the user has deleted (file|folder) "([^"]*)"$/
*
* @param string $fileOrFolder
* @param string $file
*
* @return void
*/
public function theUserHasDeletedFile(string $fileOrFolder, string $file):void {
$this->userHasDeletedResource($this->getCurrentUser(), $fileOrFolder, $file);
}
/**
* @When /^user "([^"]*)" deletes these (?:files|folders|entries) without delays using the WebDAV API$/
*
* @param string $user
* @param TableNode $table of files or folders to delete
*
* @return void
* @throws Exception
*/
public function userDeletesFilesFoldersWithoutDelays(string $user, TableNode $table):void {
$user = $this->getActualUsername($user);
$this->verifyTableNodeColumnsCount($table, 1);
foreach ($table->getTable() as $entry) {
$entryName = $entry[0];
$this->response = $this->makeDavRequest($user, 'DELETE', $entryName, []);
$this->pushToLastStatusCodesArrays();
}
$this->lastUploadDeleteTime = \time();
}
/**
* @When /^the user deletes these (?:files|folders|entries) without delays using the WebDAV API$/
*
* @param TableNode $table of files or folders to delete
*
* @return void
* @throws Exception
*/
public function theUserDeletesFilesFoldersWithoutDelays(TableNode $table):void {
$this->userDeletesFilesFoldersWithoutDelays($this->getCurrentUser(), $table);
}
/**
* @When /^user "([^"]*)" on "(LOCAL|REMOTE)" deletes (?:file|folder) "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $server
* @param string $file
*
* @return void
*/
public function userOnDeletesFile(string $user, string $server, string $file):void {
$previousServer = $this->usingServer($server);
$this->setResponse($this->deleteFile($user, $file));
$this->usingServer($previousServer);
}
/**
* @Given /^user "([^"]*)" on "(LOCAL|REMOTE)" has deleted (file|folder) "([^"]*)"$/
*
* @param string $user
* @param string $server
* @param string $fileOrFolder
* @param string $entry
*
* @return void
*/
public function userOnHasDeletedFile(string $user, string $server, string $fileOrFolder, string $entry):void {
$this->userOnDeletesFile($user, $server, $entry);
// If the file was there and got deleted then we get a 204
// If the file was already not there then we get a 404
// Either way, the outcome of the "given" step is OK
$this->theHTTPStatusCodeShouldBe(
["204", "404"],
"HTTP status code was not 204 or 404 while trying to delete $fileOrFolder '$entry' for user '$user' on server '$server'"
);
}
/**
* @When user :user creates folder :destination using the WebDAV API
*
* @param string $user
* @param string $destination
*
* @return void
* @throws JsonException
* @throws GuzzleException
*/
public function userCreatesFolder(string $user, string $destination):void {
$response = $this->createFolder($user, $destination);
$this->setResponse($response);
}
/**
* @Given user :user has created folder :destination
*
* @param string $user
* @param string $destination
*
* @return void
* @throws JsonException
* @throws GuzzleException
*/
public function userHasCreatedFolder(string $user, string $destination):void {
$user = $this->getActualUsername($user);
$response = $this->createFolder($user, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to create folder '$destination' for user '$user'",
$response
);
}
/**
* @Given admin has created folder :destination
*
* @param string $destination
*
* @return void
* @throws JsonException
* @throws GuzzleException
*/
public function adminHasCreatedFolder(string $destination):void {
$admin = $this->getAdminUsername();
Assert::assertEquals(
"admin",
$admin,
__METHOD__ . "The provided user is not admin but '" . $admin . "'"
);
$response = $this->createFolder($admin, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to create folder '$destination' for admin '$admin'",
$response
);
$this->adminResources[] = $destination;
}
/**
* @Given /^user "([^"]*)" has created the following folders$/
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userHasCreatedFollowingFolders(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["path"]);
$paths = $table->getHash();
foreach ($paths as $path) {
$this->userHasCreatedFolder($user, $path["path"]);
}
}
/**
* @When the user creates folder :destination using the WebDAV API
*
* @param string $destination
*
* @return void
*/
public function theUserCreatesFolder(string $destination):void {
$this->userCreatesFolder($this->getCurrentUser(), $destination);
}
/**
* @Given the user has created folder :destination
*
* @param string $destination
*
* @return void
*/
public function theUserHasCreatedFolder(string $destination):void {
$response = $this->createFolder($this->getCurrentUser(), $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to create folder '$destination'",
$response
);
}
/**
* @Then user :user should be able to create folder :destination
*
* @param string $user
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userShouldBeAbleToCreateFolder(string $user, string $destination):void {
$this->userHasCreatedFolder($user, $destination);
$this->asFileOrFolderShouldExist(
$user,
"folder",
$destination
);
}
/**
* @Then user :user should not be able to create folder :destination
*
* @param string $user
* @param string $destination
*
* @return void
* @throws Exception
*/
public function userShouldNotBeAbleToCreateFolder(string $user, string $destination):void {
$user = $this->getActualUsername($user);
$response = $this->createFolder($user, $destination);
$this->theHTTPStatusCodeShouldBeBetween(400, 499, $response);
$this->asFileOrFolderShouldNotExist(
$user,
"folder",
$destination
);
}
/**
* Old style chunking upload
*
* @When user :user uploads the following :total chunks to :file with old chunking and using the WebDAV API
*
* @param string $user
* @param string $total
* @param string $file
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
* content with column headings, e.g.
* | number | content |
* | 1 | first data |
* | 2 | followed by second data |
* Chunks may be numbered out-of-order if desired.
*
* @return void
* @throws Exception
*/
public function userUploadsTheFollowingTotalChunksUsingOldChunking(
string $user,
string $total,
string $file,
TableNode $chunkDetails
):void {
$this->verifyTableNodeColumns($chunkDetails, ['number', 'content']);
foreach ($chunkDetails->getHash() as $chunkDetail) {
$chunkNumber = (int)$chunkDetail['number'];
$chunkContent = $chunkDetail['content'];
$this->userUploadsChunkedFile($user, $chunkNumber, (int) $total, $chunkContent, $file);
}
}
/**
* Old style chunking upload
*
* @When user :user uploads the following chunks to :file with old chunking and using the WebDAV API
*
* @param string $user
* @param string $file
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
* content with column headings, e.g.
* | number | content |
* | 1 | first data |
* | 2 | followed by second data |
* Chunks may be numbered out-of-order if desired.
*
* @return void
* @throws Exception
*/
public function userUploadsTheFollowingChunksUsingOldChunking(
string $user,
string $file,
TableNode $chunkDetails
):void {
$total = \count($chunkDetails->getHash());
$this->userUploadsTheFollowingTotalChunksUsingOldChunking(
$user,
(string) $total,
$file,
$chunkDetails
);
}
/**
* Old style chunking upload
*
* @param string $user
* @param int $num
* @param int $total
* @param string|null $data
* @param string $destination
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
public function userUploadChunkedFile(
string $user,
int $num,
int $total,
?string $data,
string $destination,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
$num -= 1;
$file = "$destination-chunking-42-$total-$num";
$this->pauseUploadDelete();
$response = $this->makeDavRequest(
$user,
'PUT',
$file,
['OC-Chunked' => '1'],
$data,
"uploads",
null,
false,
null,
[],
null,
$isGivenStep
);
$this->lastUploadDeleteTime = \time();
return $response;
}
/**
* @When user :user uploads chunk file :num of :total with :data to :destination using the WebDAV API
*
* @param string $user
* @param int $num
* @param int $total
* @param string $data
* @param string $destination
*
* @return void
*/
public function userUploadsChunkedFile(
string $user,
int $num,
int $total,
?string $data,
string $destination
):void {
$response = $this->userUploadChunkedFile($user, $num, $total, $data, $destination);
$this->setResponse($response);
}
/**
* Old style chunking upload
*
* @Given user :user has uploaded chunk file :num of :total with :data to :destination
*
* @param string $user
* @param int|null $num
* @param int|null $total
* @param string|null $data
* @param string $destination
*
* @return void
*/
public function userHasUploadedChunkedFile(
string $user,
?int $num,
?int $total,
?string $data,
string $destination
):void {
$user = $this->getActualUsername($user);
$response = $this->userUploadChunkedFile($user, $num, $total, $data, $destination, true);
$this->theHTTPStatusCodeShouldBe(
["201", "204"],
"HTTP status code was not 201 or 204 while trying to upload chunk $num of $total to file '$destination' for user '$user'",
$response
);
}
/**
* New style chunking upload
*
* @When /^user "([^"]*)" uploads the following chunks\s?(asynchronously|) to "([^"]*)" with new chunking and using the WebDAV API$/
*
* @param string $user
* @param string $type "asynchronously" or empty
* @param string $file
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
* content, with headings e.g.
* | number | content |
* | 1 | first data |
* | 2 | second data |
* Chunks may be numbered out-of-order if desired.
*
* @return void
*/
public function userUploadsTheFollowingChunksUsingNewChunking(
string $user,
string $type,
string $file,
TableNode $chunkDetails
):void {
$this->uploadTheFollowingChunksUsingNewChunking(
$user,
$type,
$file,
$chunkDetails
);
}
/**
* New style chunking upload
*
* @Given /^user "([^"]*)" has uploaded the following chunks\s?(asynchronously|) to "([^"]*)" with new chunking$/
*
* @param string $user
* @param string $type "asynchronously" or empty
* @param string $file
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
* content without column headings, e.g.
* | number | content |
* | 1 | first data |
* | 2 | followed by second data |
* Chunks may be numbered out-of-order if desired.
*
* @return void
*/
public function userHasUploadedTheFollowingChunksUsingNewChunking(
string $user,
string $type,
string $file,
TableNode $chunkDetails
):void {
$this->uploadTheFollowingChunksUsingNewChunking(
$user,
$type,
$file,
$chunkDetails,
true
);
}
/**
* New style chunking upload
*
* @param string $user
* @param string $type "asynchronously" or empty
* @param string $file
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
* content with column headings, e.g.
* | number | content |
* | 1 | first data |
* | 2 | second data |
* Chunks may be numbered out-of-order if desired.
* @param bool|null $isGivenStep
*
* @return void
* @throws Exception
*/
public function uploadTheFollowingChunksUsingNewChunking(
string $user,
string $type,
string $file,
TableNode $chunkDetails,
?bool $isGivenStep = false
):void {
$user = $this->getActualUsername($user);
$async = false;
if ($type === "asynchronously") {
$async = true;
}
$this->verifyTableNodeColumns($chunkDetails, ["number", "content"]);
$this->userUploadsChunksUsingNewChunking(
$user,
$file,
'chunking-42',
$chunkDetails->getHash(),
$async,
$isGivenStep
);
}
/**
* New style chunking upload
*
* @param string $user
* @param string $file
* @param string $chunkingId
* @param array $chunkDetails of chunks of the file. Each array entry is
* itself an array of 2 items:
* [number] the chunk number
* [content] data content of the chunk
* Chunks may be numbered out-of-order if desired.
* @param bool $async use asynchronous MOVE at the end or not
* @param bool $isGivenStep
*
* @return void
*/
public function userUploadsChunksUsingNewChunking(
string $user,
string $file,
string $chunkingId,
array $chunkDetails,
bool $async = false,
bool $isGivenStep = false
):void {
$this->pauseUploadDelete();
if ($isGivenStep) {
$this->userHasCreatedANewChunkingUploadWithId($user, $chunkingId);
} else {
$this->userCreatesANewChunkingUploadWithId($user, $chunkingId);
}
foreach ($chunkDetails as $chunkDetail) {
$chunkNumber = (int)$chunkDetail['number'];
$chunkContent = $chunkDetail['content'];
if ($isGivenStep) {
$this->userHasUploadedNewChunkFileOfWithToId($user, $chunkNumber, $chunkContent, $chunkingId);
} else {
$this->userUploadsNewChunkFileOfWithToId($user, $chunkNumber, $chunkContent, $chunkingId);
}
}
$headers = [];
if ($async === true) {
$headers = ['OC-LazyOps' => 'true'];
}
$response = $this->moveNewDavChunkToFinalFile($user, $chunkingId, $file, $headers, $isGivenStep);
if ($isGivenStep) {
$this->theHTTPStatusCodeShouldBeBetween(200, 299, $response);
} else {
$this->setResponse($response);
}
$this->lastUploadDeleteTime = \time();
}
/**
*
* @param string $user
* @param string $id
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
public function userCreateANewChunkingUploadWithId(
string $user,
string $id,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
$destination = "/uploads/$user/$id";
return $this->makeDavRequest(
$user,
'MKCOL',
$destination,
[],
null,
"uploads",
null,
false,
null,
[],
null,
$isGivenStep
);
}
/**
* @When user :user creates a new chunking upload with id :id using the WebDAV API
*
* @param string $user
* @param string $id
*
* @return void
*/
public function userCreatesANewChunkingUploadWithId(
string $user,
string $id
):void {
$response = $this->userCreateANewChunkingUploadWithId($user, $id);
$this->setResponse($response);
}
/**
* @Given user :user has created a new chunking upload with id :id
*
* @param string $user
* @param string $id
*
* @return void
*/
public function userHasCreatedANewChunkingUploadWithId(string $user, string $id):void {
$response = $this->userCreateANewChunkingUploadWithId($user, $id, true);
$this->theHTTPStatusCodeShouldBeBetween(200, 299, $response);
}
/**
* @param string $user
* @param int $num
* @param string|null $data
* @param string $id
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
public function userUploadNewChunkFileOfWithToId(
string $user,
int $num,
?string $data,
string $id,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
$destination = "/uploads/$user/$id/$num";
return $this->makeDavRequest(
$user,
'PUT',
$destination,
[],
$data,
"uploads",
null,
false,
null,
[],
null,
$isGivenStep
);
}
/**
* @When user :user uploads new chunk file :num with :data to id :id using the WebDAV API
*
* @param string $user
* @param int $num
* @param string|null $data
* @param string $id
*
* @return void
*/
public function userUploadsNewChunkFileOfWithToId(
string $user,
int $num,
?string $data,
string $id
):void {
$response = $this->userUploadNewChunkFileOfWithToId($user, $num, $data, $id);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
/**
* @Given user :user has uploaded new chunk file :num with :data to id :id
*
* @param string $user
* @param int $num
* @param string|null $data
* @param string $id
*
* @return void
*/
public function userHasUploadedNewChunkFileOfWithToId(string $user, int $num, ?string $data, string $id):void {
$response = $this->userUploadNewChunkFileOfWithToId($user, $num, $data, $id, true);
$this->theHTTPStatusCodeShouldBeBetween(200, 299, $response);
}
/**
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
*
* @return ResponseInterface
*/
public function userMoveNewChunkFileWithIdToMychunkedfile(
string $user,
string $id,
string $type,
string $dest
):ResponseInterface {
$headers = [];
if ($type === "asynchronously") {
$headers = ['OC-LazyOps' => 'true'];
}
return $this->moveNewDavChunkToFinalFile($user, $id, $dest, $headers);
}
/**
* @When /^user "([^"]*)" moves new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
*
* @return void
*/
public function userMovesNewChunkFileWithIdToMychunkedfile(
string $user,
string $id,
string $type,
string $dest
):void {
$this->setResponse($this->userMoveNewChunkFileWithIdToMychunkedfile($user, $id, $type, $dest));
}
/**
* @Given /^user "([^"]*)" has moved new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)"$/
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
*
* @return void
*/
public function userHasMovedNewChunkFileWithIdToMychunkedfile(
string $user,
string $id,
string $type,
string $dest
):void {
$response = $this->userMoveNewChunkFileWithIdToMychunkedfile($user, $id, $type, $dest);
$this->theHTTPStatusCodeShouldBe("201", "", $response);
}
/**
* @When user :user cancels chunking-upload with id :id using the WebDAV API
*
* @param string $user
* @param string $id
*
* @return void
*/
public function userCancelsUploadWithId(
string $user,
string $id
):void {
$response = $this->deleteUpload($user, $id, []);
$this->setResponse($response);
}
/**
* @Given user :user has canceled new chunking-upload with id :id
*
* @param string $user
* @param string $id
*
* @return void
*/
public function userHasCanceledUploadWithId(
string $user,
string $id
):void {
$response = $this->deleteUpload($user, $id, []);
$this->theHTTPStatusCodeShouldBe("201", "", $response);
}
/**
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param int $size
*
* @return ResponseInterface
*/
public function userMoveNewChunkFileWithIdToMychunkedfileWithSize(
string $user,
string $id,
string $type,
string $dest,
int $size
):ResponseInterface {
$headers = ['OC-Total-Length' => $size];
if ($type === "asynchronously") {
$headers['OC-LazyOps'] = 'true';
}
return $this->moveNewDavChunkToFinalFile(
$user,
$id,
$dest,
$headers
);
}
/**
* @When /^user "([^"]*)" moves new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)" with size (.*) using the WebDAV API$/
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param int $size
*
* @return void
*/
public function userMovesNewChunkFileWithIdToMychunkedfileWithSize(
string $user,
string $id,
string $type,
string $dest,
int $size
):void {
$response = $this->userMoveNewChunkFileWithIdToMychunkedfileWithSize(
$user,
$id,
$type,
$dest,
$size
);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
/**
* @Given /^user "([^"]*)" has moved new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)" with size (.*)$/
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param int $size
*
* @return void
*/
public function userHasMovedNewChunkFileWithIdToMychunkedfileWithSize(
string $user,
string $id,
string $type,
string $dest,
int $size
):void {
$response = $this->userMoveNewChunkFileWithIdToMychunkedfileWithSize(
$user,
$id,
$type,
$dest,
$size
);
$this->theHTTPStatusCodeShouldBe("201", "", $response);
}
/**
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param string $checksum
*
* @return ResponseInterface
*/
public function userMoveNewChunkFileWithIdToMychunkedfileWithChecksum(
string $user,
string $id,
string $type,
string $dest,
string $checksum
):ResponseInterface {
$headers = ['OC-Checksum' => $checksum];
if ($type === "asynchronously") {
$headers['OC-LazyOps'] = 'true';
}
return $this->moveNewDavChunkToFinalFile(
$user,
$id,
$dest,
$headers
);
}
/**
* @When /^user "([^"]*)" moves new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)" with checksum "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param string $checksum
*
* @return void
*/
public function userMovesNewChunkFileWithIdToMychunkedfileWithChecksum(
string $user,
string $id,
string $type,
string $dest,
string $checksum
):void {
$response = $this->userMoveNewChunkFileWithIdToMychunkedfileWithChecksum($user, $id, $type, $dest, $checksum);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
/**
* @Given /^user "([^"]*)" has moved new chunk file with id "([^"]*)"\s?(asynchronously|) to "([^"]*)" with checksum "([^"]*)"
*
* @param string $user
* @param string $id
* @param string $type "asynchronously" or empty
* @param string $dest
* @param string $checksum
*
* @return void
*/
public function userHasMovedNewChunkFileWithIdToMychunkedfileWithChecksum(
string $user,
string $id,
string $type,
string $dest,
string $checksum
):void {
$response = $this->userMoveNewChunkFileWithIdToMychunkedfileWithChecksum(
$user,
$id,
$type,
$dest,
$checksum
);
$this->theHTTPStatusCodeShouldBe("201", "", $response);
}
/**
* Move chunked new DAV file to final file
*
* @param string $user user
* @param string $id upload id
* @param string $destination destination path
* @param array $headers extra headers
* @param bool|null $isGivenStep
*
* @return ResponseInterface
*/
private function moveNewDavChunkToFinalFile(
string $user,
string $id,
string $destination,
array $headers,
?bool $isGivenStep = false
):ResponseInterface {
$user = $this->getActualUsername($user);
$source = "/uploads/$user/$id/.file";
$headers['Destination'] = $this->destinationHeaderValue(
$user,
$destination
);
return $this->makeDavRequest(
$user,
'MOVE',
$source,
$headers,
null,
"uploads",
null,
false,
null,
[],
null,
$isGivenStep
);
}
/**
* Delete chunked-upload directory
*
* @param string $user user
* @param string $id upload id
* @param array $headers extra headers
*
* @return ResponseInterface
*/
private function deleteUpload(string $user, string $id, array $headers):ResponseInterface {
$source = "/uploads/$user/$id";
return $this->makeDavRequest(
$user,
'DELETE',
$source,
$headers,
null,
"uploads"
);
}
/**
* URL encodes the given path but keeps the slashes
*
* @param string $path to encode
*
* @return string encoded path
*/
public function encodePath(string $path):string {
// slashes need to stay
// in ocis even brackets are encoded
return \str_replace('%2F', '/', \rawurlencode($path));
}
/**
* an unauthenticated client connects to the DAV endpoint using the WebDAV API
*
* @return ResponseInterface
*/
public function connectToDavEndpoint():ResponseInterface {
return $this->makeDavRequest(
null,
'PROPFIND',
'',
[]
);
}
/**
* @When an unauthenticated client connects to the DAV endpoint using the WebDAV API
*
* @return void
*/
public function connectingToDavEndpoint():void {
$this->setResponse($this->connectToDavEndpoint());
}
/**
* @Given an unauthenticated client has connected to the DAV endpoint
*
* @return void
*/
public function hasConnectedToDavEndpoint():void {
$response = $this->connectToDavEndpoint();
$this->theHTTPStatusCodeShouldBe("401", "", $response);
}
/**
* @Then there should be no duplicate headers
*
* @return void
* @throws Exception
*/
public function thereAreNoDuplicateHeaders():void {
$headers = $this->response->getHeaders();
foreach ($headers as $headerName => $headerValues) {
// if a header has multiple values, they must be different
if (\count($headerValues) > 1
&& \count(\array_unique($headerValues)) < \count($headerValues)
) {
throw new Exception("Duplicate header found: $headerName");
}
}
}
/**
* @Then the following headers should not be set
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theFollowingHeadersShouldNotBeSet(TableNode $table):void {
$this->verifyTableNodeColumns(
$table,
['header']
);
foreach ($table->getColumnsHash() as $header) {
$headerName = $header['header'];
$headerValue = $this->response->getHeader($headerName);
//Note: getHeader returns an empty array if the named header does not exist
$headerValue0 = $headerValue[0] ?? '';
Assert::assertEmpty(
$headerValue,
"header $headerName should not exist " .
"but does and is set to $headerValue0"
);
}
}
/**
* @Then the following headers should match these regular expressions
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function headersShouldMatchRegularExpressions(TableNode $table):void {
$this->verifyTableNodeColumnsCount($table, 2);
foreach ($table->getTable() as $header) {
$headerName = $header[0];
$expectedHeaderValue = $header[1];
$expectedHeaderValue = $this->substituteInLineCodes(
$expectedHeaderValue,
null,
['preg_quote' => ['/']]
);
$returnedHeaders = $this->response->getHeader($headerName);
$returnedHeader = $returnedHeaders[0];
Assert::assertNotFalse(
(bool) \preg_match($expectedHeaderValue, $returnedHeader),
"'$expectedHeaderValue' does not match '$returnedHeader'"
);
}
}
/**
* @Then /^if the HTTP status code was "([^"]*)" then the following headers should match these regular expressions$/
*
* @param int $statusCode
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function statusCodeShouldMatchTheseRegularExpressions(int $statusCode, TableNode $table):void {
$actualStatusCode = $this->response->getStatusCode();
if ($actualStatusCode === $statusCode) {
$this->headersShouldMatchRegularExpressions($table);
}
}
/**
* @Then the following headers should match these regular expressions for user :user
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function headersShouldMatchRegularExpressionsForUser(string $user, TableNode $table):void {
$this->verifyTableNodeColumnsCount($table, 2);
$user = $this->getActualUsername($user);
foreach ($table->getTable() as $header) {
$headerName = $header[0];
$expectedHeaderValue = $header[1];
$expectedHeaderValue = $this->substituteInLineCodes(
$expectedHeaderValue,
$user,
['preg_quote' => ['/']]
);
$returnedHeaders = $this->response->getHeader($headerName);
$returnedHeader = $returnedHeaders[0];
Assert::assertNotFalse(
(bool) \preg_match($expectedHeaderValue, $returnedHeader),
"'$expectedHeaderValue' does not match '$returnedHeader'"
);
}
}
/**
* @When /^user "([^"]*)" deletes everything from folder "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $folder
* @param bool $checkEachDelete
*
* @return void
* @throws Exception
*/
public function userDeletesEverythingInFolder(
string $user,
string $folder,
bool $checkEachDelete = false
):void {
$user = $this->getActualUsername($user);
$responseXmlObject = $this->listFolderAndReturnResponseXml(
$user,
$folder,
'1'
);
$elementList = $responseXmlObject->xpath("//d:response/d:href");
if (\is_array($elementList) && \count($elementList)) {
\array_shift($elementList); //don't delete the folder itself
$davPrefix = "/" . $this->getFullDavFilesPath($user);
foreach ($elementList as $element) {
$element = \substr((string)$element, \strlen($davPrefix));
if ($checkEachDelete) {
$this->userHasDeletedResource($user, "deleted", "file", $element);
} else {
$this->userDeletesFile($user, $element);
}
}
}
}
/**
* @Given /^user "([^"]*)" has deleted everything from folder "([^"]*)"$/
*
* @param string $user
* @param string $folder
*
* @return void
* @throws Exception
*/
public function userHasDeletedEverythingInFolder(string $user, string $folder):void {
$this->userDeletesEverythingInFolder($user, $folder, true);
}
/**
* @When user :user downloads the preview of :path with width :width and height :height using the WebDAV API
* @When user :user tries to download the preview of nonexistent file :path with width :width and height :height using the WebDAV API
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function downloadPreviewOfFiles(string $user, string $path, string $width, string $height):void {
$response = $this->downloadPreviews(
$user,
$path,
null,
$width,
$height
);
$this->setResponse($response);
}
/**
* @When user :user downloads the preview of shared resource :path with width :width and height :height using the WebDAV API
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function userDownloadsThePreviewOfSharedResourceWithWidthAndHeightUsingTheWebdavApi(string $user, string $path, string $width, string $height): void {
if ($this->getDavPathVersion() === 3) {
$this->setResponse($this->downloadSharedFilePreview($user, $path, $width, $height));
} else {
$this->setResponse($this->downloadPreviews($user, $path, null, $width, $height));
}
}
/**
* @Given user :user has downloaded the preview of shared resource :path with width :width and height :height
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function userHasDownloadedThePreviewOfSharedResourceWithWidthAndHeight(string $user, string $path, string $width, string $height): void {
if ($this->getDavPathVersion() === 3) {
$response = $this->downloadSharedFilePreview($user, $path, $width, $height);
} else {
$response = $this->downloadPreviews($user, $path, null, $width, $height);
}
$this->setResponse($response);
$this->theHTTPStatusCodeShouldBe(200, '', $response);
$this->imageDimensionsShouldBe($width, $height);
// save response to user response dictionary for further comparisons
$this->userResponseBodyContents[$user] = $this->responseBodyContent;
}
/**
* @Then as user :user the preview of shared resource :path with width :width and height :height should have been changed
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function asUserThePreviewOfSharedResourceWithWidthAndHeightShouldHaveBeenChanged(string $user, string $path, string $width, string $height):void {
if ($this->getDavPathVersion() === 3) {
$response = $this->downloadSharedFilePreview($user, $path, $width, $height);
} else {
$response = $this->downloadPreviews($user, $path, null, $width, $height);
}
$this->setResponse($response);
$this->theHTTPStatusCodeShouldBe(200, '', $response);
$newResponseBodyContents = $this->response->getBody()->getContents();
Assert::assertNotEquals(
$newResponseBodyContents,
// different users can download files before and after an update is made to a file
// previous response content is fetched from user response body content array for that user
$this->userResponseBodyContents[$user],
__METHOD__ . " previous and current previews content is same but expected to be different",
);
// update the saved content for the next comparison
$this->userResponseBodyContents[$user] = $newResponseBodyContents;
}
/**
* @When user :user uploads file with content :content to shared resource :destination using the WebDAV API
*
* @param string $user
* @param string $content
* @param string $destination
*
* @return void
*/
public function userUploadsFileWithContentSharedResourceToUsingTheWebdavApi(string $user, string $content, string $destination): void {
if ($this->getDavPathVersion() === 3) {
$this->setResponse($this->uploadToSharedFolder($user, $destination, $content));
} else {
$this->setResponse($this->uploadFileWithContent($user, $content, $destination));
}
}
/**
* @param string $user
* @param string $path
*
* @return string
* @throws GuzzleException
*/
public function getMountSharesPath(
string $user,
string $path
): string {
$user = $this->getActualUsername($user);
$path = trim($path, "/");
$pathArray = explode("/", $path);
$shareMountId = GraphHelper::getShareMountId(
$this->getBaseUrl(),
$this->getStepLineRef(),
$user,
$this->getPasswordForUser($user),
$pathArray[1]
);
if (\count($pathArray) > 2) {
$pathArray = \array_slice($pathArray, 2);
$path = '/' . implode("/", array_map("strval", $pathArray));
} else {
$path = null;
}
return $shareMountId . $path;
}
/**
* @param string $user
* @param string $path
* @param string|null $width
* @param string|null $height
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function downloadSharedFilePreview(
string $user,
string $path,
?string $width = null,
?string $height = null
): ResponseInterface {
if ($width !== null && $height !== null) {
$urlParameter = [
'x' => $width,
'y' => $height,
'forceIcon' => '0',
'preview' => '1'
];
$urlParameter = \http_build_query($urlParameter, '', '&');
} else {
$urlParameter = null;
}
$sharesPath = $this->getMountSharesPath($user, $path) . '/?' . $urlParameter;
$davPath = WebDavHelper::getDavPath($user, $this->getDavPathVersion());
$fullUrl = $this->getBaseUrl() . $davPath . $sharesPath;
return HttpRequestHelper::sendRequest(
$fullUrl,
$this->getStepLineRef(),
'GET',
$user,
$this->getPasswordForUser($user)
);
}
/**
* @param string $user
* @param string $destination
* @param string|null $content
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function uploadToSharedFolder(
string $user,
string $destination,
?string $content = null
): ResponseInterface {
$sharesPath = $this->getMountSharesPath($user, $destination);
$davPath = WebDavHelper::getDavPath($user, $this->getDavPathVersion());
$fullUrl = $this->getBaseUrl() . $davPath . $sharesPath;
return HttpRequestHelper::sendRequest(
$fullUrl,
$this->getStepLineRef(),
'PUT',
$user,
$this->getPasswordForUser($user),
null,
$content
);
}
/**
* @When user :user1 downloads the preview of :path of :user2 with width :width and height :height using the WebDAV API
*
* @param string $user1
* @param string $path
* @param string $doDavRequestAsUser
* @param string $width
* @param string $height
*
* @return void
*/
public function downloadPreviewOfOtherUser(string $user1, string $path, string $doDavRequestAsUser, string $width, string $height):void {
$response = $this->downloadPreviews(
$user1,
$path,
$doDavRequestAsUser,
$width,
$height
);
$this->setResponse($response);
}
/**
* @Then the downloaded image for user :user should be :width pixels wide and :height pixels high
*
* @param string $user
* @param string $width
* @param string $height
*
* @return void
*/
public function imageDimensionsForAUserShouldBe(string $user, string $width, string $height):void {
if ($this->userResponseBodyContents[$user] === null) {
$this->userResponseBodyContents[$user] = $this->response->getBody()->getContents();
}
$size = \getimagesizefromstring($this->userResponseBodyContents[$user]);
Assert::assertNotFalse($size, "could not get size of image");
Assert::assertEquals($width, $size[0], "width not as expected");
Assert::assertEquals($height, $size[1], "height not as expected");
}
/**
* @Then the downloaded image should be :width pixels wide and :height pixels high
*
* @param string $width
* @param string $height
* @param ResponseInterface|null $response
*
* @return void
*/
public function imageDimensionsShouldBe(string $width, string $height, ?ResponseInterface $response = null): void {
$response = $response ?? $this->getResponse();
if ($this->responseBodyContent === null) {
$this->responseBodyContent = $response->getBody()->getContents();
}
$size = \getimagesizefromstring($this->responseBodyContent);
Assert::assertNotFalse($size, "could not get size of image");
Assert::assertEquals($width, $size[0], "width not as expected");
Assert::assertEquals($height, $size[1], "height not as expected");
}
/**
* @Then the requested JPEG image should have a quality value of :size
*
* @param string $value
*
* @return void
*/
public function jpgQualityValueShouldBe(string $value): void {
$this->responseBodyContent = $this->response->getBody()->getContents();
// quality value is embedded in the string content for JPEG images
$qualityString = "quality = $value";
Assert::assertStringContainsString($qualityString, $this->responseBodyContent);
}
/**
* @Then the downloaded preview content should match with :preview fixtures preview content
*
* @param string $filename relative path from fixtures directory
*
* @return void
* @throws Exception
*/
public function theDownloadedPreviewContentShouldMatchWithFixturesPreviewContentFor(string $filename):void {
$expectedPreview = \file_get_contents(__DIR__ . "/../../fixtures/" . $filename);
Assert::assertEquals($expectedPreview, $this->responseBodyContent);
}
/**
* @Given user :user has downloaded the preview of :path with width :width and height :height
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function userDownloadsThePreviewOfWithWidthAndHeight(string $user, string $path, string $width, string $height):void {
$response = $this->downloadPreviews(
$user,
$path,
null,
$width,
$height
);
$this->theHTTPStatusCodeShouldBe(200, "", $response);
$this->imageDimensionsShouldBe($width, $height, $response);
// save response to user response dictionary for further comparisons
$this->userResponseBodyContents[$user] = $this->responseBodyContent;
}
/**
* @Then as user :user the preview of :path with width :width and height :height should have been changed
*
* @param string $user
* @param string $path
* @param string $width
* @param string $height
*
* @return void
*/
public function asUserThePreviewOfPathWithHeightAndWidthShouldHaveBeenChanged(string $user, string $path, string $width, string $height):void {
$response = $this->downloadPreviews(
$user,
$path,
null,
$width,
$height
);
$this->theHTTPStatusCodeShouldBe(200, "", $response);
$newResponseBodyContents = $response->getBody()->getContents();
Assert::assertNotEquals(
$newResponseBodyContents,
// different users can download files before and after an update is made to a file
// previous response content is fetched from user response body content array for that user
$this->userResponseBodyContents[$user],
__METHOD__ . " previous and current previews content is same but expected to be different",
);
// update the saved content for the next comparison
$this->userResponseBodyContents[$user] = $newResponseBodyContents;
}
/**
* @param string $user
* @param string $path
*
* @return string|null
*/
public function getFileIdForPath(string $user, string $path): ?string {
$user = $this->getActualUsername($user);
try {
return WebDavHelper::getFileIdForPath(
$this->getBaseUrl(),
$user,
$this->getPasswordForUser($user),
$path,
$this->getStepLineRef(),
$this->getDavPathVersion()
);
} catch (Exception $e) {
return null;
}
}
/**
* @Given /^user "([^"]*)" has stored id of (file|folder) "([^"]*)"$/
* @When /^user "([^"]*)" stores id of (file|folder) "([^"]*)"$/
*
* @param string $user
* @param string $fileOrFolder
* @param string $path
*
* @return void
*/
public function userStoresFileIdForPath(string $user, string $fileOrFolder, string $path):void {
$this->storedFileID = $this->getFileIdForPath($user, $path);
}
/**
* @Then /^user "([^"]*)" (file|folder) "([^"]*)" should have the previously stored id$/
*
* @param string $user
* @param string $fileOrFolder
* @param string $path
*
* @return void
*/
public function userFileShouldHaveStoredId(string $user, string $fileOrFolder, string $path):void {
$user = $this->getActualUsername($user);
$currentFileID = $this->getFileIdForPath($user, $path);
Assert::assertEquals(
$currentFileID,
$this->storedFileID,
__METHOD__
. " User '$user' $fileOrFolder '$path' does not have the previously stored id '$this->storedFileID', but has '$currentFileID'."
);
}
/**
* @Then /^the (?:Cal|Card)?DAV (exception|message|reason) should be "([^"]*)"$/
*
* @param string $element exception|message|reason
* @param string $message
*
* @return void
* @throws Exception
*/
public function theDavElementShouldBe(string $element, string $message):void {
$resXmlArray = HttpRequestHelper::parseResponseAsXml($this->getResponse());
WebDavAssert::assertDavResponseElementIs(
$element,
$message,
$resXmlArray,
__METHOD__
);
}
/**
* @param string $shouldOrNot (not|)
* @param TableNode $expectedFiles
* @param string|null $user
* @param string|null $method
* @param string|null $folderpath
*
* @return void
* @throws GuzzleException
*/
public function propfindResultShouldContainEntries(
string $shouldOrNot,
TableNode $expectedFiles,
?string $user = null,
?string $method = 'REPORT',
?string $folderpath = ''
):void {
if ($folderpath === "/") {
$folderpath = "";
}
$this->verifyTableNodeColumnsCount($expectedFiles, 1);
$elementRows = $expectedFiles->getRows();
$should = ($shouldOrNot !== "not");
foreach ($elementRows as $expectedFile) {
$resource = $expectedFile[0];
if ($resource === '') {
continue;
}
if ($method === "REPORT") {
$fileFound = $this->findEntryFromSearchResponse(
$resource
);
if (\is_object($fileFound)) {
$fileFound = $fileFound->xpath("d:propstat//oc:name");
}
} else {
$fileFound = $this->findEntryFromPropfindResponse(
$resource,
$user,
"files",
$folderpath
);
}
if ($should) {
Assert::assertNotEmpty(
$fileFound,
"response does not contain the entry '$resource'"
);
} else {
Assert::assertFalse(
$fileFound,
"response does contain the entry '$resource' but should not"
);
}
}
}
/**
* @Then /^the (?:propfind|search) result of user "([^"]*)" should (not|)\s?contain these (?:files|entries):$/
*
* @param string $user
* @param string $shouldOrNot (not|)
* @param TableNode $expectedFiles
*
* @return void
* @throws Exception
*/
public function thePropfindResultShouldContainEntries(
string $user,
string $shouldOrNot,
TableNode $expectedFiles
):void {
$user = $this->getActualUsername($user);
$this->propfindResultShouldContainEntries(
$shouldOrNot,
$expectedFiles,
$user
);
}
/**
* @Then /^the (?:propfind|search) result of user "([^"]*)" should not contain any (?:files|entries)$/
*
* @param string $user
*
* @return void
* @throws Exception
*/
public function thePropfindResultShouldNotContainAnyEntries(
string $user
):void {
$multistatusResults = $this->getMultiStatusResultFromPropfindResult($user);
Assert::assertEmpty($multistatusResults, 'The propfind response was expected to be empty but was not');
}
/**
* @Then /^the (?:propfind|search) result of user "([^"]*)" should contain only these (?:files|entries):$/
*
* @param string $user
* @param TableNode $expectedFiles
*
* @return void
* @throws Exception
*/
public function thePropfindResultShouldContainOnlyEntries(
string $user,
TableNode $expectedFiles
):void {
$user = $this->getActualUsername($user);
Assert::assertEquals(
\count($expectedFiles->getTable()),
$this->getNumberOfEntriesInPropfindResponse(
$user
),
"The number of elements in the response doesn't matches with expected number of elements"
);
$this->propfindResultShouldContainEntries(
'',
$expectedFiles,
$user
);
}
/**
* @Then the propfind/search result should contain :numFiles files/entries
*
* @param int $numFiles
*
* @return void
*/
public function propfindResultShouldContainNumEntries(int $numFiles):void {
//if we are using that step the second time in a scenario e.g. 'But ... should not'
//then don't parse the result again, because the result in a ResponseInterface
if (empty($this->responseXml)) {
$this->setResponseXml(
HttpRequestHelper::parseResponseAsXml($this->response)
);
}
Assert::assertIsArray(
$this->responseXml,
__METHOD__ . " responseXml is not an array"
);
Assert::assertArrayHasKey(
"value",
$this->responseXml,
__METHOD__ . " responseXml does not have key 'value'"
);
$multistatusResults = $this->responseXml["value"];
if ($multistatusResults === null) {
$multistatusResults = [];
}
Assert::assertEquals(
$numFiles,
\count($multistatusResults),
__METHOD__
. " Expected result to contain '"
. $numFiles
. "' files/entries, but got '"
. \count($multistatusResults)
. "' files/entries."
);
}
/**
* @Then the propfind/search result should contain any :expectedNumber of these files/entries:
*
* @param integer $expectedNumber
* @param TableNode $expectedFiles
*
* @return void
* @throws Exception
*/
public function theSearchResultShouldContainAnyOfTheseEntries(
int $expectedNumber,
TableNode $expectedFiles
):void {
$this->theSearchResultOfUserShouldContainAnyOfTheseEntries(
$this->getCurrentUser(),
$expectedNumber,
$expectedFiles
);
}
/**
* @Then the propfind/search result of user :user should contain any :expectedNumber of these files/entries:
*
* @param string $user
* @param integer $expectedNumber
* @param TableNode $expectedFiles
*
* @return void
* @throws Exception
*/
public function theSearchResultOfUserShouldContainAnyOfTheseEntries(
string $user,
int $expectedNumber,
TableNode $expectedFiles
):void {
$user = $this->getActualUsername($user);
$this->verifyTableNodeColumnsCount($expectedFiles, 1);
$this->propfindResultShouldContainNumEntries($expectedNumber);
$elementRows = $expectedFiles->getColumn(0);
// Remove any "/" from the front (or back) of the expected values passed
// into the step. findEntryFromPropfindResponse returns entries without
// any leading (or trailing) slash
$expectedEntries = \array_map(
function ($value) {
return \trim($value, "/");
},
$elementRows
);
$resultEntries = $this->findEntryFromSearchResponse();
foreach ($resultEntries as $resultEntry) {
Assert::assertContains($resultEntry, $expectedEntries);
}
}
/**
* @When user :arg1 lists the resources in :path with depth :depth using the WebDAV API
*
* @param string $user
* @param string $path
* @param string $depth
*
* @return void
* @throws Exception
*/
public function userListsTheResourcesInPathWithDepthUsingTheWebdavApi(string $user, string $path, string $depth):void {
$response = $this->listFolder(
$user,
$path,
$depth
);
$this->setResponse($response);
$this->setResponseXml(HttpRequestHelper::parseResponseAsXml($this->response));
}
/**
* @Then the last DAV response for user :user should contain these nodes/elements
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theLastDavResponseShouldContainTheseNodes(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["name"]);
foreach ($table->getHash() as $row) {
$path = $this->substituteInLineCodes($row['name']);
$res = $this->findEntryFromPropfindResponse($path, $user);
Assert::assertNotFalse($res, "expected $path to be in DAV response but was not found");
}
}
/**
* @Then the last DAV response for user :user should not contain these nodes/elements
*
* @param string $user
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theLastDavResponseShouldNotContainTheseNodes(string $user, TableNode $table):void {
$this->verifyTableNodeColumns($table, ["name"]);
foreach ($table->getHash() as $row) {
$path = $this->substituteInLineCodes($row['name']);
$res = $this->findEntryFromPropfindResponse($path, $user);
Assert::assertFalse($res, "expected $path to not be in DAV response but was found");
}
}
/**
* @Then the last public link DAV response should contain these nodes/elements
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theLastPublicDavResponseShouldContainTheseNodes(TableNode $table):void {
$user = $this->getLastCreatedPublicShareToken();
$this->verifyTableNodeColumns($table, ["name"]);
$type = $this->usingOldDavPath ? "public-files" : "public-files-new";
foreach ($table->getHash() as $row) {
$path = $this->substituteInLineCodes($row['name']);
$res = $this->findEntryFromPropfindResponse($path, $user, $type);
Assert::assertNotFalse($res, "expected $path to be in DAV response but was not found");
}
}
/**
* @Then the last public link DAV response should not contain these nodes/elements
*
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theLastPublicDavResponseShouldNotContainTheseNodes(TableNode $table):void {
$user = $this->getLastCreatedPublicShareToken();
$this->verifyTableNodeColumns($table, ["name"]);
$type = $this->usingOldDavPath ? "public-files" : "public-files-new";
foreach ($table->getHash() as $row) {
$path = $this->substituteInLineCodes($row['name']);
$res = $this->findEntryFromPropfindResponse($path, $user, $type);
Assert::assertFalse($res, "expected $path to not be in DAV response but was found");
}
}
/**
* @When the public lists the resources in the last created public link with depth :depth using the WebDAV API
*
* @param string $depth
*
* @return void
* @throws Exception
*/
public function thePublicListsTheResourcesInTheLastCreatedPublicLinkWithDepthUsingTheWebdavApi(string $depth):void {
$user = $this->getLastCreatedPublicShareToken();
$response = $this->listFolder(
$user,
'/',
$depth,
null,
$this->usingOldDavPath ? "public-files" : "public-files-new"
);
$this->setResponse($response);
$this->setResponseXml(HttpRequestHelper::parseResponseAsXml($this->response));
}
/**
* @param string|null $user
*
* @return array
*/
public function findEntryFromReportResponse(?string $user):array {
$responseXmlObj = $this->getResponseXmlObject();
$responseResources = [];
$hrefs = $responseXmlObj->xpath('//d:href');
foreach ($hrefs as $href) {
$hrefParts = \explode("/", (string)$href[0]);
if (\in_array($user, $hrefParts)) {
$entry = \urldecode(\end($hrefParts));
$responseResources[] = $entry;
} else {
throw new Error("Expected user: $hrefParts[5] but found: $user");
}
}
return $responseResources;
}
/**
* parses a PROPFIND response from $this->response into xml
* and returns found search results if found else returns false
*
* @param string|null $user
*
* @return int
*/
public function getNumberOfEntriesInPropfindResponse(
?string $user = null
):int {
$multistatusResults = $this->getMultiStatusResultFromPropfindResult($user);
return \count($multistatusResults);
}
/**
* parses a PROPFIND response from $this->response
* and returns multistatus data from the response
*
* @param string|null $user
*
* @return array
*/
public function getMultiStatusResultFromPropfindResult(
?string $user = null
):array {
//if we are using that step the second time in a scenario e.g. 'But ... should not'
//then don't parse the result again, because the result in a ResponseInterface
if (empty($this->responseXml)) {
$this->setResponseXml(
HttpRequestHelper::parseResponseAsXml($this->response)
);
}
Assert::assertNotEmpty($this->responseXml, __METHOD__ . ' Response is empty');
if ($user === null) {
$user = $this->getCurrentUser();
}
Assert::assertIsArray(
$this->responseXml,
__METHOD__ . " responseXml for user $user is not an array"
);
Assert::assertArrayHasKey(
"value",
$this->responseXml,
__METHOD__ . " responseXml for user $user does not have key 'value'"
);
$multistatus = $this->responseXml["value"];
if ($multistatus == null) {
$multistatus = [];
}
return $multistatus;
}
/**
* Escapes the given string for
* 1. Space --> %20
* 2. Opening Small Bracket --> %28
* 3. Closing Small Bracket --> %29
*
* @param string $path - File path to parse
*
* @return string
*/
public function escapePath(string $path): string {
return \str_replace([" ", "(", ")"], ["%20", "%28", "%29"], $path);
}
/**
* parses a PROPFIND response from $this->response into xml
* and returns found search results if found else returns false
*
* @param string|null $entryNameToSearch
* @param string|null $user
* @param string $type
* @param string $folderPath
*
* @return string|array|boolean
*
* string if $entryNameToSearch is given and is found
* array if $entryNameToSearch is not given
* boolean false if $entryNameToSearch is given and is not found
*
* @throws GuzzleException
*/
public function findEntryFromPropfindResponse(
?string $entryNameToSearch = null,
?string $user = null,
string $type = "files",
string $folderPath = ''
) {
$trimmedEntryNameToSearch = '';
// trim any leading "/" passed by the caller, we can just match the "raw" name
if ($entryNameToSearch != null) {
$trimmedEntryNameToSearch = \trim($entryNameToSearch, "/");
}
// url encode for spaces and brackets that may appear in the filePath
$folderPath = $this->escapePath($folderPath);
// topWebDavPath should be something like /remote.php/webdav/ or
// /remote.php/dav/files/alice/
$topWebDavPath = "/" . $this->getFullDavFilesPath($user) . "/" . $folderPath;
switch ($type) {
case "files":
break;
case "public-files":
case "public-files-old":
case "public-files-new":
$topWebDavPath = "/" . $this->getPublicLinkDavPath($user, $type) . "/";
break;
default:
throw new Exception("error");
}
$multistatusResults = $this->getMultiStatusResultFromPropfindResult($user);
$results = [];
foreach ($multistatusResults as $multistatusResult) {
$entryPath = $multistatusResult['value'][0]['value'];
$entryName = \str_replace($topWebDavPath, "", $entryPath);
$entryName = \rawurldecode($entryName);
$entryName = \trim($entryName, "/");
if ($trimmedEntryNameToSearch === $entryName) {
return $multistatusResult;
}
$results[] = $entryName;
}
if ($entryNameToSearch === null) {
return $results;
}
return false;
}
/**
* parses a REPORT response from $this->response into xml
* and returns found search results if found else returns false
*
* @param string|null $entryNameToSearch
* @param bool|null $searchForHighlightString
*
* @return string|array|boolean
*
* string if $entryNameToSearch is given and is found
* array if $entryNameToSearch is not given
* boolean false if $entryNameToSearch is given and is not found
*
* @throws Exception
*/
public function findEntryFromSearchResponse(
?string $entryNameToSearch = null,
?bool $searchForHighlightString = false
) {
// trim any leading "/" passed by the caller, we can just match the "raw" name
if ($entryNameToSearch !== null) {
$entryNameToSearch = \trim($entryNameToSearch, "/");
}
$spacesBaseUrl = webDavHelper::getDavPath(null, webDavHelper::DAV_VERSION_SPACES);
$searchResults = $this->getResponseXml()->xpath("//d:multistatus/d:response");
$results = [];
foreach ($searchResults as $item) {
$href = (string)$item->xpath("d:href")[0];
$shareRootXml = $item->xpath("d:propstat//oc:shareroot");
$href = \str_replace($spacesBaseUrl, "", $href);
$resourcePath = \substr($href, \strpos($href, '/') + 1);
if (\count($shareRootXml)) {
$shareroot = \trim((string)$shareRootXml[0], "/");
$resourcePath = $shareroot . "/" . $resourcePath;
}
$resourcePath = \rawurldecode($resourcePath);
if ($entryNameToSearch === $resourcePath) {
// If searching for single entry,
// we return an SimpleXmlElement of found item
return $item;
}
if ($searchForHighlightString) {
// If searching for highlighted string,
// we return an array of entries with highlighted content as value
// Example:
// [
// "<entryName1>" => "<highlighted-content>"
// "<entryName2>" => "<highlighted-content>"
// ]
$actualHighlightString = $item->xpath("d:propstat//oc:highlights");
$results[$resourcePath] = (string)$actualHighlightString[0];
} else {
// If list all the entries i.e. $entryNameToSearch=null,
// we return an array of entries in the response
// Example:
// ["<entry1>", "<entry2>"]
$results[] = $resourcePath;
}
}
if ($entryNameToSearch === null) {
return $results;
}
return false;
}
/**
* Prevent creating two uploads and/or deletes with the same "stime"
* That is based on seconds in some implementations.
* This prevents duplication of etags or other problems with
* trashbin/versions save/restore.
*
* Set env var UPLOAD_DELETE_WAIT_TIME to 1 to activate a 1-second pause.
* By default, there is no pause. That allows testing of implementations
* which should be able to cope with multiple upload/delete actions in the
* same second.
*
* @return void
*/
public function pauseUploadDelete():void {
$time = \time();
$uploadWaitTime = \getenv("UPLOAD_DELETE_WAIT_TIME");
$uploadWaitTime = $uploadWaitTime ? (int)$uploadWaitTime : 0;
if (($this->lastUploadDeleteTime !== null)
&& ($uploadWaitTime > 0)
&& (($time - $this->lastUploadDeleteTime) < $uploadWaitTime)
) {
\sleep($uploadWaitTime);
}
}
/**
* @Then the author of the created version with index :index should be :expectedUsername
*
* @param string $index
* @param string $expectedUsername
*
* @return void
* @throws Exception
*/
public function theAuthorOfEditedVersionFile(string $index, string $expectedUsername): void {
$expectedUserDisplayName = $this->getUserDisplayName($expectedUsername);
$resXml = $this->getResponseXmlObject();
if ($resXml === null) {
$resXml = HttpRequestHelper::getResponseXml(
$this->getResponse(),
__METHOD__
);
$this->setResponseXmlObject($resXml);
}
// the username should be in oc:meta-version-edited-by
$xmlPart = $resXml->xpath("//oc:meta-version-edited-by");
$authors = [];
foreach ($xmlPart as $idx => $author) {
// The first element is the root path element which is not a version
// So skipping it
if ($idx !== 0) {
$authors[] = $author->__toString();
}
}
// reverse the array to get the latest version first
\array_reverse($authors);
if (!isset($authors[$index - 1])) {
Assert::fail(
'could not find version with index "' . $index . '" for oc:meta-version-edited-by property in response to user "' . $this->responseUser . '"'
);
}
$actualUser = $authors[$index - 1];
Assert::assertEquals(
$expectedUsername,
$actualUser,
"Expected user of version with index $index in response to user '$this->responseUser' was '$expectedUsername', but got '$actualUser'"
);
// the user's display name should be in oc:meta-version-edited-by-name
$xmlPart = $resXml->xpath("//oc:meta-version-edited-by-name");
$displaynames = [];
foreach ($xmlPart as $idx => $displayname) {
// The first element is the root path element which is not a version
// So skipping it
if ($idx !== 0) {
$displaynames[] = $displayname->__toString();
}
}
// reverse the array to get the latest version first
\array_reverse($displaynames);
if (!isset($displaynames[$index - 1])) {
Assert::fail(
'could not find version with index "' . $index . '" for oc:meta-version-edited-by-name property in response to user "' . $this->responseUser . '"'
);
}
$actualUserDisplayName = $displaynames[$index - 1];
Assert::assertEquals(
$expectedUserDisplayName,
$actualUserDisplayName,
"Expected display name of version with index $index in response to user '$this->responseUser' was '$expectedUserDisplayName', but got '$actualUserDisplayName'"
);
}
/**
* @When user :user downloads the content of GDPR report :pathToFile
*
* @param string $user
* @param string $pathToFile
*
* @return void
* @throws Exception
*/
public function userGetsTheContentOfGeneratedJsonReport(string $user, string $pathToFile): void {
$password = $this->getPasswordForUser($user);
$response = $this->downloadFileAsUserUsingPassword($user, $pathToFile, $password);
$this->setResponse($response);
$this->pushToLastStatusCodesArrays();
}
}