mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-10 12:48:29 -04:00
Compare commits
83
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a65ddccfbd | ||
|
|
bfe56d5b56 | ||
|
|
994086ceb2 | ||
|
|
88cc9cdfd3 | ||
|
|
639fe9ae55 | ||
|
|
58bb8eb561 | ||
|
|
5a59f6e1c4 | ||
|
|
cad3850c8e | ||
|
|
9f56fdb72d | ||
|
|
c8f2f76932 | ||
|
|
dbb1b57e61 | ||
|
|
851719e216 | ||
|
|
9498efee25 | ||
|
|
1223d68578 | ||
|
|
bb851534c1 | ||
|
|
106cce2915 | ||
|
|
ba0319ad89 | ||
|
|
d8a2eb71f3 | ||
|
|
aa5509e81e | ||
|
|
94b6424df3 | ||
|
|
ef56e1f89f | ||
|
|
32a7149ee4 | ||
|
|
17425668ee | ||
|
|
e6ecf05851 | ||
|
|
ab1fcc8bd3 | ||
|
|
96d3b01874 | ||
|
|
90c14ebe7c | ||
|
|
567e9e6000 | ||
|
|
48ccc21801 | ||
|
|
bb61499290 | ||
|
|
0c29ac2c55 | ||
|
|
62567d40c2 | ||
|
|
5641382a07 | ||
|
|
b122fcfa41 | ||
|
|
eafef79d68 | ||
|
|
024e438fce | ||
|
|
aa277ac5e8 | ||
|
|
70cdd843a1 | ||
|
|
6f0e07ca58 | ||
|
|
921a542ae6 | ||
|
|
82e283abfc | ||
|
|
f9fb19a159 | ||
|
|
4077469bf8 | ||
|
|
b6501d9e5e | ||
|
|
90110ee4fa | ||
|
|
a51ddf6f78 | ||
|
|
7c7fbb44e5 | ||
|
|
a3a46bdd4c | ||
|
|
ca835e0646 | ||
|
|
b7f4af0690 | ||
|
|
055f267ffe | ||
|
|
f8d7dbb146 | ||
|
|
816d41ae77 | ||
|
|
f3feda6718 | ||
|
|
817ece71dc | ||
|
|
e97e0ac428 | ||
|
|
07e00b997f | ||
|
|
8667b86f7c | ||
|
|
53e74c81d3 | ||
|
|
28cc89fd5b | ||
|
|
c73cb9061f | ||
|
|
8de224d8ec | ||
|
|
e68ac44ad9 | ||
|
|
d4e7f65947 | ||
|
|
1aed180422 | ||
|
|
cc3b1e11e0 | ||
|
|
ba64626527 | ||
|
|
b3aed20121 | ||
|
|
29133cad43 | ||
|
|
71322f3d7d | ||
|
|
e7abed196e | ||
|
|
9de0f5361a | ||
|
|
340aa72e40 | ||
|
|
8a7e24ada9 | ||
|
|
ca3ed61e85 | ||
|
|
4cdf03ae86 | ||
|
|
e170a4a9b5 | ||
|
|
b342821967 | ||
|
|
48dd679bec | ||
|
|
4143344770 | ||
|
|
826c20ce90 | ||
|
|
64611ec34b | ||
|
|
0ea3a330bc |
No files matched your search
@@ -1,5 +1,66 @@
|
||||
# Changelog
|
||||
|
||||
## [8.0.0](https://github.com/opencloud-eu/opencloud/releases/tag/v8.0.0) - 2026-09-09
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@butonic, @dschmidt, @fredrikblau, @fschade, @maki5, @pbleser-oc, @v-scharf, @zerox80
|
||||
|
||||
### 💥 Breaking changes
|
||||
|
||||
- feat(search): check the index schema on startup and refuse breaking changes [[#3197](https://github.com/opencloud-eu/opencloud/pull/3197)]
|
||||
- refactor: reflection-based search mapping + location geopoint [[#3345](https://github.com/opencloud-eu/opencloud/pull/3345)]
|
||||
- fix(search): make openSearch and bleve behave the same [[#3408](https://github.com/opencloud-eu/opencloud/pull/3408)]
|
||||
|
||||
### ✅ Tests
|
||||
|
||||
- api-tests: add search retry to all search tests [[#3500](https://github.com/opencloud-eu/opencloud/pull/3500)]
|
||||
- test(search): fail the parity suite when the committed matrix is stale [[#3423](https://github.com/opencloud-eu/opencloud/pull/3423)]
|
||||
- ci: run search API and e2e suites against OpenSearch [[#3379](https://github.com/opencloud-eu/opencloud/pull/3379)]
|
||||
- test(search): engine parity suite for bleve and opensearch [[#3418](https://github.com/opencloud-eu/opencloud/pull/3418)]
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- fix(search): extract facets from the main tika document only [[#3484](https://github.com/opencloud-eu/opencloud/pull/3484)]
|
||||
- fix(thumbnails): bound declared image dimensions before decoding [[#3457](https://github.com/opencloud-eu/opencloud/pull/3457)]
|
||||
- test(search): re-search until the expected files are in the result [[#3488](https://github.com/opencloud-eu/opencloud/pull/3488)]
|
||||
- fix(config): correct pending version annotations [[#3487](https://github.com/opencloud-eu/opencloud/pull/3487)]
|
||||
- Activitylog event handler split [[#3241](https://github.com/opencloud-eu/opencloud/pull/3241)]
|
||||
- test(search): wait for expected properties and documents [[#3486](https://github.com/opencloud-eu/opencloud/pull/3486)]
|
||||
- fix: log jwt expired on debug level instead of error [[#3463](https://github.com/opencloud-eu/opencloud/pull/3463)]
|
||||
- fix(proxy): restrict JWT signed urls to the allowed HTTP methods [[#3481](https://github.com/opencloud-eu/opencloud/pull/3481)]
|
||||
- fix: notification handling for share removal and space membership expiry [[#3257](https://github.com/opencloud-eu/opencloud/pull/3257)]
|
||||
- fix: posix cli commands [[#3348](https://github.com/opencloud-eu/opencloud/pull/3348)]
|
||||
|
||||
### 📈 Enhancement
|
||||
|
||||
- feat(graph): expand thumbnails on driveItems [[#3471](https://github.com/opencloud-eu/opencloud/pull/3471)]
|
||||
- graph: expose lockInfo on driveItems [[#3444](https://github.com/opencloud-eu/opencloud/pull/3444)]
|
||||
- graph: expose @libre.graph.shareTypes on driveItems [[#3438](https://github.com/opencloud-eu/opencloud/pull/3438)]
|
||||
- feat(search): live photo facet [[#3202](https://github.com/opencloud-eu/opencloud/pull/3202)]
|
||||
- feat: support $expand=children on the driveItem endpoint [[#3445](https://github.com/opencloud-eu/opencloud/pull/3445)]
|
||||
- feat(search): motion photo facet [[#3200](https://github.com/opencloud-eu/opencloud/pull/3200)]
|
||||
- feat(search): video facet [[#3201](https://github.com/opencloud-eu/opencloud/pull/3201)]
|
||||
- graph: expose pendingOperations on driveItems [[#3437](https://github.com/opencloud-eu/opencloud/pull/3437)]
|
||||
- feat(search): extract more data from tika 4 (if available) [[#3198](https://github.com/opencloud-eu/opencloud/pull/3198)]
|
||||
- graph: expose following state, tags and allowed actions on driveItems [[#3113](https://github.com/opencloud-eu/opencloud/pull/3113)]
|
||||
- feat(search): scope searches to a drive via the driveId field [[#3424](https://github.com/opencloud-eu/opencloud/pull/3424)]
|
||||
- chore(policies): disable gRPC or event handlers by configuration + add metrics [[#3287](https://github.com/opencloud-eu/opencloud/pull/3287)]
|
||||
- moved ShareCreated event consumer from frontend to shared service [[#3389](https://github.com/opencloud-eu/opencloud/pull/3389)]
|
||||
|
||||
### 📦️ Dependencies
|
||||
|
||||
- build(deps): bump go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp from 0.70.0 to 0.71.0 [[#3441](https://github.com/opencloud-eu/opencloud/pull/3441)]
|
||||
- build(deps): bump go.opentelemetry.io/contrib/zpages from 0.70.0 to 0.71.0 [[#3442](https://github.com/opencloud-eu/opencloud/pull/3442)]
|
||||
- build(deps): bump go.opentelemetry.io/otel from 1.45.0 to 1.46.0 [[#3426](https://github.com/opencloud-eu/opencloud/pull/3426)]
|
||||
- build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2 [[#3429](https://github.com/opencloud-eu/opencloud/pull/3429)]
|
||||
- build(deps): bump github.com/blevesearch/bleve/v2 from 2.6.0 to 2.6.1 [[#3417](https://github.com/opencloud-eu/opencloud/pull/3417)]
|
||||
- build(deps): bump github.com/sirupsen/logrus from 1.10.0 to 1.10.1 [[#3409](https://github.com/opencloud-eu/opencloud/pull/3409)]
|
||||
- build(deps): bump github.com/opensearch-project/opensearch-go/v4 from 4.6.0 to 4.7.3 [[#3216](https://github.com/opencloud-eu/opencloud/pull/3216)]
|
||||
- build(deps): bump github.com/KimMachineGun/automemlimit from 0.7.5 to 1.0.0 [[#3404](https://github.com/opencloud-eu/opencloud/pull/3404)]
|
||||
- build(deps): bump github.com/nats-io/nats.go from 1.52.0 to 1.53.1 [[#3403](https://github.com/opencloud-eu/opencloud/pull/3403)]
|
||||
- build(deps): bump github.com/open-policy-agent/opa from 1.19.0 to 1.19.1 [[#3402](https://github.com/opencloud-eu/opencloud/pull/3402)]
|
||||
|
||||
## [7.5.0](https://github.com/opencloud-eu/opencloud/releases/tag/v7.5.0) - 2026-08-25
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@@ -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