Skip to content

Commit 867da07

Browse files
committed
[full-ci][tests-only] test: fix some test flakiness (#2003)
* test: check content after upload Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> test: check content with retry Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: check empty body before json decoding Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: wait post-processing for webdav requests if applicable Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: check token before doing request Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> test: check body before json decoding Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> test: add wait step Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> --------- Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>
1 parent 3896388 commit 867da07

10 files changed

Lines changed: 215 additions & 27 deletions

File tree

tests/acceptance/TestHelpers/HttpRequestHelper.php

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Sabre\Xml\LibXMLException;
3434
use Sabre\Xml\Reader;
3535
use GuzzleHttp\Pool;
36+
use Symfony\Component\HttpFoundation\Response;
3637

3738
/**
3839
* Helper for HTTP requests
@@ -74,7 +75,6 @@ public static function numRetriesOnHttpTooEarly(): int {
7475
* than download it all up-front.
7576
* @param int|null $timeout
7677
* @param Client|null $client
77-
* @param string|null $bearerToken
7878
*
7979
* @return ResponseInterface
8080
* @throws GuzzleException
@@ -92,8 +92,42 @@ public static function sendRequestOnce(
9292
bool $stream = false,
9393
?int $timeout = 0,
9494
?Client $client = null,
95-
?string $bearerToken = null
9695
): ResponseInterface {
96+
$bearerToken = null;
97+
if (TokenHelper::useBearerToken() && $user && $user !== 'public') {
98+
$bearerToken = TokenHelper::getTokens($user, $password, $url)['access_token'];
99+
// check token is still valid
100+
$parsedUrl = parse_url($url);
101+
$baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
102+
$baseUrl .= isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';
103+
$testUrl = $baseUrl . "/graph/v1.0/use/$user";
104+
if (OcHelper::isTestingOnReva()) {
105+
$url = $baseUrl . "/ocs/v2.php/cloud/users/$user";
106+
}
107+
// check token validity with a GET request
108+
$c = self::createClient(
109+
$user,
110+
$password,
111+
$config,
112+
$cookies,
113+
$stream,
114+
$timeout,
115+
$bearerToken
116+
);
117+
$testReq = self::createRequest($testUrl, $xRequestId, 'GET');
118+
try {
119+
$testRes = $c->send($testReq);
120+
} catch (RequestException $ex) {
121+
$testRes = $ex->getResponse();
122+
if ($testRes && $testRes->getStatusCode() === Response::HTTP_UNAUTHORIZED) {
123+
// token is invalid or expired, get a new one
124+
echo "[INFO] Bearer token expired or invalid, getting a new one...\n";
125+
TokenHelper::clearAllTokens();
126+
$bearerToken = TokenHelper::getTokens($user, $password, $url)['access_token'];
127+
}
128+
}
129+
}
130+
97131
if ($client === null) {
98132
$client = self::createClient(
99133
$user,
@@ -160,6 +194,24 @@ public static function sendRequestOnce(
160194
}
161195

162196
HttpLogger::logResponse($response);
197+
198+
// wait for post-processing to finish if applicable
199+
if (WebdavHelper::isDAVRequest($url)
200+
&& \str_starts_with($url, OcHelper::getServerUrl())
201+
&& \in_array($method, ["PUT", "MOVE", "COPY"])
202+
&& \in_array($response->getStatusCode(), [Response::HTTP_CREATED, Response::HTTP_NO_CONTENT])
203+
&& OcConfigHelper::getPostProcessingDelay() === 0
204+
) {
205+
if (\in_array($method, ["MOVE", "COPY"])) {
206+
$url = $headers['Destination'];
207+
}
208+
WebDavHelper::waitForPostProcessingToFinish(
209+
$url,
210+
$user,
211+
$password,
212+
$headers,
213+
);
214+
}
163215
return $response;
164216
}
165217

@@ -203,13 +255,6 @@ public static function sendRequest(
203255
} else {
204256
$debugResponses = false;
205257
}
206-
// use basic auth for 'public' user or no user
207-
if ($user === 'public' || $user === null || $user === '') {
208-
$bearerToken = null;
209-
} else {
210-
$useBearerToken = TokenHelper::useBearerToken();
211-
$bearerToken = $useBearerToken ? TokenHelper::getTokens($user, $password, $url)['access_token'] : null;
212-
}
213258

214259
$sendRetryLimit = self::numRetriesOnHttpTooEarly();
215260
$sendCount = 0;
@@ -228,7 +273,6 @@ public static function sendRequest(
228273
$stream,
229274
$timeout,
230275
$client,
231-
$bearerToken,
232276
);
233277

234278
if ($response->getStatusCode() >= 400
@@ -256,7 +300,8 @@ public static function sendRequest(
256300
// we need to repeat the send request, because we got HTTP_TOO_EARLY or HTTP_CONFLICT
257301
// wait 1 second before sending again, to give the server some time
258302
// to finish whatever post-processing it might be doing.
259-
self::debugResponse($response);
303+
echo "[INFO] Received '" . $response->getStatusCode() .
304+
"' status code, retrying request ($sendCount)...\n";
260305
\sleep(1);
261306
}
262307
} while ($loopAgain);

tests/acceptance/TestHelpers/OcConfigHelper.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@
3030
* A helper class for configuring OpenCloud server
3131
*/
3232
class OcConfigHelper {
33+
public static $postProcessingDelay = 0;
34+
35+
/**
36+
* @return int
37+
*/
38+
public static function getPostProcessingDelay(): int {
39+
return self::$postProcessingDelay;
40+
}
41+
42+
/**
43+
* @param string $postProcessingDelay
44+
*
45+
* @return void
46+
*/
47+
public static function setPostProcessingDelay(string $postProcessingDelay): void {
48+
// extract number from string
49+
$delay = (int) filter_var($postProcessingDelay, FILTER_SANITIZE_NUMBER_INT);
50+
self::$postProcessingDelay = $delay;
51+
}
52+
3353
/**
3454
* @param string $url
3555
* @param string $method

tests/acceptance/TestHelpers/TokenHelper.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ public static function getTokens(string $username, string $password, string $url
8484
$tokenData = [
8585
'access_token' => $refreshedToken['access_token'],
8686
'refresh_token' => $refreshedToken['refresh_token'],
87-
'expires_at' => time() + 300 // 5 minutes
87+
// set expiry to 240 (4 minutes) seconds to allow for some buffer
88+
// token actually expires in 300 seconds (5 minutes)
89+
'expires_at' => time() + 240
8890
];
8991
self::$tokenCache[$cacheKey] = $tokenData;
9092
return $tokenData;
@@ -100,7 +102,9 @@ public static function getTokens(string $username, string $password, string $url
100102
$tokenData = [
101103
'access_token' => $tokens['access_token'],
102104
'refresh_token' => $tokens['refresh_token'],
103-
'expires_at' => time() + 290 // set expiry to 290 seconds to allow for some buffer
105+
// set expiry to 240 (4 minutes) seconds to allow for some buffer
106+
// token actually expires in 300 seconds (5 minutes)
107+
'expires_at' => time() + 240
104108
];
105109

106110
// Save to cache

tests/acceptance/TestHelpers/WebDavHelper.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,4 +923,45 @@ public static function getMtimeOfResource(
923923
$mtime = new DateTime($xmlPart[0]->__toString());
924924
return $mtime->format('U');
925925
}
926+
927+
/**
928+
* wait until the reqeust doesn't return 425 anymore
929+
*
930+
* @param string $url
931+
* @param ?string $user
932+
* @param ?string $password
933+
* @param ?array $headers
934+
*
935+
* @return void
936+
*/
937+
public static function waitForPostProcessingToFinish(
938+
string $url,
939+
?string $user = null,
940+
?string $password = null,
941+
?array $headers = [],
942+
): void {
943+
$retried = 0;
944+
do {
945+
$response = HttpRequestHelper::sendRequest(
946+
$url,
947+
'check-425-status',
948+
'GET',
949+
$user,
950+
$password,
951+
$headers,
952+
);
953+
$statusCode = $response->getStatusCode();
954+
if ($statusCode !== 425) {
955+
return;
956+
}
957+
$tryAgain = $retried < HttpRequestHelper::numRetriesOnHttpTooEarly();
958+
if ($tryAgain) {
959+
$retried += 1;
960+
echo "[INFO] Waiting for post processing to finish, attempt ($retried)...\n";
961+
// wait 1s and try again
962+
\sleep(1);
963+
}
964+
} while ($tryAgain);
965+
echo "[ERROR] 10 seconds timeout! Post processing did not finish in time.\n";
966+
}
926967
}

tests/acceptance/bootstrap/FeatureContext.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2026,8 +2026,12 @@ public function getJsonDecodedResponse(?ResponseInterface $response = null): arr
20262026
if ($response === null) {
20272027
$response = $this->getResponse();
20282028
}
2029+
$body = (string)$response->getBody();
2030+
if (!$body) {
2031+
return [];
2032+
}
20292033
return \json_decode(
2030-
(string)$response->getBody(),
2034+
$body,
20312035
true
20322036
);
20332037
}

