mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
api-test: replace sleeps with WaitHelper poll for async state (#3239)
* api-test: replace sleeps with WaitHelper poll for async state * php-style
This commit is contained in:
1 parent
799f98339e
commit
76ff13b47e
4 files changed
+117
-55
No files matched your search
@@ -0,0 +1,56 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @author Viktor Scharf OpenCloud GmbH <v.scharf@opencloud.eu>
|
||||
* @copyright Copyright (c) 2026 OpenCloud GmbH <v.scharf@opencloud.eu>
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License,
|
||||
* as published by the Free Software Foundation;
|
||||
* either version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
/**
|
||||
* Helper for waiting on asynchronous/eventually-consistent server state.
|
||||
*
|
||||
* @package TestHelpers
|
||||
*/
|
||||
class WaitHelper {
|
||||
/**
|
||||
* Overall time to keep polling before giving up (seconds).
|
||||
*/
|
||||
public const TIMEOUT_SECONDS = 10;
|
||||
|
||||
/**
|
||||
* Pause between two attempts (milliseconds).
|
||||
*/
|
||||
public const INTERVAL_MS = 500;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return mixed the last result from $makeAttempt
|
||||
*/
|
||||
public static function waitUntil(callable $makeAttempt, callable $shouldStop): mixed {
|
||||
$deadline = \microtime(true) + self::TIMEOUT_SECONDS;
|
||||
$result = $makeAttempt();
|
||||
while (!$shouldStop($result) && \microtime(true) < $deadline) {
|
||||
\usleep(self::INTERVAL_MS * 1000);
|
||||
$result = $makeAttempt();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ use TestHelpers\WebDavHelper;
|
||||
use TestHelpers\HttpRequestHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use TestHelpers\TokenHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\Then;
|
||||
use Behat\Step\When;
|
||||
@@ -2650,40 +2651,34 @@ class GraphContext implements Context {
|
||||
|
||||
$credentials = $this->getAdminOrUserCredentials($user);
|
||||
|
||||
// Sometimes listing shares might not return the updated shares list
|
||||
// so try again until @client.synchronize is true for the max. number of retries (i.e. 10)
|
||||
// and do not retry when the share is expected to be not synced
|
||||
// Sometimes listing shares might not return the updated shares list, so poll
|
||||
// until every share reports @client.synchronize (auto-synced). Do not wait
|
||||
// when retry is disabled or when the user has auto-sync turned off, i.e. when
|
||||
// the share is expected to be not synced.
|
||||
$retryEnabled = ($retryOption === '');
|
||||
$tryAgain = false;
|
||||
$retried = 0;
|
||||
do {
|
||||
$response = GraphHelper::getSharesSharedWithMe(
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => GraphHelper::getSharesSharedWithMe(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$credentials['username'],
|
||||
$credentials['password']
|
||||
);
|
||||
|
||||
$jsonBody = $this->featureContext->getJsonDecodedResponseBodyContent($response);
|
||||
|
||||
if ($retryEnabled) {
|
||||
),
|
||||
function ($response) use ($retryEnabled, $credentials) {
|
||||
if (!$retryEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (!$this->featureContext->getUserAutoSyncSetting($credentials['username'])) {
|
||||
return true;
|
||||
}
|
||||
$jsonBody = $this->featureContext->getJsonDecodedResponseBodyContent($response);
|
||||
foreach ($jsonBody->value as $share) {
|
||||
$autoSync = $this->featureContext->getUserAutoSyncSetting($credentials['username']);
|
||||
$tryAgain = !$share->{'@client.synchronize'}
|
||||
&& $autoSync
|
||||
&& $retried < HttpRequestHelper::numRetriesOnHttpTooEarly();
|
||||
|
||||
if ($tryAgain) {
|
||||
$retried += 1;
|
||||
echo "auto-sync share for user '$user' is enabled\n";
|
||||
echo "but share '$share->name' was not auto-synced, retrying ($retried)...\n";
|
||||
// wait 500ms and try again
|
||||
\usleep(500 * 1000);
|
||||
break;
|
||||
if (!$share->{'@client.synchronize'}) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} while ($tryAgain);
|
||||
);
|
||||
|
||||
$this->featureContext->setResponse($response);
|
||||
$this->featureContext->pushToLastStatusCodesArrays();
|
||||
|
||||
@@ -32,6 +32,7 @@ use TestHelpers\WebDavHelper;
|
||||
use TestHelpers\GraphHelper;
|
||||
use TestHelpers\OcHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\Then;
|
||||
use Behat\Step\When;
|
||||
@@ -3856,15 +3857,26 @@ class SpacesContext implements Context {
|
||||
string $spaceName,
|
||||
TableNode $propertiesTable
|
||||
): void {
|
||||
// NOTE: extracting properties occurs asynchronously
|
||||
// short wait is necessary before getting those properties
|
||||
sleep(2);
|
||||
// NOTE: extracting properties occurs asynchronously after upload, so we need to wait until the properties are available
|
||||
$spaceId = $this->getSpaceIdByName($user, $spaceName);
|
||||
$response = $this->webDavPropertiesContext->getPropertiesOfFolder(
|
||||
$user,
|
||||
$resourceName,
|
||||
$spaceId,
|
||||
$propertiesTable
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => $this->webDavPropertiesContext->getPropertiesOfFolder(
|
||||
$user,
|
||||
$resourceName,
|
||||
$spaceId,
|
||||
$propertiesTable
|
||||
),
|
||||
function ($response) {
|
||||
// check if the response body contains any of the extracted-property leaf names
|
||||
$body = (string) $response->getBody();
|
||||
$response->getBody()->rewind();
|
||||
foreach (["camera-make", "latitude", "longitude", "album", "artist", "width", "height"] as $leaf) {
|
||||
if (\str_contains($body, $leaf)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
@@ -4544,16 +4556,26 @@ class SpacesContext implements Context {
|
||||
$itemId = $this->getFileId($user, $space, $file);
|
||||
}
|
||||
$url = $this->featureContext->getBaseUrl() . "/graph/v1.0/drives/$spaceId/items/$itemId";
|
||||
// NOTE: extracting properties occurs asynchronously
|
||||
// short wait is necessary before getting those properties
|
||||
sleep(2);
|
||||
$this->featureContext->setResponse(
|
||||
HttpRequestHelper::get(
|
||||
|
||||
// NOTE: extracting properties occurs asynchronously after upload, so we need to wait until the properties are available
|
||||
$extractionFacets = ["image", "photo", "location", "audio", "video"];
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => HttpRequestHelper::get(
|
||||
$url,
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
)
|
||||
),
|
||||
function ($response) use ($extractionFacets) {
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
return true;
|
||||
}
|
||||
$body = $this->featureContext->getJsonDecodedResponseBodyContent($response);
|
||||
return \is_object($body)
|
||||
&& !empty(\array_intersect($extractionFacets, \array_keys((array) $body)));
|
||||
}
|
||||
);
|
||||
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ use TestHelpers\HttpRequestHelper;
|
||||
use TestHelpers\WebDavHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use TestHelpers\UploadHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\When;
|
||||
|
||||
@@ -238,22 +239,10 @@ class TUSContext implements Context {
|
||||
): void {
|
||||
$resourceLocation = $this->getLastTusResourceLocation();
|
||||
|
||||
$retried = 0;
|
||||
do {
|
||||
$tryAgain = false;
|
||||
$response = $this->uploadChunkToTUSLocation($user, $resourceLocation, $offset, $data);
|
||||
// retry on 409 Conflict (Offset mismatch during TUS upload)
|
||||
if ($response->getStatusCode() === 409) {
|
||||
$tryAgain = true;
|
||||
}
|
||||
$tryAgain = $tryAgain && $retried < HttpRequestHelper::numRetriesOnHttpTooEarly();
|
||||
if ($tryAgain) {
|
||||
$retried += 1;
|
||||
echo "Offset mismatch during TUS upload, retrying ($retried)...\n";
|
||||
// wait 1s and try again
|
||||
\sleep(1);
|
||||
}
|
||||
} while ($tryAgain);
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => $this->uploadChunkToTUSLocation($user, $resourceLocation, $offset, $data),
|
||||
fn ($response) => $response->getStatusCode() !== 409
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user