mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-10 12:48:29 -04:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89fa598fd1 | ||
|
|
6937774311 | ||
|
|
994086ceb2 |
No files matched your search
@@ -32,10 +32,36 @@ class TokenHelper {
|
||||
private const LOGON_URL = '/signin/v1/identifier/_/logon';
|
||||
private const REDIRECT_URL = '/oidc-callback.html';
|
||||
private const TOKEN_URL = '/konnect/v1/token';
|
||||
private const TRANSPORT_RETRY_LIMIT = 3;
|
||||
|
||||
// Static cache [username => token_data]
|
||||
private static array $tokenCache = [];
|
||||
|
||||
/**
|
||||
* Run a token exchange and retry it a few times if it fails with a transport error
|
||||
*
|
||||
* @param callable $exchange returns the token data array
|
||||
*
|
||||
* @return array
|
||||
* @throws GuzzleException the last error if every attempt fails
|
||||
*/
|
||||
private static function retryOnTransportError(callable $exchange): array {
|
||||
$attempt = 0;
|
||||
while (true) {
|
||||
try {
|
||||
return $exchange();
|
||||
} catch (GuzzleException $e) {
|
||||
if ($attempt == self::TRANSPORT_RETRY_LIMIT) {
|
||||
throw $e;
|
||||
}
|
||||
$attempt++;
|
||||
echo "[INFO] token exchange failed with '" . $e->getMessage() .
|
||||
"', retrying ($attempt)...\n";
|
||||
\sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@@ -80,24 +106,35 @@ class TokenHelper {
|
||||
return $cachedToken;
|
||||
}
|
||||
|
||||
$refreshedToken = self::refreshToken($cachedToken['refresh_token'], $baseUrl);
|
||||
$tokenData = [
|
||||
'access_token' => $refreshedToken['access_token'],
|
||||
'refresh_token' => $refreshedToken['refresh_token'],
|
||||
// set expiry to 240 (4 minutes) seconds to allow for some buffer
|
||||
// token actually expires in 300 seconds (5 minutes)
|
||||
'expires_at' => time() + 240
|
||||
];
|
||||
self::$tokenCache[$cacheKey] = $tokenData;
|
||||
return $tokenData;
|
||||
try {
|
||||
$refreshedToken = self::retryOnTransportError(
|
||||
fn () => self::refreshToken($cachedToken['refresh_token'], $baseUrl)
|
||||
);
|
||||
$tokenData = [
|
||||
'access_token' => $refreshedToken['access_token'],
|
||||
'refresh_token' => $refreshedToken['refresh_token'],
|
||||
// set expiry to 240 (4 minutes) seconds to allow for some buffer
|
||||
// token actually expires in 300 seconds (5 minutes)
|
||||
'expires_at' => time() + 240
|
||||
];
|
||||
self::$tokenCache[$cacheKey] = $tokenData;
|
||||
return $tokenData;
|
||||
} catch (\Throwable $e) {
|
||||
echo "[INFO] token refresh failed with '" . $e->getMessage() .
|
||||
"', falling back to a full login...\n";
|
||||
unset(self::$tokenCache[$cacheKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Get new tokens
|
||||
$cookieJar = new CookieJar();
|
||||
|
||||
$continueUrl = self::getAuthorizedEndPoint($username, $password, $baseUrl, $cookieJar);
|
||||
$code = self::getCode($continueUrl, $baseUrl, $cookieJar);
|
||||
$tokens = self::getToken($code, $baseUrl, $cookieJar);
|
||||
$tokens = self::retryOnTransportError(
|
||||
function () use ($username, $password, $baseUrl) {
|
||||
$cookieJar = new CookieJar();
|
||||
$continueUrl = self::getAuthorizedEndPoint($username, $password, $baseUrl, $cookieJar);
|
||||
$code = self::getCode($continueUrl, $baseUrl, $cookieJar);
|
||||
return self::getToken($code, $baseUrl, $cookieJar);
|
||||
}
|
||||
);
|
||||
|
||||
$tokenData = [
|
||||
'access_token' => $tokens['access_token'],
|
||||
|
||||
@@ -39,16 +39,25 @@ class WaitHelper {
|
||||
/**
|
||||
* Repeat $makeAttempt until $shouldStop returns true or the timeout elapses.
|
||||
*
|
||||
* @param callable $makeAttempt makes one attempt (e.g. sends a request) and returns its result
|
||||
* @param callable $shouldStop receives that result, returns true to stop polling
|
||||
* @param callable $makeAttempt makes one attempt (e.g. sends a request) and returns its result
|
||||
* @param callable $shouldStop receives that result, returns true to stop polling
|
||||
* @param int|null $intervalMs pause between attempts in ms; defaults to self::INTERVAL_MS
|
||||
* @param int|null $timeoutSeconds overall time to keep polling; defaults to self::TIMEOUT_SECONDS
|
||||
*
|
||||
* @return mixed the last result from $makeAttempt
|
||||
*/
|
||||
public static function waitUntil(callable $makeAttempt, callable $shouldStop): mixed {
|
||||
$deadline = \microtime(true) + self::TIMEOUT_SECONDS;
|
||||
public static function waitUntil(
|
||||
callable $makeAttempt,
|
||||
callable $shouldStop,
|
||||
?int $intervalMs = null,
|
||||
?int $timeoutSeconds = null
|
||||
): mixed {
|
||||
$intervalMs ??= self::INTERVAL_MS;
|
||||
$timeoutSeconds ??= self::TIMEOUT_SECONDS;
|
||||
$deadline = \microtime(true) + $timeoutSeconds;
|
||||
$result = $makeAttempt();
|
||||
while (!$shouldStop($result) && \microtime(true) < $deadline) {
|
||||
\usleep(self::INTERVAL_MS * 1000);
|
||||
\usleep($intervalMs * 1000);
|
||||
$result = $makeAttempt();
|
||||
}
|
||||
return $result;
|
||||
|
||||
@@ -150,7 +150,7 @@ class SearchContext implements Context {
|
||||
): void {
|
||||
// NOTE: because indexing of newly uploaded files or directories with OpenCloud is decoupled and occurs asynchronously
|
||||
// short wait is necessary before searching
|
||||
sleep(10);
|
||||
sleep(2);
|
||||
// remember the query so "should eventually contain" steps can re-search
|
||||
$this->lastSearchQuery = [
|
||||
"user" => $user,
|
||||
@@ -176,6 +176,25 @@ class SearchContext implements Context {
|
||||
string $path,
|
||||
string $user,
|
||||
TableNode $properties
|
||||
): void {
|
||||
$assert = fn () => $this->assertFileOrFolderInSearchResultContainsProperties($path, $user, $properties);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
* @param TableNode $properties
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function assertFileOrFolderInSearchResultContainsProperties(
|
||||
string $path,
|
||||
string $user,
|
||||
TableNode $properties
|
||||
): void {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$this->featureContext->verifyTableNodeColumns($properties, ['name', 'value']);
|
||||
@@ -234,8 +253,7 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the search result should contain these (?:files|entries) with highlight on keyword "([^"]*)"/')]
|
||||
public function theSearchResultShouldContainEntriesWithHighlight(
|
||||
private function assertSearchResultContainsEntriesWithHighlight(
|
||||
TableNode $expectedFiles,
|
||||
string $expectedContent
|
||||
): void {
|
||||
@@ -284,8 +302,15 @@ class SearchContext implements Context {
|
||||
): void {
|
||||
// NOTE: since indexing of newly uploaded files or directories with OpenCloud is decoupled and occurs asynchronously,
|
||||
// a short wait is necessary before searching
|
||||
sleep(5);
|
||||
$response = $this-> searchFiles($user, $pattern, null, $scopeType, $scope, $spaceName);
|
||||
sleep(2);
|
||||
$this->lastSearchQuery = [
|
||||
"user" => $user,
|
||||
"pattern" => $pattern,
|
||||
"scopeType" => $scopeType,
|
||||
"scope" => $scope,
|
||||
"spaceName" => $spaceName,
|
||||
];
|
||||
$response = $this->searchFiles($user, $pattern, null, $scopeType, $scope, $spaceName);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
@@ -300,7 +325,7 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function retrySearchUntilSatisfied(callable $assert): void {
|
||||
public function retrySearchUntilSatisfied(callable $assert): void {
|
||||
Assert::assertNotEmpty(
|
||||
$this->lastSearchQuery,
|
||||
'No search to retry. Use a "searches for ... using the WebDAV API" step first.'
|
||||
@@ -310,33 +335,67 @@ class SearchContext implements Context {
|
||||
fn () => $this->searchFiles(
|
||||
$query["user"],
|
||||
$query["pattern"],
|
||||
$query["limit"],
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$query["properties"]
|
||||
$query["limit"] ?? null,
|
||||
$query["scopeType"] ?? null,
|
||||
$query["scope"] ?? null,
|
||||
$query["spaceName"] ?? null,
|
||||
$query["properties"] ?? null
|
||||
),
|
||||
function ($response) use ($assert) {
|
||||
$this->featureContext->setResponse($response);
|
||||
try {
|
||||
$assert();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
2000,
|
||||
20
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $numFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the search result should contain :numFiles files/entries')]
|
||||
public function theSearchResultShouldContainNumEntries(int $numFiles): void {
|
||||
$assert = fn () => $this->featureContext->checkIFResponseContainsNumberEntries($numFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $user
|
||||
* @param int $expectedNumber
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the search result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
public function theSearchResultShouldContainAnyOfTheseEntries(
|
||||
string $user,
|
||||
int $expectedNumber,
|
||||
TableNode $expectedFiles
|
||||
): void {
|
||||
$assert = fn () => $this->featureContext->checkIfSearchResultContainsFiles($user, $expectedNumber, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should eventually contain only these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldEventuallyContainOnlyEntries(string $user, TableNode $expectedFiles): void {
|
||||
#[Then('/^the search result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldContainOnlyEntries(string $user, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainOnlyEntries($user, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
@@ -348,9 +407,31 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should eventually contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldEventuallyContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainEntries($user, '', $expectedFiles);
|
||||
#[Then('/^the search result of user "([^"]*)" should contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$this->assertSearchResultContainsEntries($user, "", $expectedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should not contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldNotContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$this->assertSearchResultContainsEntries($user, "not", $expectedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $shouldOrNot (not|)
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertSearchResultContainsEntries(string $user, string $shouldOrNot, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainEntries($user, $shouldOrNot, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
@@ -361,9 +442,12 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result should eventually contain these (?:files|entries) with highlight on keyword "([^"]*)"$/')]
|
||||
public function theSearchResultShouldEventuallyContainEntriesWithHighlight(TableNode $expectedFiles, string $expectedContent): void {
|
||||
$assert = fn () => $this->theSearchResultShouldContainEntriesWithHighlight($expectedFiles, $expectedContent);
|
||||
#[Then('/^the search result should contain these (?:files|entries) with highlight on keyword "([^"]*)"$/')]
|
||||
public function theSearchResultShouldContainEntriesWithHighlight(
|
||||
TableNode $expectedFiles,
|
||||
string $expectedContent
|
||||
): void {
|
||||
$assert = fn () => $this->assertSearchResultContainsEntriesWithHighlight($expectedFiles, $expectedContent);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ class SpacesContext implements Context {
|
||||
private ChecksumContext $checksumContext;
|
||||
private FilesVersionsContext $filesVersionsContext;
|
||||
private ArchiverContext $archiverContext;
|
||||
private SearchContext $searchContext;
|
||||
|
||||
/**
|
||||
* key is space name and value is the username that created the space
|
||||
@@ -512,6 +513,7 @@ class SpacesContext implements Context {
|
||||
$this->checksumContext = BehatHelper::getContext($scope, $environment, 'ChecksumContext');
|
||||
$this->filesVersionsContext = BehatHelper::getContext($scope, $environment, 'FilesVersionsContext');
|
||||
$this->archiverContext = BehatHelper::getContext($scope, $environment, 'ArchiverContext');
|
||||
$this->searchContext = BehatHelper::getContext($scope, $environment, 'SearchContext');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3925,41 +3927,47 @@ class SpacesContext implements Context {
|
||||
*/
|
||||
#[Then('for user :user the search result should contain space :spaceName')]
|
||||
public function searchResultShouldContainSpace(string $user, string $spaceName): void {
|
||||
$responseArray = json_decode(
|
||||
json_encode(
|
||||
HttpRequestHelper::getResponseXml($this->featureContext->getResponse())->xpath("//d:response/d:href")
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
Assert::assertNotEmpty($responseArray, "search result is empty");
|
||||
// searching a space by name goes through the asynchronous file index, so
|
||||
// re-run the search until the space shows up (or the timeout elapses).
|
||||
$assert = function () use ($user, $spaceName): void {
|
||||
$responseArray = json_decode(
|
||||
json_encode(
|
||||
HttpRequestHelper::getResponseXml($this->featureContext->getResponse())->xpath("//d:response/d:href")
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
Assert::assertNotEmpty($responseArray, "search result is empty");
|
||||
|
||||
// for mountpoint, id looks a little different than for project space
|
||||
if (str_contains($spaceName, 'mountpoint')) {
|
||||
$splitSpaceName = explode("/", $spaceName);
|
||||
$space = $this->getSpaceByName($user, $splitSpaceName[1]);
|
||||
$splitSpaceId = explode("$", $space['id']);
|
||||
$spaceId = str_replace('!', '%21', $splitSpaceId[1]);
|
||||
} else {
|
||||
$space = $this->getSpaceByName($user, $spaceName);
|
||||
$spaceId = $space['id'];
|
||||
}
|
||||
$suffixPath = $user;
|
||||
$davPathVersion = $this->featureContext->getDavPathVersion();
|
||||
if ($davPathVersion === WebDavHelper::DAV_VERSION_SPACES) {
|
||||
$suffixPath = $spaceId;
|
||||
}
|
||||
|
||||
$topWebDavPath = "/" . WebDavHelper::getDavPath($davPathVersion, $suffixPath);
|
||||
|
||||
$spaceFound = false;
|
||||
foreach ($responseArray as $value) {
|
||||
if ($topWebDavPath === $value[0]) {
|
||||
$spaceFound = true;
|
||||
// for mountpoint, id looks a little different than for project space
|
||||
if (str_contains($spaceName, 'mountpoint')) {
|
||||
$splitSpaceName = explode("/", $spaceName);
|
||||
$space = $this->getSpaceByName($user, $splitSpaceName[1]);
|
||||
$splitSpaceId = explode("$", $space['id']);
|
||||
$spaceId = str_replace('!', '%21', $splitSpaceId[1]);
|
||||
} else {
|
||||
$space = $this->getSpaceByName($user, $spaceName);
|
||||
$spaceId = $space['id'];
|
||||
}
|
||||
}
|
||||
Assert::assertTrue($spaceFound, "response does not contain the space '$spaceName'");
|
||||
$suffixPath = $user;
|
||||
$davPathVersion = $this->featureContext->getDavPathVersion();
|
||||
if ($davPathVersion === WebDavHelper::DAV_VERSION_SPACES) {
|
||||
$suffixPath = $spaceId;
|
||||
}
|
||||
|
||||
$topWebDavPath = "/" . WebDavHelper::getDavPath($davPathVersion, $suffixPath);
|
||||
|
||||
$spaceFound = false;
|
||||
foreach ($responseArray as $value) {
|
||||
if ($topWebDavPath === $value[0]) {
|
||||
$spaceFound = true;
|
||||
}
|
||||
}
|
||||
Assert::assertTrue($spaceFound, "response does not contain the space '$spaceName'");
|
||||
};
|
||||
$this->searchContext->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ use Behat\Gherkin\Node\TableNode;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TestHelpers\GraphHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\Then;
|
||||
@@ -38,6 +39,7 @@ require_once 'bootstrap.php';
|
||||
class TagContext implements Context {
|
||||
private FeatureContext $featureContext;
|
||||
private SpacesContext $spacesContext;
|
||||
private array $lastTagsQuery = [];
|
||||
|
||||
/**
|
||||
* This will run before EVERY scenario.
|
||||
@@ -175,19 +177,79 @@ class TagContext implements Context {
|
||||
*/
|
||||
#[When('user :user lists all available tag(s) via the Graph API')]
|
||||
public function theUserGetsAllAvailableTags(string $user): void {
|
||||
// Note: after creating or deleting tags, in some cases tags do not appear or disappear immediately,
|
||||
// So wait is necessary before listing tags
|
||||
sleep(5);
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::getTags(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$this->featureContext->getStepLineRef()
|
||||
)
|
||||
// Note: after creating or deleting tags, in some cases tags do not appear or disappear immediately
|
||||
sleep(2);
|
||||
$this->lastTagsQuery = ["user" => $user];
|
||||
$this->featureContext->setResponse($this->fetchTags($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
*/
|
||||
private function fetchTags(string $user): ResponseInterface {
|
||||
return GraphHelper::getTags(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$this->featureContext->getStepLineRef()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* re-run the last tag listing until the assertion passes or the WaitHelper
|
||||
*
|
||||
* @param callable $assert
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function retryTagsUntilSatisfied(callable $assert): void {
|
||||
Assert::assertNotEmpty(
|
||||
$this->lastTagsQuery,
|
||||
'No tag listing to retry. Use a "lists all available tags via the Graph API" step first.'
|
||||
);
|
||||
$query = $this->lastTagsQuery;
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => $this->fetchTags($query["user"]),
|
||||
function ($response) use ($assert) {
|
||||
$this->featureContext->setResponse($response);
|
||||
try {
|
||||
$assert();
|
||||
return true;
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should contain following tags:$/')]
|
||||
public function theResponseShouldContainFollowingTags(TableNode $table): void {
|
||||
$this->assertResponseContainsFollowingTags("", $table);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should not contain following tags:$/')]
|
||||
public function theResponseShouldNotContainFollowingTags(TableNode $table): void {
|
||||
$this->assertResponseContainsFollowingTags("not", $table);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $shouldOrNot (not|)
|
||||
@@ -196,27 +258,29 @@ class TagContext implements Context {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should (not|)\\s?contain following tag(s):$/')]
|
||||
public function theFollowingTagsShouldExistForUser(string $shouldOrNot, TableNode $table): void {
|
||||
$rows = $table->getRows();
|
||||
foreach ($rows as $row) {
|
||||
private function assertResponseContainsFollowingTags(string $shouldOrNot, TableNode $table): void {
|
||||
$assert = function () use ($shouldOrNot, $table): void {
|
||||
$responseArray = $this->featureContext->getJsonDecodedResponse(
|
||||
$this->featureContext->getResponse()
|
||||
)['value'];
|
||||
if ($shouldOrNot === "not") {
|
||||
Assert::assertFalse(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response should not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
} else {
|
||||
Assert::assertTrue(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response does not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
foreach ($table->getRows() as $row) {
|
||||
if ($shouldOrNot === "not") {
|
||||
Assert::assertFalse(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response should not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
} else {
|
||||
Assert::assertTrue(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response does not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$this->retryTagsUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4014,7 +4014,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the (?:propfind|search) result of user "([^"]*)" should (not|)\\s?contain these (?:files|entries):$/')]
|
||||
#[Then('/^the propfind result of user "([^"]*)" should (not|)\\s?contain these (?:files|entries):$/')]
|
||||
public function thePropfindResultShouldContainEntries(
|
||||
string $user,
|
||||
string $shouldOrNot,
|
||||
@@ -4036,7 +4036,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the (?:propfind|search) result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
#[Then('/^the propfind result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
public function thePropfindResultShouldContainOnlyEntries(
|
||||
string $user,
|
||||
TableNode $expectedFiles
|
||||
@@ -4063,7 +4063,7 @@ trait WebDav {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the propfind/search result should contain :numFiles files/entries')]
|
||||
#[Then('the propfind result should contain :numFiles files/entries')]
|
||||
public function propfindResultShouldContainNumEntries(int $numFiles): void {
|
||||
$this->checkIFResponseContainsNumberEntries($numFiles);
|
||||
}
|
||||
@@ -4110,7 +4110,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('the propfind/search result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
#[Then('the propfind result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
public function theSearchResultOfUserShouldContainAnyOfTheseEntries(
|
||||
string $user,
|
||||
int $expectedNumber,
|
||||
|
||||
@@ -186,15 +186,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
- [apiSearch1/search.feature:438](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L438)
|
||||
- [apiSearch1/search.feature:439](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L439)
|
||||
- [apiSearch1/search.feature:465](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L465)
|
||||
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
|
||||
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
|
||||
|
||||
## Scenarios from core API tests that are expected to fail with decomposed storage
|
||||
|
||||
### File
|
||||
|
||||
@@ -186,15 +186,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
- [apiSearch1/search.feature:438](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L438)
|
||||
- [apiSearch1/search.feature:439](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L439)
|
||||
- [apiSearch1/search.feature:465](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L465)
|
||||
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
|
||||
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
|
||||
|
||||
## Scenarios from core API tests that are expected to fail with posix storage
|
||||
|
||||
### File
|
||||
|
||||
@@ -409,7 +409,7 @@ Feature: Search
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-4712 @issue-9780 @issue-9781 @issue-9783 @issue-10329
|
||||
@skip @issue-3501 @issue-9780
|
||||
Scenario Outline: report extra properties in search entries for a file
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "*insideTheFo*" using the WebDAV API requesting these properties:
|
||||
@@ -438,7 +438,7 @@ Feature: Search
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-4712 @issue-9780 @issue-9781 @issue-9783 @issue-10329
|
||||
@skip @issue-3501 @issue-9780
|
||||
Scenario Outline: report extra properties in search entries for a folder
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "*folderMain*" using the WebDAV API requesting these properties:
|
||||
|
||||
@@ -16,7 +16,7 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "namaste from nepal" to "hello.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -34,15 +34,15 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "alan@example.org want to say hello" to "findByEmail.docs"
|
||||
When user "Alice" searches for "Content:k6" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| wordWithNumber.md |
|
||||
When user "Alice" searches for "Content:https://opencloud.eu/" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| findByWebSite.txt |
|
||||
When user "Alice" searches for "Content:alan@" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| findByEmail.docs |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -71,11 +71,11 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "He has expirience, we must to have, I have to find ...." to "fileWithStopWords.txt"
|
||||
When user "Alice" searches for 'Content:"he has"' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| fileWithStopWords.txt |
|
||||
When user "Alice" searches for 'Content:"I have"' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| fileWithStopWords.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -101,7 +101,7 @@ Feature: content search
|
||||
And user "Brian" has a share "uploadFolder" synced
|
||||
When user "Brian" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Brian" should eventually contain only these files:
|
||||
And the search result of user "Brian" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -121,7 +121,7 @@ Feature: content search
|
||||
And user "Alice" has deleted file "keywordAtLast.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
Examples:
|
||||
@@ -139,7 +139,7 @@ Feature: content search
|
||||
And user "Alice" has restored the file with original path "keywordAtStart.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -154,7 +154,7 @@ Feature: content search
|
||||
And user "Alice" has restored version index "1" of file "test.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| test.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -175,7 +175,7 @@ Feature: content search
|
||||
And using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -204,7 +204,7 @@ Feature: content search
|
||||
And using <dav-path-version> DAV path
|
||||
When user "Brian" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -224,7 +224,7 @@ Feature: content search
|
||||
| technical task.txt | test |
|
||||
When user "Alice" searches for '<pattern>' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain these entries:
|
||||
And the search result of user "Alice" should contain these entries:
|
||||
| <search-result-1> |
|
||||
| <search-result-2> |
|
||||
And the search result should contain "<result-count>" entries
|
||||
@@ -251,7 +251,7 @@ Feature: content search
|
||||
And user "Alice" has uploaded a file inside space "project-space" with content "this is a simple odt file" to "test-odt-file.odt"
|
||||
When user "Alice" searches for "Content:simple" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result should eventually contain these entries with highlight on keyword "simple"
|
||||
And the search result should contain these entries with highlight on keyword "simple"
|
||||
| test-text-file.txt |
|
||||
| test-pdf-file.pdf |
|
||||
| test-cpp-file.cpp |
|
||||
|
||||
Reference in new issue
Block a user