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:
@@ -0,0 +1,92 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers\Asserts;
|
||||
|
||||
use PHPUnit\Framework\Assert;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* WebDAV related asserts
|
||||
*/
|
||||
class WebDav extends Assert {
|
||||
/**
|
||||
*
|
||||
* @param string|null $element exception|message|reason
|
||||
* @param string|null $expectedValue
|
||||
* @param array|null $responseXml
|
||||
* @param string|null $extraErrorText
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function assertDavResponseElementIs(
|
||||
?string $element,
|
||||
?string $expectedValue,
|
||||
?array $responseXml,
|
||||
?string $extraErrorText = ''
|
||||
):void {
|
||||
if ($extraErrorText !== '') {
|
||||
$extraErrorText = $extraErrorText . " ";
|
||||
}
|
||||
self::assertArrayHasKey(
|
||||
'value',
|
||||
$responseXml,
|
||||
$extraErrorText . "responseXml does not have key 'value'"
|
||||
);
|
||||
if ($element === "exception") {
|
||||
$result = $responseXml['value'][0]['value'];
|
||||
} elseif ($element === "message") {
|
||||
$result = $responseXml['value'][1]['value'];
|
||||
} elseif ($element === "reason") {
|
||||
$result = $responseXml['value'][3]['value'];
|
||||
} else {
|
||||
self::fail(__METHOD__ . " element must be one of exception, response or reason. But '$element' was passed in.");
|
||||
}
|
||||
|
||||
self::assertEquals(
|
||||
$expectedValue,
|
||||
$result,
|
||||
__METHOD__ . " " . $extraErrorText . "Expected '$expectedValue' in element $element got '$result'"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param SimpleXMLElement $responseXmlObject
|
||||
* @param array|null $expectedShareTypes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function assertResponseContainsShareTypes(
|
||||
SimpleXMLElement $responseXmlObject,
|
||||
?array $expectedShareTypes
|
||||
):void {
|
||||
foreach ($expectedShareTypes as $row) {
|
||||
$xmlPart = $responseXmlObject->xpath(
|
||||
"//d:prop/oc:share-types/oc:share-type[.=" . $row[0] . "]"
|
||||
);
|
||||
self::assertNotEmpty(
|
||||
$xmlPart,
|
||||
"cannot find share-type '" . $row[0] . "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TestHelpers\OcisConfigHelper;
|
||||
|
||||
/**
|
||||
* A helper class for running oCIS CLI commands
|
||||
*/
|
||||
class CliHelper {
|
||||
/**
|
||||
* @param array $body
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function runCommand(array $body): ResponseInterface {
|
||||
$url = OcisConfigHelper::getWrapperUrl() . "/command";
|
||||
return OcisConfigHelper::sendRequest($url, "POST", \json_encode($body));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Prajwol Amatya <prajwol@jankaritech.com>
|
||||
* @copyright Copyright (c) 2023 Prajwol Amatya prajwol@jankaritech.com
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A helper class for managing emails
|
||||
*/
|
||||
class EmailHelper {
|
||||
/**
|
||||
* @param string $emailAddress
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getMailBoxFromEmail(string $emailAddress):string {
|
||||
return explode("@", $emailAddress)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host and port where Email messages can be read and deleted
|
||||
* by the test runner.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLocalEmailUrl():string {
|
||||
$localEmailHost = self::getLocalEmailHost();
|
||||
$emailPort = \getenv('EMAIL_PORT');
|
||||
if ($emailPort === false) {
|
||||
$emailPort = "9000";
|
||||
}
|
||||
return "http://$localEmailHost:$emailPort";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host name or address of the Email server as seen from the
|
||||
* point of view of the system-under-test.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getEmailHost():string {
|
||||
$emailHost = \getenv('EMAIL_HOST');
|
||||
if ($emailHost === false) {
|
||||
$emailHost = "127.0.0.1";
|
||||
}
|
||||
return $emailHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host name or address of the Email server as seen from the
|
||||
* point of view of the test runner.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLocalEmailHost():string {
|
||||
$localEmailHost = \getenv('LOCAL_EMAIL_HOST');
|
||||
if ($localEmailHost === false) {
|
||||
$localEmailHost = self::getEmailHost();
|
||||
}
|
||||
return $localEmailHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general response information about the provided mailbox
|
||||
* A mailbox is created automatically in InBucket for every unique email sender|receiver
|
||||
*
|
||||
* @param string $mailBox
|
||||
* @param string|null $xRequestId
|
||||
*
|
||||
* @return array
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getMailBoxInformation(string $mailBox, ?string $xRequestId = null):array {
|
||||
$response = HttpRequestHelper::get(
|
||||
self::getLocalEmailUrl() . "/api/v1/mailbox/" . $mailBox,
|
||||
$xRequestId,
|
||||
null,
|
||||
null,
|
||||
['Content-Type' => 'application/json']
|
||||
);
|
||||
return \json_decode($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* returns body content of a specific email (mailBox) with email ID (mailbox Id)
|
||||
*
|
||||
* @param string $mailBox
|
||||
* @param string $mailboxId
|
||||
* @param string|null $xRequestId
|
||||
*
|
||||
* @return object
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getBodyOfAnEmailById(string $mailBox, string $mailboxId, ?string $xRequestId = null):object {
|
||||
$response = HttpRequestHelper::get(
|
||||
self::getLocalEmailUrl() . "/api/v1/mailbox/" . $mailBox . "/" . $mailboxId,
|
||||
$xRequestId,
|
||||
null,
|
||||
null,
|
||||
['Content-Type' => 'application/json']
|
||||
);
|
||||
return \json_decode($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the body of the last received email for the provided receiver according to the provided email address and the serial number
|
||||
* For email number, 1 means the latest one
|
||||
*
|
||||
* @param string $emailAddress
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $emailNumber For email number, 1 means the latest one
|
||||
* @param int|null $waitTimeSec Time to wait for the email if the email has been delivered
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getBodyOfLastEmail(
|
||||
string $emailAddress,
|
||||
string $xRequestId,
|
||||
?int $emailNumber = 1,
|
||||
?int $waitTimeSec = EMAIL_WAIT_TIMEOUT_SEC
|
||||
):string {
|
||||
$currentTime = \time();
|
||||
$endTime = $currentTime + $waitTimeSec;
|
||||
$mailBox = self::getMailBoxFromEmail($emailAddress);
|
||||
while ($currentTime <= $endTime) {
|
||||
$mailboxResponse = self::getMailboxInformation($mailBox, $xRequestId);
|
||||
if (!empty($mailboxResponse) && \sizeof($mailboxResponse) >= $emailNumber) {
|
||||
$mailboxId = $mailboxResponse[\sizeof($mailboxResponse) - $emailNumber]->id;
|
||||
$response = self::getBodyOfAnEmailById($mailBox, $mailboxId, $xRequestId);
|
||||
$body = \str_replace(
|
||||
"\r\n",
|
||||
"\n",
|
||||
\quoted_printable_decode($response->body->text . "\n" . $response->body->html)
|
||||
);
|
||||
return $body;
|
||||
}
|
||||
\usleep(STANDARD_SLEEP_TIME_MICROSEC * 50);
|
||||
$currentTime = \time();
|
||||
}
|
||||
throw new Exception("Could not find the email to the address: " . $emailAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the emails for the provided mailbox
|
||||
*
|
||||
* @param string $localInbucketUrl
|
||||
* @param string|null $xRequestId
|
||||
* @param string $mailBox
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function deleteAllEmailsForAMailbox(
|
||||
string $localInbucketUrl,
|
||||
?string $xRequestId,
|
||||
string $mailBox
|
||||
):ResponseInterface {
|
||||
return HttpRequestHelper::delete(
|
||||
$localInbucketUrl . "/api/v1/mailbox/" . $mailBox,
|
||||
$xRequestId
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Sajan Gurung <sajan@jankaritech.com>
|
||||
* @copyright Copyright (c) 2023, 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Helper for logging HTTP requests and responses
|
||||
*/
|
||||
class HttpLogger {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogDir(): string {
|
||||
return __DIR__ . '/../logs';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getFailedLogPath(): string {
|
||||
return self::getLogDir() . "/failed.log";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getScenarioLogPath(): string {
|
||||
return self::getLogDir() . "/scenario.log";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $logFile
|
||||
* @param string $logMessage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function writeLog(string $logFile, string $logMessage): void {
|
||||
$file = \fopen($logFile, 'a+') or die('Cannot open file: ' . $logFile);
|
||||
\fwrite($file, $logMessage);
|
||||
\fclose($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function logRequest(RequestInterface $request): void {
|
||||
$method = $request->getMethod();
|
||||
$path = $request->getUri()->getPath();
|
||||
$query = $request->getUri()->getQuery();
|
||||
$body = $request->getBody();
|
||||
|
||||
$headers = "";
|
||||
foreach ($request->getHeaders() as $key => $value) {
|
||||
$headers = $key . ": " . $value[0] . "\n";
|
||||
}
|
||||
|
||||
$logMessage = "\t\t_______________________________________________________________________\n\n";
|
||||
$logMessage .= "\t\t==> REQUEST\n";
|
||||
$logMessage .= "\t\t$method $path\n";
|
||||
$logMessage .= $query ? "\t\tQUERY: $query\n" : "";
|
||||
$logMessage .= "\t\t$headers";
|
||||
|
||||
if ($body->getSize() > 0) {
|
||||
$logMessage .= "\t\t==> REQ BODY\n";
|
||||
$logMessage .= "\t\t$body\n";
|
||||
}
|
||||
$logMessage .= "\n";
|
||||
self::writeLog(self::getScenarioLogPath(), $logMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function logResponse(ResponseInterface $response): void {
|
||||
$statusCode = $response->getStatusCode();
|
||||
$statusMessage = $response->getReasonPhrase();
|
||||
$body = $response->getBody();
|
||||
$headers = "";
|
||||
|
||||
foreach ($response->getHeaders() as $key => $value) {
|
||||
$headers = $key . ": " . $value[0] . "\n";
|
||||
}
|
||||
|
||||
$logMessage = "\t\t<== RESPONSE\n";
|
||||
$logMessage .= "\t\t$statusCode $statusMessage\n";
|
||||
$logMessage .= "\t\t$headers";
|
||||
|
||||
if ($body->getSize() > 0) {
|
||||
$logMessage .= "\t\t<== RES BODY\n";
|
||||
foreach (\explode("\n", \strval($body)) as $line) {
|
||||
$logMessage .= "\t\t$line\n";
|
||||
}
|
||||
}
|
||||
// rewind the body stream so that later code can read from the start.
|
||||
$response->getBody()->rewind();
|
||||
|
||||
$logMessage = \rtrim($logMessage) . "\n\n";
|
||||
self::writeLog(self::getScenarioLogPath(), $logMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Cookie\CookieJar;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use SimpleXMLElement;
|
||||
use Sabre\Xml\LibXMLException;
|
||||
use Sabre\Xml\Reader;
|
||||
use GuzzleHttp\Pool;
|
||||
|
||||
/**
|
||||
* Helper for HTTP requests
|
||||
*/
|
||||
class HttpRequestHelper {
|
||||
public const HTTP_TOO_EARLY = 425;
|
||||
public const HTTP_CONFLICT = 409;
|
||||
private static ?string $oCSelectorCookie = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getOCSelectorCookie(): string {
|
||||
return self::$oCSelectorCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $oCSelectorCookie "owncloud-selector=oc10;path=/;"
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setOCSelectorCookie(string $oCSelectorCookie): void {
|
||||
self::$oCSelectorCookie = $oCSelectorCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some systems-under-test do async post-processing of operations like upload,
|
||||
* move, etc. If a client does a request on the resource before the post-processing
|
||||
* is finished, then the server should return HTTP_TOO_EARLY "425". Clients are
|
||||
* expected to retry the request "some time later" (tm).
|
||||
*
|
||||
* On such systems, when HTTP_TOO_EARLY status is received, the test code will
|
||||
* retry the request at 1-second intervals until either some other HTTP status
|
||||
* is received or the retry-limit is reached.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function numRetriesOnHttpTooEarly():int {
|
||||
// Currently reva and oCIS may return HTTP_TOO_EARLY
|
||||
// So try up to 10 times before giving up.
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $method
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param bool $stream Set to true to stream a response rather
|
||||
* than download it all up-front.
|
||||
* @param int|null $timeout
|
||||
* @param Client|null $client
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function sendRequestOnce(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $method = 'GET',
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
bool $stream = false,
|
||||
?int $timeout = 0,
|
||||
?Client $client = null
|
||||
):ResponseInterface {
|
||||
if ($client === null) {
|
||||
$client = self::createClient(
|
||||
$user,
|
||||
$password,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream,
|
||||
$timeout
|
||||
);
|
||||
}
|
||||
$request = self::createRequest(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$method,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
|
||||
if ((\getenv('DEBUG_ACCEPTANCE_REQUESTS') !== false) || (\getenv('DEBUG_ACCEPTANCE_API_CALLS') !== false)) {
|
||||
$debugRequests = true;
|
||||
} else {
|
||||
$debugRequests = false;
|
||||
}
|
||||
|
||||
if ($debugRequests) {
|
||||
self::debugRequest($request, $user, $password);
|
||||
}
|
||||
|
||||
// The exceptions that might happen here include:
|
||||
// ConnectException - in that case there is no response. Don't catch the exception.
|
||||
// RequestException - if there is something in the response then pass it back.
|
||||
// Otherwise, re-throw the exception.
|
||||
// GuzzleException - something else unexpected happened. Don't catch the exception.
|
||||
try {
|
||||
$response = $client->send($request);
|
||||
} catch (RequestException $ex) {
|
||||
$response = $ex->getResponse();
|
||||
|
||||
//if the response was null for some reason do not return it but re-throw
|
||||
if ($response === null) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
HttpLogger::logResponse($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $method
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param bool $stream Set to true to stream a response rather
|
||||
* than download it all up-front.
|
||||
* @param int|null $timeout
|
||||
* @param Client|null $client
|
||||
* @param bool|null $isGivenStep
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function sendRequest(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $method = 'GET',
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
bool $stream = false,
|
||||
?int $timeout = 0,
|
||||
?Client $client = null,
|
||||
?bool $isGivenStep = false
|
||||
):ResponseInterface {
|
||||
if ((\getenv('DEBUG_ACCEPTANCE_RESPONSES') !== false) || (\getenv('DEBUG_ACCEPTANCE_API_CALLS') !== false)) {
|
||||
$debugResponses = true;
|
||||
} else {
|
||||
$debugResponses = false;
|
||||
}
|
||||
|
||||
$sendRetryLimit = self::numRetriesOnHttpTooEarly();
|
||||
$sendCount = 0;
|
||||
$sendExceptionHappened = false;
|
||||
do {
|
||||
$response = self::sendRequestOnce(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$method,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream,
|
||||
$timeout,
|
||||
$client
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() >= 400 && $response->getStatusCode() !== self::HTTP_TOO_EARLY && $response->getStatusCode() !== self::HTTP_CONFLICT) {
|
||||
$sendExceptionHappened = true;
|
||||
}
|
||||
|
||||
if ($debugResponses) {
|
||||
self::debugResponse($response);
|
||||
}
|
||||
$sendCount = $sendCount + 1;
|
||||
// Here we check if the response has status code 425 or is a 409 gotten from a Given step
|
||||
// HTTP_TOO_EARLY (425) can happen if async processing of a previous request is still happening.
|
||||
// For example, if a test uploads a file and then immediately tries to download it.
|
||||
// HTTP_CONFLICT (409) can happen if the user has just been created in the previous step.
|
||||
// The OCS API might not "realize" yet that the user exists. A folder creation (MKCOL) or maybe even
|
||||
// a file upload might return 409.
|
||||
// In all these cases we can try the API request again after a short time.
|
||||
$loopAgain = !$sendExceptionHappened && ($response->getStatusCode() === self::HTTP_TOO_EARLY ||
|
||||
($response->getStatusCode() === self::HTTP_CONFLICT && $isGivenStep)) &&
|
||||
$sendCount <= $sendRetryLimit;
|
||||
if ($loopAgain) {
|
||||
// we need to repeat the send request, because we got HTTP_TOO_EARLY or HTTP_CONFLICT
|
||||
// wait 1 second before sending again, to give the server some time
|
||||
// to finish whatever post-processing it might be doing.
|
||||
self::debugResponse($response);
|
||||
\sleep(1);
|
||||
}
|
||||
} while ($loopAgain);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print details about the request.
|
||||
*
|
||||
* @param RequestInterface|null $request
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function debugRequest(?RequestInterface $request, ?string $user, ?string $password):void {
|
||||
print("### AUTH: $user:$password\n");
|
||||
print("### REQUEST: " . $request->getMethod() . " " . $request->getUri() . "\n");
|
||||
self::printHeaders($request->getHeaders());
|
||||
self::printBody($request->getBody());
|
||||
print("\n### END REQUEST\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print details about the response.
|
||||
*
|
||||
* @param ResponseInterface|null $response
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function debugResponse(?ResponseInterface $response):void {
|
||||
print("### RESPONSE\n");
|
||||
print("Status: " . $response->getStatusCode() . "\n");
|
||||
self::printHeaders($response->getHeaders());
|
||||
self::printBody($response->getBody());
|
||||
print("\n### END RESPONSE\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print details about the headers.
|
||||
*
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function printHeaders(?array $headers):void {
|
||||
if ($headers) {
|
||||
print("Headers:\n");
|
||||
foreach ($headers as $header => $value) {
|
||||
if (\is_array($value)) {
|
||||
print($header . ": " . \implode(', ', $value) . "\n");
|
||||
} else {
|
||||
print($header . ": " . $value . "\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("Headers: none\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print details about the body.
|
||||
*
|
||||
* @param StreamInterface|null $body
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function printBody(?StreamInterface $body):void {
|
||||
print("Body:\n");
|
||||
\var_dump($body->getContents());
|
||||
// Rewind the stream so that later code can read from the start.
|
||||
$body->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the requests to the server in parallel.
|
||||
* This function takes an array of requests and an optional client.
|
||||
* It will send all the requests to the server using the Pool object in guzzle.
|
||||
*
|
||||
* @param array|null $requests
|
||||
* @param Client|null $client
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function sendBatchRequest(
|
||||
?array $requests,
|
||||
?Client $client
|
||||
):array {
|
||||
return Pool::batch($client, $requests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Guzzle Client
|
||||
* This creates a client object that can be used later to send a request object(s)
|
||||
*
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param bool $stream Set to true to stream a response rather
|
||||
* than download it all up-front.
|
||||
* @param int|null $timeout
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public static function createClient(
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
?bool $stream = false,
|
||||
?int $timeout = 0
|
||||
):Client {
|
||||
$options = [];
|
||||
if ($user !== null) {
|
||||
$options['auth'] = [$user, $password];
|
||||
}
|
||||
if ($config !== null) {
|
||||
$options['config'] = $config;
|
||||
}
|
||||
if ($cookies !== null) {
|
||||
$options['cookies'] = $cookies;
|
||||
}
|
||||
$options['stream'] = $stream;
|
||||
$options['verify'] = false;
|
||||
$options['timeout'] = $timeout ?: self::getRequestTimeout();
|
||||
return new Client($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTP request based on given parameters.
|
||||
* This creates a RequestInterface object that can be used with a client to send a request.
|
||||
* This enables us to create multiple requests in advance so that we can send them to the server at once in parallel.
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $method
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param string|array $body either the actual string to send in the body,
|
||||
* or an array of key-value pairs to be converted
|
||||
* into a body with http_build_query.
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public static function createRequest(
|
||||
?string $url,
|
||||
?string $xRequestId = '',
|
||||
?string $method = 'GET',
|
||||
?array $headers = null,
|
||||
$body = null
|
||||
):RequestInterface {
|
||||
if ($headers === null) {
|
||||
$headers = [];
|
||||
}
|
||||
if ($xRequestId !== '') {
|
||||
$headers['X-Request-ID'] = $xRequestId;
|
||||
}
|
||||
if (\is_array($body)) {
|
||||
// When creating the client, it is possible to set 'form_params' and
|
||||
// the Client constructor sorts out doing this http_build_query stuff.
|
||||
// But 'new Request' does not have the flexibility to do that.
|
||||
// So we need to do it here.
|
||||
$body = \http_build_query($body, '', '&');
|
||||
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
|
||||
if (OcisHelper::isTestingParallelDeployment()) {
|
||||
// oCIS cannot handle '/apps/testing' endpoints
|
||||
// so those requests must be redirected to oC10 server
|
||||
// change server to oC10 if the request url has `/apps/testing`
|
||||
if (strpos($url, "/apps/testing") !== false) {
|
||||
$oCISServerUrl = \getenv('TEST_SERVER_URL');
|
||||
$oC10ServerUrl = \getenv('TEST_OC10_URL');
|
||||
$url = str_replace($oCISServerUrl, $oC10ServerUrl, $url);
|
||||
} else {
|
||||
// set 'owncloud-server' selector cookie for oCIS requests
|
||||
$headers['Cookie'] = self::getOCSelectorCookie();
|
||||
}
|
||||
}
|
||||
|
||||
$request = new Request(
|
||||
$method,
|
||||
$url,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
HttpLogger::logRequest($request);
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* same as HttpRequestHelper::sendRequest() but with "GET" as method
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param boolean $stream
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @see HttpRequestHelper::sendRequest()
|
||||
*/
|
||||
public static function get(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
?bool $stream = false
|
||||
):ResponseInterface {
|
||||
return self::sendRequest(
|
||||
$url,
|
||||
$xRequestId,
|
||||
'GET',
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* same as HttpRequestHelper::sendRequest() but with "POST" as method
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param boolean $stream
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @see HttpRequestHelper::sendRequest()
|
||||
*/
|
||||
public static function post(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
?bool $stream = false
|
||||
):ResponseInterface {
|
||||
return self::sendRequest(
|
||||
$url,
|
||||
$xRequestId,
|
||||
'POST',
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* same as HttpRequestHelper::sendRequest() but with "PUT" as method
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param boolean $stream
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @see HttpRequestHelper::sendRequest()
|
||||
*/
|
||||
public static function put(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
?bool $stream = false
|
||||
):ResponseInterface {
|
||||
return self::sendRequest(
|
||||
$url,
|
||||
$xRequestId,
|
||||
'PUT',
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* same as HttpRequestHelper::sendRequest() but with "DELETE" as method
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param array|null $headers ['X-MyHeader' => 'value']
|
||||
* @param mixed $body
|
||||
* @param array|null $config
|
||||
* @param CookieJar|null $cookies
|
||||
* @param boolean $stream
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @see HttpRequestHelper::sendRequest()
|
||||
*
|
||||
*/
|
||||
public static function delete(
|
||||
?string $url,
|
||||
?string $xRequestId,
|
||||
?string $user = null,
|
||||
?string $password = null,
|
||||
?array $headers = null,
|
||||
$body = null,
|
||||
?array $config = null,
|
||||
?CookieJar $cookies = null,
|
||||
?bool $stream = false
|
||||
):ResponseInterface {
|
||||
return self::sendRequest(
|
||||
$url,
|
||||
$xRequestId,
|
||||
'DELETE',
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
$cookies,
|
||||
$stream
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the response as XML and returns a SimpleXMLElement with these
|
||||
* registered namespaces:
|
||||
* | prefix | namespace |
|
||||
* | d | DAV: |
|
||||
* | oc | http://owncloud.org/ns |
|
||||
* | ocs | http://open-collaboration-services.org/ns |
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param string|null $exceptionText text to put at the front of exception messages
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getResponseXml(ResponseInterface $response, ?string $exceptionText = ''):SimpleXMLElement {
|
||||
// rewind just to make sure we can reparse it in case it was parsed already...
|
||||
$response->getBody()->rewind();
|
||||
$contents = $response->getBody()->getContents();
|
||||
try {
|
||||
$responseXmlObject = new SimpleXMLElement($contents);
|
||||
$responseXmlObject->registerXPathNamespace(
|
||||
'ocs',
|
||||
'http://open-collaboration-services.org/ns'
|
||||
);
|
||||
$responseXmlObject->registerXPathNamespace(
|
||||
'oc',
|
||||
'http://owncloud.org/ns'
|
||||
);
|
||||
$responseXmlObject->registerXPathNamespace(
|
||||
'd',
|
||||
'DAV:'
|
||||
);
|
||||
return $responseXmlObject;
|
||||
} catch (Exception $e) {
|
||||
if ($exceptionText !== '') {
|
||||
$exceptionText = $exceptionText . ' ';
|
||||
}
|
||||
if ($contents === '') {
|
||||
throw new Exception($exceptionText . "Received empty response where XML was expected");
|
||||
}
|
||||
$message = $exceptionText . "Exception parsing response body: \"" . $contents . "\"";
|
||||
throw new Exception($message, 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parses the body content of $response and returns an array representing the XML
|
||||
* This function returns an array with the following three elements:
|
||||
* * name - The root element name.
|
||||
* * value - The value for the root element.
|
||||
* * attributes - An array of attributes.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parseResponseAsXml(ResponseInterface $response):array {
|
||||
// rewind so that we can reparse it if it was parsed already
|
||||
$response->getBody()->rewind();
|
||||
$body = $response->getBody()->getContents();
|
||||
$parsedResponse = [];
|
||||
if ($body && \substr($body, 0, 1) === '<') {
|
||||
try {
|
||||
$reader = new Reader();
|
||||
$reader->xml($body);
|
||||
$parsedResponse = $reader->parse();
|
||||
} catch (LibXMLException $e) {
|
||||
// Sometimes the body can be a real page of HTML and text.
|
||||
// So it may not be a complete ordinary piece of XML.
|
||||
// The XML parse might fail with an exception message like:
|
||||
// Opening and ending tag mismatch: link line 31 and head.
|
||||
}
|
||||
}
|
||||
return $parsedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public static function getRequestTimeout(): int {
|
||||
$timeout = \getenv("REQUEST_TIMEOUT");
|
||||
return (int)$timeout ?: 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns json decoded body content of a json response as an object
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getJsonDecodedResponseBodyContent(ResponseInterface $response): mixed {
|
||||
return json_decode($response->getBody()->getContents(), null, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use TestHelpers\HttpRequestHelper;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A helper class for configuring oCIS server
|
||||
*/
|
||||
class OcisConfigHelper {
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param ?string $body
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function sendRequest(
|
||||
string $url,
|
||||
string $method,
|
||||
?string $body = ""
|
||||
): ResponseInterface {
|
||||
$client = HttpRequestHelper::createClient();
|
||||
$request = new Request(
|
||||
$method,
|
||||
$url,
|
||||
[],
|
||||
$body
|
||||
);
|
||||
|
||||
try {
|
||||
$response = $client->send($request);
|
||||
} catch (ConnectException $e) {
|
||||
throw new \Error("Cannot connect to the ociswrapper at the moment, make sure that ociswrapper is running before proceeding with the test run.\n" . $e->getMessage());
|
||||
} catch (GuzzleException $ex) {
|
||||
$response = $ex->getResponse();
|
||||
|
||||
if ($response === null) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getWrapperUrl(): string {
|
||||
$url = \getenv("OCIS_WRAPPER_URL");
|
||||
if ($url === false) {
|
||||
$url = "http://localhost:5200";
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $envs
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function reConfigureOcis(array $envs): ResponseInterface {
|
||||
$url = self::getWrapperUrl() . "/config";
|
||||
return self::sendRequest($url, "PUT", \json_encode($envs));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function rollbackOcis(): ResponseInterface {
|
||||
$url = self::getWrapperUrl() . "/rollback";
|
||||
return self::sendRequest($url, "DELETE");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function stopOcis(): ResponseInterface {
|
||||
$url = self::getWrapperUrl() . "/stop";
|
||||
return self::sendRequest($url, "POST");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function startOcis(): ResponseInterface {
|
||||
$url = self::getWrapperUrl() . "/start";
|
||||
return self::sendRequest($url, "POST");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2020 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
/**
|
||||
* Class OcisHelper
|
||||
*
|
||||
* Helper functions that are needed to run tests on OCIS
|
||||
*
|
||||
* @package TestHelpers
|
||||
*/
|
||||
class OcisHelper {
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTestingOnReva():bool {
|
||||
return (\getenv("TEST_REVA") === "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTestingParallelDeployment(): bool {
|
||||
return (\getenv("TEST_PARALLEL_DEPLOYMENT") === "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|string false if no command given or the command as string
|
||||
*/
|
||||
public static function getDeleteUserDataCommand() {
|
||||
$cmd = \getenv("DELETE_USER_DATA_CMD");
|
||||
if ($cmd === false || \trim($cmd) === "") {
|
||||
return false;
|
||||
}
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getStorageDriver():string {
|
||||
$storageDriver = (\getenv("STORAGE_DRIVER"));
|
||||
if ($storageDriver === false) {
|
||||
return "OWNCLOUD";
|
||||
}
|
||||
$storageDriver = \strtoupper($storageDriver);
|
||||
if ($storageDriver !== "OCIS" && $storageDriver !== "EOS" && $storageDriver !== "OWNCLOUD" && $storageDriver !== "S3NG" && $storageDriver !== "POSIX") {
|
||||
throw new Exception(
|
||||
"Invalid storage driver. " .
|
||||
"STORAGE_DRIVER must be OCIS|EOS|OWNCLOUD|S3NG|POSIX"
|
||||
);
|
||||
}
|
||||
return $storageDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $user
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function deleteRevaUserData(?string $user = ""):void {
|
||||
$deleteCmd = self::getDeleteUserDataCommand();
|
||||
if ($deleteCmd === false) {
|
||||
if (self::getStorageDriver() === "OWNCLOUD") {
|
||||
self::recurseRmdir(self::getOcisRevaDataRoot() . $user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (self::getStorageDriver() === "EOS") {
|
||||
$deleteCmd = \str_replace(
|
||||
"%s",
|
||||
$user[0] . '/' . $user,
|
||||
$deleteCmd
|
||||
);
|
||||
} else {
|
||||
$deleteCmd = \sprintf($deleteCmd, $user);
|
||||
}
|
||||
\exec($deleteCmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for Recursive Copy of file/folder
|
||||
* For more info check this out https://gist.github.com/gserrano/4c9648ec9eb293b9377b
|
||||
*
|
||||
* @param string|null $source
|
||||
* @param string|null $destination
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function recurseCopy(?string $source, ?string $destination):void {
|
||||
$dir = \opendir($source);
|
||||
@\mkdir($destination);
|
||||
while (($file = \readdir($dir)) !== false) {
|
||||
if (($file != '.') && ($file != '..')) {
|
||||
if (\is_dir($source . '/' . $file)) {
|
||||
self::recurseCopy($source . '/' . $file, $destination . '/' . $file);
|
||||
} else {
|
||||
\copy($source . '/' . $file, $destination . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
\closedir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for Recursive Upload of file/folder
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $source
|
||||
* @param string|null $userId
|
||||
* @param string|null $password
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $destination
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function recurseUpload(
|
||||
?string $baseUrl,
|
||||
?string $source,
|
||||
?string $userId,
|
||||
?string $password,
|
||||
?string $xRequestId = '',
|
||||
?string $destination = ''
|
||||
):void {
|
||||
if ($destination !== '') {
|
||||
$response = WebDavHelper::makeDavRequest(
|
||||
$baseUrl,
|
||||
$userId,
|
||||
$password,
|
||||
"MKCOL",
|
||||
$destination,
|
||||
[],
|
||||
$xRequestId
|
||||
);
|
||||
if ($response->getStatusCode() !== 201) {
|
||||
throw new Exception("Could not create folder destination" . $response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
|
||||
$dir = \opendir($source);
|
||||
while (($file = \readdir($dir)) !== false) {
|
||||
if (($file != '.') && ($file != '..')) {
|
||||
$sourcePath = $source . '/' . $file;
|
||||
$destinationPath = $destination . '/' . $file;
|
||||
if (\is_dir($sourcePath)) {
|
||||
self::recurseUpload(
|
||||
$baseUrl,
|
||||
$sourcePath,
|
||||
$userId,
|
||||
$password,
|
||||
$xRequestId,
|
||||
$destinationPath
|
||||
);
|
||||
} else {
|
||||
$response = UploadHelper::upload(
|
||||
$baseUrl,
|
||||
$userId,
|
||||
$password,
|
||||
$sourcePath,
|
||||
$destinationPath,
|
||||
$xRequestId
|
||||
);
|
||||
$responseStatus = $response->getStatusCode();
|
||||
if ($responseStatus !== 201) {
|
||||
throw new Exception(
|
||||
"Could not upload skeleton file $sourcePath to $destinationPath for user '$userId' status '$responseStatus' response body: '"
|
||||
. $response->getBody()->getContents() . "'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
\closedir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public static function getLdapPort():int {
|
||||
$port = \getenv("REVA_LDAP_PORT");
|
||||
return $port ? (int)$port : 636;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function useSsl():bool {
|
||||
$useSsl = \getenv("REVA_LDAP_USESSL");
|
||||
if ($useSsl === false) {
|
||||
return (self::getLdapPort() === 636);
|
||||
} else {
|
||||
return $useSsl === "true";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getBaseDN():string {
|
||||
$dn = \getenv("REVA_LDAP_BASE_DN");
|
||||
return $dn ?: "dc=owncloud,dc=com";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getGroupsOU():string {
|
||||
$ou = \getenv("REVA_LDAP_GROUPS_OU");
|
||||
return $ou ?: "TestGroups";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getUsersOU():string {
|
||||
$ou = \getenv("REVA_LDAP_USERS_OU");
|
||||
return $ou ?: "TestUsers";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getGroupSchema():string {
|
||||
$schema = \getenv("REVA_LDAP_GROUP_SCHEMA");
|
||||
return $schema ?: "rfc2307";
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getHostname():string {
|
||||
$hostname = \getenv("REVA_LDAP_HOSTNAME");
|
||||
return $hostname ?: "localhost";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getBindDN():string {
|
||||
$dn = \getenv("REVA_LDAP_BIND_DN");
|
||||
return $dn ?: "cn=admin,dc=owncloud,dc=com";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function getBindPassword():string {
|
||||
$pw = \getenv("REVA_LDAP_BIND_PASSWORD");
|
||||
return $pw ?: "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getOcisRevaDataRoot():string {
|
||||
$root = \getenv("OCIS_REVA_DATA_ROOT");
|
||||
if ($root === false || $root === "") {
|
||||
$root = "/var/tmp/ocis/owncloud/";
|
||||
}
|
||||
if (!\file_exists($root)) {
|
||||
echo "WARNING: reva data root folder ($root) does not exist\n";
|
||||
}
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $dir
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function recurseRmdir(?string $dir):bool {
|
||||
if (\file_exists($dir) === true) {
|
||||
$files = \array_diff(\scandir($dir), ['.', '..']);
|
||||
foreach ($files as $file) {
|
||||
if (\is_dir("$dir/$file")) {
|
||||
self::recurseRmdir("$dir/$file");
|
||||
} else {
|
||||
\unlink("$dir/$file");
|
||||
}
|
||||
}
|
||||
return \rmdir($dir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* On Eos storage backend when the user data is cleared after test run
|
||||
* Running another test immediately fails. So Send this request to create user home directory
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $xRequestId
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function createEOSStorageHome(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $xRequestId = ''
|
||||
):void {
|
||||
HttpRequestHelper::get(
|
||||
$baseUrl . "/ocs/v2.php/apps/notifications/api/v1/notifications",
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Viktor Scharf <scharf.vi@gmail.com>
|
||||
* @copyright Copyright (c) 2024 Viktor Scharf <scharf.vi@gmail.com>
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* A helper class for managing federation server requests
|
||||
*/
|
||||
class OcmHelper {
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private static function getRequestHeaders(): array {
|
||||
return [
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getFullUrl(string $baseUrl, string $path): string {
|
||||
$fullUrl = $baseUrl;
|
||||
if (\substr($fullUrl, -1) !== '/') {
|
||||
$fullUrl .= '/';
|
||||
}
|
||||
$fullUrl .= 'sciencemesh/' . $path;
|
||||
return $fullUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string|null $email
|
||||
* @param string|null $description
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function createInvitation(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $user,
|
||||
string $password,
|
||||
?string $email = null,
|
||||
?string $description = null
|
||||
): ResponseInterface {
|
||||
$body['params'] = [
|
||||
"description" => $description,
|
||||
"recipient" => $email
|
||||
];
|
||||
$url = self::getFullUrl($baseUrl, 'generate-invite');
|
||||
return HttpRequestHelper::post(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders(),
|
||||
\json_encode($body)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $token
|
||||
* @param string $providerDomain
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function acceptInvitation(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $user,
|
||||
string $password,
|
||||
string $token,
|
||||
string $providerDomain
|
||||
): ResponseInterface {
|
||||
$body = [
|
||||
"token" => $token,
|
||||
"providerDomain" => $providerDomain
|
||||
];
|
||||
$url = self::getFullUrl($baseUrl, 'accept-invite');
|
||||
return HttpRequestHelper::post(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders(),
|
||||
\json_encode($body)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function findAcceptedUsers(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $user,
|
||||
string $password
|
||||
): ResponseInterface {
|
||||
$url = self::getFullUrl($baseUrl, 'find-accepted-users');
|
||||
return HttpRequestHelper::get(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $xRequestId
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function listInvite(
|
||||
string $baseUrl,
|
||||
string $xRequestId,
|
||||
string $user,
|
||||
string $password
|
||||
): ResponseInterface {
|
||||
$url = self::getFullUrl($baseUrl, 'list-invite');
|
||||
return HttpRequestHelper::get(
|
||||
$url,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
self::getRequestHeaders()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Helper to make requests to the OCS API
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class OcsApiHelper {
|
||||
/**
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user if set to null no authentication header will be sent
|
||||
* @param string|null $password
|
||||
* @param string|null $method HTTP Method
|
||||
* @param string|null $path
|
||||
* @param string|null $xRequestId
|
||||
* @param mixed $body array of key, value pairs e.g ['value' => 'yes']
|
||||
* @param int|null $ocsApiVersion (1|2) default 2
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function sendRequest(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $method,
|
||||
?string $path,
|
||||
?string $xRequestId = '',
|
||||
$body = [],
|
||||
?int $ocsApiVersion = 2,
|
||||
?array $headers = []
|
||||
):ResponseInterface {
|
||||
$fullUrl = $baseUrl;
|
||||
if (\substr($fullUrl, -1) !== '/') {
|
||||
$fullUrl .= '/';
|
||||
}
|
||||
$fullUrl .= "ocs/v$ocsApiVersion.php" . $path;
|
||||
$headers['OCS-APIREQUEST'] = true;
|
||||
return HttpRequestHelper::sendRequest($fullUrl, $xRequestId, $method, $user, $password, $headers, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $method HTTP Method
|
||||
* @param string|null $path
|
||||
* @param string|null $xRequestId
|
||||
* @param mixed $body array of key, value pairs e.g ['value' => 'yes']
|
||||
* @param int|null $ocsApiVersion (1|2) default 2
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return RequestInterface
|
||||
*/
|
||||
public static function createOcsRequest(
|
||||
?string $baseUrl,
|
||||
?string $method,
|
||||
?string $path,
|
||||
?string $xRequestId = '',
|
||||
$body = [],
|
||||
?int $ocsApiVersion = 2,
|
||||
?array $headers = []
|
||||
):RequestInterface {
|
||||
$fullUrl = $baseUrl;
|
||||
if (\substr($fullUrl, -1) !== '/') {
|
||||
$fullUrl .= '/';
|
||||
}
|
||||
$fullUrl .= "ocs/v$ocsApiVersion.php" . $path;
|
||||
return HttpRequestHelper::createRequest(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$method,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<?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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use TestHelpers\HttpRequestHelper;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
/**
|
||||
* A helper class for ocis settings
|
||||
*/
|
||||
class SettingsHelper {
|
||||
private static string $settingsEndpoint = '/api/v0/settings/';
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function buildFullUrl(string $baseUrl, string $path): string {
|
||||
return $baseUrl . self::$settingsEndpoint . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getBundlesList(string $baseUrl, string $user, string $password, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "bundles-list");
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
"{}"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $bundleName
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getBundleByName(string $baseUrl, string $user, string $password, string $bundleName, string $xRequestId): array {
|
||||
$response = self::getBundlesList($baseUrl, $user, $password, $xRequestId);
|
||||
Assert::assertEquals(201, $response->getStatusCode(), "Failed to get bundles list");
|
||||
|
||||
$bundlesList = HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
||||
foreach ($bundlesList->bundles as $value) {
|
||||
if ($value->displayName === $bundleName) {
|
||||
return json_decode(json_encode($value), true);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getRolesList(string $baseUrl, string $user, string $password, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "roles-list");
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
"{}"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $assigneeId
|
||||
* @param string $roleId
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function assignRoleToUser(string $baseUrl, string $user, string $password, string $assigneeId, string $roleId, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "assignments-add");
|
||||
$body = json_encode(["account_uuid" => $assigneeId, "role_id" => $roleId], JSON_THROW_ON_ERROR);
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $userId
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getAssignmentsList(string $baseUrl, string $user, string $password, string $userId, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "assignments-list");
|
||||
$body = json_encode(["account_uuid" => $userId], JSON_THROW_ON_ERROR);
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getValuesList(string $baseUrl, string $user, string $password, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "values-list");
|
||||
$body = json_encode(["account_uuid" => "me"], JSON_THROW_ON_ERROR);
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getAutoAcceptSharesSettingValue(string $baseUrl, string $user, string $password, string $xRequestId): bool {
|
||||
$response = self::getValuesList($baseUrl, $user, $password, $xRequestId);
|
||||
Assert::assertEquals(201, $response->getStatusCode(), "Failed to get values list");
|
||||
|
||||
$valuesList = HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
||||
|
||||
if (empty($valuesList)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($valuesList->values as $value) {
|
||||
if ($value->identifier->setting === "auto-accept-shares") {
|
||||
return $value->value->boolValue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getLanguageSettingValue(string $baseUrl, string $user, string $password, string $xRequestId): string {
|
||||
$response = self::getValuesList($baseUrl, $user, $password, $xRequestId);
|
||||
Assert::assertEquals(201, $response->getStatusCode(), "Failed to get values list");
|
||||
|
||||
$valuesList = HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
||||
|
||||
// if no language is set, the request body is empty return English as the default language
|
||||
if (empty($valuesList)) {
|
||||
return "en";
|
||||
}
|
||||
foreach ($valuesList->values as $value) {
|
||||
if ($value->identifier->setting === "language") {
|
||||
return $value->value->listValue->values[0]->stringValue;
|
||||
}
|
||||
}
|
||||
// if a language setting was still not found, return English
|
||||
return "en";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $body
|
||||
* @param string $xRequestId
|
||||
* @param array $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function updateSettings(string $baseUrl, string $user, string $password, string $body, string $xRequestId, array $headers = []): ResponseInterface {
|
||||
$fullUrl = self::buildFullUrl($baseUrl, "values-save");
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use Behat\Testwork\Hook\Scope\HookScope;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Exception;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Helper to set up UI / Integration tests
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class SetupHelper extends \PHPUnit\Framework\Assert {
|
||||
private static ?string $baseUrl = null;
|
||||
private static ?string $adminUsername = null;
|
||||
private static ?string $adminPassword = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param HookScope $scope
|
||||
*
|
||||
* @return array of suite context parameters
|
||||
*/
|
||||
public static function getSuiteParameters(HookScope $scope):array {
|
||||
return $scope->getEnvironment()->getSuite()
|
||||
->getSettings() ['context'] ['parameters'];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $ocPath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(
|
||||
?string $adminUsername,
|
||||
?string $adminPassword,
|
||||
?string $baseUrl,
|
||||
?string $ocPath
|
||||
): void {
|
||||
foreach (\func_get_args() as $variableToCheck) {
|
||||
if (!\is_string($variableToCheck)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"mandatory argument missing or wrong type ($variableToCheck => "
|
||||
. \gettype($variableToCheck) . ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
self::$adminUsername = $adminUsername;
|
||||
self::$adminPassword = $adminPassword;
|
||||
self::$baseUrl = \rtrim($baseUrl, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSysInfo(
|
||||
?string $baseUrl,
|
||||
?string $adminUsername,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = ''
|
||||
):SimpleXMLElement {
|
||||
$result = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
"GET",
|
||||
"/apps/testing/api/v1/sysinfo",
|
||||
$xRequestId
|
||||
);
|
||||
if ($result->getStatusCode() !== 200) {
|
||||
throw new \Exception(
|
||||
"could not get sysinfo " . $result->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
return HttpRequestHelper::getResponseXml($result, __METHOD__)->data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getServerRoot(
|
||||
?string $baseUrl,
|
||||
?string $adminUsername,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = ''
|
||||
):string {
|
||||
$sysInfo = self::getSysInfo(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
$xRequestId
|
||||
);
|
||||
// server_root is a SimpleXMLElement object that "wraps" a string.
|
||||
/// We want the bare string.
|
||||
return (string) $sysInfo->server_root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $callerName
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function checkAdminUsername(?string $adminUsername, ?string $callerName):?string {
|
||||
if (self::$adminUsername === null
|
||||
&& $adminUsername === null
|
||||
) {
|
||||
throw new Exception(
|
||||
"$callerName called without adminUsername - pass the username or call SetupHelper::init"
|
||||
);
|
||||
}
|
||||
if ($adminUsername === null) {
|
||||
$adminUsername = self::$adminUsername;
|
||||
}
|
||||
return $adminUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $callerName
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function checkAdminPassword(?string $adminPassword, ?string $callerName):string {
|
||||
if (self::$adminPassword === null
|
||||
&& $adminPassword === null
|
||||
) {
|
||||
throw new Exception(
|
||||
"$callerName called without adminPassword - pass the password or call SetupHelper::init"
|
||||
);
|
||||
}
|
||||
if ($adminPassword === null) {
|
||||
$adminPassword = self::$adminPassword;
|
||||
}
|
||||
return $adminPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $callerName
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function checkBaseUrl(?string $baseUrl, ?string $callerName):?string {
|
||||
if (self::$baseUrl === null
|
||||
&& $baseUrl === null
|
||||
) {
|
||||
throw new Exception(
|
||||
"$callerName called without baseUrl - pass the baseUrl or call SetupHelper::init"
|
||||
);
|
||||
}
|
||||
if ($baseUrl === null) {
|
||||
$baseUrl = self::$baseUrl;
|
||||
}
|
||||
return $baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $dirPathFromServerRoot e.g. 'apps2/myapp/appinfo'
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function mkDirOnServer(
|
||||
?string $dirPathFromServerRoot,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):void {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "mkDirOnServer");
|
||||
$adminUsername = self::checkAdminUsername($adminUsername, "mkDirOnServer");
|
||||
$adminPassword = self::checkAdminPassword($adminPassword, "mkDirOnServer");
|
||||
$result = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
"POST",
|
||||
"/apps/testing/api/v1/dir",
|
||||
$xRequestId,
|
||||
['dir' => $dirPathFromServerRoot]
|
||||
);
|
||||
|
||||
if ($result->getStatusCode() !== 200) {
|
||||
throw new \Exception(
|
||||
"could not create directory $dirPathFromServerRoot " . $result->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $dirPathFromServerRoot e.g. 'apps2/myapp/appinfo'
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function rmDirOnServer(
|
||||
?string $dirPathFromServerRoot,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):void {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "rmDirOnServer");
|
||||
$adminUsername = self::checkAdminUsername($adminUsername, "rmDirOnServer");
|
||||
$adminPassword = self::checkAdminPassword($adminPassword, "rmDirOnServer");
|
||||
$result = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
"DELETE",
|
||||
"/apps/testing/api/v1/dir",
|
||||
$xRequestId,
|
||||
['dir' => $dirPathFromServerRoot]
|
||||
);
|
||||
|
||||
if ($result->getStatusCode() !== 200) {
|
||||
throw new \Exception(
|
||||
"could not delete directory $dirPathFromServerRoot " . $result->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $filePathFromServerRoot e.g. 'app2/myapp/appinfo/info.xml'
|
||||
* @param string|null $content
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function createFileOnServer(
|
||||
?string $filePathFromServerRoot,
|
||||
?string $content,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):void {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "createFileOnServer");
|
||||
$adminUsername = self::checkAdminUsername($adminUsername, "createFileOnServer");
|
||||
$adminPassword = self::checkAdminPassword($adminPassword, "createFileOnServer");
|
||||
$result = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
"POST",
|
||||
"/apps/testing/api/v1/file",
|
||||
$xRequestId,
|
||||
[
|
||||
'file' => $filePathFromServerRoot,
|
||||
'content' => $content
|
||||
]
|
||||
);
|
||||
|
||||
if ($result->getStatusCode() !== 200) {
|
||||
throw new \Exception(
|
||||
"could not create file $filePathFromServerRoot " . $result->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $filePathFromServerRoot e.g. 'app2/myapp/appinfo/info.xml'
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function deleteFileOnServer(
|
||||
?string $filePathFromServerRoot,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):void {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "deleteFileOnServer");
|
||||
$adminUsername = self::checkAdminUsername($adminUsername, "deleteFileOnServer");
|
||||
$adminPassword = self::checkAdminPassword($adminPassword, "deleteFileOnServer");
|
||||
$result = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
"DELETE",
|
||||
"/apps/testing/api/v1/file",
|
||||
$xRequestId,
|
||||
[
|
||||
'file' => $filePathFromServerRoot
|
||||
]
|
||||
);
|
||||
|
||||
if ($result->getStatusCode() !== 200) {
|
||||
throw new \Exception(
|
||||
"could not delete file $filePathFromServerRoot " . $result->getReasonPhrase()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $fileInCore e.g. 'app2/myapp/appinfo/info.xml'
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function readFileFromServer(
|
||||
?string $fileInCore,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):string {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "readFile");
|
||||
$adminUsername = self::checkAdminUsername(
|
||||
$adminUsername,
|
||||
"readFile"
|
||||
);
|
||||
$adminPassword = self::checkAdminPassword(
|
||||
$adminPassword,
|
||||
"readFile"
|
||||
);
|
||||
|
||||
$response = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
'GET',
|
||||
"/apps/testing/api/v1/file?file=$fileInCore",
|
||||
$xRequestId
|
||||
);
|
||||
self::assertSame(
|
||||
200,
|
||||
$response->getStatusCode(),
|
||||
"Failed to read the file $fileInCore"
|
||||
);
|
||||
$localContent = HttpRequestHelper::getResponseXml($response, __METHOD__);
|
||||
$localContent = (string)$localContent->data->element->contentUrlEncoded;
|
||||
return \urldecode($localContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the content of a file in a skeleton folder
|
||||
*
|
||||
* @param string|null $fileInSkeletonFolder
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUsername
|
||||
* @param string|null $adminPassword
|
||||
*
|
||||
* @return string content of the file
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function readSkeletonFile(
|
||||
?string $fileInSkeletonFolder,
|
||||
?string $xRequestId = '',
|
||||
?string $baseUrl = null,
|
||||
?string $adminUsername = null,
|
||||
?string $adminPassword = null
|
||||
):string {
|
||||
$baseUrl = self::checkBaseUrl($baseUrl, "readSkeletonFile");
|
||||
$adminUsername = self::checkAdminUsername(
|
||||
$adminUsername,
|
||||
"readSkeletonFile"
|
||||
);
|
||||
$adminPassword = self::checkAdminPassword(
|
||||
$adminPassword,
|
||||
"readSkeletonFile"
|
||||
);
|
||||
|
||||
$fileInSkeletonFolder = \rawurlencode("/$fileInSkeletonFolder");
|
||||
$response = OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUsername,
|
||||
$adminPassword,
|
||||
'GET',
|
||||
"/apps/testing/api/v1/file?file=$fileInSkeletonFolder&absolute=true",
|
||||
$xRequestId
|
||||
);
|
||||
self::assertSame(
|
||||
200,
|
||||
$response->getStatusCode(),
|
||||
"Failed to read the file $fileInSkeletonFolder"
|
||||
);
|
||||
$localContent = HttpRequestHelper::getResponseXml($response, __METHOD__);
|
||||
$localContent = (string)$localContent->data->element->contentUrlEncoded;
|
||||
return \urldecode($localContent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* manage Shares via OCS API
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class SharingHelper {
|
||||
public const PERMISSION_TYPES = [
|
||||
'read' => 1,
|
||||
'update' => 2,
|
||||
'create' => 4,
|
||||
'delete' => 8,
|
||||
'invite' => 0
|
||||
];
|
||||
|
||||
public const SHARE_TYPES = [
|
||||
'user' => 0,
|
||||
'group' => 1,
|
||||
'public_link' => 3,
|
||||
'federated' => 6,
|
||||
];
|
||||
|
||||
public const SHARE_STATES = [
|
||||
'accepted' => 0,
|
||||
'pending' => 1,
|
||||
'rejected' => 2,
|
||||
'declined' => 2, // declined is a synonym for rejected.
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $baseUrl baseURL of the ownCloud installation without /ocs.
|
||||
* @param string $user user that creates the share.
|
||||
* @param string $password password of the user that creates the share.
|
||||
* @param string $path The path to the file or folder which should be shared.
|
||||
* @param string|int $shareType The type of the share. This can be one of:
|
||||
* 0 = user, 1 = group, 3 = public (link),
|
||||
* 6 = federated (cloud share).
|
||||
* Pass either the number or the keyword.
|
||||
* @param string $xRequestId
|
||||
* @param string|null $shareWith The user or group id with which the file should
|
||||
* be shared.
|
||||
* @param boolean|null $publicUpload Whether to allow public upload to a public
|
||||
* shared folder.
|
||||
* @param string|null $sharePassword The password to protect the public link
|
||||
* share with.
|
||||
* @param string|int|string[]|int[]|null $permissions The permissions to set on the share.
|
||||
* 1 = read; 2 = update; 4 = create;
|
||||
* 8 = delete; 16 = share
|
||||
* (default: 31, for public shares: 1)
|
||||
* Pass either the (total) number or array of numbers,
|
||||
* or any of the above keywords or array of keywords.
|
||||
* @param string|null $linkName A (human-readable) name for the share,
|
||||
* which can be up to 64 characters in length.
|
||||
* @param string|null $expireDate An expire date for public link shares.
|
||||
* This argument expects a date string
|
||||
* in the format 'YYYY-MM-DD'.
|
||||
* @param string|null $space_ref
|
||||
* @param int $ocsApiVersion
|
||||
* @param int $sharingApiVersion
|
||||
* @param string $sharingApp
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws InvalidArgumentException|GuzzleException
|
||||
*/
|
||||
public static function createShare(
|
||||
string $baseUrl,
|
||||
string $user,
|
||||
string $password,
|
||||
string $path,
|
||||
$shareType,
|
||||
string $xRequestId = '',
|
||||
?string $shareWith = null,
|
||||
?bool $publicUpload = false,
|
||||
string $sharePassword = null,
|
||||
$permissions = null,
|
||||
?string $linkName = null,
|
||||
?string $expireDate = null,
|
||||
?string $space_ref = null,
|
||||
int $ocsApiVersion = 1,
|
||||
int $sharingApiVersion = 1,
|
||||
string $sharingApp = 'files_sharing'
|
||||
): ResponseInterface {
|
||||
$fd = [];
|
||||
foreach ([$path, $baseUrl, $user, $password] as $variableToCheck) {
|
||||
if (!\is_string($variableToCheck)) {
|
||||
throw new InvalidArgumentException(
|
||||
"mandatory argument missing or wrong type ($variableToCheck => "
|
||||
. \gettype($variableToCheck) . ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($permissions !== null) {
|
||||
$fd['permissions'] = self::getPermissionSum($permissions);
|
||||
} elseif ($shareType !== "public_link") {
|
||||
// sharing without permissions should automatically set the permission to 15 for share and 1 for public link
|
||||
$fd['permissions'] = 15;
|
||||
}
|
||||
|
||||
if (!\in_array($ocsApiVersion, [1, 2], true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"invalid ocsApiVersion ($ocsApiVersion)"
|
||||
);
|
||||
}
|
||||
if (!\in_array($sharingApiVersion, [1, 2], true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"invalid sharingApiVersion ($sharingApiVersion)"
|
||||
);
|
||||
}
|
||||
|
||||
$fullUrl = $baseUrl;
|
||||
if (\substr($fullUrl, -1) !== '/') {
|
||||
$fullUrl .= '/';
|
||||
}
|
||||
$fullUrl .= "ocs/v$ocsApiVersion.php/apps/$sharingApp/api/v$sharingApiVersion/shares";
|
||||
|
||||
$fd['path'] = $path;
|
||||
$fd['shareType'] = self::getShareType($shareType);
|
||||
|
||||
if ($shareWith !== null) {
|
||||
$fd['shareWith'] = $shareWith;
|
||||
}
|
||||
if ($publicUpload !== null) {
|
||||
$fd['publicUpload'] = $publicUpload;
|
||||
}
|
||||
if ($sharePassword !== null) {
|
||||
$fd['password'] = $sharePassword;
|
||||
}
|
||||
if ($linkName !== null) {
|
||||
$fd['name'] = $linkName;
|
||||
}
|
||||
if ($expireDate !== null) {
|
||||
$fd['expireDate'] = $expireDate;
|
||||
}
|
||||
if ($space_ref !== null) {
|
||||
$fd['space_ref'] = $space_ref;
|
||||
}
|
||||
$headers = ['OCS-APIREQUEST' => 'true'];
|
||||
return HttpRequestHelper::post(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$fd
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the permission sum (int) from the given permissions.
|
||||
* Permissions can be passed in as int, string or array of int or string
|
||||
* 'read' => 1
|
||||
* 'update' => 2
|
||||
* 'create' => 4
|
||||
* 'delete' => 8
|
||||
* 'share' => 16
|
||||
* 'invite' => 0
|
||||
*
|
||||
* @param string[]|string|int|int[] $permissions
|
||||
*
|
||||
* @return int
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
*/
|
||||
public static function getPermissionSum($permissions):int {
|
||||
if (\is_numeric($permissions)) {
|
||||
// Allow any permission number so that test scenarios can
|
||||
// specifically test invalid permission values
|
||||
return (int) $permissions;
|
||||
}
|
||||
if (!\is_array($permissions)) {
|
||||
$permissions = [$permissions];
|
||||
}
|
||||
$permissionSum = 0;
|
||||
foreach ($permissions as $permission) {
|
||||
if (\array_key_exists($permission, self::PERMISSION_TYPES)) {
|
||||
$permissionSum += self::PERMISSION_TYPES[$permission];
|
||||
} elseif (\in_array($permission, self::PERMISSION_TYPES, true)) {
|
||||
$permissionSum += $permission;
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"invalid permission type ($permission)"
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($permissionSum < 0 || $permissionSum > 31) {
|
||||
throw new InvalidArgumentException(
|
||||
"invalid permission total ($permissionSum)"
|
||||
);
|
||||
}
|
||||
return $permissionSum;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the share type number corresponding to the share type keyword
|
||||
*
|
||||
* @param string|int $shareType a keyword from SHARE_TYPES or a share type number
|
||||
*
|
||||
* @return int
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
*/
|
||||
public static function getShareType($shareType):int {
|
||||
if (\array_key_exists($shareType, self::SHARE_TYPES)) {
|
||||
return self::SHARE_TYPES[$shareType];
|
||||
} else {
|
||||
if (\ctype_digit($shareType)) {
|
||||
$shareType = (int) $shareType;
|
||||
}
|
||||
$key = \array_search($shareType, self::SHARE_TYPES, true);
|
||||
if ($key !== false) {
|
||||
return self::SHARE_TYPES[$key];
|
||||
}
|
||||
throw new InvalidArgumentException(
|
||||
"invalid share type ($shareType)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param SimpleXMLElement $responseXmlObject
|
||||
* @param string $errorMessage
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*
|
||||
*/
|
||||
public static function getLastShareIdFromResponse(
|
||||
SimpleXMLElement $responseXmlObject,
|
||||
string $errorMessage = "cannot find share id in response"
|
||||
):string {
|
||||
$xmlPart = $responseXmlObject->xpath("//data/element[last()]/id");
|
||||
|
||||
if (!\is_array($xmlPart) || (\count($xmlPart) === 0)) {
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
return $xmlPart[0]->__toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Sagar Gurung <sagar@jankatitech.com>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class SpaceNotFoundException
|
||||
* Exception when space id for a user is not found
|
||||
*/
|
||||
class SpaceNotFoundException extends Exception {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Talank Baral <talank@jankaritech.com>
|
||||
* @copyright Copyright (c) 2021 Talank Baral talank@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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
/**
|
||||
* Class TranslationHelper
|
||||
*
|
||||
* Helper functions that are needed to run tests in different languages
|
||||
*
|
||||
* @package TestHelpers
|
||||
*/
|
||||
class TranslationHelper {
|
||||
/**
|
||||
* @param string|null $language
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getLanguage(?string $language): ?string {
|
||||
if (!isset($language)) {
|
||||
if (\getenv('OC_LANGUAGE') !== false) {
|
||||
$language = \getenv('OC_LANGUAGE');
|
||||
}
|
||||
}
|
||||
return $language;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Helper for Uploads
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class UploadHelper extends Assert {
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl URL of owncloud
|
||||
* e.g. http://localhost:8080
|
||||
* should include the subfolder
|
||||
* if owncloud runs in a subfolder
|
||||
* e.g. http://localhost:8080/owncloud-core
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $source
|
||||
* @param string|null $destination
|
||||
* @param string|null $xRequestId
|
||||
* @param array|null $headers
|
||||
* @param int|null $davPathVersionToUse (1|2)
|
||||
* @param int|null $chunkingVersion (1|2|null)
|
||||
* if set to null chunking will not be used
|
||||
* @param int|null $noOfChunks how many chunks to upload
|
||||
* @param bool|null $isGivenStep
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function upload(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $source,
|
||||
?string $destination,
|
||||
?string $xRequestId = '',
|
||||
?array $headers = [],
|
||||
?int $davPathVersionToUse = 1,
|
||||
?int $chunkingVersion = null,
|
||||
?int $noOfChunks = 1,
|
||||
?bool $isGivenStep = false
|
||||
): ResponseInterface {
|
||||
//simple upload with no chunking
|
||||
if ($chunkingVersion === null) {
|
||||
$data = \file_get_contents($source);
|
||||
return WebDavHelper::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PUT",
|
||||
$destination,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
$data,
|
||||
$davPathVersionToUse,
|
||||
"files",
|
||||
null,
|
||||
"basic",
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
[],
|
||||
null,
|
||||
$isGivenStep
|
||||
);
|
||||
} else {
|
||||
//prepare chunking
|
||||
$chunks = self::chunkFile($source, $noOfChunks);
|
||||
$chunkingId = 'chunking-' . \rand(1000, 9999);
|
||||
$v2ChunksDestination = '/uploads/' . $user . '/' . $chunkingId;
|
||||
}
|
||||
|
||||
$result = null;
|
||||
|
||||
//prepare chunking version specific stuff
|
||||
if ($chunkingVersion === 1) {
|
||||
$headers['OC-Chunked'] = '1';
|
||||
} elseif ($chunkingVersion === 2) {
|
||||
$result = WebDavHelper::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
'MKCOL',
|
||||
$v2ChunksDestination,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
null,
|
||||
$davPathVersionToUse,
|
||||
"uploads",
|
||||
null,
|
||||
"basic",
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
[],
|
||||
null,
|
||||
$isGivenStep
|
||||
);
|
||||
if ($result->getStatusCode() >= 400) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
//upload chunks
|
||||
foreach ($chunks as $index => $chunk) {
|
||||
if ($chunkingVersion === 1) {
|
||||
$filename = $destination . "-" . $chunkingId . "-" .
|
||||
\count($chunks) . '-' . $index;
|
||||
$davRequestType = "files";
|
||||
} else {
|
||||
// do chunking version 2
|
||||
$filename = $v2ChunksDestination . '/' . $index;
|
||||
$davRequestType = "uploads";
|
||||
}
|
||||
$result = WebDavHelper::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PUT",
|
||||
$filename,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
$chunk,
|
||||
$davPathVersionToUse,
|
||||
$davRequestType,
|
||||
null,
|
||||
"basic",
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
[],
|
||||
null,
|
||||
$isGivenStep
|
||||
);
|
||||
if ($result->getStatusCode() >= 400) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
//finish upload for new chunking
|
||||
if ($chunkingVersion === 2) {
|
||||
$source = $v2ChunksDestination . '/.file';
|
||||
$headers['Destination'] = $baseUrl . "/" .
|
||||
WebDavHelper::getDavPath($user, $davPathVersionToUse) .
|
||||
$destination;
|
||||
$result = WebDavHelper::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
'MOVE',
|
||||
$source,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
null,
|
||||
$davPathVersionToUse,
|
||||
"uploads",
|
||||
null,
|
||||
"basic",
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
[],
|
||||
null,
|
||||
$isGivenStep
|
||||
);
|
||||
if ($result->getStatusCode() >= 400) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
self::assertNotNull($result, __METHOD__ . " chunking version $chunkingVersion was requested but no upload was done.");
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the same file multiple times with different mechanisms.
|
||||
*
|
||||
* @param string|null $baseUrl URL of owncloud
|
||||
* @param string|null $user user who uploads
|
||||
* @param string|null $password
|
||||
* @param string|null $source source file path
|
||||
* @param string|null $destination destination path on the server
|
||||
* @param string|null $xRequestId
|
||||
* @param bool $overwriteMode when false creates separate files to test uploading brand-new files,
|
||||
* when true it just overwrites the same file over and over again with the same name
|
||||
* @param string|null $exceptChunkingType empty string or "old" or "new"
|
||||
*
|
||||
* @return array of ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function uploadWithAllMechanisms(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $source,
|
||||
?string $destination,
|
||||
?string $xRequestId = '',
|
||||
?bool $overwriteMode = false,
|
||||
?string $exceptChunkingType = ''
|
||||
):array {
|
||||
$responses = [];
|
||||
foreach ([1, 2] as $davPathVersion) {
|
||||
if ($davPathVersion === 1) {
|
||||
$davHuman = 'old';
|
||||
} else {
|
||||
$davHuman = 'new';
|
||||
}
|
||||
|
||||
switch ($exceptChunkingType) {
|
||||
case 'old':
|
||||
$exceptChunkingVersion = 1;
|
||||
break;
|
||||
case 'new':
|
||||
$exceptChunkingVersion = 2;
|
||||
break;
|
||||
default:
|
||||
$exceptChunkingVersion = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ([null, 1, 2] as $chunkingVersion) {
|
||||
if ($chunkingVersion === $exceptChunkingVersion) {
|
||||
continue;
|
||||
}
|
||||
$valid = WebDavHelper::isValidDavChunkingCombination(
|
||||
$davPathVersion,
|
||||
$chunkingVersion
|
||||
);
|
||||
if ($valid === false) {
|
||||
continue;
|
||||
}
|
||||
$finalDestination = $destination;
|
||||
if (!$overwriteMode && $chunkingVersion !== null) {
|
||||
$finalDestination .= "-{$davHuman}dav-{$davHuman}chunking";
|
||||
} elseif (!$overwriteMode && $chunkingVersion === null) {
|
||||
$finalDestination .= "-{$davHuman}dav-regular";
|
||||
}
|
||||
$responses[] = self::upload(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$source,
|
||||
$finalDestination,
|
||||
$xRequestId,
|
||||
[],
|
||||
$davPathVersion,
|
||||
$chunkingVersion,
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* cut the file in multiple chunks
|
||||
* returns an array of chunks with the content of the file
|
||||
*
|
||||
* @param string|null $file
|
||||
* @param int|null $noOfChunks
|
||||
*
|
||||
* @return array $string
|
||||
*/
|
||||
public static function chunkFile(?string $file, ?int $noOfChunks = 1):array {
|
||||
$size = \filesize($file);
|
||||
$chunkSize = \ceil($size / $noOfChunks);
|
||||
$chunks = [];
|
||||
$fp = \fopen($file, 'r');
|
||||
while (!\feof($fp) && \ftell($fp) < $size) {
|
||||
$chunks[] = \fread($fp, (int)$chunkSize);
|
||||
}
|
||||
\fclose($fp);
|
||||
if (\count($chunks) === 0) {
|
||||
// chunk an empty file
|
||||
$chunks[] = '';
|
||||
}
|
||||
return $chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a File with a specific size
|
||||
*
|
||||
* @param string|null $name full path of the file to create
|
||||
* @param int|null $size
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function createFileSpecificSize(?string $name, ?int $size):void {
|
||||
if (\file_exists($name)) {
|
||||
\unlink($name);
|
||||
}
|
||||
$file = \fopen($name, 'w');
|
||||
\fseek($file, \max($size - 1, 0), SEEK_CUR);
|
||||
if ($size) {
|
||||
\fwrite($file, 'a'); // write a dummy char at SIZE position
|
||||
}
|
||||
\fclose($file);
|
||||
self::assertEquals(
|
||||
1,
|
||||
\file_exists($name)
|
||||
);
|
||||
self::assertEquals(
|
||||
$size,
|
||||
\filesize($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a File with a specific text content
|
||||
*
|
||||
* @param string|null $name full path of the file to create
|
||||
* @param string|null $text
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function createFileWithText(?string $name, ?string $text):void {
|
||||
$file = \fopen($name, 'w');
|
||||
\fwrite($file, $text);
|
||||
\fclose($file);
|
||||
self::assertEquals(
|
||||
1,
|
||||
\file_exists($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the path of a file from FilesForUpload directory
|
||||
*
|
||||
* @param string|null $name name of the file to upload
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getUploadFilesDir(?string $name):string {
|
||||
return \getenv("FILES_FOR_UPLOAD") . $name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Helper to administrate users (and groups) through the provisioning API
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class UserHelper {
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string $user
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param string $adminUser
|
||||
* @param string $adminPassword
|
||||
* @param string $xRequestId
|
||||
* @param int|null $ocsApiVersion
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function editUser(
|
||||
?string $baseUrl,
|
||||
string $user,
|
||||
string $key,
|
||||
string $value,
|
||||
string $adminUser,
|
||||
string $adminPassword,
|
||||
string $xRequestId = '',
|
||||
?int $ocsApiVersion = 2
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"PUT",
|
||||
"/cloud/users/" . $user,
|
||||
$xRequestId,
|
||||
["key" => $key, "value" => $value],
|
||||
$ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send batch requests to edit the user.
|
||||
* This will send multiple requests in parallel to the server which will be faster in comparison to waiting for each request to complete.
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param array|null $editData ['user' => '', 'key' => '', 'value' => '']
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $ocsApiVersion
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function editUserBatch(
|
||||
?string $baseUrl,
|
||||
?array $editData,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?int $ocsApiVersion = 2
|
||||
):array {
|
||||
$requests = [];
|
||||
$client = HttpRequestHelper::createClient(
|
||||
$adminUser,
|
||||
$adminPassword
|
||||
);
|
||||
|
||||
foreach ($editData as $data) {
|
||||
$path = "/cloud/users/" . $data['user'];
|
||||
$body = ["key" => $data['key'], 'value' => $data["value"]];
|
||||
// Create the OCS API requests and push them to an array.
|
||||
$requests[] = OcsApiHelper::createOcsRequest(
|
||||
$baseUrl,
|
||||
'PUT',
|
||||
$path,
|
||||
$xRequestId,
|
||||
$body
|
||||
);
|
||||
}
|
||||
// Send the array of requests at once in parallel.
|
||||
$results = HttpRequestHelper::sendBatchRequest($requests, $client);
|
||||
|
||||
foreach ($results as $e) {
|
||||
if ($e instanceof ClientException) {
|
||||
$httpStatusCode = $e->getResponse()->getStatusCode();
|
||||
$reasonPhrase = $e->getResponse()->getReasonPhrase();
|
||||
throw new Exception(
|
||||
"Unexpected failure when editing a user: HTTP status $httpStatusCode HTTP reason $reasonPhrase"
|
||||
);
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $userName
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $ocsApiVersion
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getUser(
|
||||
?string $baseUrl,
|
||||
?string $userName,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?int $ocsApiVersion = 2
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"GET",
|
||||
"/cloud/users/" . $userName,
|
||||
$xRequestId,
|
||||
[],
|
||||
$ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $userName
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $ocsApiVersion
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function deleteUser(
|
||||
?string $baseUrl,
|
||||
?string $userName,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?int $ocsApiVersion = 2
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"DELETE",
|
||||
"/cloud/users/" . $userName,
|
||||
$xRequestId,
|
||||
[],
|
||||
$ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $group
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $ocsApiVersion (1|2)
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function addUserToGroup(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $group,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?int $ocsApiVersion = 2
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"POST",
|
||||
"/cloud/users/" . $user . "/groups",
|
||||
$xRequestId,
|
||||
['groupid' => $group],
|
||||
$ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $group
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $ocsApiVersion (1|2)
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function removeUserFromGroup(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $group,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId,
|
||||
?int $ocsApiVersion = 2
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"DELETE",
|
||||
"/cloud/users/" . $user . "/groups",
|
||||
$xRequestId,
|
||||
['groupid' => $group],
|
||||
$ocsApiVersion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $search
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getGroups(
|
||||
?string $baseUrl,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?string $search =""
|
||||
):ResponseInterface {
|
||||
return OcsApiHelper::sendRequest(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
"GET",
|
||||
"/cloud/groups?search=" . $search,
|
||||
$xRequestId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $adminUser
|
||||
* @param string|null $adminPassword
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $search
|
||||
*
|
||||
* @return string[]
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getGroupsAsArray(
|
||||
?string $baseUrl,
|
||||
?string $adminUser,
|
||||
?string $adminPassword,
|
||||
?string $xRequestId = '',
|
||||
?string $search = ""
|
||||
):array {
|
||||
$result = self::getGroups(
|
||||
$baseUrl,
|
||||
$adminUser,
|
||||
$adminPassword,
|
||||
$xRequestId,
|
||||
$search
|
||||
);
|
||||
$groups = HttpRequestHelper::getResponseXml($result, __METHOD__)->xpath(".//groups")[0];
|
||||
$return = [];
|
||||
foreach ($groups as $group) {
|
||||
$return[] = $group->__toString();
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,972 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 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/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Helper to make WebDav Requests
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class WebDavHelper {
|
||||
public const DAV_VERSION_OLD = 1;
|
||||
public const DAV_VERSION_NEW = 2;
|
||||
public const DAV_VERSION_SPACES = 3;
|
||||
public static string $SPACE_ID_FROM_OCIS = '';
|
||||
|
||||
/**
|
||||
* @var array of users with their different space ids
|
||||
*/
|
||||
public static array $spacesIdRef = [];
|
||||
|
||||
/**
|
||||
* clear space id reference for user
|
||||
*
|
||||
* @param string|null $user
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function removeSpaceIdReferenceForUser(
|
||||
?string $user
|
||||
):void {
|
||||
if (\array_key_exists($user, self::$spacesIdRef)) {
|
||||
unset(self::$spacesIdRef[$user]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespaceString
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function parseNamespace(string $namespaceString): object {
|
||||
//calculate the namespace prefix and namespace
|
||||
$matches = [];
|
||||
\preg_match("/^(.*)='(.*)'$/", $namespaceString, $matches);
|
||||
return (object)["namespace" => $matches[2], "prefix" => $matches[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the id of a file
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getFileIdForPath(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
): string {
|
||||
$body
|
||||
= '<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:fileid />
|
||||
</d:prop>
|
||||
</d:propfind>';
|
||||
$response = self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPFIND",
|
||||
$path,
|
||||
null,
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse
|
||||
);
|
||||
\preg_match(
|
||||
'/\<oc:fileid\>([^\<]*)\<\/oc:fileid\>/',
|
||||
$response->getBody()->getContents(),
|
||||
$matches
|
||||
);
|
||||
|
||||
if (!isset($matches[1])) {
|
||||
throw new Exception("could not find fileId of $path");
|
||||
}
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns body for propfind
|
||||
*
|
||||
* @param array|null $properties
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getBodyForPropfind(?array $properties): string {
|
||||
$propertyBody = "";
|
||||
$extraNamespaces = "";
|
||||
foreach ($properties as $namespaceString => $property) {
|
||||
if (\is_int($namespaceString)) {
|
||||
//default namespace prefix if the property has no array key
|
||||
//also used if no prefix is given in the property value
|
||||
$namespacePrefix = null;
|
||||
} else {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$namespacePrefix = $ns->prefix;
|
||||
$extraNamespaces .= " xmlns:$namespacePrefix=\"$ns->namespace\" ";
|
||||
}
|
||||
//if a namespace prefix is given in the property value use that
|
||||
if (\strpos($property, ":") !== false) {
|
||||
$propertyParts = \explode(":", $property);
|
||||
$namespacePrefix = $propertyParts[0];
|
||||
$property = $propertyParts[1];
|
||||
}
|
||||
|
||||
if ($namespacePrefix) {
|
||||
$propertyBody .= "<$namespacePrefix:$property/>";
|
||||
} else {
|
||||
$propertyBody .= "<$property/>";
|
||||
}
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propfind
|
||||
xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\"
|
||||
xmlns:ocs=\"http://open-collaboration-services.org/ns\"
|
||||
$extraNamespaces>
|
||||
<d:prop>$propertyBody</d:prop>
|
||||
</d:propfind>";
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a PROPFIND request
|
||||
* with these registered namespaces:
|
||||
* | prefix | namespace |
|
||||
* | d | DAV: |
|
||||
* | oc | http://owncloud.org/ns |
|
||||
* | ocs | http://open-collaboration-services.org/ns |
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string[] $properties
|
||||
* string can contain namespace prefix,
|
||||
* if no prefix is given 'd:' is used as prefix
|
||||
* if an associative array is used, then the key will be used as namespace
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $folderDepth
|
||||
* @param string|null $type
|
||||
* @param int|null $davPathVersionToUse
|
||||
* @param string|null $doDavRequestAsUser
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function propfind(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?array $properties,
|
||||
?string $xRequestId = '',
|
||||
?string $folderDepth = '1',
|
||||
?string $type = "files",
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW,
|
||||
?string $doDavRequestAsUser = null,
|
||||
?array $headers = []
|
||||
):ResponseInterface {
|
||||
$body = self::getBodyForPropfind($properties);
|
||||
$folderDepth = (string) $folderDepth;
|
||||
if ($folderDepth !== '0' && $folderDepth !== '1' && $folderDepth !== 'infinity') {
|
||||
if ($folderDepth !== '') {
|
||||
throw new InvalidArgumentException('Invalid depth value ' . $folderDepth);
|
||||
}
|
||||
$folderDepth = '1'; // oCIS server's default value
|
||||
}
|
||||
$headers['Depth'] = $folderDepth;
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPFIND",
|
||||
$path,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse,
|
||||
$type,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
[],
|
||||
$doDavRequestAsUser
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $propertyName
|
||||
* @param string|null $propertyValue
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $namespaceString string containing prefix and namespace
|
||||
* e.g "x1='http://whatever.org/ns'"
|
||||
* @param int|null $davPathVersionToUse
|
||||
* @param string|null $type
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function proppatch(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $propertyName,
|
||||
?string $propertyValue,
|
||||
?string $xRequestId = '',
|
||||
?string $namespaceString = null,
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW,
|
||||
?string $type="files"
|
||||
):ResponseInterface {
|
||||
if ($namespaceString !== null) {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$propertyBody = "<$ns->prefix:$propertyName" .
|
||||
" xmlns:$ns->prefix=\"$ns->namespace\">" .
|
||||
"$propertyValue" .
|
||||
"</$ns->prefix:$propertyName>";
|
||||
} else {
|
||||
$propertyBody = "<$propertyName>$propertyValue</$propertyName>";
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propertyupdate xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\">
|
||||
<d:set>
|
||||
<d:prop>$propertyBody</d:prop>
|
||||
</d:set>
|
||||
</d:propertyupdate>";
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPPATCH",
|
||||
$path,
|
||||
[],
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse,
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets namespace-prefix, namespace url and propName from provided namespaceString or property
|
||||
* or otherwise use default
|
||||
*
|
||||
* @param string $namespaceString
|
||||
* @param string $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPropertyWithNamespaceInfo(string $namespaceString = "", string $property = ""):array {
|
||||
$namespace = "";
|
||||
$namespacePrefix = "";
|
||||
if (\is_int($namespaceString)) {
|
||||
//default namespace prefix if the property has no array key
|
||||
//also used if no prefix is given in the property value
|
||||
$namespacePrefix = "d";
|
||||
$namespace = "DAV:";
|
||||
} elseif ($namespaceString) {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$namespacePrefix = $ns->prefix;
|
||||
$namespace = $ns->namespace;
|
||||
}
|
||||
//if a namespace prefix is given in the property value use that
|
||||
if ($property && \strpos($property, ":")) {
|
||||
$propertyParts = \explode(":", $property);
|
||||
$namespacePrefix = $propertyParts[0];
|
||||
$property = $propertyParts[1];
|
||||
}
|
||||
return [$namespacePrefix, $namespace, $property];
|
||||
}
|
||||
|
||||
/**
|
||||
* sends HTTP request PROPPATCH method with multiple properties
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string $path
|
||||
* @param array|null $propertiesArray
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersion
|
||||
* @param string|null $namespaceString
|
||||
* @param string|null $type
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function proppatchWithMultipleProps(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
string $path,
|
||||
?array $propertiesArray,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersion = null,
|
||||
?string $namespaceString = null,
|
||||
?string $type="files"
|
||||
):ResponseInterface {
|
||||
$propertyBody = "";
|
||||
foreach ($propertiesArray as $propertyArray) {
|
||||
$property = $propertyArray["propertyName"];
|
||||
$value = $propertyArray["propertyValue"];
|
||||
|
||||
if ($namespaceString !== null) {
|
||||
$matches = [];
|
||||
[$namespacePrefix, $namespace, $property] = self::getPropertyWithNamespaceInfo(
|
||||
$namespaceString,
|
||||
$property
|
||||
);
|
||||
$propertyBody .= "\n\t<$namespacePrefix:$property>" .
|
||||
"$value" .
|
||||
"</$namespacePrefix:$property>";
|
||||
} else {
|
||||
$propertyBody .= "<$property>$value</$property>";
|
||||
}
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propertyupdate xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\">
|
||||
<d:set>
|
||||
<d:prop>$propertyBody
|
||||
</d:prop>
|
||||
</d:set>
|
||||
</d:propertyupdate>";
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPPATCH",
|
||||
$path,
|
||||
[],
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersion,
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the response to listing a folder (collection)
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $folderDepth
|
||||
* @param string|null $xRequestId
|
||||
* @param string[] $properties
|
||||
* @param string|null $type
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function listFolder(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $folderDepth,
|
||||
?string $xRequestId = '',
|
||||
?array $properties = null,
|
||||
?string $type = "files",
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
):ResponseInterface {
|
||||
if (!$properties) {
|
||||
$properties = [
|
||||
'd:getetag', 'd:resourcetype'
|
||||
];
|
||||
}
|
||||
return self::propfind(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$path,
|
||||
$properties,
|
||||
$xRequestId,
|
||||
$folderDepth,
|
||||
$type,
|
||||
$davPathVersionToUse
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates UUIDv4
|
||||
* Example: 123e4567-e89b-12d3-a456-426614174000
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function generateUUIDv4():string {
|
||||
// generate 16 bytes (128 bits) of random data or use the data passed into the function.
|
||||
$data = random_bytes(16);
|
||||
\assert(\strlen($data) == 16);
|
||||
|
||||
$data[6] = \chr(\ord($data[6]) & 0x0f | 0x40); // set version to 0100
|
||||
$data[8] = \chr(\ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
|
||||
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSharesSpaceIdForUser(string $baseUrl, string $user, string $password, string $xRequestId): string {
|
||||
if (\array_key_exists($user, self::$spacesIdRef) && \array_key_exists("virtual", self::$spacesIdRef[$user])) {
|
||||
return self::$spacesIdRef[$user]["virtual"];
|
||||
}
|
||||
|
||||
$response = GraphHelper::getMySpaces($baseUrl, $user, $password, '', $xRequestId);
|
||||
$body = HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
||||
|
||||
$spaceId = null;
|
||||
foreach ($body->value as $spaces) {
|
||||
if ($spaces->driveType === "virtual") {
|
||||
$spaceId = $spaces->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches personal space id for provided user
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getPersonalSpaceIdForUser(string $baseUrl, string $user, string $password, string $xRequestId):string {
|
||||
if (\array_key_exists($user, self::$spacesIdRef) && \array_key_exists("personal", self::$spacesIdRef[$user])) {
|
||||
return self::$spacesIdRef[$user]["personal"];
|
||||
}
|
||||
$trimmedBaseUrl = \trim($baseUrl, "/");
|
||||
$drivesPath = '/graph/v1.0/me/drives';
|
||||
$fullUrl = $trimmedBaseUrl . $drivesPath;
|
||||
$response = HttpRequestHelper::get(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password
|
||||
);
|
||||
$bodyContents = $response->getBody()->getContents();
|
||||
$json = \json_decode($bodyContents);
|
||||
$personalSpaceId = '';
|
||||
if ($json === null) {
|
||||
// the graph endpoint did not give a useful answer
|
||||
// try getting the information from the webdav endpoint
|
||||
$fullUrl = $trimmedBaseUrl . '/remote.php/webdav';
|
||||
$response = HttpRequestHelper::sendRequest(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
'PROPFIND',
|
||||
$user,
|
||||
$password
|
||||
);
|
||||
// we expect to get a multipart XML response with status 207
|
||||
$status = $response->getStatusCode();
|
||||
if ($status === 401) {
|
||||
throw new SpaceNotFoundException(__METHOD__ . " Personal space not found for user " . $user);
|
||||
} elseif ($status !== 207) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " webdav propfind for user $user failed with status $status - so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("/d:multistatus/d:response[1]/d:propstat/d:prop/oc:id");
|
||||
if ($xmlPart === false) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " oc:id not found in webdav propfind for user $user - so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$ocIdRawString = $xmlPart[0]->__toString();
|
||||
$separator = "!";
|
||||
if (\strpos($ocIdRawString, $separator) !== false) {
|
||||
// The string is not base64-encoded, because the exclamation mark is not in the base64 alphabet.
|
||||
// We expect to have a string with 2 parts separated by the exclamation mark.
|
||||
// This is the format introduced in 2022-02
|
||||
// oc:id should be something like:
|
||||
// "7464caf6-1799-103c-9046-c7b74deb5f63!7464caf6-1799-103c-9046-c7b74deb5f63"
|
||||
// There is no encoding to decode.
|
||||
$decodedId = $ocIdRawString;
|
||||
} else {
|
||||
// fall-back to assuming that the oc:id is base64-encoded
|
||||
// That is the format used before and up to 2022-02
|
||||
// This can be removed after both the edge and master branches of cs3org/reva are using the new format.
|
||||
// oc:id should be some base64 encoded string like:
|
||||
// "NzQ2NGNhZjYtMTc5OS0xMDNjLTkwNDYtYzdiNzRkZWI1ZjYzOjc0NjRjYWY2LTE3OTktMTAzYy05MDQ2LWM3Yjc0ZGViNWY2Mw=="
|
||||
// That should decode to something like:
|
||||
// "7464caf6-1799-103c-9046-c7b74deb5f63:7464caf6-1799-103c-9046-c7b74deb5f63"
|
||||
$decodedId = base64_decode($ocIdRawString);
|
||||
$separator = ":";
|
||||
}
|
||||
$ocIdParts = \explode($separator, $decodedId);
|
||||
if (\count($ocIdParts) !== 2) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " the oc:id $decodedId for user $user does not have 2 parts separated by '$separator', so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$personalSpaceId = $ocIdParts[0];
|
||||
} else {
|
||||
foreach ($json->value as $spaces) {
|
||||
if ($spaces->driveType === "personal") {
|
||||
$personalSpaceId = $spaces->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($personalSpaceId) {
|
||||
// If env var LOG_PERSONAL_SPACE_ID is defined, then output the details of the personal space id.
|
||||
// This is a useful debugging tool to have confidence that the personal space id is found correctly.
|
||||
if (\getenv('LOG_PERSONAL_SPACE_ID') !== false) {
|
||||
echo __METHOD__ . " personal space id of user $user is $personalSpaceId\n";
|
||||
}
|
||||
self::$spacesIdRef[$user] = [];
|
||||
self::$spacesIdRef[$user]["personal"] = $personalSpaceId;
|
||||
return $personalSpaceId;
|
||||
} else {
|
||||
throw new SpaceNotFoundException(__METHOD__ . " Personal space not found for user " . $user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First checks if a user exists to return its space ID
|
||||
* In case of any exception, it returns a fake space ID
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getPersonalSpaceIdForUserOrFakeIfNotFound(string $baseUrl, string $user, string $password, string $xRequestId):string {
|
||||
try {
|
||||
$spaceId = self::getPersonalSpaceIdForUser(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$xRequestId,
|
||||
);
|
||||
} catch (SpaceNotFoundException $e) {
|
||||
// if the fetch fails, and the user is not found, then a fake space id is prepared
|
||||
// this is useful for testing when the personal space is of a non-existing user
|
||||
$fakeSpaceId = self::generateUUIDv4();
|
||||
self::$spacesIdRef[$user]["personal"] = $fakeSpaceId;
|
||||
$spaceId = $fakeSpaceId;
|
||||
}
|
||||
return $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a DAV request
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* URL of owncloud e.g. http://localhost:8080
|
||||
* should include the subfolder if owncloud runs in a subfolder
|
||||
* e.g. http://localhost:8080/owncloud-core
|
||||
* @param string|null $user
|
||||
* @param string|null $password or token when bearer auth is used
|
||||
* @param string|null $method PUT, GET, DELETE, etc.
|
||||
* @param string|null $path
|
||||
* @param array|null $headers
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null|resource|StreamInterface $body
|
||||
* @param int|null $davPathVersionToUse (1|2|3)
|
||||
* @param string|null $type of request
|
||||
* @param string|null $sourceIpAddress to initiate the request from
|
||||
* @param string|null $authType basic|bearer
|
||||
* @param bool $stream Set to true to stream a response rather
|
||||
* than download it all up-front.
|
||||
* @param int|null $timeout
|
||||
* @param Client|null $client
|
||||
* @param array|null $urlParameter to concatenate with path
|
||||
* @param string|null $doDavRequestAsUser run the DAV as this user, if null it is the same as $user
|
||||
* @param bool $isGivenStep is set to true if makeDavRequest is called from a "given" step
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function makeDavRequest(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $method,
|
||||
?string $path,
|
||||
?array $headers,
|
||||
?string $xRequestId = '',
|
||||
$body = null,
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_OLD,
|
||||
?string $type = "files",
|
||||
?string $sourceIpAddress = null,
|
||||
?string $authType = "basic",
|
||||
?bool $stream = false,
|
||||
?int $timeout = 0,
|
||||
?Client $client = null,
|
||||
?array $urlParameter = [],
|
||||
?string $doDavRequestAsUser = null,
|
||||
?bool $isGivenStep = false
|
||||
):ResponseInterface {
|
||||
$baseUrl = self::sanitizeUrl($baseUrl, true);
|
||||
|
||||
// We need to manipulate and use path as a string.
|
||||
// So ensure that it is a string to avoid any type-conversion errors.
|
||||
if ($path === null) {
|
||||
$path = "";
|
||||
}
|
||||
|
||||
// get space id if testing with spaces dav
|
||||
if (self::$SPACE_ID_FROM_OCIS === '' && $davPathVersionToUse === self::DAV_VERSION_SPACES) {
|
||||
$path = \ltrim($path, "/");
|
||||
if (\str_starts_with($path, "Shares/")) {
|
||||
$spaceId = self::getSharesSpaceIdForUser(
|
||||
$baseUrl,
|
||||
$doDavRequestAsUser ?? $user,
|
||||
$password,
|
||||
$xRequestId
|
||||
);
|
||||
$path = "/" . preg_replace("/^Shares\//", "", $path);
|
||||
} else {
|
||||
$spaceId = self::getPersonalSpaceIdForUserOrFakeIfNotFound(
|
||||
$baseUrl,
|
||||
$doDavRequestAsUser ?? $user,
|
||||
$password,
|
||||
$xRequestId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$spaceId = self::$SPACE_ID_FROM_OCIS;
|
||||
}
|
||||
|
||||
$davPath = self::getDavPath($doDavRequestAsUser ?? $user, $davPathVersionToUse, $type, $spaceId);
|
||||
|
||||
//replace %, # and ? and in the path, Guzzle will not encode them
|
||||
$urlSpecialChar = [['%', '#', '?'], ['%25', '%23', '%3F']];
|
||||
$path = \str_replace($urlSpecialChar[0], $urlSpecialChar[1], $path);
|
||||
|
||||
if (!empty($urlParameter)) {
|
||||
$urlParameter = \http_build_query($urlParameter, '', '&');
|
||||
$path .= '?' . $urlParameter;
|
||||
}
|
||||
$fullUrl = self::sanitizeUrl($baseUrl . "/$davPath" . $path);
|
||||
|
||||
if ($authType === 'bearer') {
|
||||
$headers['Authorization'] = 'Bearer ' . $password;
|
||||
$user = null;
|
||||
$password = null;
|
||||
}
|
||||
if ($type === "public-files-new") {
|
||||
if ($password === null || $password === "") {
|
||||
$user = null;
|
||||
} else {
|
||||
$user = "public";
|
||||
}
|
||||
}
|
||||
$config = null;
|
||||
if ($sourceIpAddress !== null) {
|
||||
$config = [ 'curl' => [ CURLOPT_INTERFACE => $sourceIpAddress ]];
|
||||
}
|
||||
|
||||
if ($headers !== null) {
|
||||
foreach ($headers as $key => $value) {
|
||||
//? and # need to be encoded in the Destination URL
|
||||
if ($key === "Destination") {
|
||||
$headers[$key] = \str_replace(
|
||||
$urlSpecialChar[0],
|
||||
$urlSpecialChar[1],
|
||||
$value
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clear the space ID from ocis after each request
|
||||
self::$SPACE_ID_FROM_OCIS = '';
|
||||
return HttpRequestHelper::sendRequest(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$method,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
null,
|
||||
$stream,
|
||||
$timeout,
|
||||
$client,
|
||||
$isGivenStep
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the dav path
|
||||
*
|
||||
* @param string|null $user
|
||||
* @param int|null $davPathVersionToUse (1|2)
|
||||
* @param string|null $type
|
||||
* @param string|null $spaceId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDavPath(
|
||||
?string $user,
|
||||
?int $davPathVersionToUse = null,
|
||||
?string $type = "files",
|
||||
?string $spaceId = null
|
||||
):string {
|
||||
$newTrashbinDavPath = "remote.php/dav/trash-bin/$user/";
|
||||
|
||||
switch ($type) {
|
||||
case 'public-files':
|
||||
case 'public-files-old':
|
||||
return "public.php/webdav/";
|
||||
case 'public-files-new':
|
||||
return "remote.php/dav/public-files/$user/";
|
||||
case 'archive':
|
||||
return "remote.php/dav/archive/$user/files";
|
||||
case 'versions':
|
||||
case 'customgroups':
|
||||
return "remote.php/dav/";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ($davPathVersionToUse === self::DAV_VERSION_SPACES) {
|
||||
// return spaces root path if spaceid is null
|
||||
// REPORT request uses spaces root path
|
||||
if ($spaceId === null) {
|
||||
return "remote.php/dav/spaces/";
|
||||
}
|
||||
if ($type === "trash-bin") {
|
||||
return "remote.php/dav/spaces/trash-bin/" . $spaceId . '/';
|
||||
}
|
||||
return "remote.php/dav/spaces/" . $spaceId . '/';
|
||||
} else {
|
||||
if ($davPathVersionToUse === self::DAV_VERSION_OLD) {
|
||||
if ($type === "trash-bin") {
|
||||
// Since there is no trash bin endpoint for old dav version, new dav version's endpoint is used here.
|
||||
return $newTrashbinDavPath;
|
||||
}
|
||||
return "remote.php/webdav/";
|
||||
} elseif ($davPathVersionToUse === self::DAV_VERSION_NEW) {
|
||||
if ($type === "files") {
|
||||
$path = 'remote.php/dav/files/';
|
||||
return $path . $user . '/';
|
||||
} elseif ($type === "trash-bin") {
|
||||
return $newTrashbinDavPath;
|
||||
} else {
|
||||
return "remote.php/dav";
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"DAV path version $davPathVersionToUse is unknown"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure there are no double-slashes in the URL
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param bool|null $trailingSlash forces a trailing slash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizeUrl(?string $url, ?bool $trailingSlash = false):string {
|
||||
if ($trailingSlash === true) {
|
||||
$url = $url . "/";
|
||||
} else {
|
||||
$url = \rtrim($url, "/");
|
||||
}
|
||||
return \preg_replace("/([^:]\/)\/+/", '$1', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if the proposed dav version and chunking version are
|
||||
* a valid combination.
|
||||
* If no chunkingVersion is specified, then any dav version is valid.
|
||||
* If a chunkingVersion is specified, then it has to match the dav version.
|
||||
* Note: in future, the dav and chunking versions might or might not
|
||||
* move together and/or be supported together. So a more complex
|
||||
* matrix could be needed here.
|
||||
*
|
||||
* @param string|int $davPathVersion
|
||||
* @param string|int|null $chunkingVersion
|
||||
*
|
||||
* @return boolean is this a valid combination
|
||||
*/
|
||||
public static function isValidDavChunkingCombination(
|
||||
$davPathVersion,
|
||||
$chunkingVersion
|
||||
): bool {
|
||||
if ($davPathVersion === self::DAV_VERSION_SPACES) {
|
||||
// allow only old chunking version when using the spaces dav
|
||||
return $chunkingVersion === 1;
|
||||
}
|
||||
return (
|
||||
($chunkingVersion === 'no' || $chunkingVersion === null) ||
|
||||
($davPathVersion === $chunkingVersion)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get Mtime of File in a public link share
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $fileName
|
||||
* @param string|null $token
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getMtimeOfFileInPublicLinkShare(
|
||||
?string $baseUrl,
|
||||
?string $fileName,
|
||||
?string $token,
|
||||
?string $xRequestId = '',
|
||||
?int $davVersionToUse = self::DAV_VERSION_NEW
|
||||
):string {
|
||||
$response = self::propfind(
|
||||
$baseUrl,
|
||||
null,
|
||||
null,
|
||||
"/public-files/$token/$fileName",
|
||||
['d:getlastmodified'],
|
||||
$xRequestId,
|
||||
'1',
|
||||
null,
|
||||
$davVersionToUse
|
||||
);
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("//d:getlastmodified");
|
||||
|
||||
return $xmlPart[0]->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* get Mtime of a resource
|
||||
*
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $resource
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getMtimeOfResource(
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $baseUrl,
|
||||
?string $resource,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
):string {
|
||||
$response = self::propfind(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$resource,
|
||||
["d:getlastmodified"],
|
||||
$xRequestId,
|
||||
"0",
|
||||
"files",
|
||||
$davPathVersionToUse
|
||||
);
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("//d:getlastmodified");
|
||||
Assert::assertArrayHasKey(
|
||||
0,
|
||||
$xmlPart,
|
||||
__METHOD__ . " XML part does not have key 0. Expected a value at index 0 of 'xmlPart' but, found: " . json_encode($xmlPart)
|
||||
);
|
||||
$mtime = new DateTime($xmlPart[0]->__toString());
|
||||
return $mtime->format('U');
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -2453,7 +2453,7 @@ class FeatureContext extends BehatVariablesContext {
|
||||
* @return string
|
||||
*/
|
||||
public function acceptanceTestsDirLocation(): string {
|
||||
return \dirname(__FILE__) . "/../../";
|
||||
return \dirname(__FILE__) . "/../";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2857,9 +2857,9 @@ class FeatureContext extends BehatVariablesContext {
|
||||
public static function isExpectedToFail(string $scenarioLine): bool {
|
||||
$expectedFailFile = \getenv('EXPECTED_FAILURES_FILE');
|
||||
if (!$expectedFailFile) {
|
||||
$expectedFailFile = __DIR__ . '/../../expected-failures-localAPI-on-OCIS-storage.md';
|
||||
$expectedFailFile = __DIR__ . '/../expected-failures-localAPI-on-OCIS-storage.md';
|
||||
if (\strpos($scenarioLine, "coreApi") === 0) {
|
||||
$expectedFailFile = __DIR__ . '/../../expected-failures-API-on-OCIS-storage.md';
|
||||
$expectedFailFile = __DIR__ . '/../expected-failures-API-on-OCIS-storage.md';
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -613,7 +613,7 @@ class OCSContext implements Context {
|
||||
public function getActualStatusMessage(string $statusMessage, ?string $language = null):string {
|
||||
if ($language !== null) {
|
||||
$multiLingualMessage = \json_decode(
|
||||
\file_get_contents(__DIR__ . "/../../fixtures/multiLanguageErrors.json"),
|
||||
\file_get_contents(__DIR__ . "/../fixtures/multiLanguageErrors.json"),
|
||||
true
|
||||
);
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ class OcisConfigContext implements Context {
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function theConfigHasBeenSetPathTo(string $configVariable, string $path): void {
|
||||
$path = \dirname(__FILE__) . "/../../../" . $path;
|
||||
$path = \dirname(__FILE__) . "/../../" . $path;
|
||||
$response = OcisConfigHelper::reConfigureOcis(
|
||||
[
|
||||
$configVariable => $path
|
||||
+1
-1
@@ -3939,7 +3939,7 @@ trait WebDav {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theDownloadedPreviewContentShouldMatchWithFixturesPreviewContentFor(string $filename):void {
|
||||
$expectedPreview = \file_get_contents(__DIR__ . "/../../fixtures/" . $filename);
|
||||
$expectedPreview = \file_get_contents(__DIR__ . "/../fixtures/" . $filename);
|
||||
Assert::assertEquals($expectedPreview, $this->responseBodyContent);
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ use Composer\Autoload\ClassLoader;
|
||||
|
||||
$classLoader = new ClassLoader();
|
||||
|
||||
$classLoader->addPsr4("TestHelpers\\", __DIR__ . "/../../../TestHelpers", true);
|
||||
$classLoader->addPsr4("TestHelpers\\", __DIR__ . "/../TestHelpers", true);
|
||||
|
||||
$classLoader->register();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
default:
|
||||
autoload:
|
||||
"": "%paths.base%/../features/bootstrap"
|
||||
"": "%paths.base%/../bootstrap"
|
||||
suites:
|
||||
coreApiMain:
|
||||
paths:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
default:
|
||||
autoload:
|
||||
"": "%paths.base%/../features/bootstrap"
|
||||
"": "%paths.base%/../bootstrap"
|
||||
|
||||
suites:
|
||||
apiAccountsHashDifficulty:
|
||||
|
||||
Reference in New Issue
Block a user