Merge pull request #9924 from owncloud/reorganize-test-folders

[tests-only][full-ci] reorganize test folders within the acceptance directory
This commit is contained in:
Sawjan Gurung
2024-08-28 15:44:48 +05:45
committed by GitHub
51 changed files with 13 additions and 13 deletions
@@ -1,309 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2021 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use TestHelpers\HttpRequestHelper;
use TestHelpers\SetupHelper;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use splitbrain\PHPArchive\Tar;
use splitbrain\PHPArchive\Zip;
use splitbrain\PHPArchive\Archive;
require_once 'bootstrap.php';
/**
* Context for Archiver specific steps
*/
class ArchiverContext implements Context {
/**
* @var FeatureContext
*/
private FeatureContext $featureContext;
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*
* @throws Exception
*/
public function setUpScenario(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
SetupHelper::init(
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getBaseUrl(),
$this->featureContext->getOcPath()
);
}
/**
* @param string $type
*
* @return Archive
*/
public function getArchiveClass(string $type): Archive {
if ($type === 'zip') {
return new Zip();
} elseif ($type === 'tar') {
return new Tar();
} else {
throw new Exception('Unknown archive type: ' . $type);
}
}
/**
* @param string $dir
*
* @return void
*/
public function removeDir(string $dir): void {
$items = array_diff(scandir($dir), ['.', '..']);
foreach ($items as $item) {
$itemPath = $dir . DIRECTORY_SEPARATOR . $item;
if (\is_dir($itemPath)) {
$this->removeDir($itemPath);
} else {
\unlink($itemPath);
}
}
\rmdir($dir);
}
/**
* @param string $user
* @param string $resource
* @param string $addressType id|ids|path|paths
*
* @return string
*
* @throws Exception
*/
private function getArchiverQueryString(
string $user,
string $resource,
string $addressType
): string {
switch ($addressType) {
case 'id':
case 'ids':
return 'id=' . $this->featureContext->getFileIdForPath($user, $resource);
case 'path':
case 'paths':
return 'path=' . $resource;
default:
throw new Exception(
'"' . $addressType .
'" is not a legal value for $addressType, must be id|ids|path|paths'
);
}
}
/**
* @When /^user "([^"]*)" downloads the (zip|tar) archive of "([^"]*)" using the resource (id|ids|path|paths) and setting these headers:$/
*
* @param string $user
* @param string $archiveType
* @param string $resource
* @param string $addressType id|path
* @param TableNode $headersTable
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function userDownloadsTheZipOrTarArchiveOfResourceUsingResourceIdOrPathAndSettingTheseHeaders(
string $user,
string $archiveType,
string $resource,
string $addressType,
TableNode $headersTable
): void {
$this->featureContext->verifyTableNodeColumns(
$headersTable,
['header', 'value']
);
$headers = [];
foreach ($headersTable as $row) {
$headers[$row['header']] = $row ['value'];
}
$this->featureContext->setResponse($this->downloadArchive($user, $resource, $addressType, $archiveType, null, $headers));
}
/**
* @When user :downloader downloads the archive of :item of user :owner using the resource :addressType
*
* @param string $downloader Who sends the request
* @param string $resource
* @param string $owner Who is the real owner of the file
* @param string $addressType id|path
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function userDownloadsTheArchiveOfItemOfUser(
string $downloader,
string $resource,
string $owner,
string $addressType
): void {
$this->featureContext->setResponse($this->downloadArchive($downloader, $resource, $addressType, null, $owner));
}
/**
* @param string $downloader
* @param string $resource
* @param string $addressType
* @param string|null $archiveType
* @param string|null $owner
* @param array|null $headers
*
* @return ResponseInterface
* @throws GuzzleException
* @throws JsonException
*/
public function downloadArchive(
string $downloader,
string $resource,
string $addressType,
?string $archiveType = null,
?string $owner = null,
?array $headers = null
): ResponseInterface {
$owner = $owner ?? $downloader;
$downloader = $this->featureContext->getActualUsername($downloader);
$queryString = $this->getArchiverQueryString($owner, $resource, $addressType);
if ($archiveType !== null) {
$queryString .= '&output-format=' . $archiveType;
}
return HttpRequestHelper::get(
$this->featureContext->getBaseUrl() . '/archiver?' . $queryString,
$this->featureContext->getStepLineRef(),
$downloader,
$this->featureContext->getPasswordForUser($downloader),
$headers
);
}
/**
* @When user :user downloads the archive of these items using the resource :addressType
*
* @param string $user
* @param TableNode $items
* @param string $addressType ids|paths
*
* @return void
*
* @throws GuzzleException|Exception
*/
public function userDownloadsTheArchiveOfTheseItems(
string $user,
TableNode $items,
string $addressType
): void {
$user = $this->featureContext->getActualUsername($user);
$queryString = '';
foreach ($items->getRows() as $item) {
$queryString .= $this->getArchiverQueryString($user, $item[0], $addressType) . '&';
}
$queryString = \rtrim($queryString, '&');
$this->featureContext->setResponse(
HttpRequestHelper::get(
$this->featureContext->getBaseUrl() . '/archiver?' . $queryString,
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user),
)
);
}
/**
* @Then the downloaded :type archive should contain these files:
*
* @param string $type
* @param TableNode $expectedFiles
*
* @return void
*
* @throws Exception
*/
public function theDownloadedArchiveShouldContainTheseFiles(string $type, TableNode $expectedFiles):void {
$this->featureContext->verifyTableNodeColumns($expectedFiles, ['name', 'content']);
$contents = $this->featureContext->getResponse()->getBody()->getContents();
$tempFile = \tempnam(\sys_get_temp_dir(), 'OcAcceptanceTests_');
$tempExtractFolder = $tempFile;
\unlink($tempFile); // we only need the name
$tempFile = $tempFile . '.' . $type; // it needs the extension
\file_put_contents($tempFile, $contents);
// open the archive
$tar = $this->getArchiveClass($type);
$tar->open($tempFile);
$archiveData = $tar->contents();
// extract the archive
$tar->open($tempFile);
$tar->extract($tempExtractFolder);
$tar->close();
foreach ($expectedFiles->getHash() as $expectedItem) {
$expectedPath = trim($expectedItem['name'], "/");
$found = false;
foreach ($archiveData as $info) {
// get only the parent folder path for the given item
$actualPath = $info->getPath();
if ($expectedPath === $actualPath) {
if (!$info->getIsdir()) {
$fileContent = \file_get_contents("$tempExtractFolder/$actualPath");
Assert::assertEquals(
$expectedItem['content'],
$fileContent,
__METHOD__ .
" content of '" . $expectedPath . "' not as expected"
);
}
$found = true;
break;
}
}
if (!$found) {
Assert::fail("Resource '" . $expectedPath . "' is not in the downloaded archive.");
}
}
\unlink($tempFile);
$this->removeDir($tempExtractFolder);
}
}
@@ -1,648 +0,0 @@
<?php declare(strict_types=1);
/**
* @author Christoph Wurst <christoph@owncloud.com>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use TestHelpers\HttpRequestHelper;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Context\Context;
use TestHelpers\SetupHelper;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\WebDavHelper;
/**
* Authentication functions
*/
class AuthContext implements Context {
private FeatureContext $featureContext;
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function setUpScenario(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
// Reset ResponseXml
$this->featureContext->setResponseXml([]);
// Initialize SetupHelper class
SetupHelper::init(
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getBaseUrl(),
$this->featureContext->getOcPath()
);
}
/**
* @param string $user
* @param string $password
*
* @return array
*/
public function createBasicAuthHeader(string $user, string $password): array {
$header = [];
$authString = \base64_encode("$user:$password");
$header["Authorization"] = "basic $authString";
return $header;
}
/**
* @param string $url
* @param string $method
* @param string|null $body
* @param array|null $headers
*
* @return ResponseInterface
*/
public function sendRequest(
string $url,
string $method,
?string $body = null,
?array $headers = []
): ResponseInterface {
$fullUrl = $this->featureContext->getBaseUrl() . $url;
return HttpRequestHelper::sendRequest(
$fullUrl,
$this->featureContext->getStepLineRef(),
$method,
null,
null,
$headers,
$body,
);
}
/**
* @param string $user
* @param string $url
* @param string $method
* @param string|null $body
* @param array|null $headers
* @param string|null $property
*
* @return ResponseInterface
*/
public function requestUrlWithBasicAuth(
string $user,
string $url,
string $method,
?string $body = null,
?array $headers = null,
?string $property = null
): ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$url = $this->featureContext->substituteInLineCodes(
$url,
$user
);
$authHeader = $this->createBasicAuthHeader($user, $this->featureContext->getPasswordForUser($user));
$headers = \array_merge($headers ?? [], $authHeader);
if ($property !== null) {
$body = $this->featureContext->getBodyForOCSRequest($method, $property);
}
return $this->sendRequest(
$url,
$method,
$body,
$headers
);
}
/**
* @When a user requests :url with :method and no authentication
*
* @param string $url
* @param string $method
*
* @return void
*/
public function userRequestsURLWithNoAuth(string $url, string $method):void {
$this->featureContext->setResponse($this->sendRequest($url, $method));
}
/**
* @When a user requests these endpoints with :method with body :body and no authentication about user :user
*
* @param string $method
* @param string $body
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws JsonException
*/
public function userRequestsEndpointsWithBodyAndNoAuthThenStatusCodeAboutUser(string $method, string $body, string $ofUser, TableNode $table): void {
$ofUser = \strtolower($this->featureContext->getActualUsername($ofUser));
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->sendRequest($row['endpoint'], $method, $body);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When a user requests these endpoints with :method with no authentication about user :user
*
* @param string $method
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsEndpointsWithoutBodyAndNoAuthAboutUser(string $method, string $ofUser, TableNode $table): void {
$ofUser = \strtolower($this->featureContext->getActualUsername($ofUser));
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->sendRequest($row['endpoint'], $method);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When a user requests these endpoints with :method and no authentication
*
* @param string $method
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsEndpointsWithNoAuthentication(string $method, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$this->featureContext->setResponse($this->sendRequest($row['endpoint'], $method));
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When the user :user requests these endpoints with :method with basic auth
*
* @param string $user
* @param string $method
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsEndpointsWithBasicAuth(string $user, string $method, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$response = $this->requestUrlWithBasicAuth($user, $row['endpoint'], $method);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When /^user "([^"]*)" requests these endpoints with "([^"]*)" to (?:get|set) property "([^"]*)" about user "([^"]*)"$/
*
* @param string $user
* @param string $method
* @param string $property
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theUserRequestsTheseEndpointsToGetOrSetPropertyAboutUser(
string $user,
string $method,
string $property,
string $ofUser,
TableNode $table
):void {
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->requestUrlWithBasicAuth($user, $row['endpoint'], $method, null, null, $property);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When the administrator requests these endpoints with :method
*
* @param string $method
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theAdminRequestsTheseEndpointsWithMethod(string $method, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
foreach ($table->getHash() as $row) {
$response = $this->requestUrlWithBasicAuth(
$this->featureContext->getAdminUsername(),
$row['endpoint'],
$method
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests :url with :method using basic auth
*
* @param string $user
* @param string $url
* @param string $method
*
* @return void
*/
public function userRequestsURLUsingBasicAuth(
string $user,
string $url,
string $method
):void {
$response = $this->requestUrlWithBasicAuth($user, $url, $method);
$this->featureContext->setResponse($response);
}
/**
* @When user :user requests :url with :method using basic auth and with headers
*
* @param string $user
* @param string $url
* @param string $method
* @param TableNode $headersTable
*
* @return void
* @throws Exception
*/
public function userRequestsURLWithUsingBasicAuthAndDepthHeader(string $user, string $url, string $method, TableNode $headersTable):void {
$user = $this->featureContext->getActualUsername($user);
$url = $this->featureContext->substituteInLineCodes(
$url,
$user
);
$this->featureContext->verifyTableNodeColumns(
$headersTable,
['header', 'value']
);
$headers = [];
foreach ($headersTable as $row) {
$headers[$row['header']] = $row ['value'];
}
$this->featureContext->setResponse(
$this->requestUrlWithBasicAuth(
$user,
$url,
$method,
null,
$headers
)
);
}
/**
* @When user :user requests these endpoints with :method using password :password
*
* @param string $user
* @param string $method
* @param string $password
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsWithPassword(
string $user,
string $method,
string $password,
TableNode $table
): void {
$user = $this->featureContext->getActualUsername($user);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
$authHeader = $this->createBasicAuthHeader($user, $this->featureContext->getActualPassword($password));
foreach ($table->getHash() as $row) {
$response = $this->sendRequest($row['endpoint'], $method, null, $authHeader);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests these endpoints with :method using password :password about user :ofUser
*
* @param string $user
* @param string $method
* @param string $password
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsUsingPasswordAboutUser(
string $user,
string $method,
string $password,
string $ofUser,
TableNode $table
):void {
$user = $this->featureContext->getActualUsername($user);
$ofUser = $this->featureContext->getActualUsername($ofUser);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint'], ['destination']);
$headers = $this->createBasicAuthHeader($user, $this->featureContext->getActualPassword($password));
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
if (isset($row['destination'])) {
$headers['Destination'] = $this->featureContext->substituteInLineCodes(
$this->featureContext->getBaseUrl() . $row['destination'],
$ofUser
);
}
$response = $this->sendRequest(
$row['endpoint'],
$method,
null,
$headers
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests these endpoints with :method including body :body using password :password about user :ofUser
*
* @param string $user
* @param string $method
* @param string $body
* @param string $password
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsWithBodyUsingPasswordAboutUser(
string $user,
string $method,
string $body,
string $password,
string $ofUser,
TableNode $table
):void {
$user = $this->featureContext->getActualUsername($user);
$ofUser = $this->featureContext->getActualUsername($ofUser);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint'], ['destination']);
$headers = $this->createBasicAuthHeader($user, $this->featureContext->getActualPassword($password));
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
if (isset($row['destination'])) {
$headers['Destination'] = $this->featureContext->substituteInLineCodes(
$this->featureContext->getBaseUrl() . $row['destination'],
$ofUser
);
}
$response = $this->sendRequest(
$row['endpoint'],
$method,
$body,
$headers
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests these endpoints with :method including body :body about user :ofUser
*
* @param string $user
* @param string $method
* @param string $body
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsIncludingBodyAboutUser(string $user, string $method, string $body, string $ofUser, TableNode $table):void {
$user = $this->featureContext->getActualUsername($user);
$ofUser = $this->featureContext->getActualUsername($ofUser);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
$headers = [];
if ($method === 'MOVE' || $method === 'COPY') {
$headers['Destination'] = '/path/to/destination';
}
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->requestUrlWithBasicAuth(
$user,
$row['endpoint'],
$method,
$body,
$headers
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :asUser requests these endpoints with :method using the password of user :ofUser
*
* @param string $asUser
* @param string $method
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsWithoutBodyUsingThePasswordOfUser(string $asUser, string $method, string $ofUser, TableNode $table):void {
$asUser = $this->featureContext->getActualUsername($asUser);
$ofUser = $this->featureContext->getActualUsername($ofUser);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
// do request as $asUser using password of $ofUser
$authHeader = $this->createBasicAuthHeader($asUser, $this->featureContext->getPasswordForUser($ofUser));
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->sendRequest(
$row['endpoint'],
$method,
null,
$authHeader
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :asUser requests these endpoints with :method including body :body using the password of user :user
*
* @param string $asUser
* @param string $method
* @param string|null $body
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws JsonException
*/
public function userRequestsTheseEndpointsIncludingBodyUsingPasswordOfUser(string $asUser, string $method, ?string $body, string $ofUser, TableNode $table):void {
$asUser = $this->featureContext->getActualUsername($asUser);
$ofUser = $this->featureContext->getActualUsername($ofUser);
$this->featureContext->verifyTableNodeColumns($table, ['endpoint']);
// do request as $asUser using password of $ofUser
$authHeader = $this->createBasicAuthHeader($asUser, $this->featureContext->getPasswordForUser($ofUser));
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->sendRequest(
$row['endpoint'],
$method,
$body,
$authHeader
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests these endpoints with :method about user :ofUser
*
* @param string $user
* @param string $method
* @param string $ofUser
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRequestsTheseEndpointsAboutUser(string $user, string $method, string $ofUser, TableNode $table):void {
$headers = [];
if ($method === 'MOVE' || $method === 'COPY') {
$baseUrl = $this->featureContext->getBaseUrl();
$suffix = "";
if ($this->featureContext->getDavPathVersion() === WebDavHelper::DAV_VERSION_SPACES) {
$suffix = $this->featureContext->spacesContext->getSpaceIdByName($user, "Personal") . "/";
}
$davPath = WebDavHelper::getDavPath($user, $this->featureContext->getDavPathVersion());
$headers['Destination'] = "{$baseUrl}/{$davPath}{$suffix}moved";
}
foreach ($table->getHash() as $row) {
$row['endpoint'] = $this->featureContext->substituteInLineCodes(
$row['endpoint'],
$ofUser
);
$response = $this->requestUrlWithBasicAuth(
$user,
$row['endpoint'],
$method,
null,
$headers
);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastStatusCodesArrays();
}
}
/**
* @When user :user requests :endpoint with :method without retrying
*
* @param string $user
* @param string $endpoint
* @param string $method
*
* @return void
*/
public function userRequestsURLWithoutRetry(
string $user,
string $endpoint,
string $method
):void {
$username = $this->featureContext->getActualUsername($user);
$endpoint = $this->featureContext->substituteInLineCodes(
$endpoint,
$username
);
$fullUrl = $this->featureContext->getBaseUrl() . $endpoint;
$response = HttpRequestHelper::sendRequestOnce(
$fullUrl,
$this->featureContext->getStepLineRef(),
$method,
$username,
$this->featureContext->getPasswordForUser($user)
);
$this->featureContext->setResponse($response);
}
}
@@ -1,327 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Joas Schilling <coding@schilljs.com>
* @author Sergio Bertolin <sbertolin@owncloud.com>
* @author Phillip Davis <phil@jankaritech.com>
* @copyright Copyright (c) 2018, ownCloud GmbH
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\PyStringNode;
use Psr\Http\Message\ResponseInterface;
use PHPUnit\Framework\Assert;
use TestHelpers\OcsApiHelper;
require_once 'bootstrap.php';
/**
* Capabilities context.
*/
class CapabilitiesContext implements Context {
private FeatureContext $featureContext;
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
/**
*
* @param string $username
* @param boolean $formatJson // if true then formats the response in json
*
* @return ResponseInterface
* @throws GuzzleException
* @throws JsonException
*/
public function userGetsCapabilities(string $username, ?bool $formatJson = false): ResponseInterface {
$user = $this->featureContext->getActualUsername($username);
$password = $this->featureContext->getPasswordForUser($user);
return OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$user,
$password,
'GET',
'/cloud/capabilities' . ($formatJson ? '?format=json' : ''),
$this->featureContext->getStepLineRef(),
[],
$this->featureContext->getOcsApiVersion()
);
}
/**
* @return string
* @throws Exception|GuzzleException
*/
public function getAdminUsernameForCapabilitiesCheck(): string {
if (\TestHelpers\OcisHelper::isTestingOnReva()) {
// When testing on reva we don't have a user called "admin" to use
// to access the capabilities. So create an ordinary user on-the-fly
// with a default password. That user should be able to get a
// capabilities response that the test can process.
$adminUsername = "PseudoAdminForRevaTest";
$createdUsers = $this->featureContext->getCreatedUsers();
if (!\array_key_exists($adminUsername, $createdUsers)) {
$this->featureContext->userHasBeenCreated(["userName" => $adminUsername]);
}
} else {
$adminUsername = $this->featureContext->getAdminUsername();
}
return $adminUsername;
}
/**
* @param SimpleXMLElement $xml of the capabilities
* @param string $capabilitiesApp the "app" name in the capabilities response
* @param string $capabilitiesPath the path to the element
*
* @return string
*/
public function getParameterValueFromXml(
SimpleXMLElement $xml,
string $capabilitiesApp,
string $capabilitiesPath
): string {
$path_to_element = \explode('@@@', $capabilitiesPath);
$answeredValue = $xml->{$capabilitiesApp};
foreach ($path_to_element as $element) {
$nameIndexParts = \explode('[', $element);
if (isset($nameIndexParts[1])) {
// This part of the path should be something like "some_element[1]"
// Separately extract the name and the index
$name = $nameIndexParts[0];
$index = (int) \explode(']', $nameIndexParts[1])[0];
// and use those to construct the reference into the next XML level
$answeredValue = $answeredValue->{$name}[$index];
} else {
if ($element !== "") {
$answeredValue = $answeredValue->{$element};
}
}
}
return (string) $answeredValue;
}
/**
* @When user :username retrieves the capabilities using the capabilities API
*
* @param string $username
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function userRetrievesCapabilities(string $username): void {
$user = $this->featureContext->getActualUsername($username);
$this->featureContext->setResponse($this->userGetsCapabilities($user, true));
}
/**
* @When the administrator retrieves the capabilities using the capabilities API
*
* @return void
*/
public function theAdministratorGetsCapabilities(): void {
$user = $this->getAdminUsernameForCapabilitiesCheck();
$this->featureContext->setResponse($this->userGetsCapabilities($user, true));
}
/**
* @Then the major-minor-micro version data in the response should match the version string
*
* @return void
* @throws Exception
*/
public function checkVersionMajorMinorMicroResponse():void {
$jsonResponse = $this->featureContext->getJsonDecodedResponseBodyContent();
$versionData = $jsonResponse->ocs->data->version;
$versionString = (string) $versionData->string;
// We expect that versionString will be in a format like "10.9.2 beta" or "10.9.2-alpha" or "10.9.2"
$result = \preg_match('/^[0-9]+\.[0-9]+\.[0-9]+/', $versionString, $matches);
Assert::assertSame(
1,
$result,
__METHOD__ . " version string '$versionString' does not start with a semver version"
);
// semVerParts should have an array with the 3 semver components of the version, e.g. "1", "9" and "2".
$semVerParts = \explode('.', $matches[0]);
$expectedMajor = $semVerParts[0];
$expectedMinor = $semVerParts[1];
$expectedMicro = $semVerParts[2];
$actualMajor = (string) $versionData->major;
$actualMinor = (string) $versionData->minor;
$actualMicro = (string) $versionData->micro;
Assert::assertSame(
$expectedMajor,
$actualMajor,
__METHOD__ . "'major' data item does not match with major version in string '$versionString'"
);
Assert::assertSame(
$expectedMinor,
$actualMinor,
__METHOD__ . "'minor' data item does not match with minor version in string '$versionString'"
);
Assert::assertSame(
$expectedMicro,
$actualMicro,
__METHOD__ . "'micro' data item does not match with micro (patch) version in string '$versionString'"
);
}
/**
* @Then the status.php response should include
*
* @param PyStringNode $jsonExpected
*
* @return void
* @throws Exception
*/
public function statusPhpRespondedShouldMatch(PyStringNode $jsonExpected): void {
$jsonExpectedDecoded = \json_decode($jsonExpected->getRaw(), true);
$jsonRespondedDecoded = $this->featureContext->getJsonDecodedResponse();
$response = $this->userGetsCapabilities($this->getAdminUsernameForCapabilitiesCheck());
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
$responseXml = $this->featureContext->getResponseXml($response)->data->capabilities;
$edition = $this->getParameterValueFromXml(
$responseXml,
'core',
'status@@@edition'
);
if (!\strlen($edition)) {
Assert::fail(
"Cannot get edition from core capabilities"
);
}
$product = $this->getParameterValueFromXml(
$responseXml,
'core',
'status@@@product'
);
if (!\strlen($product)) {
Assert::fail(
"Cannot get product from core capabilities"
);
}
$productName = $this->getParameterValueFromXml(
$responseXml,
'core',
'status@@@productname'
);
if (!\strlen($productName)) {
Assert::fail(
"Cannot get productname from core capabilities"
);
}
$jsonExpectedDecoded['edition'] = $edition;
$jsonExpectedDecoded['product'] = $product;
$jsonExpectedDecoded['productname'] = $productName;
// We are on oCIS or reva or some other implementation. We cannot do "occ status".
// So get the expected version values by looking in the capabilities response.
$version = $this->getParameterValueFromXml(
$responseXml,
'core',
'status@@@version'
);
if (!\strlen($version)) {
Assert::fail(
"Cannot get version from core capabilities"
);
}
$versionString = $this->getParameterValueFromXml(
$responseXml,
'core',
'status@@@versionstring'
);
if (!\strlen($versionString)) {
Assert::fail(
"Cannot get versionstring from core capabilities"
);
}
$jsonExpectedDecoded['version'] = $version;
$jsonExpectedDecoded['versionstring'] = $versionString;
$errorMessage = "";
$errorFound = false;
foreach ($jsonExpectedDecoded as $key => $expectedValue) {
if (\array_key_exists($key, $jsonRespondedDecoded)) {
$actualValue = $jsonRespondedDecoded[$key];
if ($actualValue !== $expectedValue) {
$errorMessage .= "$key expected value was $expectedValue but actual value was $actualValue\n";
$errorFound = true;
}
} else {
$errorMessage .= "$key was not found in the status response\n";
$errorFound = true;
}
}
Assert::assertFalse($errorFound, $errorMessage);
// We have checked that the status.php response has data that matches up with
// data found in the capabilities response and/or the "occ status" command output.
// But the output might be reported wrongly in all of these in the same way.
// So check that the values also seem "reasonable".
$version = $jsonExpectedDecoded['version'];
$versionString = $jsonExpectedDecoded['versionstring'];
Assert::assertMatchesRegularExpression(
"/^\d+\.\d+\.\d+\.\d+$/",
$version,
"version should be in a form like 10.9.8.1 but is $version"
);
if (\preg_match("/^(\d+\.\d+\.\d+)\.\d+(-[0-9A-Za-z-]+)?(\+[0-9A-Za-z-]+)?$/", $version, $matches)) {
// We should have matched something like 10.9.8 - the first 3 numbers in the version.
// Ignore pre-releases and meta information
Assert::assertArrayHasKey(
1,
$matches,
"version $version could not match the pattern Major.Minor.Patch"
);
$majorMinorPatchVersion = $matches[1];
} else {
Assert::fail("version '$version' does not start in a form like 10.9.8");
}
Assert::assertStringStartsWith(
$majorMinorPatchVersion,
$versionString,
"versionstring should start with $majorMinorPatchVersion but is $versionString"
);
}
}
@@ -1,488 +0,0 @@
<?php declare(strict_types=1);
/**
* @author Roeland Jago Douma <rullzer@owncloud.com>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use PHPUnit\Framework\Assert;
use TestHelpers\WebDavHelper;
use Psr\Http\Message\ResponseInterface;
require_once 'bootstrap.php';
/**
* Checksum functions
*/
class ChecksumContext implements Context {
private FeatureContext $featureContext;
/**
* @param string $user
* @param string $source
* @param string $destination
* @param string $checksum
*
* @return ResponseInterface
*/
public function uploadFileToWithChecksumUsingTheAPI(
string $user,
string $source,
string $destination,
string $checksum
):ResponseInterface {
$file = \file_get_contents(
$this->featureContext->acceptanceTestsDirLocation() . $source
);
return $this->featureContext->makeDavRequest(
$user,
'PUT',
$destination,
['OC-Checksum' => $checksum],
$file
);
}
/**
* @When user :user uploads file :source to :destination with checksum :checksum using the WebDAV API
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $checksum
*
* @return void
*/
public function userUploadsFileToWithChecksumUsingTheAPI(
string $user,
string $source,
string $destination,
string $checksum
):void {
$this->featureContext->setResponse($this->uploadFileToWithChecksumUsingTheAPI($user, $source, $destination, $checksum));
}
/**
* @Given user :user has uploaded file :source to :destination with checksum :checksum
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $checksum
*
* @return void
*/
public function userHasUploadedFileToWithChecksumUsingTheAPI(
string $user,
string $source,
string $destination,
string $checksum
):void {
$user = $this->featureContext->getActualUsername($user);
$response = $this->uploadFileToWithChecksumUsingTheAPI(
$user,
$source,
$destination,
$checksum
);
$this->featureContext->theHTTPStatusCodeShouldBe([201,204], '', $response);
}
/**
* @param string $user
* @param string $content
* @param string $checksum
* @param string $destination
*
* @return ResponseInterface
*/
public function uploadFileWithContentAndChecksumToUsingTheAPI(
string $user,
string $content,
string $checksum,
string $destination
):ResponseInterface {
return $this->featureContext->makeDavRequest(
$user,
'PUT',
$destination,
['OC-Checksum' => $checksum],
$content
);
}
/**
* @Given user :user has uploaded file with content :content and checksum :checksum to :destination
*
* @param string $user
* @param string $content
* @param string $checksum
* @param string $destination
*
* @return void
*/
public function userHasUploadedFileWithContentAndChecksumToUsingTheAPI(
string $user,
string $content,
string $checksum,
string $destination
):void {
$user = $this->featureContext->getActualUsername($user);
$response = $this->uploadFileWithContentAndChecksumToUsingTheAPI($user, $content, $checksum, $destination);
$this->featureContext->theHTTPStatusCodeShouldBe(201, '', $response);
}
/**
* @When user :user requests the checksum of :path via propfind
*
* @param string $user
* @param string $path
*
* @return void
*/
public function userRequestsTheChecksumOfViaPropfind(string $user, string $path):void {
$this->featureContext->setResponse($this->propfindResourceChecksum($user, $path));
}
/**
* @param string $user
* @param string $path
*
* @return ResponseInterface
*/
public function propfindResourceChecksum(string $user, string $path) : ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$body = '<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<oc:checksums />
</d:prop>
</d:propfind>';
$password = $this->featureContext->getPasswordForUser($user);
return WebDavHelper::makeDavRequest(
$this->featureContext->getBaseUrl(),
$user,
$password,
'PROPFIND',
$path,
null,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion()
);
}
/**
* @Then the webdav checksum should match :expectedChecksum
*
* @param string $expectedChecksum
*
* @return void
* @throws Exception
*/
public function theWebdavChecksumShouldMatch(string $expectedChecksum):void {
$bodyContents = $this->featureContext->getResponse()->getBody()->getContents();
$this->validateChecksum($bodyContents, $expectedChecksum);
}
/**
* @Then as user :user the webdav checksum of :path via propfind should match :expectedChecksum
*
* @param string $user
* @param string $path
* @param string $expectedChecksum
*
* @return void
* @throws Exception
*/
public function asUserTheWebdavChecksumOfPathViaPropfindShouldMatch(string $user, string $path, string $expectedChecksum):void {
$user = $this->featureContext->getActualUsername($user);
$resource = $this->propfindResourceChecksum($user, $path);
$bodyContents = $resource->getBody()->getContents();
$this->validateChecksum($bodyContents, $expectedChecksum);
}
/**
* @param string $bodyContents
* @param string $expectedChecksum
*
* @return void
*/
public function validateChecksum(string $bodyContents, string $expectedChecksum):void {
$service = new Sabre\Xml\Service();
$parsed = $service->parse($bodyContents);
/*
* Fetch the checksum array
* The checksums are way down in the array:
* $checksums = $parsed[0]['value'][1]['value'][0]['value'][0];
* And inside is the actual checksum string:
* $checksums['value'][0]['value']
* The Asserts below check the existence of the expected key at every level
* of the nested array. This helps to see what happened if a test fails
* because the response structure is not as expected.
*/
Assert::assertIsArray(
$parsed,
__METHOD__ . " could not parse response as XML. Expected parsed XML to be an array but found " . $bodyContents
);
Assert::assertArrayHasKey(
0,
$parsed,
__METHOD__ . " parsed XML does not have key 0"
);
$parsed0 = $parsed[0];
Assert::assertArrayHasKey(
'value',
$parsed0,
__METHOD__ . " parsed XML parsed0 does not have key value"
);
$parsed0Value = $parsed0['value'];
Assert::assertArrayHasKey(
1,
$parsed0Value,
__METHOD__ . " parsed XML parsed0Value does not have key 1"
);
$parsed0Value1 = $parsed0Value[1];
Assert::assertArrayHasKey(
'value',
$parsed0Value1,
__METHOD__ . " parsed XML parsed0Value1 does not have key value after key 1"
);
$parsed0Value1Value = $parsed0Value1['value'];
Assert::assertArrayHasKey(
0,
$parsed0Value1Value,
__METHOD__ . " parsed XML parsed0Value1Value does not have key 0"
);
$parsed0Value1Value0 = $parsed0Value1Value[0];
Assert::assertArrayHasKey(
'value',
$parsed0Value1Value0,
__METHOD__ . " parsed XML parsed0Value1Value0 does not have key value"
);
$parsed0Value1Value0Value = $parsed0Value1Value0['value'];
Assert::assertArrayHasKey(
0,
$parsed0Value1Value0Value,
__METHOD__ . " parsed XML parsed0Value1Value0Value does not have key 0"
);
$checksums = $parsed0Value1Value0Value[0];
Assert::assertArrayHasKey(
'value',
$checksums,
__METHOD__ . " parsed XML checksums does not have key value"
);
$checksumsValue = $checksums['value'];
Assert::assertArrayHasKey(
0,
$checksumsValue,
__METHOD__ . " parsed XML checksumsValue does not have key 0"
);
$checksumsValue0 = $checksumsValue[0];
Assert::assertArrayHasKey(
'value',
$checksumsValue0,
__METHOD__ . " parsed XML checksumsValue0 does not have key value"
);
$actualChecksum = $checksumsValue0['value'];
Assert::assertEquals(
$expectedChecksum,
$actualChecksum,
"Expected: webDav checksum should be $expectedChecksum but got $actualChecksum"
);
}
/**
* @Then the header checksum should match :expectedChecksum
*
* @param string $expectedChecksum
*
* @return void
* @throws Exception
*/
public function theHeaderChecksumShouldMatch(string $expectedChecksum):void {
$headerChecksums
= $this->featureContext->getResponse()->getHeader('OC-Checksum');
Assert::assertIsArray(
$headerChecksums,
__METHOD__ . " getHeader('OC-Checksum') did not return an array"
);
Assert::assertNotEmpty(
$headerChecksums,
__METHOD__ . " getHeader('OC-Checksum') returned an empty array. No checksum header was found."
);
$checksumCount = \count($headerChecksums);
Assert::assertTrue(
$checksumCount === 1,
__METHOD__ . " Expected 1 checksum in the header but found $checksumCount checksums"
);
$headerChecksum
= $headerChecksums[0];
Assert::assertEquals(
$expectedChecksum,
$headerChecksum,
"Expected: header checksum should match $expectedChecksum but got $headerChecksum"
);
}
/**
* @Then the header checksum when user :arg1 downloads file :arg2 using the WebDAV API should match :arg3
*
* @param string $user
* @param string $fileName
* @param string $expectedChecksum
*
* @return void
* @throws Exception
*/
public function theHeaderChecksumWhenUserDownloadsFileUsingTheWebdavApiShouldMatch(string $user, string $fileName, string $expectedChecksum):void {
$response = $this->featureContext->downloadFileAsUserUsingPassword($user, $fileName);
$headerChecksums = $response->getHeader('OC-Checksum');
Assert::assertIsArray(
$headerChecksums,
__METHOD__ . " getHeader('OC-Checksum') did not return an array"
);
Assert::assertNotEmpty(
$headerChecksums,
__METHOD__ . " getHeader('OC-Checksum') returned an empty array. No checksum header was found."
);
$checksumCount = \count($headerChecksums);
Assert::assertTrue(
$checksumCount === 1,
__METHOD__ . " Expected 1 checksum in the header but found $checksumCount checksums"
);
$headerChecksum
= $headerChecksums[0];
Assert::assertEquals(
$expectedChecksum,
$headerChecksum,
"Expected: header checksum should match $expectedChecksum but got $headerChecksum"
);
}
/**
* @param string $user
* @param int $num
* @param int $total
* @param string $data
* @param string $destination
* @param string $expectedChecksum
*
* @return ResponseInterface
*/
public function uploadChunkFileOfWithToWithChecksum(
string $user,
int $num,
int $total,
string $data,
string $destination,
string $expectedChecksum
):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$num -= 1;
$file = "$destination-chunking-42-$total-$num";
return $this->featureContext->makeDavRequest(
$user,
'PUT',
$file,
['OC-Checksum' => $expectedChecksum, 'OC-Chunked' => '1'],
$data
);
}
/**
* @When user :user uploads chunk file :num of :total with :data to :destination with checksum :expectedChecksum using the WebDAV API
*
* @param string $user
* @param int $num
* @param int $total
* @param string $data
* @param string $destination
* @param string $expectedChecksum
*
* @return void
*/
public function userUploadsChunkFileOfWithToWithChecksum(
string $user,
int $num,
int $total,
string $data,
string $destination,
string $expectedChecksum
):void {
$response = $this->uploadChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination, $expectedChecksum);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has uploaded chunk file :num of :total with :data to :destination with checksum :expectedChecksum
*
* @param string $user
* @param int $num
* @param int $total
* @param string $data
* @param string $destination
* @param string $expectedChecksum
*
* @return void
*/
public function userHasUploadedChunkFileOfWithToWithChecksum(
string $user,
int $num,
int $total,
string $data,
string $destination,
string $expectedChecksum
):void {
$response = $this->uploadChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination, $expectedChecksum);
$this->featureContext->theHTTPStatusCodeShouldBe(
[201, 206],
'',
$response
);
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
}
@@ -1,237 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Sajan Gurung <sajan@jankaritech.com>
* @copyright Copyright (c) 2024 Sajan Gurung sajan@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Context\Context;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\CliHelper;
use TestHelpers\OcisConfigHelper;
/**
* CLI context
*/
class CliContext implements Context {
private FeatureContext $featureContext;
private SpacesContext $spacesContext;
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function setUpScenario(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->spacesContext = $environment->getContext('SpacesContext');
}
/**
* @Given the administrator has stopped the server
*
* @return void
*/
public function theAdministratorHasStoppedTheServer(): void {
$response = OcisConfigHelper::stopOcis();
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @Given /^the administrator (?:starts|has started) the server$/
*
* @return void
*/
public function theAdministratorHasStartedTheServer(): void {
$response = OcisConfigHelper::startOcis();
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @When /^the administrator resets the password of (non-existing|existing) user "([^"]*)" to "([^"]*)" using the CLI$/
*
* @param string $status
* @param string $user
* @param string $password
*
* @return void
*/
public function theAdministratorResetsThePasswordOfUserUsingTheCLI(string $status, string $user, string $password): void {
$command = "idm resetpassword -u $user";
$body = [
"command" => $command,
"inputs" => [$password, $password]
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
if ($status === "non-existing") {
return;
}
$this->featureContext->updateUserPassword($user, $password);
}
/**
* @When the administrator deletes the empty trashbin folders using the CLI
*
* @return void
*/
public function theAdministratorDeletesEmptyTrashbinFoldersUsingTheCli():void {
$path = $this->featureContext->getStorageUsersRoot();
$command = "trash purge-empty-dirs -p $path --dry-run=false";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator checks the backup consistency using the CLI
*
* @return void
*/
public function theAdministratorChecksTheBackupConsistencyUsingTheCli():void {
$path = $this->featureContext->getStorageUsersRoot();
$command = "backup consistency -p $path";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator removes all the file versions using the CLI
*
* @return void
*/
public function theAdministratorRemovesAllVersionsOfResources() {
$path = $this->featureContext->getStorageUsersRoot();
$command = "revisions purge -p $path --dry-run=false";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator removes the versions of file :file of user :user from space :space using the CLI
*
* @param string $file
* @param string $user
* @param string $space
*
* @return void
*/
public function theAdministratorRemovesTheVersionsOfFileUsingFileId($file, $user, $space) {
$path = $this->featureContext->getStorageUsersRoot();
$fileId = $this->spacesContext->getFileId($user, $space, $file);
$command = "revisions purge -p $path -r $fileId --dry-run=false";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When /^the administrator reindexes all spaces using the CLI$/
*
* @return void
*/
public function theAdministratorReindexesAllSpacesUsingTheCli(): void {
$command = "search index --all-spaces";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When /^the administrator reindexes a space "([^"]*)" using the CLI$/
*
* @param string $spaceName
*
* @return void
*/
public function theAdministratorReindexesASpaceUsingTheCli(string $spaceName): void {
$spaceId = $this->spacesContext->getSpaceIdByName($this->featureContext->getAdminUsername(), $spaceName);
$command = "search index --space $spaceId";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @When the administrator removes the file versions of space :space using the CLI
*
* @param string $space
*
* @return void
*/
public function theAdministratorRemovesTheVersionsOfFilesInSpaceUsingSpaceId(string $space):void {
$path = $this->featureContext->getStorageUsersRoot();
$adminUsername = $this->featureContext->getAdminUsername();
$spaceId = $this->spacesContext->getSpaceIdByName($adminUsername, $space);
$command = "revisions purge -p $path -r $spaceId --dry-run=false";
$body = [
"command" => $command
];
$this->featureContext->setResponse(CliHelper::runCommand($body));
}
/**
* @Then the command should be successful
*
* @return void
*/
public function theCommandShouldBeSuccessful(): void {
$response = $this->featureContext->getResponse();
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
$jsonResponse = $this->featureContext->getJsonDecodedResponse($response);
Assert::assertSame("OK", $jsonResponse["status"]);
Assert::assertSame(0, $jsonResponse["exitCode"], "Expected exit code to be 0, but got " . $jsonResponse["exitCode"]);
}
/**
* @Then /^the command output (should|should not) contain "([^"]*)"$/
*
* @param string $shouldOrNot
* @param string $output
*
* @return void
*/
public function theCommandOutputShouldContain(string $shouldOrNot, string $output): void {
$response = $this->featureContext->getResponse();
$jsonResponse = $this->featureContext->getJsonDecodedResponse($response);
$output = $this->featureContext->substituteInLineCodes($output);
if ($shouldOrNot === "should") {
Assert::assertStringContainsString($output, $jsonResponse["message"]);
} else {
Assert::assertStringNotContainsString($output, $jsonResponse["message"]);
}
}
}
@@ -1,242 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\WebDavHelper;
require_once 'bootstrap.php';
/**
* context containing favorites related API steps
*/
class FavoritesContext implements Context {
private FeatureContext $featureContext;
private WebDavPropertiesContext $webDavPropertiesContext;
/**
* @param string$user
* @param string $path
*
* @return ResponseInterface
*/
public function userFavoritesElement(string $user, string $path):ResponseInterface {
return $this->changeFavStateOfAnElement(
$user,
$path,
1
);
}
/**
* @When user :user favorites element :path using the WebDAV API
*
* @param string $user
* @param string $path
*
* @return void
*/
public function userFavoritesElementUsingWebDavApi(string $user, string $path):void {
$this->featureContext->setResponse($this->userFavoritesElement($user, $path));
}
/**
* @Given user :user has favorited element :path
*
* @param string $user
* @param string $path
*
* @return void
*/
public function userHasFavoritedElementUsingWebDavApi(string $user, string $path):void {
$this->featureContext->theHTTPStatusCodeShouldBe(207, '', $this->userFavoritesElement($user, $path));
}
/**
* @param string $user
* @param string $path
*
* @return ResponseInterface
*/
public function userUnfavoritesElement(string $user, string $path):ResponseInterface {
return $this->changeFavStateOfAnElement(
$user,
$path,
0
);
}
/**
* @When user :user unfavorites element :path using the WebDAV API
*
* @param string $user
* @param string $path
*
* @return void
*/
public function userUnfavoritesElementUsingWebDavApi(string $user, string $path):void {
$this->featureContext->setResponse($this->userUnfavoritesElement($user, $path));
}
/**
* @Then /^user "([^"]*)" should (not|)\s?have the following favorited items$/
*
* @param string $user
* @param string $shouldOrNot (not|)
* @param TableNode $expectedElements
*
* @return void
*/
public function checkFavoritedElements(
string $user,
string $shouldOrNot,
TableNode $expectedElements
):void {
$user = $this->featureContext->getActualUsername($user);
$this->userListsFavorites($user);
$this->featureContext->propfindResultShouldContainEntries(
$shouldOrNot,
$expectedElements,
$user
);
}
/**
* @When /^user "([^"]*)" lists the favorites and limits the result to ([\d*]) elements using the WebDAV API$/
*
* @param string $user
* @param int|null $limit
*
* @return void
*/
public function userListsFavorites(string $user, ?int $limit = null):void {
$renamedUser = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
$body
= "<?xml version='1.0' encoding='utf-8' ?>\n" .
" <oc:filter-files xmlns:a='DAV:' xmlns:oc='http://owncloud.org/ns' >\n" .
" <a:prop><oc:favorite/></a:prop>\n" .
" <oc:filter-rules><oc:favorite>1</oc:favorite></oc:filter-rules>\n";
if ($limit !== null) {
$body .= " <oc:search>\n" .
" <oc:limit>$limit</oc:limit>\n" .
" </oc:search>\n";
}
$body .= " </oc:filter-files>";
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$renamedUser,
$password,
"REPORT",
"/",
null,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion()
);
$this->featureContext->setResponse($response);
}
/**
* @Then /^as user "([^"]*)" (?:file|folder|entry) "([^"]*)" should be favorited$/
*
* @param string $user
* @param string $path
* @param integer $expectedValue 0|1
*
* @return void
*/
public function asUserFileOrFolderShouldBeFavorited(string $user, string $path, int $expectedValue = 1):void {
$property = "oc:favorite";
$this->webDavPropertiesContext->checkPropertyOfAFolder(
$user,
$path,
$property,
(string)$expectedValue
);
}
/**
* @Then /^as user "([^"]*)" (?:file|folder|entry) "([^"]*)" should not be favorited$/
*
* @param string $user
* @param string $path
*
* @return void
*/
public function asUserFileShouldNotBeFavorited(string $user, string $path):void {
$this->asUserFileOrFolderShouldBeFavorited($user, $path, 0);
}
/**
* Set the elements of a proppatch
*
* @param string $user
* @param string $path
* @param int|null $favOrUnfav 1 = favorite, 0 = unfavorite
*
* @return ResponseInterface
*/
public function changeFavStateOfAnElement(
string $user,
string $path,
?int $favOrUnfav
):ResponseInterface {
$renamedUser = $this->featureContext->getActualUsername($user);
return WebDavHelper::proppatch(
$this->featureContext->getBaseUrl(),
$renamedUser,
$this->featureContext->getPasswordForUser($user),
$path,
'favorite',
(string)$favOrUnfav,
$this->featureContext->getStepLineRef(),
"oc='http://owncloud.org/ns'",
$this->featureContext->getDavPathVersion()
);
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->webDavPropertiesContext = $environment->getContext(
'WebDavPropertiesContext'
);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,541 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018, ownCloud GmbH
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use TestHelpers\HttpRequestHelper;
use TestHelpers\WebDavHelper;
use Psr\Http\Message\ResponseInterface;
require_once 'bootstrap.php';
/**
* Steps that relate to files_versions app
*/
class FilesVersionsContext implements Context {
private FeatureContext $featureContext;
/**
* @param string $fileId
*
* @return string
*/
private function getVersionsPathForFileId(string $fileId):string {
return "/meta/$fileId/v";
}
/**
* @When user :user tries to get versions of file :file from :fileOwner
*
* @param string $user
* @param string $file
* @param string $fileOwner
*
* @return void
* @throws Exception
*/
public function userTriesToGetFileVersions(string $user, string $file, string $fileOwner):void {
$this->featureContext->setResponse($this->getFileVersions($user, $file, $fileOwner));
}
/**
* @When user :user gets the number of versions of file :file
*
* @param string $user
* @param string $file
*
* @return void
* @throws Exception
*/
public function userGetsFileVersions(string $user, string $file):void {
$this->featureContext->setResponse($this->getFileVersions($user, $file));
}
/**
* @param string $user
* @param string $file
* @param string|null $fileOwner
*
* @return ResponseInterface
* @throws JsonException
* @throws GuzzleException
*/
public function getFileVersions(
string $user,
string $file,
?string $fileOwner = null
): ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$fileOwner = $fileOwner ? $this->featureContext->getActualUsername($fileOwner) : $user;
$fileId = $this->featureContext->getFileIdForPath($fileOwner, $file);
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $file user $fileOwner not found (the file may not exist)");
return $this->featureContext->makeDavRequest(
$user,
"PROPFIND",
$this->getVersionsPathForFileId($fileId),
null,
null,
null,
'2'
);
}
/**
* @When user :user gets the number of versions of file :resource using file-id path :endpoint
* @When user :user tries to get the number of versions of file :resource using file-id path :endpoint
*
* @param string $user
* @param string $endpoint
*
* @return void
*/
public function userGetsTheNumberOfVersionsOfFileOfTheSpace(string $user, string $endpoint):void {
$this->featureContext->setResponse(
$this->featureContext->makeDavRequest(
$user,
"PROPFIND",
$endpoint,
null,
null,
"versions",
(string)$this->featureContext->getDavPathVersion()
)
);
}
/**
* @When user :user gets the version metadata of file :file
*
* @param string $user
* @param string $file
*
* @return void
* @throws Exception
*/
public function userGetsVersionMetadataOfFile(string $user, string $file):void {
$response = $this->getFileVersionMetadata($user, $file);
$this->featureContext->setResponse($response, $user);
}
/**
* @param string $user
* @param string $file
*
* @return ResponseInterface
*/
public function getFileVersionMetadata(string $user, string $file) : ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$fileId = $this->featureContext->getFileIdForPath($user, $file);
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $file user $user not found (the file may not exist)");
$body = '<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<oc:meta-version-edited-by />
<oc:meta-version-edited-by-name />
</d:prop>
</d:propfind>';
return $this->featureContext->makeDavRequest(
$user,
"PROPFIND",
$this->getVersionsPathForFileId($fileId),
null,
$body,
null,
'2'
);
}
/**
* @param string $user
* @param int $versionIndex
* @param string $path
*
* @return ResponseInterface
* @throws Exception
*/
public function restoreVersionIndexOfFile(string $user, int $versionIndex, string $path):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$fileId = $this->featureContext->getFileIdForPath($user, $path);
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $path user $user not found (the file may not exist)");
$response = $this->listVersionFolder($user, $fileId, 1);
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$xmlPart = $responseXml->xpath("//d:response/d:href");
//restoring the version only works with DAV path v2
$destinationUrl = $this->featureContext->getBaseUrl() . "/" .
WebDavHelper::getDavPath($user, 2) . \trim($path, "/");
$fullUrl = $this->featureContext->getBaseUrlWithoutPath() .
$xmlPart[$versionIndex];
return HttpRequestHelper::sendRequest(
$fullUrl,
$this->featureContext->getStepLineRef(),
'COPY',
$user,
$this->featureContext->getPasswordForUser($user),
['Destination' => $destinationUrl]
);
}
/**
* @Given user :user has restored version index :versionIndex of file :path
*
* @param string $user
* @param int $versionIndex
* @param string $path
*
* @return void
* @throws Exception
*/
public function userHasRestoredVersionIndexOfFile(string $user, int $versionIndex, string $path):void {
$response = $this->restoreVersionIndexOfFile($user, $versionIndex, $path);
$this->featureContext->theHTTPStatusCodeShouldBe(204, "", $response);
}
/**
* @When user :user restores version index :versionIndex of file :path using the WebDAV API
*
* @param string $user
* @param int $versionIndex
* @param string $path
*
* @return void
* @throws Exception
*/
public function userRestoresVersionIndexOfFile(string $user, int $versionIndex, string $path):void {
$response = $this->restoreVersionIndexOfFile($user, $versionIndex, $path);
$this->featureContext->setResponse($response, $user);
}
/**
* assert file versions count
*
* @param string $user
* @param string $fileId
* @param int $expectedCount
*
* @return void
* @throws Exception
*/
public function assertFileVersionsCount(string $user, string $fileId, int $expectedCount):void {
$response = $this->listVersionFolder($user, $fileId, 1);
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$actualCount = \count($responseXml->xpath("//d:prop/d:getetag")) - 1;
if ($actualCount === -1) {
$actualCount = 0;
}
Assert::assertEquals(
$expectedCount,
$actualCount,
"Expected $expectedCount versions but found $actualCount in \n" . $responseXml->asXML()
);
}
/**
* @Then the version folder of file :path for user :user should contain :count element(s)
*
* @param string $path
* @param string $user
* @param int $count
*
* @return void
* @throws Exception
*/
public function theVersionFolderOfFileShouldContainElements(
string $path,
string $user,
int $count
):void {
$user = $this->featureContext->getActualUsername($user);
$fileId = $this->featureContext->getFileIdForPath($user, $path);
Assert::assertNotNull($fileId, __METHOD__ . " file '$path' for user '$user' not found (the file may not exist)");
$this->assertFileVersionsCount($user, $fileId, $count);
}
/**
* @Then the version folder of fileId :fileId for user :user should contain :count element(s)
*
* @param string $fileId
* @param string $user
* @param int $count
*
* @return void
* @throws Exception
*/
public function theVersionFolderOfFileIdShouldContainElements(
string $fileId,
string $user,
int $count
):void {
$this->assertFileVersionsCount($user, $fileId, $count);
}
/**
* @Then the content length of file :path with version index :index for user :user in versions folder should be :length
*
* @param string $path
* @param int $index
* @param string $user
* @param int $length
*
* @return void
* @throws Exception
*/
public function theContentLengthOfFileForUserInVersionsFolderIs(
string $path,
int $index,
string $user,
int $length
):void {
$user = $this->featureContext->getActualUsername($user);
$fileId = $this->featureContext->getFileIdForPath($user, $path);
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $path user $user not found (the file may not exist)");
$response = $this->listVersionFolder($user, $fileId, 1, ['d:getcontentlength']);
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$xmlPart = $responseXml->xpath("//d:prop/d:getcontentlength");
Assert::assertEquals(
$length,
(int) $xmlPart[$index],
"The content length of file $path with version $index for user $user was
expected to be $length but the actual content length is $xmlPart[$index]"
);
}
/**
* @Then /^as (?:users|user) "([^"]*)" the authors of the versions of file "([^"]*)" should be:$/
*
* @param string $users comma-separated list of usernames
* @param string $filename
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function asUsersAuthorsOfVersionsOfFileShouldBe(
string $users,
string $filename,
TableNode $table
): void {
$this->featureContext->verifyTableNodeColumns(
$table,
['index', 'author']
);
$requiredVersionMetadata = $table->getHash();
$usersArray = \explode(",", $users);
foreach ($usersArray as $username) {
$actualUsername = $this->featureContext->getActualUsername($username);
$this->featureContext->setResponse(
$this->getFileVersionMetadata($actualUsername, $filename)
);
foreach ($requiredVersionMetadata as $versionMetadata) {
$this->featureContext->checkAuthorOfAVersionOfFile(
$versionMetadata['index'],
$versionMetadata['author']
);
}
}
}
/**
* @param string $user
* @param string $path
* @param string $index
*
* @return ResponseInterface
* @throws Exception
*/
public function downloadVersion(string $user, string $path, string $index):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$fileId = $this->featureContext->getFileIdForPath($user, $path);
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $path user $user not found (the file may not exist)");
$index = (int)$index;
$response = $this->listVersionFolder($user, $fileId, 1);
if ($response->getStatusCode() === 403) {
return $response;
}
$responseXml = new SimpleXMLElement($response->getBody()->getContents());
$xmlPart = $responseXml->xpath("//d:response/d:href");
if (!isset($xmlPart[$index])) {
Assert::fail(
'could not find version of path "' . $path . '" with index "' . $index . '"'
);
}
// the href already contains the path
$url = WebDavHelper::sanitizeUrl(
$this->featureContext->getBaseUrlWithoutPath() . $xmlPart[$index]
);
return HttpRequestHelper::get(
$url,
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user)
);
}
/**
* @When user :user downloads the version of file :path with the index :index
*
* @param string $user
* @param string $path
* @param string $index
*
* @return void
* @throws Exception
*/
public function userDownloadsVersion(string $user, string $path, string $index):void {
$this->featureContext->setResponse($this->downloadVersion($user, $path, $index), $user);
}
/**
* @Then /^the content of version index "([^"]*)" of file "([^"]*)" for user "([^"]*)" should be "([^"]*)"$/
*
* @param string $index
* @param string $path
* @param string $user
* @param string $content
*
* @return void
* @throws Exception
*/
public function theContentOfVersionIndexOfFileForUserShouldBe(
string $index,
string $path,
string $user,
string $content
): void {
$response = $this->downloadVersion($user, $path, $index);
$this->featureContext->theHTTPStatusCodeShouldBe("200", '', $response);
$this->featureContext->checkDownloadedContentMatches($content, '', $response);
}
/**
* @When /^user "([^"]*)" retrieves the meta information of (file|fileId) "([^"]*)" using the meta API$/
*
* @param string $user
* @param string $fileOrFileId
* @param string $path
*
* @return void
*/
public function userGetMetaInfo(string $user, string $fileOrFileId, string $path):void {
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
if ($fileOrFileId === "file") {
$fileId = $this->featureContext->getFileIdForPath($user, $path);
$metaPath = "/meta/$fileId/";
} else {
$metaPath = "/meta/$path/";
}
$body = '<?xml version="1.0" encoding="utf-8"?>
<a:propfind xmlns:a="DAV:" xmlns:oc="http://owncloud.org/ns">
<a:prop>
<oc:meta-path-for-user />
</a:prop>
</a:propfind>';
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"PROPFIND",
$metaPath,
['Content-Type' => 'text/xml','Depth' => '0'],
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion(),
null
);
$this->featureContext->setResponse($response);
$responseXml = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$this->featureContext->setResponseXmlObject($responseXml);
}
/**
* returns the result parsed into a SimpleXMLElement
* with a registered namespace with 'd' as prefix and 'DAV:' as namespace
*
* @param string $user
* @param string $fileId
* @param int $folderDepth
* @param string[]|null $properties
*
* @return ResponseInterface
* @throws Exception
*/
public function listVersionFolder(
string $user,
string $fileId,
int $folderDepth,
?array $properties = null
):ResponseInterface {
if (!$properties) {
$properties = [
'd:getetag'
];
}
$user = $this->featureContext->getActualUsername($user);
$password = $this->featureContext->getPasswordForUser($user);
$response = WebDavHelper::propfind(
$this->featureContext->getBaseUrl(),
$user,
$password,
$this->getVersionsPathForFileId($fileId),
$properties,
$this->featureContext->getStepLineRef(),
(string) $folderDepth,
"versions"
);
return $response;
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
}
File diff suppressed because it is too large Load Diff
@@ -1,656 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Viktor Scharf <vscharf@owncloud.com>
* @copyright Copyright (c) 2023 Viktor Scharf vscharf@owncloud.com
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use TestHelpers\OcsApiHelper;
use Behat\Gherkin\Node\PyStringNode;
use TestHelpers\EmailHelper;
use PHPUnit\Framework\Assert;
use TestHelpers\GraphHelper;
use TestHelpers\SettingsHelper;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
require_once 'bootstrap.php';
/**
* Defines application features from the specific context.
*/
class NotificationContext implements Context {
private FeatureContext $featureContext;
private SpacesContext $spacesContext;
private SettingsContext $settingsContext;
private string $notificationEndpointPath = '/apps/notifications/api/v1/notifications?format=json';
private string $globalNotificationEndpointPath = '/apps/notifications/api/v1/notifications/global';
private array $notificationIds;
/**
* @return array[]
*/
public function getNotificationIds():array {
return $this->notificationIds;
}
/**
* @return array[]
*/
public function getLastNotificationId():array {
return \end($this->notificationIds);
}
/**
* @AfterScenario
*
* @return void
*/
public function deleteDeprovisioningNotification(): void {
$payload["ids"] = ["deprovision"];
OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
'DELETE',
$this->globalNotificationEndpointPath,
$this->featureContext->getStepLineRef(),
json_encode($payload)
);
}
/**
* @var string
*/
private string $userRecipient;
/**
* @param string $userRecipient
*
* @return void
*/
public function setUserRecipient(string $userRecipient): void {
$this->userRecipient = $userRecipient;
}
/**
* @return string
*/
public function getUserRecipient(): string {
return $this->userRecipient;
}
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
* @throws Exception
*/
public function setUpScenario(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->spacesContext = $environment->getContext('SpacesContext');
$this->settingsContext = $environment->getContext('SettingsContext');
}
/**
* @param string $user
*
* @return ResponseInterface
*/
public function listAllNotifications(string $user):ResponseInterface {
$this->setUserRecipient($user);
$language = SettingsHelper::getLanguageSettingValue(
$this->featureContext->getBaseUrl(),
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef()
);
$headers = ["accept-language" => $language];
return OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
'GET',
$this->notificationEndpointPath,
$this->featureContext->getStepLineRef(),
[],
2,
$headers
);
}
/**
* @When /^user "([^"]*)" lists all notifications$/
*
* @param string $user
*
* @return void
*/
public function userListAllNotifications(string $user):void {
$response = $this->listAllNotifications($user);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastHttpStatusCodesArray();
}
/**
* @param string $user
*
* @return ResponseInterface
* @throws GuzzleException
* @throws JsonException
*/
public function deleteAllNotifications(string $user):ResponseInterface {
$response = $this->listAllNotifications($user);
if (isset($this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data)) {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data;
foreach ($responseBody as $value) {
// set notificationId
$this->notificationIds[] = $value->notification_id;
}
}
return $this->userDeletesNotification($user);
}
/**
* @When user :user deletes all notifications
*
* @param string $user
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function userDeletesAllNotifications(string $user):void {
$response = $this->deleteAllNotifications($user);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has deleted all notifications
*
* @param string $user
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function userHasDeletedAllNotifications(string $user):void {
$response = $this->deleteAllNotifications($user);
$this->featureContext->theHTTPStatusCodeShouldBe(200, "", $response);
}
/**
* @When user :user deletes a notification related to resource :resource with subject :subject
*
* @param string $user
* @param string $resource
* @param string $subject
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
public function userDeletesNotificationOfResourceAndSubject(string $user, string $resource, string $subject):void {
$response = $this->listAllNotifications($user);
$this->filterResponseByNotificationSubjectAndResource($subject, $resource, $response);
$this->featureContext->setResponse($this->userDeletesNotification($user));
}
/**
* deletes notification
*
* @param string $user
*
* @return ResponseInterface
* @throws GuzzleException
* @throws JsonException
*/
public function userDeletesNotification(string $user):ResponseInterface {
$this->setUserRecipient($user);
$payload["ids"] = $this->getNotificationIds();
return OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
'DELETE',
$this->notificationEndpointPath,
$this->featureContext->getStepLineRef(),
\json_encode($payload),
2
);
}
/**
* @Then the notifications should be empty
*
* @return void
* @throws Exception
*/
public function theNotificationsShouldBeEmpty(): void {
$statusCode = $this->featureContext->getResponse()->getStatusCode();
if ($statusCode !== 200) {
$response = $this->featureContext->getResponse()->getBody()->getContents();
throw new \Exception(
__METHOD__
. " Failed to get user notification list" . $response
);
}
$notifications = $this->featureContext->getJsonDecodedResponseBodyContent()->ocs->data;
Assert::assertNull($notifications, "response should not contain any notification");
}
/**
* @Then user :user should not have any notification
*
* @param $user
*
* @return void
* @throws Exception
*/
public function userShouldNotHaveAnyNotification($user): void {
$response = $this->listAllNotifications($user);
$notifications = $this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data;
Assert::assertNull($notifications, "response should not contain any notification");
}
/**
* @Then /^there should be "([^"]*)" notifications$/
*
* @param int $numberOfNotification
*
* @return void
* @throws Exception
*/
public function userShouldHaveNotifications(int $numberOfNotification): void {
if (!isset($this->featureContext->getJsonDecodedResponseBodyContent()->ocs->data)) {
throw new Exception("Notification is empty");
}
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent()->ocs->data;
$actualNumber = \count($responseBody);
Assert::assertEquals(
$numberOfNotification,
$actualNumber,
"Expected number of notifications was '$numberOfNotification', but got '$actualNumber'"
);
}
/**
* @Then /^the JSON response should contain a notification message with the subject "([^"]*)" and the message-details should match$/
*
* @param string $subject
* @param PyStringNode $schemaString
*
* @return void
* @throws Exception
*/
public function theJsonDataFromLastResponseShouldMatch(
string $subject,
PyStringNode $schemaString
): void {
$responseBody = $this->filterResponseAccordingToNotificationSubject($subject);
// substitute the value here
$schemaString = $schemaString->getRaw();
$schemaString = $this->featureContext->substituteInLineCodes(
$schemaString,
$this->featureContext->getCurrentUser(),
[],
[],
null,
$this->getUserRecipient(),
);
$this->featureContext->assertJsonDocumentMatchesSchema(
$responseBody,
$this->featureContext->getJSONSchema($schemaString)
);
}
/**
* filter notification according to subject
*
* @param string $subject
* @param ResponseInterface|null $response
*
* @return object
*/
public function filterResponseAccordingToNotificationSubject(string $subject, ?ResponseInterface $response = null): object {
$response = $response ?? $this->featureContext->getResponse();
if (isset($this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data)) {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data;
foreach ($responseBody as $value) {
if (isset($value->subject) && $value->subject === $subject) {
$responseBody = $value;
// set notificationId
$this->notificationIds[] = $value->notification_id;
break;
}
}
} else {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent($response);
}
return $responseBody;
}
/**
* filter notification according to subject and resource
*
* @param string $subject
* @param string $resource
* @param ResponseInterface|null $response
*
* @return array
*/
public function filterResponseByNotificationSubjectAndResource(string $subject, string $resource, ?ResponseInterface $response = null): array {
$responseBodyArray = [];
$response = $response ?? $this->featureContext->getResponse();
$statusCode = $response->getStatusCode();
if ($statusCode !== 200) {
$response = $response->getBody()->getContents();
Assert::fail($response . " Response should contain status code 200");
}
if (isset($this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data)) {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent($response)->ocs->data;
foreach ($responseBody as $value) {
if (isset($value->subject) && $value->subject === $subject && isset($value->messageRichParameters->resource->name) && $value->messageRichParameters->resource->name === $resource) {
$this->notificationIds[] = $value->notification_id;
$responseBodyArray[] = $value;
}
}
} else {
$responseBodyArray[] = $this->featureContext->getJsonDecodedResponseBodyContent($response);
Assert::fail("Response should contain notification but found: " . print_r($responseBodyArray, true));
}
return $responseBodyArray;
}
/**
* @Then /^user "([^"]*)" should (?:get|have) a notification with subject "([^"]*)" and message:$/
*
* @param string $user
* @param string $subject
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userShouldGetANotificationWithMessage(string $user, string $subject, TableNode $table):void {
$count = 0;
// Sometimes the test might try to get the notifications before the server has created the notification.
// To prevent the test from failing because of that, try to list the notifications again
do {
if ($count > 0) {
\sleep(1);
}
$this->featureContext->setResponse(null);
$response = $this->listAllNotifications($user);
$this->featureContext->theHTTPStatusCodeShouldBe(200, "", $response);
++$count;
} while (!isset($this->filterResponseAccordingToNotificationSubject($subject, $response)->message) && $count <= 5);
if (isset($this->filterResponseAccordingToNotificationSubject($subject, $response)->message)) {
$actualMessage = str_replace(["\r", "\n"], " ", $this->filterResponseAccordingToNotificationSubject($subject, $response)->message);
} else {
throw new \Exception("Notification was not found even after retrying for 5 seconds.");
}
$expectedMessage = $table->getColumnsHash()[0]['message'];
Assert::assertSame(
$expectedMessage,
$actualMessage,
__METHOD__ . "expected message to be '$expectedMessage' but found'$actualMessage'"
);
}
/**
* @Then user :user should get a notification for resource :resource with subject :subject and message:
*
* @param string $user
* @param string $resource
* @param string $subject
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userShouldGetNotificationForResourceWithMessage(string $user, string $resource, string $subject, TableNode $table):void {
$response = $this->listAllNotifications($user);
$notification = $this->filterResponseByNotificationSubjectAndResource($subject, $resource, $response);
if (\count($notification) === 1) {
$actualMessage = str_replace(["\r", "\r"], " ", $notification[0]->message);
$expectedMessage = $table->getColumnsHash()[0]['message'];
Assert::assertSame(
$expectedMessage,
$actualMessage,
__METHOD__ . "expected message to be '$expectedMessage' but found'$actualMessage'"
);
$response = $this->userDeletesNotification($user);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
} elseif (\count($notification) === 0) {
throw new \Exception("Response doesn't contain any notification with resource '$resource' and subject '$subject'.\n" . print_r($notification, true));
} else {
throw new \Exception("Response contains more than one notification with resource '$resource' and subject '$subject'.\n" . print_r($notification, true));
}
}
/**
* @Then user :user should not have a notification related to resource :resource with subject :subject
*
* @param string $user
* @param string $resource
* @param string $subject
*
* @return void
*/
public function userShouldNotHaveANotificationRelatedToResourceWithSubject(string $user, string $resource, string $subject):void {
$response = $this->listAllNotifications($user);
$filteredResponse = $this->filterResponseByNotificationSubjectAndResource($subject, $resource, $response);
Assert::assertCount(0, $filteredResponse, "Response should not contain notification related to resource '$resource' with subject '$subject' but found" . print_r($filteredResponse, true));
}
/**
* @Then user :user should have received the following email from user :sender about the share of project space :spaceName
*
* @param string $user
* @param string $sender
* @param string $spaceName
* @param PyStringNode $content
*
* @return void
* @throws Exception
*/
public function userShouldHaveReceivedTheFollowingEmailFromUserAboutTheShareOfProjectSpace(string $user, string $sender, string $spaceName, PyStringNode $content):void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$this->featureContext->setResponse(
GraphHelper::getMySpaces(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
'',
$this->featureContext->getStepLineRef()
)
);
$expectedEmailBodyContent = $this->featureContext->substituteInLineCodes(
$rawExpectedEmailBodyContent,
$sender,
[],
[
[
"code" => "%space_id%",
"function" =>
[$this->spacesContext, "getSpaceIdByName"],
"parameter" => [$sender, $spaceName]
],
]
);
$this->assertEmailContains($user, $expectedEmailBodyContent);
}
/**
* @Then user :user should have received the following email from user :sender
*
* @param string $user
* @param string $sender
* @param PyStringNode $content
*
* @return void
* @throws Exception
*/
public function userShouldHaveReceivedTheFollowingEmailFromUser(string $user, string $sender, PyStringNode $content):void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$expectedEmailBodyContent = $this->featureContext->substituteInLineCodes(
$rawExpectedEmailBodyContent,
$sender
);
$this->assertEmailContains($user, $expectedEmailBodyContent);
}
/***
* @param string $user
* @param string $expectedEmailBodyContent
*
* @return void
* @throws GuzzleException
*/
public function assertEmailContains(string $user, string $expectedEmailBodyContent):void {
$address = $this->featureContext->getEmailAddressForUser($user);
$this->featureContext->pushEmailRecipientAsMailBox($address);
$actualEmailBodyContent = EmailHelper::getBodyOfLastEmail($address, $this->featureContext->getStepLineRef());
Assert::assertStringContainsString(
$expectedEmailBodyContent,
$actualEmailBodyContent,
"The email address '$address' should have received an email with the body containing $expectedEmailBodyContent
but the received email is $actualEmailBodyContent"
);
}
/**
* Delete all the inbucket emails
*
* @AfterScenario @email
*
* @return void
*/
public function clearInbucketMessages():void {
try {
if (!empty($this->featureContext->emailRecipients)) {
foreach ($this->featureContext->emailRecipients as $emailRecipient) {
EmailHelper::deleteAllEmailsForAMailbox(
EmailHelper::getLocalEmailUrl(),
$this->featureContext->getStepLineRef(),
$emailRecipient
);
}
}
} catch (Exception $e) {
echo __METHOD__ .
" could not delete inbucket messages, is inbucket set up?\n" .
$e->getMessage();
}
}
/**
*
* @param string|null $user
* @param string|null $deprovision_date
* @param string|null $deprovision_date_format
*
* @return ResponseInterface
*
* @throws GuzzleException
*
* @throws JsonException
*/
public function userCreatesDeprovisioningNotification(
?string $user = null,
?string $deprovision_date = "2043-07-04T11:23:12Z",
?string $deprovision_date_format= "2006-01-02T15:04:05Z07:00"
):ResponseInterface {
$payload["type"] = "deprovision";
$payload["data"] = ["deprovision_date" => $deprovision_date, "deprovision_date_format" => $deprovision_date_format];
return OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$user ? $this->featureContext->getActualUsername($user) : $this->featureContext->getAdminUsername(),
$user ? $this->featureContext->getPasswordForUser($user) : $this->featureContext->getAdminPassword(),
'POST',
$this->globalNotificationEndpointPath,
$this->featureContext->getStepLineRef(),
json_encode($payload)
);
}
/**
* @When the administrator creates a deprovisioning notification
* @When user :user tries to create a deprovisioning notification
*
* @param string|null $user
*
* @return void
*
* @throws GuzzleException
* @throws JsonException
*/
public function theAdministratorCreatesADeprovisioningNotification(?string $user = null) {
$response = $this->userCreatesDeprovisioningNotification($user);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastHttpStatusCodesArray();
}
/**
* @When the administrator creates a deprovisioning notification for date :deprovision_date of format :deprovision_date_format
*
* @param $deprovision_date
* @param $deprovision_date_format
*
* @return void
*
* @throws GuzzleException
* @throws JsonException
*/
public function theAdministratorCreatesADeprovisioningNotificationUsingDateFormat($deprovision_date, $deprovision_date_format) {
$response = $this->userCreatesDeprovisioningNotification(null, $deprovision_date, $deprovision_date_format);
$this->featureContext->setResponse($response);
$this->featureContext->pushToLastHttpStatusCodesArray();
}
/**
* @Given the administrator has created a deprovisioning notification
*
* @return void
*/
public function userHasCreatedDeprovisioningNotification():void {
$response = $this->userCreatesDeprovisioningNotification();
$this->featureContext->theHTTPStatusCodeShouldBe(200, "", $response);
}
/**
* @When the administrator deletes the deprovisioning notification
* @When user :user tries to delete the deprovisioning notification
*
* @param string|null $user
*
* @return void
*/
public function userDeletesDeprovisioningNotification(?string $user = null):void {
$payload["ids"] = ["deprovision"];
$response = OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$user ? $this->featureContext->getActualUsername($user) : $this->featureContext->getAdminUsername(),
$user ? $this->featureContext->getPasswordForUser($user) : $this->featureContext->getAdminPassword(),
'DELETE',
$this->globalNotificationEndpointPath,
$this->featureContext->getStepLineRef(),
json_encode($payload)
);
$this->featureContext->setResponse($response);
}
}
@@ -1,663 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2019, ownCloud GmbH
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Psr\Http\Message\ResponseInterface;
use PHPUnit\Framework\Assert;
use TestHelpers\OcsApiHelper;
use TestHelpers\TranslationHelper;
require_once 'bootstrap.php';
/**
* steps needed to send requests to the OCS API
*/
class OCSContext implements Context {
private FeatureContext $featureContext;
/**
* @When /^the user sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)"$/
*
* @param string $verb
* @param string $url
*
* @return void
*/
public function theUserSendsToOcsApiEndpoint(string $verb, string $url):void {
$response = $this->theUserSendsToOcsApiEndpointWithBody($verb, $url);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)"$/
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" using password "([^"]*)"$/
*
* @param string $user
* @param string $verb
* @param string $url
* @param string|null $password
*
* @return void
*/
public function userSendsToOcsApiEndpoint(string $user, string $verb, string $url, ?string $password = null):void {
$response = $this->sendRequestToOcsEndpoint(
$user,
$verb,
$url,
null,
$password
);
$this->featureContext->setResponse($response);
}
/**
* @param string $user
* @param string $verb
* @param string $url
* @param TableNode|null $body
* @param string|null $password
* @param array|null $headers
*
* @return ResponseInterface
*/
public function sendRequestToOcsEndpoint(
string $user,
string $verb,
string $url,
?TableNode $body = null,
?string $password = null,
?array $headers = null
):ResponseInterface {
/**
* array of the data to be sent in the body.
* contains $body data converted to an array
*/
$bodyArray = [];
if ($body instanceof TableNode) {
$bodyArray = $body->getRowsHash();
}
if ($user !== 'UNAUTHORIZED_USER') {
if ($password === null) {
$password = $this->featureContext->getPasswordForUser($user);
}
$user = $this->featureContext->getActualUsername($user);
} else {
$user = null;
$password = null;
}
return OcsApiHelper::sendRequest(
$this->featureContext->getBaseUrl(),
$user,
$password,
$verb,
$url,
$this->featureContext->getStepLineRef(),
$bodyArray,
$this->featureContext->getOcsApiVersion(),
$headers
);
}
/**
* @param string $verb
* @param string $url
* @param TableNode|null $body
*
* @return ResponseInterface
*/
public function adminSendsHttpMethodToOcsApiEndpointWithBody(
string $verb,
string $url,
?TableNode $body
):ResponseInterface {
$admin = $this->featureContext->getAdminUsername();
return $this->sendRequestToOcsEndpoint(
$admin,
$verb,
$url,
$body
);
}
/**
* @param string $verb
* @param string $url
* @param TableNode|null $body
*
* @return ResponseInterface
*/
public function theUserSendsToOcsApiEndpointWithBody(string $verb, string $url, ?TableNode $body = null):ResponseInterface {
return $this->sendRequestToOcsEndpoint(
$this->featureContext->getCurrentUser(),
$verb,
$url,
$body
);
}
/**
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with body$/
*
* @param string $user
* @param string $verb
* @param string $url
* @param TableNode|null $body
* @param string|null $password
*
* @return void
*/
public function userSendHTTPMethodToOcsApiEndpointWithBody(
string $user,
string $verb,
string $url,
?TableNode $body = null,
?string $password = null
):void {
$response = $this->sendRequestToOcsEndpoint(
$user,
$verb,
$url,
$body,
$password
);
$this->featureContext->setResponse($response);
}
/**
* @When the administrator sends HTTP method :verb to OCS API endpoint :url
* @When the administrator sends HTTP method :verb to OCS API endpoint :url using password :password
*
* @param string $verb
* @param string $url
* @param string|null $password
*
* @return void
*/
public function theAdministratorSendsHttpMethodToOcsApiEndpoint(
string $verb,
string $url,
?string $password = null
):void {
$this->featureContext->setResponse(
$this->sendRequestToOcsEndpoint(
$this->featureContext->getAdminUsername(),
$verb,
$url,
null,
$password
)
);
}
/**
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with headers$/
*
* @param string $user
* @param string $verb
* @param string $url
* @param TableNode $headersTable
*
* @return void
* @throws Exception
*/
public function userSendsToOcsApiEndpointWithHeaders(
string $user,
string $verb,
string $url,
TableNode $headersTable
):void {
$user = $this->featureContext->getActualUsername($user);
$password = $this->featureContext->getPasswordForUser($user);
$this->featureContext->setResponse(
$this->sendRequestToOcsEndpoint(
$user,
$verb,
$url,
null,
$password,
$headersTable->getRowsHash()
)
);
}
/**
* @When /^the administrator sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with headers$/
*
* @param string $verb
* @param string $url
* @param TableNode $headersTable
*
* @return void
* @throws Exception
*/
public function administratorSendsToOcsApiEndpointWithHeaders(
string $verb,
string $url,
TableNode $headersTable
):void {
$user = $this->featureContext->getAdminUsername();
$password = $this->featureContext->getPasswordForUser($user);
$this->featureContext->setResponse(
$this->sendRequestToOcsEndpoint(
$user,
$verb,
$url,
null,
$password,
$headersTable->getRowsHash()
)
);
}
/**
* @When /^the administrator sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with headers using password "([^"]*)"$/
*
* @param string $verb
* @param string $url
* @param string $password
* @param TableNode $headersTable
*
* @return void
* @throws Exception
*/
public function administratorSendsToOcsApiEndpointWithHeadersAndPassword(
string $verb,
string $url,
string $password,
TableNode $headersTable
):void {
$this->featureContext->setResponse(
$this->sendRequestToOcsEndpoint(
$this->featureContext->getAdminUsername(),
$verb,
$url,
null,
$password,
$headersTable->getRowsHash()
)
);
}
/**
* @When the administrator sends HTTP method :verb to OCS API endpoint :url with body
*
* @param string $verb
* @param string $url
* @param TableNode|null $body
*
* @return void
*/
public function theAdministratorSendsHttpMethodToOcsApiEndpointWithBody(
string $verb,
string $url,
?TableNode $body
):void {
$response = $this->adminSendsHttpMethodToOcsApiEndpointWithBody(
$verb,
$url,
$body
);
$this->featureContext->setResponse($response);
}
/**
* @When /^the user sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with body$/
*
* @param string $verb
* @param string $url
* @param TableNode $body
*
* @return void
*/
public function theUserSendsHTTPMethodToOcsApiEndpointWithBody(string $verb, string $url, TableNode $body):void {
$response = $this->theUserSendsToOcsApiEndpointWithBody(
$verb,
$url,
$body
);
$this->featureContext->setResponse($response);
}
/**
* @When the administrator sends HTTP method :verb to OCS API endpoint :url with body using password :password
*
* @param string $verb
* @param string $url
* @param string $password
* @param TableNode $body
*
* @return void
*/
public function theAdministratorSendsHttpMethodToOcsApiWithBodyAndPassword(
string $verb,
string $url,
string $password,
TableNode $body
):void {
$admin = $this->featureContext->getAdminUsername();
$response = $this->sendRequestToOcsEndpoint(
$admin,
$verb,
$url,
$body,
$password
);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to OCS API endpoint "([^"]*)" with body using password "([^"]*)"$/
*
* @param string $user
* @param string $verb
* @param string $url
* @param string $password
* @param TableNode $body
*
* @return void
*/
public function userSendsHTTPMethodToOcsApiEndpointWithBodyAndPassword(
string $user,
string $verb,
string $url,
string $password,
TableNode $body
):void {
$response = $this->sendRequestToOcsEndpoint(
$user,
$verb,
$url,
$body,
$password
);
$this->featureContext->setResponse($response);
}
/**
* @Then /^the OCS status code should be "([^"]*)"$/
*
* @param string $statusCode
* @param string $message
* @param ResponseInterface|null $response
*
* @return void
* @throws Exception
*/
public function theOCSStatusCodeShouldBe(string $statusCode, string $message = "", ?ResponseInterface $response = null):void {
$statusCodes = explode(",", $statusCode);
$response = $response ?? $this->featureContext->getResponse();
$responseStatusCode = $this->getOCSResponseStatusCode(
$response
);
if (\is_array($statusCodes)) {
if ($message === "") {
$message = "OCS status code is not any of the expected values " . \implode(",", $statusCodes) . " got " . $responseStatusCode;
}
Assert::assertContainsEquals(
$responseStatusCode,
$statusCodes,
$message
);
$this->featureContext->emptyLastOCSStatusCodesArray();
} else {
if ($message === "") {
$message = "OCS status code is not the expected value " . $statusCodes . " got " . $responseStatusCode;
}
Assert::assertEquals(
$statusCodes,
$responseStatusCode,
$message
);
}
}
/**
* @Then /^the OCS status code should be "([^"]*)" or "([^"]*)"$/
*
* @param string $statusCode1
* @param string $statusCode2
*
* @return void
* @throws Exception
*/
public function theOcsStatusCodeShouldBeOr(string $statusCode1, string $statusCode2):void {
$statusCodes = [$statusCode1,$statusCode1];
$response = $this->featureContext->getResponse();
$responseStatusCode = $this->getOCSResponseStatusCode(
$response
);
Assert::assertContainsEquals(
$responseStatusCode,
$statusCodes,
"OCS status code is not any of the expected values " . \implode(",", $statusCodes) . " got " . $responseStatusCode
);
$this->featureContext->emptyLastOCSStatusCodesArray();
}
/**
* Check the text in an OCS status message
*
* @Then /^the OCS status message should be "([^"]*)"$/
* @Then /^the OCS status message should be "([^"]*)" in language "([^"]*)"$/
*
* @param string $statusMessage
* @param string|null $language
*
* @return void
*/
public function theOCSStatusMessageShouldBe(string $statusMessage, ?string $language = null):void {
$language = TranslationHelper::getLanguage($language);
$statusMessage = $this->getActualStatusMessage($statusMessage, $language);
Assert::assertEquals(
$statusMessage,
$this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
),
'Unexpected OCS status message :"' . $this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
) . '" in response'
);
}
/**
* Check the text in an OCS status message
*
* @Then /^the OCS status message about user "([^"]*)" should be "([^"]*)"$/
*
* @param string $user
* @param string $statusMessage
*
* @return void
*/
public function theOCSStatusMessageAboutUserShouldBe(string $user, string $statusMessage):void {
$user = \strtolower($this->featureContext->getActualUsername($user));
$statusMessage = $this->featureContext->substituteInLineCodes(
$statusMessage,
$user
);
Assert::assertEquals(
$statusMessage,
$this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
),
'Unexpected OCS status message :"' . $this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
) . '" in response'
);
}
/**
* Check the text in an OCS status message.
* Use this step form if the expected text contains double quotes,
* single quotes and other content that theOCSStatusMessageShouldBe()
* cannot handle.
*
* After the step, write the expected text in PyString form like:
*
* """
* File "abc.txt" can't be shared due to reason "xyz"
* """
*
* @Then /^the OCS status message should be:$/
*
* @param PyStringNode $statusMessage
*
* @return void
*/
public function theOCSStatusMessageShouldBePyString(
PyStringNode $statusMessage
):void {
Assert::assertEquals(
$statusMessage->getRaw(),
$this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
),
'Unexpected OCS status message: "' . $this->getOCSResponseStatusMessage(
$this->featureContext->getResponse()
) . '" in response'
);
}
/**
* Parses the xml answer to get ocs response which doesn't match with
* http one in v1 of the api.
*
* @param ResponseInterface $response
*
* @return string
* @throws Exception
*/
public function getOCSResponseStatusCode(ResponseInterface $response):string {
try {
$jsonResponse = $this->featureContext->getJsonDecodedResponseBodyContent($response);
} catch (JsonException $e) {
$jsonResponse = null;
}
if (\is_object($jsonResponse) && $jsonResponse->ocs->meta->statuscode) {
return (string) $jsonResponse->ocs->meta->statuscode;
}
// go to xml response when json response is null (it means not formatted and get status code)
$responseXml = $this->featureContext->getResponseXml($response, __METHOD__);
if (isset($responseXml->meta[0], $responseXml->meta[0]->statuscode)) {
return (string) $responseXml->meta[0]->statuscode;
}
throw new Exception(
"No OCS status code found in response"
);
}
/**
* Parses the xml answer to return data items from ocs response
*
* @param ResponseInterface $response
*
* @return SimpleXMLElement
* @throws Exception
*/
public function getOCSResponseData(ResponseInterface $response): SimpleXMLElement {
$responseXml = $this->featureContext->getResponseXml($response, __METHOD__);
if (isset($responseXml->data)) {
return $responseXml->data;
}
throw new Exception(
"No OCS data items found in responseXml"
);
}
/**
* Parses the xml answer to get ocs response message which doesn't match with
* http one in v1 of the api.
*
* @param ResponseInterface $response
*
* @return string
*/
public function getOCSResponseStatusMessage(ResponseInterface $response):string {
return (string) $this->featureContext->getResponseXml($response, __METHOD__)->meta[0]->message;
}
/**
* convert status message in the desired language
*
* @param string $statusMessage
* @param string|null $language
*
* @return string
*/
public function getActualStatusMessage(string $statusMessage, ?string $language = null):string {
if ($language !== null) {
$multiLingualMessage = \json_decode(
\file_get_contents(__DIR__ . "/../../fixtures/multiLanguageErrors.json"),
true
);
if (isset($multiLingualMessage[$statusMessage][$language])) {
$statusMessage = $multiLingualMessage[$statusMessage][$language];
}
}
return $statusMessage;
}
/**
* check if the HTTP status code and the OCS status code indicate that the request was successful
* this function is aware of the currently used OCS version
*
* @param string|null $message
* @param ResponseInterface|null $response
*
* @return void
* @throws Exception
*/
public function assertOCSResponseIndicatesSuccess(?string $message = "", ?ResponseInterface $response = null):void {
$response = $response ?? $this->featureContext->getResponse();
$this->featureContext->theHTTPStatusCodeShouldBe('200', $message, $response);
if ($this->featureContext->getOcsApiVersion() === 1) {
$this->theOCSStatusCodeShouldBe('100', $message, $response);
} else {
$this->theOCSStatusCodeShouldBe('200', $message, $response);
}
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
}
@@ -1,136 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Sajan Gurung <sajan@jankaritech.com>
* @copyright Copyright (c) 2023 Sajan Gurung sajan@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use TestHelpers\OcisConfigHelper;
use PHPUnit\Framework\Assert;
/**
* steps needed to re-configure oCIS server
*/
class OcisConfigContext implements Context {
/**
* @Given async upload has been enabled with post-processing delayed to :delayTime seconds
*
* @param string $delayTime
*
* @return void
* @throws GuzzleException
*/
public function asyncUploadHasBeenEnabledWithDelayedPostProcessing(string $delayTime): void {
$envs = [
"OCIS_ASYNC_UPLOADS" => true,
"OCIS_EVENTS_ENABLE_TLS" => false,
"POSTPROCESSING_DELAY" => $delayTime . "s",
];
$response = OcisConfigHelper::reConfigureOcis($envs);
Assert::assertEquals(
200,
$response->getStatusCode(),
"Failed to set async upload with delayed post processing"
);
}
/**
* @Given the config :configVariable has been set to :configValue
*
* @param string $configVariable
* @param string $configValue
*
* @return void
* @throws GuzzleException
*/
public function theConfigHasBeenSetTo(string $configVariable, string $configValue): void {
$envs = [
$configVariable => $configValue,
];
$response = OcisConfigHelper::reConfigureOcis($envs);
Assert::assertEquals(
200,
$response->getStatusCode(),
"Failed to set config $configVariable=$configValue"
);
}
/**
* @Given the config :configVariable has been set to path :path
*
* @param string $configVariable
* @param string $path
*
* @return void
* @throws GuzzleException
*/
public function theConfigHasBeenSetPathTo(string $configVariable, string $path): void {
$path = \dirname(__FILE__) . "/../../../" . $path;
$response = OcisConfigHelper::reConfigureOcis(
[
$configVariable => $path
]
);
Assert::assertEquals(
200,
$response->getStatusCode(),
"Failed to set config $configVariable=$path"
);
}
/**
* @Given the following configs have been set:
*
* @param TableNode $table
*
* @return void
* @throws GuzzleException
*/
public function theConfigHasBeenSetToValue(TableNode $table): void {
$envs = [];
foreach ($table->getHash() as $row) {
$envs[$row['config']] = $row['value'];
}
$response = OcisConfigHelper::reConfigureOcis($envs);
Assert::assertEquals(
200,
$response->getStatusCode(),
"Failed to set config"
);
}
/**
* @AfterScenario @env-config
*
* @return void
*/
public function rollbackOcis(): void {
$response = OcisConfigHelper::rollbackOcis();
Assert::assertEquals(
200,
$response->getStatusCode(),
"Failed to rollback ocis server. Check if oCIS is started with ociswrapper."
);
}
}
@@ -1,262 +0,0 @@
<?php declare(strict_types=1);
/**
* @author Viktor Scharf <scharf.vi@gmail.com>
*
* @copyright Copyright (c) 2024, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\OcisHelper;
use TestHelpers\OcmHelper;
use TestHelpers\WebDavHelper;
/**
* Acceptance test steps related to testing federation share(ocm) features
*/
class OcmContext implements Context {
private FeatureContext $featureContext;
private string $invitationToken;
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope): void {
if (OcisHelper::isTestingOnReva()) {
return;
}
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context from here
$this->featureContext = $environment->getContext('FeatureContext');
}
/**
* @return string
*/
public function getOcisDomain(): string {
return $this->extractDomain(\getenv('TEST_SERVER_URL'));
}
/**
* @return string
*/
public function getFedOcisDomain(): string {
return $this->extractDomain(\getenv('TEST_SERVER_FED_URL'));
}
/**
* @param string $url
*
* @return string
*/
public function extractDomain($url): string {
if (!$url) {
return "localhost";
}
return parse_url($url)["host"];
}
/**
* @param string $user
* @param string $email
* @param string $description
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function createInvitation(string $user, $email = null, $description = null): ResponseInterface {
$response = OcmHelper::createInvitation(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user),
$email,
$description
);
$responseData = \json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (isset($responseData["token"])) {
$this->invitationToken = $responseData["token"];
} else {
throw new Exception(__METHOD__ . " response doesn't contain token");
}
return $response;
}
/**
* @When :user creates the federation share invitation
* @When :user creates the federation share invitation with email :email and description :description
*
* @param string $user
* @param string $email
* @param string $description
*
* @return void
* @throws GuzzleException
*/
public function userCreatesTheFederationShareInvitation(string $user, $email = null, $description = null): void {
$this->featureContext->setResponse($this->createInvitation($user, $email, $description));
}
/**
* @Given :user has created the federation share invitation
* @Given :user has created the federation share invitation with email :email and description :description
*
* @param string $user
* @param string $email
* @param string $description
*
* @return void
* @throws GuzzleException
*/
public function userHasCreatedTheFederationShareInvitation(string $user, $email = null, $description = null): void {
$response = $this->createInvitation($user, $email, $description);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @param string $user
* @param string $token
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function acceptInvitation(string $user, string $token = null): ResponseInterface {
$providerDomain = ($this->featureContext->getCurrentServer() === "LOCAL") ? $this->getFedOcisDomain() : $this->getOcisDomain();
return OcmHelper::acceptInvitation(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user),
$token ? $token : $this->invitationToken,
$providerDomain
);
}
/**
* @When :user accepts the last federation share invitation
* @When :user tries to accept the last federation share invitation
*
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function userAcceptsTheLastFederationShareInvitation(string $user): void {
$this->featureContext->setResponse($this->acceptInvitation($user));
}
/**
* @When :user tries to accept the invitation with invalid token
*
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function userTriesToAcceptInvitationWithInvalidToken(string $user): void {
$this->featureContext->setResponse($this->acceptInvitation($user, WebDavHelper::generateUUIDv4()));
}
/**
* @Given :user has accepted invitation
*
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function userHasAcceptedTheLastFederationShareInvitation(string $user): void {
$response = $this->acceptInvitation($user);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @param string $user
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function findAcceptedUsers(string $user): ResponseInterface {
return OcmHelper::findAcceptedUsers(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user)
);
}
/**
* @When :user searches for accepted users
*
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function userFindsAcceptedUsers(string $user): void {
$this->featureContext->setResponse($this->findAcceptedUsers($user));
}
/**
* @param string $user
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function listInvitations(string $user): ResponseInterface {
return OcmHelper::listInvite(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user)
);
}
/**
* @When :user lists the created invitations
*
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function userListsCreatedInvitations(string $user): void {
$this->featureContext->setResponse($this->listInvitations($user));
}
/**
* @When the user waits :number seconds for the token to expire
*
* @param int $number
*
* @return void
* @throws GuzzleException
*/
public function theUserWaitsForTokenToExpire(int $number): void {
\sleep($number);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,214 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use TestHelpers\WebDavHelper;
require_once 'bootstrap.php';
/**
* context containing search related API steps
*/
class SearchContext implements Context {
private FeatureContext $featureContext;
/**
* @When user :user searches for :pattern using the WebDAV API
* @When user :user searches for :pattern and limits the results to :limit items using the WebDAV API
* @When user :user searches for :pattern using the WebDAV API requesting these properties:
* @When user :user searches for :pattern and limits the results to :limit items using the WebDAV API requesting these properties:
* @When user :user searches for :pattern inside folder :scope using the WebDAV API
* @When user :user searches for :pattern inside folder :scope in space :spaceName using the WebDAV API
*
* @param string $user
* @param string $pattern
* @param string|null $limit
* @param string|null $scope
* @param string|null $spaceName
* @param TableNode|null $properties
*
* @return void
*/
public function userSearchesUsingWebDavAPI(
string $user,
string $pattern,
?string $limit = null,
?string $scope = null,
?string $spaceName = null,
TableNode $properties = null
): void {
// NOTE: because indexing of newly uploaded files or directories with ocis is decoupled and occurs asynchronously
// short wait is necessary before searching
sleep(5);
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
if (str_contains($pattern, '$')) {
$date = explode("$", $pattern);
switch ($date[1]) {
case "today":
$pattern = $date[0] . date('Y-m-d', strtotime('today'));
break;
case "yesterday":
$pattern = $date[0] . date('Y-m-d', strtotime('yesterday'));
break;
default:
throw new Exception("cannot convert the date");
}
}
$body
= "<?xml version='1.0' encoding='utf-8' ?>\n" .
" <oc:search-files xmlns:a='DAV:' xmlns:oc='http://owncloud.org/ns' >\n" .
" <oc:search>\n";
if ($scope !== null) {
$scope = \trim($scope, "/");
$resourceID = $this->featureContext->spacesContext->getResourceId($user, $spaceName ?? "Personal", $scope);
$pattern .= " scope:$resourceID";
}
$body .= "<oc:pattern>$pattern</oc:pattern>\n";
if ($limit !== null) {
$body .= " <oc:limit>$limit</oc:limit>\n";
}
$body .= " </oc:search>\n";
if ($properties !== null) {
$propertiesRows = $properties->getRows();
$body .= " <a:prop>";
foreach ($propertiesRows as $property) {
$body .= "<$property[0]/>";
}
$body .= " </a:prop>";
}
$body .= " </oc:search-files>";
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"REPORT",
"/",
null,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion()
);
$this->featureContext->setResponse($response);
}
/**
* @Then file/folder :path in the search result of user :user should contain these properties:
*
* @param string $path
* @param string $user
* @param TableNode $properties
*
* @return void
* @throws Exception
*/
public function fileOrFolderInTheSearchResultShouldContainProperties(
string $path,
string $user,
TableNode $properties
): void {
$user = $this->featureContext->getActualUsername($user);
$this->featureContext->verifyTableNodeColumns($properties, ['name', 'value']);
$properties = $properties->getHash();
$fileResult = $this->featureContext->findEntryFromSearchResponse(
$path
);
Assert::assertNotFalse(
$fileResult,
"could not find file/folder '$path'"
);
foreach ($properties as $property) {
$property['value'] = $this->featureContext->substituteInLineCodes(
$property['value'],
$user
);
$fileResultProperty = $fileResult->xpath("d:propstat//" . $property['name']);
if ($fileResultProperty) {
Assert::assertMatchesRegularExpression(
"/" . $property['value'] . "/",
\trim((string)$fileResultProperty[0])
);
continue;
}
throw new Error("Could not find property '" . $property['name'] . "'");
}
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
/**
* @Then /^the search result should contain these (?:files|entries) with highlight on keyword "([^"]*)"/
*
* @param TableNode $expectedFiles
* @param string $expectedContent
*
* @return void
*
* @throws Exception
*/
public function theSearchResultShouldContainEntriesWithHighlight(
TableNode $expectedFiles,
string $expectedContent
): void {
$this->featureContext->verifyTableNodeColumnsCount($expectedFiles, 1);
$elementRows = $expectedFiles->getRows();
$foundEntries = $this->featureContext->findEntryFromSearchResponse(
null,
true
);
foreach ($elementRows as $expectedFile) {
$filename = $expectedFile[0];
$content = $foundEntries[$filename];
// Extract the content between the <mark> tags
preg_match('/<mark>(.*?)<\/mark>/s', $content, $matches);
$actualContent = $matches[1] ?? '';
// Remove any leading/trailing whitespace for comparison
$actualContent = trim($actualContent);
Assert::assertEquals(
$expectedContent,
$actualContent,
"Expected text highlight to be '$expectedContent' but found '$actualContent'"
);
}
}
}
@@ -1,491 +0,0 @@
<?php
declare(strict_types=1);
/**
* ownCloud
*
* @author Viktor Scharf <v.scharf@owncloud.com>
* @copyright Copyright (c) 2022 Viktor Scharf v.scharf@owncloud.com
*/
use Behat\Behat\Context\Context;
use GuzzleHttp\Exception\GuzzleException;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\HttpRequestHelper;
use TestHelpers\SettingsHelper;
use Behat\Gherkin\Node\TableNode;
require_once 'bootstrap.php';
/**
* Context for the TUS-specific steps using the Graph API
*/
class SettingsContext implements Context {
private FeatureContext $featureContext;
private string $baseUrl;
private string $settingsUrl = '/api/v0/settings/';
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context from here
$this->featureContext = $environment->getContext('FeatureContext');
$this->baseUrl = \trim($this->featureContext->getBaseUrl(), "/");
}
/**
* @param string $user
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function getRoles(string $user): ResponseInterface {
return SettingsHelper::getRolesList(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef()
);
}
/**
* @When /^user "([^"]*)" tries to get all existing roles$/
*
* @param string $user
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function getAllExistingRoles(string $user): void {
$response = $this->getRoles($user);
$this->featureContext->setResponse($response);
}
/**
* @param string $user
* @param string $userId
* @param string $roleId
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function assignRoleToUser(string $user, string $userId, string $roleId): ResponseInterface {
return SettingsHelper::assignRoleToUser(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$userId,
$roleId,
$this->featureContext->getStepLineRef(),
);
}
/**
* @param string $user
* @param string $userId
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function getAssignmentsList(string $user, string $userId): ResponseInterface {
return SettingsHelper::getAssignmentsList(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$userId,
$this->featureContext->getStepLineRef(),
);
}
/**
* @Given /^the administrator has given "([^"]*)" the role "([^"]*)" using the settings api$/
*
* @param string $user
* @param string $role
*
* @return void
*
* @throws Exception
*/
public function theAdministratorHasGivenUserTheRole(string $user, string $role): void {
$admin = $this->featureContext->getAdminUserName();
$roleId = $this->getRoleIdByRoleName($admin, $role);
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id') ?: $user;
$response = $this->assignRoleToUser($admin, $userId, $roleId);
$this->featureContext->theHTTPStatusCodeShouldBe(
201,
"Expected response status code should be 201",
$response
);
}
/**
* @param string $user
* @param string $role
*
* @return string
*/
public function getRoleIdByRoleName(string $user, string $role): string {
// Sometimes the response body is not complete and results invalid json.
// So we try again until we get a valid json.
$retried = 0;
do {
$response = $this->getRoles($user);
$this->featureContext->theHTTPStatusCodeShouldBe(
201,
"Expected response status code should be 201",
$response
);
$rawBody = $response->getBody()->getContents();
try {
$decodedBody = \json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
$tryAgain = false;
} catch (Exception $e) {
$tryAgain = $retried < HttpRequestHelper::numRetriesOnHttpTooEarly();
if (!$tryAgain) {
throw $e;
}
}
if ($tryAgain) {
$retried += 1;
echo "Invalid json body, retrying ($retried)...\n";
// wait 500ms and try again
\usleep(500 * 1000);
}
} while ($tryAgain);
Assert::assertArrayHasKey(
'bundles',
$decodedBody,
__METHOD__ . " could not find bundles in body"
);
$bundles = $decodedBody["bundles"];
$roleToAssign = "";
foreach ($bundles as $value) {
// find the selected role
if ($value["displayName"] === $role) {
$roleToAssign = $value;
break;
}
}
Assert::assertNotEmpty($roleToAssign, "The selected role $role could not be found");
return $roleToAssign["id"];
}
/**
* @When /^user "([^"]*)" changes his own role to "([^"]*)"$/
*
* @param string $user
* @param string $role
*
* @return void
* @throws GuzzleException
*/
public function userChangeOwnRole(string $user, string $role): void {
// we assume that the user knows uuid role.
$roleId = $this->getRoleIdByRoleName($this->featureContext->getAdminUserName(), $role);
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$response = $this->assignRoleToUser($user, $userId, $roleId);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" changes the role "([^"]*)" for user "([^"]*)"$/
*
* @param string $user
* @param string $role
* @param string $assignedUser
*
* @return void
* @throws GuzzleException
*/
public function userChangeRoleAnotherUser(string $user, string $role, string $assignedUser): void {
// we assume that the user knows uuid role.
$roleId = $this->getRoleIdByRoleName($this->featureContext->getAdminUserName(), $role);
$userId = $this->featureContext->getAttributeOfCreatedUser($assignedUser, 'id');
$response = $this->assignRoleToUser($user, $userId, $roleId);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" tries to get list of assignment$/
*
* @param string $user
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function userGetAssignmentsList(string $user): void {
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$this->featureContext->setResponse($this->getAssignmentsList($user, $userId));
}
/**
* @Then /^user "([^"]*)" should have the role "([^"]*)"$/
*
* @param string $user
* @param string $role
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function userShouldHaveRole(string $user, string $role): void {
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$response = $this->getAssignmentsList($this->featureContext->getAdminUserName(), $userId);
$assignmentResponse = $this->featureContext->getJsonDecodedResponseBodyContent($response);
if (isset($assignmentResponse->assignments[0]->roleId)) {
$actualRoleId = $assignmentResponse->assignments[0]->roleId;
Assert::assertEquals($this->getRoleIdByRoleName($this->featureContext->getAdminUserName(), $role), $actualRoleId, "user $user has no role $role");
} else {
Assert::fail("Response should contain user role but not found.\n" . json_encode($assignmentResponse));
}
}
/**
* @Then /^the setting API response should have the role "([^"]*)"$/
*
* @param string $role
*
* @return void
*
* @throws Exception
*/
public function theSettingApiResponseShouldHaveTheRole(string $role): void {
$assignmentRoleId = $this->featureContext->getJsonDecodedResponse($this->featureContext->getResponse())["assignments"][0]["roleId"];
Assert::assertEquals($this->getRoleIdByRoleName($this->featureContext->getAdminUserName(), $role), $assignmentRoleId, "user has no role $role");
}
/**
* @param string $user
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function sendRequestGetBundlesList(string $user): ResponseInterface {
return SettingsHelper::getBundlesList(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef(),
);
}
/**
* @param string $user
* @param string $bundleName
*
* @return array
*
* @throws GuzzleException
* @throws Exception
*/
public function getBundleByName(string $user, string $bundleName): array {
return SettingsHelper::getBundleByName(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$bundleName,
$this->featureContext->getStepLineRef()
);
}
/**
* @param string $user
* @param array $headers
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function sendRequestGetSettingsValuesList(string $user, array $headers = null): ResponseInterface {
return SettingsHelper::getValuesList(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef(),
$headers
);
}
/**
* @When /^user "([^"]*)" lists values-list with headers using the Settings API$/
*
* @param string $user
* @param TableNode $headersTable
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function theUserListsAllValuesListWithHeadersUsingSettingsApi(string $user, TableNode $headersTable): void {
$this->featureContext->verifyTableNodeColumns(
$headersTable,
['header', 'value']
);
$headers = [];
foreach ($headersTable as $row) {
$headers[$row['header']] = $row ['value'];
}
$this->featureContext->setResponse($this->sendRequestGetSettingsValuesList($user, $headers));
}
/**
* @param string $user
* @param string $language
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function sendRequestToSwitchSystemLanguage(string $user, string $language): ResponseInterface {
$profileBundlesList = $this->getBundleByName($user, "Profile");
Assert::assertNotEmpty($profileBundlesList, "bundles list is empty");
$settingId = '';
foreach ($profileBundlesList["settings"] as $value) {
if ($value["name"] === "language") {
$settingId = $value["id"];
break;
}
}
Assert::assertNotEmpty($settingId, "settingId is empty");
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id');
$body = json_encode(
[
"value" => [
"account_uuid" => "me",
"bundleId" => $profileBundlesList["id"],
"id" => $userId,
"listValue" => [
"values" => [
[
"stringValue" => $language
]
]
],
"resource" => [
"type" => "TYPE_USER"
],
"settingId" => $settingId
]
],
JSON_THROW_ON_ERROR
);
return SettingsHelper::updateSettings(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$body,
$this->featureContext->getStepLineRef()
);
}
/**
* @Given /^user "([^"]*)" has switched the system language to "([^"]*)" using the settings API$/
*
* @param string $user
* @param string $language
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function theUserHasSwitchedSystemLanguage(string $user, string $language): void {
$response = $this->sendRequestToSwitchSystemLanguage($user, $language);
$this->featureContext->theHTTPStatusCodeShouldBe(
201,
"Expected response status code should be 201",
$response
);
}
/**
* @param string $user
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws Exception
*/
public function sendRequestToDisableAutoAccepting(string $user): ResponseInterface {
$body = json_encode(
[
"value" => [
"account_uuid" => "me",
"bundleId" => "2a506de7-99bd-4f0d-994e-c38e72c28fd9",
"settingId" => "ec3ed4a3-3946-4efc-8f9f-76d38b12d3a9",
"resource" => [
"type" => "TYPE_USER"
],
"boolValue" => false
]
],
JSON_THROW_ON_ERROR
);
return SettingsHelper::updateSettings(
$this->baseUrl,
$user,
$this->featureContext->getPasswordForUser($user),
$body,
$this->featureContext->getStepLineRef()
);
}
/**
* @Given user :user has disabled auto-accepting
* @Given user :user has disabled the auto-sync share
*
* @param string $user
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function theUserHasDisabledAutoAccepting(string $user): void {
$response = $this->sendRequestToDisableAutoAccepting($user);
$this->featureContext->theHTTPStatusCodeShouldBe(
201,
"Expected response status code should be 201",
$response
);
$this->featureContext->rememberUserAutoSyncSetting($user, false);
}
}
@@ -1,237 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Joas Schilling <coding@schilljs.com>
* @author Sergio Bertolin <sbertolin@owncloud.com>
* @author Phillip Davis <phil@jankaritech.com>
* @copyright Copyright (c) 2018, ownCloud GmbH
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use Psr\Http\Message\ResponseInterface;
use PHPUnit\Framework\Assert;
require_once 'bootstrap.php';
/**
* Sharees context.
*/
class ShareesContext implements Context {
private FeatureContext $featureContext;
private OCSContext $ocsContext;
/**
* @When /^the user gets the sharees using the sharing API with parameters$/
*
* @param TableNode $body
*
* @return void
*/
public function theUserGetsTheShareesWithParameters(TableNode $body):void {
$this->featureContext->setResponse(
$this->getShareesWithParameters(
$this->featureContext->getCurrentUser(),
$body
)
);
}
/**
* @When /^user "([^"]*)" gets the sharees using the sharing API with parameters$/
*
* @param string $user
* @param TableNode $body
*
* @return void
*/
public function userGetsTheShareesWithParameters(string $user, TableNode $body):void {
$this->featureContext->setResponse(
$this->getShareesWithParameters(
$user,
$body
)
);
}
/**
* @Then /^the "([^"]*)" sharees returned should be$/
*
* @param string $shareeType
* @param TableNode $shareesList
*
* @return void
* @throws Exception
*/
public function theShareesReturnedShouldBe(string $shareeType, TableNode $shareesList):void {
$this->featureContext->verifyTableNodeColumnsCount($shareesList, 4);
$sharees = $shareesList->getRows();
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
Assert::assertEquals(
$sharees,
$respondedArray,
"Returned sharees do not match the expected ones. See the differences below."
);
}
/**
* @Then /^the "([^"]*)" sharees returned should include$/
*
* @param string $shareeType
* @param TableNode $shareesList
*
* @return void
* @throws Exception
*/
public function theShareesReturnedShouldInclude(string $shareeType, TableNode $shareesList):void {
$this->featureContext->verifyTableNodeColumnsCount($shareesList, 3);
$sharees = $shareesList->getRows();
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
foreach ($sharees as $sharee) {
Assert::assertContains(
$sharee,
$respondedArray,
"Returned sharees do not match the expected ones. See the differences below."
);
}
}
/**
* @Then /^the "([^"]*)" sharees returned should be empty$/
*
* @param string $shareeType
*
* @return void
*/
public function theShareesReturnedShouldBeEmpty(string $shareeType):void {
$respondedArray = $this->getArrayOfShareesResponded(
$this->featureContext->getResponse(),
$shareeType
);
if (isset($respondedArray[0])) {
// [0] is display name and [2] is user or group id
$firstEntry = $respondedArray[0][0] . " (" . $respondedArray[0][2] . ")";
} else {
$firstEntry = "";
}
Assert::assertEmpty(
$respondedArray,
"'$shareeType' array should be empty, but it starts with $firstEntry"
);
}
/**
* @param ResponseInterface $response
* @param string $shareeType
*
* @return array
* @throws Exception
*/
public function getArrayOfShareesResponded(
ResponseInterface $response,
string $shareeType
):array {
$elements = $this->featureContext->getResponseXml($response, __METHOD__)->data;
$elements = \json_decode(\json_encode($elements), true);
if (\strpos($shareeType, 'exact ') === 0) {
$elements = $elements['exact'];
$shareeType = \substr($shareeType, 6);
}
Assert::assertArrayHasKey(
$shareeType,
$elements,
__METHOD__ . " The sharees response does not have key '$shareeType'"
);
$sharees = [];
foreach ($elements[$shareeType] as $element) {
if (\is_int(\key($element))) {
// this is a list of elements instead of just one item,
// so return the list
foreach ($element as $innerItem) {
$sharees[] = [
$innerItem['label'],
$innerItem['value']['shareType'],
$innerItem['value']['shareWith'],
$innerItem['value']['shareWithAdditionalInfo']
];
}
} else {
$sharees[] = [
$element['label'],
$element['value']['shareType'],
$element['value']['shareWith'],
$element['value']['shareWithAdditionalInfo']
];
}
}
return $sharees;
}
/**
* @param string $user
* @param TableNode $body
*
* @return ResponseInterface
*/
public function getShareesWithParameters(string $user, TableNode $body): ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$url = '/apps/files_sharing/api/v1/sharees';
$this->featureContext->verifyTableNodeColumnsCount($body, 2);
$parameters = [];
foreach ($body->getRowsHash() as $key => $value) {
$parameters[] = "$key=$value";
}
if (!empty($parameters)) {
$url .= '?' . \implode('&', $parameters);
}
return $this->ocsContext->sendRequestToOcsEndpoint(
$user,
'GET',
$url
);
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope):void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->ocsContext = $environment->getContext('OCSContext');
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,411 +0,0 @@
<?php
declare(strict_types=1);
/**
* ownCloud
*
* @author Viktor Scharf <v.scharf@owncloud.com>
* @copyright Copyright (c) 2022 Viktor Scharf v.scharf@owncloud.com
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use GuzzleHttp\Exception\GuzzleException;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use TestHelpers\WebDavHelper;
require_once 'bootstrap.php';
/**
* Context for the TUS-specific steps using the Graph API
*/
class SpacesTUSContext implements Context {
private FeatureContext $featureContext;
private TUSContext $tusContext;
private SpacesContext $spacesContext;
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context from here
$this->featureContext = $environment->getContext('FeatureContext');
$this->spacesContext = $environment->getContext('SpacesContext');
$this->tusContext = $environment->getContext('TUSContext');
}
/**
* @Given /^user "([^"]*)" has uploaded a file from "([^"]*)" to "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $spaceName
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userHasUploadedFileViaTusInSpace(string $user, string $source, string $destination, string $spaceName): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$this->tusContext->uploadFileUsingTus($user, $source, $destination);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @When /^user "([^"]*)" uploads a file from "([^"]*)" to "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $source
* @param string $destination
* @param string $spaceName
*
* @return void
* @throws Exception
* @throws GuzzleException
*/
public function userUploadsAFileViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $source,
string $destination,
string $spaceName
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$this->tusContext->uploadFileUsingTus($user, $source, $destination);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @Given user :user has created a new TUS resource for the space :spaceName with content :content using the WebDAV API with these headers:
*
* @param string $user
* @param string $spaceName
* @param string $content
* @param TableNode $headers
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userHasCreatedANewTusResourceForTheSpaceUsingTheWebdavApiWithTheseHeaders(
string $user,
string $spaceName,
string $content,
TableNode $headers
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$response = $this->tusContext->createNewTUSResourceWithHeaders($user, $headers, $content);
$this->featureContext->theHTTPStatusCodeShouldBe(201, "Expected response status code should be 201", $response);
}
/**
* @When user :user creates a new TUS resource for the space :spaceName with content :content using the WebDAV API with these headers:
*
* @param string $user
* @param string $spaceName
* @param string $content
* @param TableNode $headers
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userCreatesANewTusResourceForTheSpaceUsingTheWebdavApiWithTheseHeaders(
string $user,
string $spaceName,
string $content,
TableNode $headers
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$response = $this->tusContext->createNewTUSResourceWithHeaders($user, $headers, $content);
$this->featureContext->setResponse($response);
}
/**
* Uploads a file with content to the specified space using the TUS protocol via the WebDAV API.
*
* @param string $user
* @param string $content
* @param string $resource
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
private function uploadFileViaTus(string $user, string $content, string $resource, string $spaceName): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$tmpFile = $this->tusContext->writeDataToTempFile($content);
try {
$this->tusContext->uploadFileUsingTus(
$user,
\basename($tmpFile),
$resource
);
$this->featureContext->setLastUploadDeleteTime(\time());
} catch (Exception $e) {
Assert::assertStringContainsString('Unable to create resource', (string)$e);
}
\unlink($tmpFile);
}
/**
* @When /^user "([^"]*)" uploads a file with content "([^"]*)" to "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $content
* @param string $resource
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
public function userUploadsAFileWithContentToViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $content,
string $resource,
string $spaceName
): void {
$this->uploadFileViaTus($user, $content, $resource, $spaceName);
}
/**
* @Given /^user "([^"]*)" has uploaded a file with content "([^"]*)" to "([^"]*)" via TUS inside of the space "([^"]*)"$/
*
* @param string $user
* @param string $content
* @param string $resource
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
public function userHasUploadedAFileWithContentToViaTusInsideOfTheSpace(
string $user,
string $content,
string $resource,
string $spaceName
): void {
$this->uploadFileViaTus($user, $content, $resource, $spaceName);
}
/**
* @When /^user "([^"]*)" uploads a file "([^"]*)" to "([^"]*)" with mtime "([^"]*)" via TUS inside of the space "([^"]*)" 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 string $spaceName
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userUploadsAFileToWithMtimeViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $source,
string $destination,
string $mtime,
string $spaceName
): void {
switch ($mtime) {
case "today":
$mtime = date('Y-m-d', strtotime('today'));
break;
case "yesterday":
$mtime = date('Y-m-d', strtotime('yesterday'));
break;
case "lastWeek":
$mtime = date('Y-m-d', strtotime('-7 days'));
break;
case "lastMonth":
$mtime = date('Y-m-d', strtotime('first day of previous month'));
break;
case "lastYear":
$mtime = date('Y-m' . '-01', strtotime('-1 year'));
break;
default:
}
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
$user = $this->featureContext->getActualUsername($user);
$this->tusContext->uploadFileUsingTus(
$user,
$source,
$destination,
['mtime' => $mtime]
);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @Given /^user "([^"]*)" has uploaded file with checksum "([^"]*)" to the last created TUS Location with offset "([^"]*)" and content "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $checksum
* @param string $offset
* @param string $content
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
public function userHasUploadedFileWithChecksumToTheLastCreatedTusLocationWithOffsetAndContentViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $checksum,
string $offset,
string $content,
string $spaceName
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$response = $this->tusContext->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $content, $checksum);
$this->featureContext->theHTTPStatusCodeShouldBe(204, "", $response);
}
/**
* @When /^user "([^"]*)" uploads file with checksum "([^"]*)" to the last created TUS Location with offset "([^"]*)" and content "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $checksum
* @param string $offset
* @param string $content
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
public function userUploadsFileWithChecksumToTheLastCreatedTusLocationWithOffsetAndContentViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $checksum,
string $offset,
string $content,
string $spaceName
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$response = $this->tusContext->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $content, $checksum);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" sends a chunk to the last created TUS Location with offset "([^"]*)" and data "([^"]*)" with checksum "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
* @param string $spaceName
*
* @return void
* @throws Exception|GuzzleException
*/
public function userSendsAChunkToTheLastCreatedTusLocationWithOffsetAndDataWithChecksumViaTusInsideOfTheSpaceUsingTheWebdavApi(
string $user,
string $offset,
string $data,
string $checksum,
string $spaceName
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$response = $this->tusContext->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data, $checksum);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" sends a chunk to the last created TUS Location with data "([^"]*)" inside of the space "([^"]*)" with headers:$/
*
* @param string $user
* @param string $data
* @param string $spaceName
* @param TableNode $headers
*
* @return void
* @throws Exception|GuzzleException
*/
public function userSendsAChunkToTheLastCreatedTusLocationWithDataInsideOfTheSpaceWithHeaders(
string $user,
string $data,
string $spaceName,
TableNode $headers
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$rows = $headers->getRowsHash();
$response = $this->tusContext->sendsAChunkToTUSLocationWithOffsetAndData($user, $rows['Upload-Offset'], $data, $rows['Upload-Checksum'], ['Origin' => $rows['Origin']]);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" overwrites recently shared file with offset "([^"]*)" and data "([^"]*)" with checksum "([^"]*)" via TUS inside of the space "([^"]*)" using the WebDAV API with these headers:$/
*
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
* @param string $spaceName
* @param TableNode $headers
*
* @return void
* @throws GuzzleException
*/
public function userOverwritesRecentlySharedFileWithOffsetAndDataWithChecksumViaTusInsideOfTheSpaceUsingTheWebdavApiWithTheseHeaders(
string $user,
string $offset,
string $data,
string $checksum,
string $spaceName,
TableNode $headers
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$createResponse = $this->tusContext->createNewTUSResource($user, $headers);
$this->featureContext->theHTTPStatusCodeShouldBe(201, "", $createResponse);
$response = $this->tusContext->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data, $checksum);
$this->featureContext->setResponse($response);
}
/**
* @Then /^as "([^"]*)" the mtime of the file "([^"]*)" in space "([^"]*)" should be "([^"]*)"$/
*
* @param string $user
* @param string $resource
* @param string $spaceName
* @param string $mtime
*
* @return void
* @throws Exception|GuzzleException
*/
public function theMtimeOfTheFileInSpaceShouldBe(
string $user,
string $resource,
string $spaceName,
string $mtime
): void {
$this->spacesContext->setSpaceIDByName($user, $spaceName);
$mtime = new DateTime($mtime);
Assert::assertEquals(
$mtime->format('U'),
WebDavHelper::getMtimeOfResource(
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getBaseUrl(),
$resource,
$this->featureContext->getStepLineRef(),
$this->featureContext->getDavPathVersion()
)
);
}
}
@@ -1,567 +0,0 @@
<?php declare(strict_types=1);
/**
* @author Artur Neumann <artur@jankaritech.com>
*
* @copyright Copyright (c) 2020, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use TestHelpers\HttpRequestHelper;
use TestHelpers\WebDavHelper;
use TusPhp\Exception\ConnectionException;
use TusPhp\Exception\TusException;
use TusPhp\Tus\Client;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
require_once 'bootstrap.php';
/**
* TUS related test steps
*/
class TUSContext implements Context {
private FeatureContext $featureContext;
private ?string $resourceLocation = null;
/**
* @return string
*/
public function getTusResourceLocation(): string {
return $this->resourceLocation ?: "";
}
/**
* @param string $user
* @param TableNode $headers
* @param string $content
*
* @return ResponseInterface
*
* @throws Exception
* @throws GuzzleException
*/
public function createNewTUSResourceWithHeaders(string $user, TableNode $headers, string $content = ''): ResponseInterface {
$this->featureContext->verifyTableNodeColumnsCount($headers, 2);
$user = $this->featureContext->getActualUsername($user);
$password = $this->featureContext->getUserPassword($user);
$this->resourceLocation = null;
$response = $this->featureContext->makeDavRequest(
$user,
"POST",
null,
$headers->getRowsHash(),
$content,
"files",
null,
false,
$password
);
$locationHeader = $response->getHeader('Location');
if (\sizeof($locationHeader) > 0) {
$this->resourceLocation = $locationHeader[0];
}
return $response;
}
/**
* @When user :user creates a new TUS resource on the WebDAV API with these headers:
*
* @param string $user
* @param TableNode $headers
* @param string $content
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userCreateNewTUSResourceWithHeaders(string $user, TableNode $headers, string $content = ''): void {
$response = $this->createNewTUSResourceWithHeaders($user, $headers, $content);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has created a new TUS resource on the WebDAV API with these headers:
*
* @param string $user
* @param TableNode $headers Tus-Resumable: 1.0.0 header is added automatically
*
* @return void
*
* @throws Exception
* @throws GuzzleException
*/
public function userHasCreatedNewTUSResourceWithHeaders(string $user, TableNode $headers): void {
$response = $this->createNewTUSResource($user, $headers);
$this->featureContext->theHTTPStatusCodeShouldBe(201, "", $response);
}
/**
* @param string $user
* @param TableNode $headers
*
* @return ResponseInterface
*/
public function createNewTUSResource(string $user, TableNode $headers):ResponseInterface {
$rows = $headers->getRows();
$rows[] = ['Tus-Resumable', '1.0.0'];
return $this->createNewTUSResourceWithHeaders($user, new TableNode($rows));
}
/**
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
* @param array|null $extraHeaders
*
* @return ResponseInterface
*
* @throws GuzzleException
* @throws JsonException
*/
public function sendsAChunkToTUSLocationWithOffsetAndData(string $user, string $offset, string $data, string $checksum = '', ?array $extraHeaders = null): ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$password = $this->featureContext->getUserPassword($user);
$headers = [
'Content-Type' => 'application/offset+octet-stream',
'Tus-Resumable' => '1.0.0',
'Upload-Checksum' => $checksum,
'Upload-Offset' => $offset
];
$headers = empty($extraHeaders) ? $headers : array_merge($headers, $extraHeaders);
return HttpRequestHelper::sendRequest(
$this->resourceLocation,
$this->featureContext->getStepLineRef(),
'PATCH',
$user,
$password,
$headers,
$data
);
}
/**
* @When user :user sends a chunk to the last created TUS Location with offset :offset and data :data using the WebDAV API
*
* @param string $user
* @param string $offset
* @param string $data
*
* @return void
*
* @throws GuzzleException
* @throws JsonException
*/
public function userSendsAChunkToTUSLocationWithOffsetAndData(string $user, string $offset, string $data): void {
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data);
$this->featureContext->setResponse($response);
}
/**
* @When user :user uploads file :source to :destination using the TUS protocol on the WebDAV API
*
* @param string|null $user
* @param string $source
* @param string $destination
* @param array $uploadMetadata array of metadata to be placed in the
* `Upload-Metadata` header.
* see https://tus.io/protocols/resumable-upload.html#upload-metadata
* Don't Base64 encode the value.
* @param int $noOfChunks
* @param int|null $bytes
* @param string $checksum
*
* @return void
* @throws ConnectionException
* @throws GuzzleException
* @throws JsonException
* @throws ReflectionException
* @throws TusException
*/
public function userUploadsUsingTusAFileTo(
?string $user,
string $source,
string $destination,
array $uploadMetadata = [],
int $noOfChunks = 1,
int $bytes = null,
string $checksum = ''
): void {
$this->uploadFileUsingTus($user, $source, $destination, $uploadMetadata, $noOfChunks, $bytes, $checksum);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @param string $user
* @param string $source
* @param string $destination
* @param array $uploadMetadata
* @param integer $noOfChunks
* @param integer $bytes
* @param string $checksum
*
* @return void
*/
public function uploadFileUsingTus(
?string $user,
string $source,
string $destination,
array $uploadMetadata = [],
int $noOfChunks = 1,
int $bytes = null,
string $checksum = ''
) {
$user = $this->featureContext->getActualUsername($user);
$password = $this->featureContext->getUserPassword($user);
$headers = [
'Authorization' => 'Basic ' . \base64_encode($user . ':' . $password)
];
if ($bytes !== null) {
$creationWithUploadHeader = [
'Content-Type' => 'application/offset+octet-stream',
'Tus-Resumable' => '1.0.0'
];
$headers = \array_merge($headers, $creationWithUploadHeader);
}
if ($checksum != '') {
$checksumHeader = [
'Upload-Checksum' => $checksum
];
$headers = \array_merge($headers, $checksumHeader);
}
$client = new Client(
$this->featureContext->getBaseUrl(),
[
'verify' => false,
'headers' => $headers
]
);
$client->setChecksumAlgorithm('sha1');
$client->setApiPath(
WebDavHelper::getDavPath(
$user,
$this->featureContext->getDavPathVersion(),
"files",
WebDavHelper::$SPACE_ID_FROM_OCIS
?: $this->featureContext->getPersonalSpaceIdForUser($user)
)
);
WebDavHelper::$SPACE_ID_FROM_OCIS = '';
$client->setMetadata($uploadMetadata);
$sourceFile = $this->featureContext->acceptanceTestsDirLocation() . $source;
$client->setKey((string)rand())->file($sourceFile, $destination);
$this->featureContext->pauseUploadDelete();
if ($bytes !== null) {
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), $bytes);
} elseif (\filesize($sourceFile) === 0) {
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), 0);
} elseif ($noOfChunks === 1) {
$client->file($sourceFile, $destination)->upload();
} else {
$bytesPerChunk = (int)\ceil(\filesize($sourceFile) / $noOfChunks);
for ($i = 0; $i < $noOfChunks; $i++) {
$client->upload($bytesPerChunk);
}
}
}
/**
* @When user :user uploads file with content :content to :destination using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $content
* @param string $destination
*
* @return void
* @throws GuzzleException
* @throws Exception
*/
public function userUploadsAFileWithContentToUsingTus(
string $user,
string $content,
string $destination
): void {
$temporaryFileName = $this->writeDataToTempFile($content);
try {
$this->uploadFileUsingTus(
$user,
\basename($temporaryFileName),
$destination
);
$this->featureContext->setLastUploadDeleteTime(\time());
} catch (Exception $e) {
Assert::assertStringContainsString('TusPhp\Exception\FileException: Unable to create resource', (string)$e);
}
\unlink($temporaryFileName);
}
/**
* @When user :user uploads file with content :content in :noOfChunks chunks to :destination using the TUS protocol on the WebDAV API
*
* @param string|null $user
* @param string $content
* @param int|null $noOfChunks
* @param string $destination
*
* @return void
* @throws ConnectionException
* @throws GuzzleException
* @throws JsonException
* @throws ReflectionException
* @throws TusException
* @throws Exception
* @throws GuzzleException
*/
public function userUploadsAFileWithContentInChunksUsingTus(
?string $user,
string $content,
?int $noOfChunks,
string $destination
): void {
$temporaryFileName = $this->writeDataToTempFile($content);
$this->uploadFileUsingTus(
$user,
\basename($temporaryFileName),
$destination,
[],
$noOfChunks
);
$this->featureContext->setLastUploadDeleteTime(\time());
\unlink($temporaryFileName);
}
/**
* @When user :user uploads file :source to :destination with mtime :mtime using the TUS protocol on 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
* @throws GuzzleException
*/
public function userUploadsFileWithContentToWithMtimeUsingTUS(
string $user,
string $source,
string $destination,
string $mtime
): void {
$mtime = new DateTime($mtime);
$mtime = $mtime->format('U');
$user = $this->featureContext->getActualUsername($user);
$this->uploadFileUsingTus(
$user,
$source,
$destination,
['mtime' => $mtime]
);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @param string $content
*
* @return string the file name
* @throws Exception
*/
public function writeDataToTempFile(string $content): string {
$temporaryFileName = \tempnam(
$this->featureContext->acceptanceTestsDirLocation(),
"tus-upload-test-"
);
if ($temporaryFileName === false) {
throw new \Exception("could not create a temporary filename");
}
$temporaryFile = \fopen($temporaryFileName, "w");
if ($temporaryFile === false) {
throw new \Exception("could not open " . $temporaryFileName . " for write");
}
\fwrite($temporaryFile, $content);
\fclose($temporaryFile);
return $temporaryFileName;
}
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function setUpScenario(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
}
/**
* @When user :user creates a new TUS resource with content :content on the WebDAV API with these headers:
*
* @param string $user
* @param string $content
* @param TableNode $headers
*
* @return void
* @throws Exception
* @throws GuzzleException
*/
public function userCreatesWithUpload(
string $user,
string $content,
TableNode $headers
): void {
$response = $this->createNewTUSResourceWithHeaders($user, $headers, $content);
$this->featureContext->setResponse($response);
}
/**
* @When user :user creates file :source and uploads content :content in the same request using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $source
* @param string $content
*
* @return void
* @throws Exception
*/
public function userUploadsWithCreatesWithUpload(
string $user,
string $source,
string $content
): void {
$temporaryFileName = $this->writeDataToTempFile($content);
$this->uploadFileUsingTus(
$user,
\basename($temporaryFileName),
$source,
[],
1,
-1
);
$this->featureContext->setLastUploadDeleteTime(\time());
\unlink($temporaryFileName);
}
/**
* @When user :user uploads file with checksum :checksum to the last created TUS Location with offset :offset and content :content using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $checksum
* @param string $offset
* @param string $content
*
* @return void
* @throws Exception
*/
public function userUploadsFileWithChecksum(
string $user,
string $checksum,
string $offset,
string $content
): void {
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $content, $checksum);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has uploaded file with checksum :checksum to the last created TUS Location with offset :offset and content :content using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $checksum
* @param string $offset
* @param string $content
*
* @return void
* @throws Exception
*/
public function userHasUploadedFileWithChecksum(
string $user,
string $checksum,
string $offset,
string $content
): void {
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $content, $checksum);
$this->featureContext->theHTTPStatusCodeShouldBe(204, "", $response);
}
/**
* @When user :user sends a chunk to the last created TUS Location with offset :offset and data :data with checksum :checksum using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
*
* @return void
* @throws Exception
*/
public function userUploadsChunkFileWithChecksum(string $user, string $offset, string $data, string $checksum): void {
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data, $checksum);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has uploaded a chunk to the last created TUS Location with offset :offset and data :data with checksum :checksum using the TUS protocol on the WebDAV API
*
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
*
* @return void
* @throws Exception
*/
public function userHasUploadedChunkFileWithChecksum(string $user, string $offset, string $data, string $checksum): void {
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data, $checksum);
$this->featureContext->theHTTPStatusCodeShouldBe(204, "", $response);
}
/**
* @When user :user overwrites recently shared file with offset :offset and data :data with checksum :checksum using the TUS protocol on the WebDAV API with these headers:
* @When user :user overwrites existing file with offset :offset and data :data with checksum :checksum using the TUS protocol on the WebDAV API with these headers:
*
* @param string $user
* @param string $offset
* @param string $data
* @param string $checksum
* @param TableNode $headers Tus-Resumable: 1.0.0 header is added automatically
*
* @return void
*
* @throws GuzzleException
* @throws Exception
*/
public function userOverwritesFileWithChecksum(string $user, string $offset, string $data, string $checksum, TableNode $headers): void {
$createResponse = $this->createNewTUSResource($user, $headers);
$this->featureContext->theHTTPStatusCodeShouldBe(201, "", $createResponse);
$response = $this->sendsAChunkToTUSLocationWithOffsetAndData($user, $offset, $data, $checksum);
$this->featureContext->setResponse($response);
}
}
@@ -1,258 +0,0 @@
<?php declare(strict_types=1);
/**
* @author Viktor Scharf <scharf.vi@gmail.com>
*
* @copyright Copyright (c) 2022, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use TestHelpers\GraphHelper;
use Psr\Http\Message\ResponseInterface;
require_once 'bootstrap.php';
/**
* Acceptance test steps related to testing tags features
*/
class TagContext implements Context {
private FeatureContext $featureContext;
private SpacesContext $spacesContext;
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context from here
$this->featureContext = $environment->getContext('FeatureContext');
$this->spacesContext = $environment->getContext('SpacesContext');
}
/**
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return ResponseInterface
* @throws Exception
*/
public function createTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):ResponseInterface {
$tagNameArray = [];
foreach ($table->getRows() as $value) {
$tagNameArray[] = $value[0];
}
if ($fileOrFolder === 'folder' || $fileOrFolder === 'folders') {
$resourceId = $this->spacesContext->getResourceId($user, $space, $resource);
} else {
$resourceId = $this->spacesContext->getFileId($user, $space, $resource);
}
return GraphHelper::createTags(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user),
$resourceId,
$tagNameArray
);
}
/**
* @When /^user "([^"]*)" creates the following tags for (folder|file) "([^"]*)" of space "([^"]*)":$/
*
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theUserCreatesFollowingTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
$response = $this->createTags($user, $fileOrFolder, $resource, $space, $table);
$this->featureContext->setResponse($response);
}
/**
* @Given /^user "([^"]*)" has created the following tags for (folder|file)\s?"([^"]*)" of the space "([^"]*)":$/
*
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theUserHasCreatedFollowingTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
$response = $this->createTags($user, $fileOrFolder, $resource, $space, $table);
$this->featureContext->theHttpStatusCodeShouldBe(200, "", $response);
}
/**
* @Given /^user "([^"]*)" has tagged the following (folders|files) of the space "([^"]*)":$/
*
* @param string $user
* @param string $filesOrFolders (files|folders)
* @param string $space
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userHasCreatedTheFollowingTagsForFilesOfTheSpace(string $user, string $filesOrFolders, string $space, TableNode $table):void {
$this->featureContext->verifyTableNodeColumns($table, ["path", "tagName"]);
$rows = $table->getHash();
foreach ($rows as $row) {
$tags = explode(',', $row['tagName']);
$response = $this->createTags($user, $filesOrFolders, $row['path'], $space, new TableNode([$tags]));
$this->featureContext->theHttpStatusCodeShouldBe(200, "", $response);
}
}
/**
* @When user :user lists all available tag(s) via the Graph API
*
* @param string $user
*
* @return void
* @throws Exception
*/
public function theUserGetsAllAvailableTags(string $user):void {
// Note: after creating or deleting tags, in some cases tags do not appear or disappear immediately,
// So wait is necessary before listing tags
sleep(5);
$this->featureContext->setResponse(
GraphHelper::getTags(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
$this->featureContext->getStepLineRef()
)
);
}
/**
* @Then /^the response should (not|)\s?contain following tag(s):$/
*
* @param string $shouldOrNot (not|)
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function theFollowingTagsShouldExistForUser(string $shouldOrNot, TableNode $table):void {
$rows = $table->getRows();
foreach ($rows as $row) {
$responseArray = $this->featureContext->getJsonDecodedResponse($this->featureContext->getResponse())['value'];
if ($shouldOrNot === "not") {
Assert::assertFalse(
\in_array($row[0], $responseArray),
"the response should not contain the tag $row[0].\nResponse\n"
. print_r($responseArray, true)
);
} else {
Assert::assertTrue(
\in_array($row[0], $responseArray),
"the response does not contain the tag $row[0].\nResponse\n"
. print_r($responseArray, true)
);
}
}
}
/**
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return ResponseInterface
* @throws Exception
*/
public function removeTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):ResponseInterface {
$tagNameArray = [];
foreach ($table->getRows() as $value) {
$tagNameArray[] = $value[0];
}
if ($fileOrFolder === 'folder') {
$resourceId = $this->spacesContext->getResourceId($user, $space, $resource);
} else {
$resourceId = $this->spacesContext->getFileId($user, $space, $resource);
}
return GraphHelper::deleteTags(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$user,
$this->featureContext->getPasswordForUser($user),
$resourceId,
$tagNameArray
);
}
/**
* @When /^user "([^"]*)" removes the following tags for (folder|file) "([^"]*)" of space "([^"]*)":$/
*
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userRemovesTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
$response = $this->removeTagsFromResourceOfTheSpace($user, $fileOrFolder, $resource, $space, $table);
$this->featureContext->setResponse($response);
}
/**
* @Given /^user "([^"]*)" has removed the following tags for (folder|file) "([^"]*)" of space "([^"]*)":$/
*
* @param string $user
* @param string $fileOrFolder (file|folder)
* @param string $resource
* @param string $space
* @param TableNode $table
*
* @return void
* @throws Exception
*/
public function userHAsRemovedTheFollowingTagsForFileOfSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
$response = $this->removeTagsFromResourceOfTheSpace($user, $fileOrFolder, $resource, $space, $table);
$this->featureContext->theHttpStatusCodeShouldBe(200, "", $response);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,990 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Artur Neumann <artur@jankaritech.com>
* @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\TableNode;
use GuzzleHttp\Exception\GuzzleException;
use PHPUnit\Framework\Assert;
use TestHelpers\HttpRequestHelper;
use TestHelpers\WebDavHelper;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\OcisHelper;
require_once 'bootstrap.php';
/**
* context containing API steps needed for the locking mechanism of webdav
*/
class WebDavLockingContext implements Context {
private FeatureContext $featureContext;
private PublicWebDavContext $publicWebDavContext;
private SpacesContext $spacesContext;
/**
*
* @var string[][]
*/
private array $tokenOfLastLock = [];
/**
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
* @param string|null $fullUrl
* @param boolean $public if the file is in a public share or not
* @param boolean $expectToSucceed
*
* @return void
* @throws GuzzleException
* @throws JsonException
*/
private function lockFile(
string $user,
string $file,
TableNode $properties,
string $fullUrl = null,
bool $public = false,
bool $expectToSucceed = true
):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
if ($public === true) {
$type = "public-files-new";
$password = $this->featureContext->getActualPassword("%public%");
} else {
$type = "files";
$password = $this->featureContext->getPasswordForUser($user);
}
$body
= "<?xml version='1.0' encoding='UTF-8'?>" .
"<d:lockinfo xmlns:d='DAV:'> ";
$headers = [];
// depth is only 0 or infinity. We don't need to set it more, as there is no lock for the folder
$this->featureContext->verifyTableNodeRows($properties, [], ['lockscope', 'timeout']);
$propertiesRows = $properties->getRowsHash();
foreach ($propertiesRows as $property => $value) {
if ($property === "timeout") {
//properties that are set in the header not in the xml
$headers[$property] = $value;
} else {
$body .= "<d:$property><d:$value/></d:$property>";
}
}
$body .= "</d:lockinfo>";
if (isset($fullUrl)) {
$response = HttpRequestHelper::sendRequest(
$fullUrl,
$this->featureContext->getStepLineRef(),
"LOCK",
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
$headers,
$body
);
} else {
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"LOCK",
$file,
$headers,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion(),
$type
);
}
$responseXml = $this->featureContext->getResponseXml($response, __METHOD__);
$xmlPart = $responseXml->xpath("//d:locktoken/d:href");
if (isset($xmlPart[0])) {
$this->tokenOfLastLock[$user][$file] = (string) $xmlPart[0];
} else {
if ($expectToSucceed === true) {
Assert::fail("could not find lock token after trying to lock '$file'");
}
}
return $response;
}
/**
* @When user :user locks file :file using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userLocksFileSettingPropertiesUsingWebDavAPI(string $user, string $file, TableNode $properties) {
$response = $this->lockFile($user, $file, $properties);
$this->featureContext->setResponse($response);
}
/**
* @When user :user tries to lock file/folder :file using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userTriesToLockFileSettingPropertiesUsingWebDavAPI(string $user, string $file, TableNode $properties) {
$response = $this->lockFile($user, $file, $properties, null, false, false);
$this->featureContext->setResponse($response);
}
/**
* @When user :user locks file :file inside the space :space using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param string $space
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
* @throws GuzzleException
*/
public function userLocksFileInProjectSpaceUsingWebDavAPI(string $user, string $file, string $space, TableNode $properties) {
$this->featureContext->setResponse($this->userLocksFileInProjectSpace($user, $file, $space, $properties));
}
/**
* @param string $user
* @param string $file
* @param string $space
* @param TableNode $properties
*
* @return ResponseInterface|null
*
* @throws GuzzleException
*/
public function userLocksFileInProjectSpace(string $user, string $file, string $space, TableNode $properties): ?ResponseInterface {
$spaceId = $this->spacesContext->getSpaceIdByName($user, $space);
$fullUrl = $this->featureContext->getBaseUrl() . '/dav/spaces/' . $spaceId . '/' . $file;
return $this->lockFile($user, $file, $properties, $fullUrl);
}
/**
* @Given user :user has locked file :file inside the space :space setting the following properties
*
* @param string $user
* @param string $file
* @param string $space
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
* @throws GuzzleException
*/
public function userHasLockedFileInProjectSpaceUsingWebDavAPI(string $user, string $file, string $space, TableNode $properties): void {
$response = $this->userLocksFileInProjectSpace($user, $file, $space, $properties);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @When user :user tries to lock file :file inside the space :space using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param string $space
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userTriesToLockFileInProjectSpaceUsingWebDavAPI(string $user, string $file, string $space, TableNode $properties) {
$spaceId = $this->spacesContext->getSpaceIdByName($user, $space);
$fullUrl = $this->featureContext->getBaseUrl() . '/dav/spaces/' . $spaceId . '/' . $file;
$response = $this->lockFile($user, $file, $properties, $fullUrl, false, false);
$this->featureContext->setResponse($response);
}
/**
* @When user :user locks file :file using file-id path :path using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param string $filePath
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userLocksFileUsingFileIdUsingWebDavAPI(string $user, string $file, string $filePath, TableNode $properties) {
$fullUrl = $this->featureContext->getBaseUrl() . $filePath;
$response = $this->lockFile($user, $file, $properties, $fullUrl);
$this->featureContext->setResponse($response);
}
/**
* @When user :user tries to lock file :file using file-id path :path using the WebDAV API setting the following properties
*
* @param string $user
* @param string $file
* @param string $filePath
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userTriesToLockFileUsingFileIdUsingWebDavAPI(string $user, string $file, string $filePath, TableNode $properties) {
$fullUrl = $this->featureContext->getBaseUrl() . $filePath;
$response = $this->lockFile($user, $file, $properties, $fullUrl, false, false);
$this->featureContext->setResponse($response);
}
/**
* @Given user :user has locked file :file setting the following properties
*
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userHasLockedFile(string $user, string $file, TableNode $properties) {
$response = $this->lockFile($user, $file, $properties);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @Given user :user has locked file :file inside space :spaceName setting the following properties
*
* @param string $user
* @param string $file
* @param string $spaceName
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userHasLockedFileInsideSpaceSettingTheFollowingProperties(string $user, string $file, string $spaceName, TableNode $properties) {
$this->spacesContext->setSpaceIDByName($this->featureContext->getActualUsername($user), $spaceName);
$response = $this->lockFile($user, $file, $properties);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @Given user :user has locked file :file using file-id path :path setting the following properties
*
* @param string $user
* @param string $file
* @param string $filePath
* @param TableNode $properties table with no heading with | property | value |
*
* @return void
*/
public function userHasLockedFileUsingFileId(string $user, string $file, string $filePath, TableNode $properties) {
$fullUrl = $this->featureContext->getBaseUrl() . $filePath;
$response = $this->lockFile($user, $file, $properties, $fullUrl);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @Given the public has locked the last public link shared file/folder setting the following properties
*
* @param TableNode $properties
*
* @return void
*/
public function publicHasLockedLastSharedFile(TableNode $properties) {
$response = $this->lockFile(
$this->featureContext->getLastCreatedPublicShareToken(),
"/",
$properties,
null,
true
);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @When the public locks the last public link shared file using the WebDAV API setting the following properties
* @When the public tries to lock the last public link shared file using the WebDAV API setting the following properties
*
* @param TableNode $properties
*
* @return void
*/
public function publicLocksLastSharedFile(TableNode $properties) {
$token = ($this->featureContext->isUsingSharingNG()) ? $this->featureContext->shareNgGetLastCreatedLinkShareToken() : $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->lockFile(
$token,
"/",
$properties,
null,
true,
false
);
$this->featureContext->setResponse($response);
}
/**
* @Given the public has locked :file in the last public link shared folder setting the following properties
*
* @param string $file
* @param TableNode $properties
*
* @return void
*/
public function publicHasLockedFileLastSharedFolder(
string $file,
TableNode $properties
) {
$token = ($this->featureContext->isUsingSharingNG()) ? $this->featureContext->shareNgGetLastCreatedLinkShareToken() : $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->lockFile(
$token,
$file,
$properties,
null,
true
);
$this->featureContext->theHTTPStatusCodeShouldBe(200, '', $response);
}
/**
* @When /^the public locks "([^"]*)" in the last public link shared folder using the (old|new) public WebDAV API setting the following properties$/
* @When /^the public tries to lock "([^"]*)" in the last public link shared folder using the (old|new) public WebDAV API setting the following properties$/
*
* @param string $file
* @param string $publicWebDAVAPIVersion
* @param TableNode $properties
*
* @return void
*/
public function publicLocksFileLastSharedFolder(
string $file,
string $publicWebDAVAPIVersion,
TableNode $properties
) {
$token = ($this->featureContext->isUsingSharingNG()) ? $this->featureContext->shareNgGetLastCreatedLinkShareToken() : $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->lockFile(
$token,
$file,
$properties,
null,
true,
false
);
$this->featureContext->setResponse($response);
}
/**
* @When user :user unlocks the last created lock of file :file using the WebDAV API
*
* @param string $user
* @param string $file
*
* @return void
*/
public function unlockLastLockUsingWebDavAPI(string $user, string $file) {
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$file,
$user,
$file
);
$this->featureContext->setResponse($response);
}
/**
* @When user :user unlocks the last created lock of file :file inside space :spaceName using the WebDAV API
*
* @param string $user
* @param string $spaceName
* @param string $file
*
* @return void
*/
public function userUnlocksTheLastCreatedLockOfFileInsideSpaceUsingTheWebdavApi(string $user, string $spaceName, string $file) {
$this->spacesContext->setSpaceIDByName($this->featureContext->getActualUsername($user), $spaceName);
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$file,
$user,
$file
);
$this->featureContext->setResponse($response);
}
/**
* @When user :user unlocks the last created lock of file :itemToUnlock using file-id path :filePath using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $filePath
*
* @return void
*/
public function userUnlocksTheLastCreatedLockOfFileWithFileIdPathUsingTheWebdavApi(string $user, string $itemToUnlock, string $filePath) {
$fullUrl = $this->featureContext->getBaseUrl() . $filePath;
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI($user, $itemToUnlock, $user, $itemToUnlock, false, $fullUrl);
$this->featureContext->setResponse($response);
}
/**
* @When user :user unlocks file :itemToUnlock with the last created lock of file :itemToUseLockOf using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemWithLastLockOfOtherItemUsingWebDavAPI(
string $user,
string $itemToUnlock,
string $itemToUseLockOf
) {
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$user,
$itemToUseLockOf
);
$this->featureContext->setResponse($response);
}
/**
* @When user :user unlocks file :itemToUnlock with the last created public lock of file :itemToUseLockOf using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemWithLastPublicLockOfOtherItemUsingWebDavAPI(
string $user,
string $itemToUnlock,
string $itemToUseLockOf
) {
$lockOwner = $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf
);
$this->featureContext->setResponse($response);
}
/**
*
* @param string $user
* @param string $itemToUnlock
*
* @return int
*
* @throws Exception|GuzzleException
*/
private function countLockOfResources(
string $user,
string $itemToUnlock
):int {
$user = $this->featureContext->getActualUsername($user);
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
$body
= "<?xml version='1.0' encoding='UTF-8'?>" .
"<d:propfind xmlns:d='DAV:'> " .
"<d:prop><d:lockdiscovery/></d:prop>" .
"</d:propfind>";
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"PROPFIND",
$itemToUnlock,
null,
$this->featureContext->getStepLineRef(),
$body,
$this->featureContext->getDavPathVersion()
);
$responseXml = $this->featureContext->getResponseXml($response, __METHOD__);
$xmlPart = $responseXml->xpath("//d:response//d:lockdiscovery/d:activelock");
if (\is_array($xmlPart)) {
return \count($xmlPart);
} else {
throw new Exception("xmlPart for 'd:activelock' was expected to be array but found: $xmlPart");
}
}
/**
* @Given user :user has unlocked file :itemToUnlock with the last created lock of file :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
* @param boolean $public
*
* @return void
* @throws Exception|GuzzleException
*/
public function hasUnlockItemWithTheLastCreatedLock(
string $user,
string $itemToUnlock,
string $lockOwner,
string $itemToUseLockOf,
bool $public = false
) {
$lockCount = $this->countLockOfResources($user, $itemToUnlock);
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf,
$public
);
$this->featureContext->theHTTPStatusCodeShouldBe(204, "", $response);
$lockCountAfterUnlock = $this->countLockOfResources($user, $itemToUnlock);
Assert::assertEquals(
$lockCount - 1,
$lockCountAfterUnlock,
"Expected $lockCount lock(s) for '$itemToUnlock' but found '$lockCount'"
);
}
/**
* @param string $user
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
* @param boolean $public
* @param string|null $fullUrl
*
* @return ResponseInterface
* @throws GuzzleException
* @throws JsonException
*/
public function unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
string $user,
string $itemToUnlock,
string $lockOwner,
string $itemToUseLockOf,
bool $public = false,
string $fullUrl = null
):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
if ($public === true) {
$type = "public-files-new";
$password = $this->featureContext->getActualPassword("%public%");
} else {
$type = "files";
$password = $this->featureContext->getPasswordForUser($user);
}
$baseUrl = $this->featureContext->getBaseUrl();
if (!isset($this->tokenOfLastLock[$lockOwner][$itemToUseLockOf])) {
Assert::fail(
"could not find saved token of '$itemToUseLockOf' " .
"owned by user '$lockOwner'"
);
}
$headers = [
"Lock-Token" => $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf]
];
if (isset($fullUrl)) {
$response = HttpRequestHelper::sendRequest(
$fullUrl,
$this->featureContext->getStepLineRef(),
"UNLOCK",
$this->featureContext->getActualUsername($user),
$this->featureContext->getPasswordForUser($user),
$headers
);
} else {
$response = WebDavHelper::makeDavRequest(
$baseUrl,
$user,
$password,
"UNLOCK",
$itemToUnlock,
$headers,
$this->featureContext->getStepLineRef(),
null,
$this->featureContext->getDavPathVersion(),
$type
);
}
return $response;
}
/**
* @When user :user unlocks file :itemToUnlock with the last created lock of file :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $user
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
*
* @return void
*/
public function userUnlocksItemWithLastLockOfUserAndItemUsingWebDavAPI(
string $user,
string $itemToUnlock,
string $lockOwner,
string $itemToUseLockOf
) {
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$user,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf
);
$this->featureContext->setResponse($response);
}
/**
* @When the public unlocks file :itemToUnlock with the last created lock of file :itemToUseLockOf of user :lockOwner using the WebDAV API
*
* @param string $itemToUnlock
* @param string $lockOwner
* @param string $itemToUseLockOf
*
* @return void
*/
public function unlockItemAsPublicWithLastLockOfUserAndItemUsingWebDavAPI(
string $itemToUnlock,
string $lockOwner,
string $itemToUseLockOf
) {
$token = ($this->featureContext->isUsingSharingNG()) ? $this->featureContext->shareNgGetLastCreatedLinkShareToken() : $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$token,
$itemToUnlock,
$lockOwner,
$itemToUseLockOf,
true
);
$this->featureContext->setResponse($response);
}
/**
* @When the public unlocks file :itemToUnlock using the WebDAV API
*
* @param string $itemToUnlock
*
* @return void
*/
public function unlockItemAsPublicUsingWebDavAPI(string $itemToUnlock) {
$token = ($this->featureContext->isUsingSharingNG()) ? $this->featureContext->shareNgGetLastCreatedLinkShareToken() : $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->unlockItemWithLastLockOfUserAndItemUsingWebDavAPI(
$token,
$itemToUnlock,
$token,
$itemToUnlock,
true
);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" moves (?:file|folder|entry) "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
* @param string $itemToUseLockOf
*
* @return void
*/
public function moveItemSendingLockToken(
string $user,
string $fileSource,
string $fileDestination,
string $itemToUseLockOf
) {
$response = $this->moveItemSendingLockTokenOfUser(
$user,
$fileSource,
$fileDestination,
$itemToUseLockOf,
$user
);
$this->featureContext->setResponse($response);
}
/**
* @param string $user
* @param string $fileSource
* @param string $fileDestination
* @param string $itemToUseLockOf
* @param string $lockOwner
*
* @return ResponseInterface
*/
public function moveItemSendingLockTokenOfUser(
string $user,
string $fileSource,
string $fileDestination,
string $itemToUseLockOf,
string $lockOwner
):ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
$destination = $this->featureContext->destinationHeaderValue(
$user,
$fileDestination
);
$token = $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf];
$headers = [
"Destination" => $destination,
"If" => "(<$token>)"
];
return $this->featureContext->makeDavRequest(
$user,
"MOVE",
$fileSource,
$headers
);
}
/**
* @When /^user "([^"]*)" moves (?:file|folder|entry) "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" of user "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $fileSource
* @param string $fileDestination
* @param string $itemToUseLockOf
* @param string $lockOwner
*
* @return void
*/
public function userMovesItemSendingLockTokenOfUser(
string $user,
string $fileSource,
string $fileDestination,
string $itemToUseLockOf,
string $lockOwner
) {
$response = $this->moveItemSendingLockTokenOfUser(
$user,
$fileSource,
$fileDestination,
$itemToUseLockOf,
$lockOwner
);
$this->featureContext->setResponse($response);
}
/**
* @When /^user "([^"]*)" uploads file with content "([^"]*)" to "([^"]*)" sending the locktoken of (?:file|folder|entry) "([^"]*)" using the WebDAV API$/
*
* @param string $user
* @param string $content
* @param string $destination
* @param string $itemToUseLockOf
*
* @return void
*/
public function userUploadsAFileWithContentTo(
string $user,
string $content,
string $destination,
string $itemToUseLockOf
) {
$user = $this->featureContext->getActualUsername($user);
$token = $this->tokenOfLastLock[$user][$itemToUseLockOf];
$this->featureContext->pauseUploadDelete();
$response = $this->featureContext->makeDavRequest(
$user,
"PUT",
$destination,
["If" => "(<$token>)"],
$content
);
$this->featureContext->setResponse($response);
$this->featureContext->setLastUploadDeleteTime(\time());
}
/**
* @When /^the public uploads file "([^"]*)" with content "([^"]*)" sending the locktoken of file "([^"]*)" of user "([^"]*)" using the (old|new) public WebDAV API$/
*
* @param string $filename
* @param string $content
* @param string $itemToUseLockOf
* @param string $lockOwner
* @param string $publicWebDAVAPIVersion
*
* @return void
*
*/
public function publicUploadFileSendingLockTokenOfUser(
string $filename,
string $content,
string $itemToUseLockOf,
string $lockOwner,
string $publicWebDAVAPIVersion
) {
$response = $this->publicUploadWithUserLockToken(
$filename,
$content,
$itemToUseLockOf,
$lockOwner,
$publicWebDAVAPIVersion
);
$this->featureContext->setResponse($response);
}
/**
* @param string $filename
* @param string $content
* @param string $itemToUseLockOf
* @param string $lockOwner
* @param string $publicWebDAVAPIVersion
*
* @return ResponseInterface
*/
public function publicUploadWithUserLockToken(
string $filename,
string $content,
string $itemToUseLockOf,
string $lockOwner,
string $publicWebDAVAPIVersion
): ResponseInterface {
$lockOwner = $this->featureContext->getActualUsername($lockOwner);
$headers = [
"If" => "(<" . $this->tokenOfLastLock[$lockOwner][$itemToUseLockOf] . ">)"
];
return $this->publicWebDavContext->publicUploadContent(
$filename,
'',
$content,
false,
$headers,
$publicWebDAVAPIVersion
);
}
/**
* @When /^the public uploads file "([^"]*)" with content "([^"]*)" sending the locktoken of "([^"]*)" of the public using the (old|new) public WebDAV API$/
*
* @param string $filename
* @param string $content
* @param string $itemToUseLockOf
* @param string $publicWebDAVAPIVersion
*
* @return void
*/
public function publicUploadFileSendingLockTokenOfPublic(
string $filename,
string $content,
string $itemToUseLockOf,
string $publicWebDAVAPIVersion
) {
$lockOwner = $this->featureContext->getLastCreatedPublicShareToken();
$response = $this->publicUploadWithUserLockToken(
$filename,
$content,
$itemToUseLockOf,
$lockOwner,
$publicWebDAVAPIVersion
);
$this->featureContext->setResponse($response);
}
/**
* @Then :count locks should be reported for file :file of user :user by the WebDAV API
*
* @param int $count
* @param string $file
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function numberOfLockShouldBeReported(int $count, string $file, string $user) {
$lockCount = $this->countLockOfResources($user, $file);
Assert::assertEquals(
$count,
$lockCount,
"Expected $count lock(s) for '$file' but found '$lockCount'"
);
}
/**
* @When the user waits for :time seconds to expire the lock
*
* @param int $time
*
* @return void
*/
public function waitForCertainSecondsToExpireTheLock(int $time): void {
\sleep($time);
}
/**
* @Then :count locks should be reported for file :file inside the space :space of user :user
*
* @param int $count
* @param string $file
* @param string $spaceName
* @param string $user
*
* @return void
* @throws GuzzleException
*/
public function numberOfLockShouldBeReportedInProjectSpace(int $count, string $file, string $spaceName, string $user) {
$response = $this->spacesContext->sendPropfindRequestToSpace($user, $spaceName, $file, null, '0');
$this->featureContext->theHTTPStatusCodeShouldBe(207, "", $response);
$responseXml = $this->featureContext->getResponseXml($response);
$xmlPart = $responseXml->xpath("//d:response//d:lockdiscovery/d:activelock");
if (\is_array($xmlPart)) {
$lockCount = \count($xmlPart);
} else {
throw new Exception("xmlPart for 'd:activelock' was expected to be array but found: $xmlPart");
}
Assert::assertEquals(
$count,
$lockCount,
"Expected $count lock(s) for '$file' inside space '$spaceName' but found '$lockCount'"
);
}
/**
* This will run before EVERY scenario.
* It will set the properties for this object.
*
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
*/
public function before(BeforeScenarioScope $scope) {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
$this->publicWebDavContext = $environment->getContext('PublicWebDavContext');
if (!OcisHelper::isTestingOnReva()) {
$this->spacesContext = $environment->getContext('SpacesContext');
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,93 +0,0 @@
<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Phil Davis <phil@jankaritech.com>
* @copyright Copyright (c) 2020 Phil Davis phil@jankaritech.com
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License,
* as published by the Free Software Foundation;
* either version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
use Composer\Autoload\ClassLoader;
$classLoader = new ClassLoader();
$classLoader->addPsr4("TestHelpers\\", __DIR__ . "/../../../TestHelpers", true);
$classLoader->register();
// while running for the local API tests, the tests code from ownCloud/core is not used
// so we need the constants to be defined for the tests to use them, but for the case where,
// the tests are running for oC/core API tests, the constants are already defined in the bootstrap.php there
// so we do not declare them again to avoid the "already defined" error
// Sleep for 10 milliseconds
if (!\defined('STANDARD_SLEEP_TIME_MILLISEC')) {
\define('STANDARD_SLEEP_TIME_MILLISEC', 10);
}
if (!\defined('STANDARD_SLEEP_TIME_MICROSEC')) {
\define('STANDARD_SLEEP_TIME_MICROSEC', STANDARD_SLEEP_TIME_MILLISEC * 1000);
}
// Long timeout for use in code that needs to wait for known slow UI
if (!\defined('LONG_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('LONG_UI_WAIT_TIMEOUT_MILLISEC', 60000);
}
// Default timeout for use in code that needs to wait for the UI
if (!\defined('STANDARD_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('STANDARD_UI_WAIT_TIMEOUT_MILLISEC', 10000);
}
// Minimum timeout for use in code that needs to wait for the UI
if (!\defined('MINIMUM_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('MINIMUM_UI_WAIT_TIMEOUT_MILLISEC', 500);
}
if (!\defined('MINIMUM_UI_WAIT_TIMEOUT_MICROSEC')) {
\define('MINIMUM_UI_WAIT_TIMEOUT_MICROSEC', MINIMUM_UI_WAIT_TIMEOUT_MILLISEC * 1000);
}
// Minimum timeout for emails
if (!\defined('EMAIL_WAIT_TIMEOUT_SEC')) {
\define('EMAIL_WAIT_TIMEOUT_SEC', 10);
}
if (!\defined('EMAIL_WAIT_TIMEOUT_MILLISEC')) {
\define('EMAIL_WAIT_TIMEOUT_MILLISEC', EMAIL_WAIT_TIMEOUT_SEC * 1000);
}
// Default number of times to retry where retries are useful
if (!\defined('STANDARD_RETRY_COUNT')) {
\define('STANDARD_RETRY_COUNT', 5);
}
// Minimum number of times to retry where retries are useful
if (!\defined('MINIMUM_RETRY_COUNT')) {
\define('MINIMUM_RETRY_COUNT', 2);
}
// The remote server-under-test might or might not happen to have this directory.
// If it does not exist, then the tests may end up creating it.
if (!\defined('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER')) {
\define('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER', 'tests/acceptance');
}
// The following directory should NOT already exist on the remote server-under-test.
// Acceptance tests are free to do anything needed in this directory, and to
// delete it during or at the end of testing.
if (!\defined('TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER')) {
\define('TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER', ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER . '/server_tmp');
}