Compare commits

...
Author SHA1 Message Date
oauth a65ddccfbd 🎉 Release 8.0.0 2026-09-09 13:25:37 +00:00
oauth bfe56d5b56 Merge branch 'origin/main' into 'next-release/main' 2026-09-09 13:25:37 +00:00
Viktor Scharf 994086ceb2 add search retry to all serach tests (#3500) 2026-09-09 15:21:23 +02:00
oauth 88cc9cdfd3 🎉 Release 8.0.0 2026-09-08 13:56:18 +00:00
oauth 639fe9ae55 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 13:56:18 +00:00
oauth 58bb8eb561 🎉 Release 8.0.0 2026-09-08 13:32:39 +00:00
oauth 5a59f6e1c4 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 13:32:38 +00:00
oauth cad3850c8e 🎉 Release 8.0.0 2026-09-08 12:54:43 +00:00
oauth 9f56fdb72d Merge branch 'origin/main' into 'next-release/main' 2026-09-08 12:54:42 +00:00
oauth c8f2f76932 🎉 Release 8.0.0 2026-09-08 12:44:16 +00:00
oauth dbb1b57e61 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 12:44:16 +00:00
oauth 851719e216 🎉 Release 8.0.0 2026-09-08 12:17:23 +00:00
oauth 9498efee25 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 12:17:22 +00:00
oauth 1223d68578 🎉 Release 8.0.0 2026-09-08 12:08:40 +00:00
oauth bb851534c1 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 12:08:39 +00:00
oauth 106cce2915 🎉 Release 8.0.0 2026-09-08 08:51:25 +00:00
oauth ba0319ad89 Merge branch 'origin/main' into 'next-release/main' 2026-09-08 08:51:25 +00:00
oauth d8a2eb71f3 🎉 Release 8.0.0 2026-09-08 07:42:21 +00:00
oauth aa5509e81e Merge branch 'origin/main' into 'next-release/main' 2026-09-08 07:42:21 +00:00
oauth 94b6424df3 🎉 Release 8.0.0 2026-09-07 14:17:08 +00:00
oauth ef56e1f89f Merge branch 'origin/main' into 'next-release/main' 2026-09-07 14:17:08 +00:00
oauth 32a7149ee4 🎉 Release 8.0.0 2026-09-06 23:19:55 +00:00
oauth 17425668ee Merge branch 'origin/main' into 'next-release/main' 2026-09-06 23:19:54 +00:00
oauth e6ecf05851 🎉 Release 8.0.0 2026-09-05 23:25:18 +00:00
oauth ab1fcc8bd3 Merge branch 'origin/main' into 'next-release/main' 2026-09-05 23:25:15 +00:00
oauth 96d3b01874 🎉 Release 8.0.0 2026-09-04 08:00:18 +00:00
oauth 90c14ebe7c Merge branch 'origin/main' into 'next-release/main' 2026-09-04 08:00:18 +00:00
oauth 567e9e6000 🎉 Release 8.0.0 2026-09-03 15:55:53 +00:00
oauth 48ccc21801 Merge branch 'origin/main' into 'next-release/main' 2026-09-03 15:55:53 +00:00
oauth bb61499290 🎉 Release 8.0.0 2026-09-03 09:45:59 +00:00
oauth 0c29ac2c55 Merge branch 'origin/main' into 'next-release/main' 2026-09-03 09:45:59 +00:00
oauth 62567d40c2 🎉 Release 7.6.0 2026-09-03 02:24:38 +00:00
oauth 5641382a07 Merge branch 'origin/main' into 'next-release/main' 2026-09-03 02:24:37 +00:00
oauth b122fcfa41 🎉 Release 7.6.0 2026-09-02 22:26:02 +00:00
oauth eafef79d68 Merge branch 'origin/main' into 'next-release/main' 2026-09-02 22:26:02 +00:00
oauth 024e438fce 🎉 Release 7.6.0 2026-09-02 14:52:27 +00:00
oauth aa277ac5e8 Merge branch 'origin/main' into 'next-release/main' 2026-09-02 14:52:26 +00:00
oauth 70cdd843a1 🎉 Release 7.6.0 2026-09-02 08:28:26 +00:00
oauth 6f0e07ca58 Merge branch 'origin/main' into 'next-release/main' 2026-09-02 08:28:26 +00:00
oauth 921a542ae6 🎉 Release 7.6.0 2026-09-01 13:39:05 +00:00
oauth 82e283abfc Merge branch 'origin/main' into 'next-release/main' 2026-09-01 13:39:04 +00:00
oauth f9fb19a159 🎉 Release 7.6.0 2026-09-01 12:11:10 +00:00
oauth 4077469bf8 Merge branch 'origin/main' into 'next-release/main' 2026-09-01 12:11:09 +00:00
oauth b6501d9e5e 🎉 Release 7.6.0 2026-09-01 09:48:26 +00:00
oauth 90110ee4fa Merge branch 'origin/main' into 'next-release/main' 2026-09-01 09:48:26 +00:00
oauth a51ddf6f78 🎉 Release 7.6.0 2026-09-01 09:38:06 +00:00
oauth 7c7fbb44e5 Merge branch 'origin/main' into 'next-release/main' 2026-09-01 09:38:06 +00:00
oauth a3a46bdd4c 🎉 Release 7.6.0 2026-09-01 08:04:44 +00:00
oauth ca835e0646 Merge branch 'origin/main' into 'next-release/main' 2026-09-01 08:04:44 +00:00
oauth b7f4af0690 🎉 Release 7.6.0 2026-09-01 07:14:14 +00:00
oauth 055f267ffe Merge branch 'origin/main' into 'next-release/main' 2026-09-01 07:14:13 +00:00
oauth f8d7dbb146 🎉 Release 7.6.0 2026-09-01 05:20:43 +00:00
oauth 816d41ae77 Merge branch 'origin/main' into 'next-release/main' 2026-09-01 05:20:43 +00:00
oauth f3feda6718 🎉 Release 7.6.0 2026-09-01 04:58:52 +00:00
oauth 817ece71dc Merge branch 'origin/main' into 'next-release/main' 2026-09-01 04:58:51 +00:00
oauth e97e0ac428 🎉 Release 7.6.0 2026-08-31 16:13:36 +00:00
oauth 07e00b997f Merge branch 'origin/main' into 'next-release/main' 2026-08-31 16:13:36 +00:00
oauth 8667b86f7c 🎉 Release 7.6.0 2026-08-31 13:15:36 +00:00
oauth 53e74c81d3 Merge branch 'origin/main' into 'next-release/main' 2026-08-31 13:15:35 +00:00
oauth 28cc89fd5b 🎉 Release 7.6.0 2026-08-31 11:39:15 +00:00
oauth c73cb9061f Merge branch 'origin/main' into 'next-release/main' 2026-08-31 11:39:15 +00:00
oauth 8de224d8ec 🎉 Release 7.6.0 2026-08-31 11:26:55 +00:00
oauth e68ac44ad9 Merge branch 'origin/main' into 'next-release/main' 2026-08-31 11:26:55 +00:00
oauth d4e7f65947 🎉 Release 7.6.0 2026-08-31 08:51:23 +00:00
oauth 1aed180422 Merge branch 'origin/main' into 'next-release/main' 2026-08-31 08:51:22 +00:00
oauth cc3b1e11e0 🎉 Release 7.6.0 2026-08-31 08:24:59 +00:00
oauth ba64626527 Merge branch 'origin/main' into 'next-release/main' 2026-08-31 08:24:58 +00:00
oauth b3aed20121 🎉 Release 7.6.0 2026-08-31 08:02:25 +00:00
oauth 29133cad43 Merge branch 'origin/main' into 'next-release/main' 2026-08-31 08:02:25 +00:00
oauth 71322f3d7d 🎉 Release 7.6.0 2026-08-31 04:30:58 +00:00
oauth e7abed196e 🎉 Release 7.6.0 2026-08-30 23:19:20 +00:00
oauth 9de0f5361a Merge branch 'origin/main' into 'next-release/main' 2026-08-30 23:19:19 +00:00
oauth 340aa72e40 🎉 Release 7.6.0 2026-08-28 23:21:44 +00:00
oauth 8a7e24ada9 Merge branch 'origin/main' into 'next-release/main' 2026-08-28 23:21:43 +00:00
oauth ca3ed61e85 🎉 Release 7.6.0 2026-08-26 21:54:49 +00:00
oauth 4cdf03ae86 Merge branch 'origin/main' into 'next-release/main' 2026-08-26 21:54:49 +00:00
oauth e170a4a9b5 🎉 Release 7.5.1 2026-08-26 09:53:15 +00:00
oauth b342821967 Merge branch 'origin/main' into 'next-release/main' 2026-08-26 09:53:14 +00:00
oauth 48dd679bec 🎉 Release 7.5.1 2026-08-26 09:33:13 +00:00
oauth 4143344770 Merge branch 'origin/main' into 'next-release/main' 2026-08-26 09:33:12 +00:00
oauth 826c20ce90 🎉 Release 7.5.1 2026-08-26 06:22:50 +00:00
oauth 64611ec34b Merge branch 'origin/main' into 'next-release/main' 2026-08-26 06:22:50 +00:00
oauth 0ea3a330bc 🎉 Release 7.5.1 2026-08-25 15:44:28 +00:00
10 changed files with 332 additions and 124 deletions

No files matched your search

+61
View File
@@ -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! ❤️
+14 -5
View File
@@ -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;
+105 -21
View File
@@ -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();
}
+41 -33
View File
@@ -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();
}
/**
+91 -27
View File
@@ -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();
}
/**
+4 -4
View File
@@ -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 |