tests/acceptance/bootstrap/OcConfigContext.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public function asyncUploadHasBeenEnabledWithDelayedPostProcessing(string $delay
6868
$response->getStatusCode(),
6969
"Failed to set async upload with delayed post processing"
7070
);
71+
OcConfigHelper::setPostProcessingDelay($delayTime);
7172
}
7273

7374
/**
@@ -90,6 +91,9 @@ public function theConfigHasBeenSetTo(string $configVariable, string $configValu
9091
$response->getStatusCode(),
9192
"Failed to set config $configVariable=$configValue"
9293
);
94+
if ($configVariable === "POSTPROCESSING_DELAY") {
95+
OcConfigHelper::setPostProcessingDelay($configValue);
96+
}
9397
}
9498

9599
/**
@@ -184,6 +188,9 @@ public function theConfigHasBeenSetToValue(TableNode $table): void {
184188
$envs = [];
185189
foreach ($table->getHash() as $row) {
186190
$envs[$row['config']] = $row['value'];
191+
if ($row['config'] === "POSTPROCESSING_DELAY") {
192+
OcConfigHelper::setPostProcessingDelay($row['value']);
193+
}
187194
}
188195

189196
$response = OcConfigHelper::reConfigureOc($envs);
@@ -200,6 +207,7 @@ public function theConfigHasBeenSetToValue(TableNode $table): void {
200207
* @return void
201208
*/
202209
public function rollbackOc(): void {
210+
OcConfigHelper::setPostProcessingDelay('0');
203211
$response = OcConfigHelper::rollbackOc();
204212
Assert::assertEquals(
205213
200,

tests/acceptance/bootstrap/Provisioning.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ public function usersHaveBeenCreated(
607607
Assert::assertEquals(
608608
201,
609609
$response->getStatusCode(),
610-
__METHOD__ . " cannot create user '$userName' using Graph API.\nResponse:" .
610+
__METHOD__ . " cannot create user '$userName'.\nResponse:" .
611611
json_encode($this->getJsonDecodedResponse($response))
612612
);
613613

@@ -1083,7 +1083,7 @@ public function userHasBeenCreated(
10831083
Assert::assertEquals(
10841084
201,
10851085
$response->getStatusCode(),
1086-
__METHOD__ . " cannot create user '$user' using Graph API.\nResponse:" .
1086+
__METHOD__ . " cannot create user '$user'.\nResponse:" .
10871087
json_encode($this->getJsonDecodedResponse($response))
10881088
);
10891089
$userId = $this->getJsonDecodedResponse($response)['id'];

tests/acceptance/bootstrap/SpacesContext.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,9 @@ public function rememberTheAvailableSpaces(?ResponseInterface $response = null):
750750
} else {
751751
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
752752
}
753+
if (!$rawBody) {
754+
throw new Exception(__METHOD__ . " - Response body is empty");
755+
}
753756
$drives = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
754757
if (isset($drives["value"])) {
755758
$drives = $drives["value"];

tests/acceptance/bootstrap/WebDav.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use PHPUnit\Framework\Assert;
2626
use Psr\Http\Message\ResponseInterface;
2727
use GuzzleHttp\Stream\StreamInterface;
28+
use TestHelpers\OcConfigHelper;
2829
use TestHelpers\OcHelper;
2930
use TestHelpers\UploadHelper;
3031
use TestHelpers\WebDavHelper;
@@ -743,6 +744,7 @@ public function userDownloadsFileWithRangeUsingWebDavApi(string $user, string $f
743744

744745
/**
745746
* @When the user waits for :time seconds for postprocessing to finish
747+
* @When the user waits for :time seconds
746748
*
747749
* @param int $time
748750
*
@@ -973,6 +975,61 @@ public function contentOfFileForUserShouldBe(string $fileName, string $user, str
973975
$this->checkDownloadedContentMatches($content, '', $response);
974976
}
975977

978+
/**
979+
* check file content with retry
980+
*
981+
* @param string $user
982+
* @param string $fileName
983+
* @param string $content
984+
*
985+
* @return void
986+
* @throws Exception
987+
*/
988+
public function checkFileContentWithRetry(string $user, string $fileName, string $content): void {
989+
$retried = 0;
990+
do {
991+
$tryAgain = false;
992+
$response = $this->downloadFileAsUserUsingPassword($this->getActualUsername($user), $fileName);
993+
$status = $response->getStatusCode();
994+
$downloadedContent = $response->getBody()->getContents();
995+
if ($status !== 200) {
996+
$tryAgain = true;
997+
$message = "Expected '200' but got '$status'";
998+
} elseif ($downloadedContent !== $content) {
999+
$tryAgain = true;
1000+
$message = "Expected content '$content' but got '$downloadedContent'";
1001+
}
1002+
$tryAgain = $tryAgain && $retried < HttpRequestHelper::numRetriesOnHttpTooEarly();
1003+
if ($tryAgain) {
1004+
$retried += 1;
1005+
echo "[INFO] File content mismatch. $message, checking content again ($retried)...\n";
1006+
1007+
// break the loop if status is 425 as the request will already be retried
1008+
if ($status === HttpRequestHelper::HTTP_TOO_EARLY) {
1009+
break;
1010+
}
1011+
1012+
// wait 1s and try again
1013+
\sleep(1);
1014+
}
1015+
} while ($tryAgain);
1016+
$this->theHTTPStatusCodeShouldBe(200, '', $response);
1017+
$this->checkDownloadedContentMatches($content, '', $response);
1018+
}
1019+
1020+
/**
1021+
* @Then as :user the final content of file :fileName should be :content
1022+
*
1023+
* @param string $user
1024+
* @param string $fileName
1025+
* @param string $content
1026+
*
1027+
* @return void
1028+
*/
1029+
public function asUserFinalContentOfFileShouldBe(string $user, string $fileName, string $content): void {
1030+
$this->checkFileContentWithRetry($user, $fileName, $content);
1031+
}
1032+
9761033
/**
9771034
* @Then /^the content of the following files for user "([^"]*)" should be "([^"]*)"$/
9781035
*
@@ -2272,6 +2329,11 @@ public function userHasUploadedAFileWithContentTo(
22722329
"HTTP status code was not 201 or 204 while trying to upload file '$destination' for user '$user'",
22732330
$response
22742331
);
2332+
2333+
// check uploaded content only if post-processing delay is not configured
2334+
if (OcConfigHelper::getPostProcessingDelay() === 0) {
2335+
$this->checkFileContentWithRetry($user, $destination, $content);
2336+
}
22752337
return $response->getHeader('oc-fileid');
22762338
}
22772339

0 commit comments

Comments
 (0)