test(search): re-search until the expected files are in the result (#3488)

Indexing of newly uploaded resources is asynchronous; the search step
slept a fixed 10s and searched once, so under load a just-uploaded file
was missing from the result and the following assertion failed. The
search step now remembers its query, and new eventually-contain steps
re-run the search until the expected entries are present (or the
WaitHelper timeout elapses), then assert as before. contentSearch
scenarios use them for their positive assertions; the exact-count check
runs after the expected entries have appeared.
This commit is contained in:
Dominik Schmidt authored and GitHub committed 2026-09-08 14:50:49 +02:00
1 parent 9797ee903f
commit b5cb1cd83b
2 files changed
+103 -15

No files matched your search

@@ -25,6 +25,7 @@ use GuzzleHttp\Exception\GuzzleException;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\WebDavHelper;
use TestHelpers\WaitHelper;
use TestHelpers\HttpRequestHelper;
use TestHelpers\BehatHelper;
use Behat\Step\Then;
@@ -37,6 +38,7 @@ require_once 'bootstrap.php';
*/
class SearchContext implements Context {
private FeatureContext $featureContext;
private array $lastSearchQuery = [];
/**
* @param string $user
@@ -149,6 +151,13 @@ class SearchContext implements Context {
// NOTE: because indexing of newly uploaded files or directories with OpenCloud is decoupled and occurs asynchronously
// short wait is necessary before searching
sleep(10);
// remember the query so "should eventually contain" steps can re-search
$this->lastSearchQuery = [
"user" => $user,
"pattern" => $pattern,
"limit" => $limit,
"properties" => $properties,
];
$response = $this->searchFiles($user, $pattern, $limit, null, null, null, $properties);
$this->featureContext->setResponse($response);
}
@@ -279,4 +288,83 @@ class SearchContext implements Context {
$response = $this-> searchFiles($user, $pattern, null, $scopeType, $scope, $spaceName);
$this->featureContext->setResponse($response);
}
/**
* re-run the last WebDAV search until the assertion passes or the WaitHelper
* timeout elapses, leaving the last response set for a final assertion by the
* caller. Indexing of newly uploaded resources is asynchronous, so a wanted
* file can be missing from an early search; OpenSearch never returns a partial
* document, so once the expected entries are present the result is complete.
*
* @param callable $assert
*
* @return void
*/
private function retrySearchUntilSatisfied(callable $assert): void {
Assert::assertNotEmpty(
$this->lastSearchQuery,
'No search to retry. Use a "searches for ... using the WebDAV API" step first.'
);
$query = $this->lastSearchQuery;
$response = WaitHelper::waitUntil(
fn () => $this->searchFiles(
$query["user"],
$query["pattern"],
$query["limit"],
null,
null,
null,
$query["properties"]
),
function ($response) use ($assert) {
$this->featureContext->setResponse($response);
try {
$assert();
return true;
} catch (\Throwable $e) {
return false;
}
}
);
$this->featureContext->setResponse($response);
}
/**
* @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 {
$assert = fn () => $this->featureContext->thePropfindResultShouldContainOnlyEntries($user, $expectedFiles);
$this->retrySearchUntilSatisfied($assert);
$assert();
}
/**
* @param string $user
* @param TableNode $expectedFiles
*
* @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);
$this->retrySearchUntilSatisfied($assert);
$assert();
}
/**
* @param TableNode $expectedFiles
* @param string $expectedContent
*
* @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);
$this->retrySearchUntilSatisfied($assert);
$assert();
}
}
@@ -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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Brian" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually 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 contain only these files:
And the search result of user "Alice" should eventually contain only these files:
| keywordAtStart.txt |
| keywordAtMiddle.txt |
| keywordAtLast.txt |
@@ -224,10 +224,10 @@ 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 should contain "<result-count>" entries
And the search result of user "Alice" should contain these entries:
And the search result of user "Alice" should eventually contain these entries:
| <search-result-1> |
| <search-result-2> |
And the search result should contain "<result-count>" entries
Examples:
| pattern | result-count | search-result-1 | search-result-2 |
| Content:hello | 1 | technical task.txt | |
@@ -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 contain these entries with highlight on keyword "simple"
And the search result should eventually contain these entries with highlight on keyword "simple"
| test-text-file.txt |
| test-pdf-file.pdf |
| test-cpp-file.cpp |