[full-ci][tests-only] test: check last email content with retries as emails can be delayed (#2038)

* test: check last email content with retries

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

* test: cleanup unused const

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>

---------

Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>
This commit is contained in:
Sawjan Gurung
2025-12-16 18:08:22 +05:45
committed by GitHub
parent eb1545c087
commit 3ea736c283
4 changed files with 77 additions and 87 deletions

View File

@@ -127,13 +127,10 @@ class EmailHelper {
} }
/** /**
* Returns the body of the last received email for the provided receiver according to the provided email address and the serial number * Returns the body of the last received email for the provided receiver
* For email number, 1 means the latest one
* *
* @param string $emailAddress * @param string $emailAddress
* @param string|null $xRequestId * @param string $xRequestId
* @param int|null $emailNumber For email number, 1 means the latest one
* @param int|null $waitTimeSec Time to wait for the email if the email has been delivered
* *
* @return string * @return string
* @throws GuzzleException * @throws GuzzleException
@@ -142,48 +139,40 @@ class EmailHelper {
public static function getBodyOfLastEmail( public static function getBodyOfLastEmail(
string $emailAddress, string $emailAddress,
string $xRequestId, string $xRequestId,
?int $emailNumber = 1,
?int $waitTimeSec = EMAIL_WAIT_TIMEOUT_SEC
): string { ): string {
$currentTime = \time();
$endTime = $currentTime + $waitTimeSec;
$mailBox = self::getMailBoxFromEmail($emailAddress); $mailBox = self::getMailBoxFromEmail($emailAddress);
while ($currentTime <= $endTime) { $emails = self::getMailboxInformation($mailBox, $xRequestId);
$mailboxResponse = self::getMailboxInformation($mailBox, $xRequestId); if (!empty($emails)) {
if (!empty($mailboxResponse) && \sizeof($mailboxResponse) >= $emailNumber) { $emailId = \array_pop($emails)->id;
$mailboxId = $mailboxResponse[\sizeof($mailboxResponse) - $emailNumber]->id; $response = self::getBodyOfAnEmailById($mailBox, $emailId, $xRequestId);
$response = self::getBodyOfAnEmailById($mailBox, $mailboxId, $xRequestId); $body = \str_replace(
$body = \str_replace( "\r\n",
"\r\n", "\n",
"\n", \quoted_printable_decode($response->body->text . "\n" . $response->body->html)
\quoted_printable_decode($response->body->text . "\n" . $response->body->html) );
); return $body;
return $body;
}
\usleep(STANDARD_SLEEP_TIME_MICROSEC * 50);
$currentTime = \time();
} }
throw new Exception("Could not find the email to the address: " . $emailAddress); return "";
} }
/** /**
* Deletes all the emails for the provided mailbox * Deletes all the emails for the provided mailbox
* *
* @param string $localInbucketUrl * @param string $url
* @param string|null $xRequestId
* @param string $mailBox * @param string $mailBox
* @param string $xRequestId
* *
* @return ResponseInterface * @return ResponseInterface
* @throws GuzzleException * @throws GuzzleException
*/ */
public static function deleteAllEmailsForAMailbox( public static function deleteAllEmails(
string $localInbucketUrl, string $url,
?string $xRequestId, string $mailBox,
string $mailBox string $xRequestId,
): ResponseInterface { ): ResponseInterface {
return HttpRequestHelper::delete( return HttpRequestHelper::delete(
$localInbucketUrl . "/api/v1/mailbox/" . $mailBox, $url . "/api/v1/mailbox/" . $mailBox,
$xRequestId $xRequestId,
); );
} }
} }

View File

@@ -57,7 +57,7 @@ class HttpRequestHelper {
public static function numRetriesOnHttpTooEarly(): int { public static function numRetriesOnHttpTooEarly(): int {
// Currently reva and OpenCloud may return HTTP_TOO_EARLY // Currently reva and OpenCloud may return HTTP_TOO_EARLY
// So try up to 10 times before giving up. // So try up to 10 times before giving up.
return 10; return STANDARD_RETRY_COUNT;
} }
/** /**
@@ -732,7 +732,7 @@ class HttpRequestHelper {
*/ */
public static function getRequestTimeout(): int { public static function getRequestTimeout(): int {
$timeout = \getenv("REQUEST_TIMEOUT"); $timeout = \getenv("REQUEST_TIMEOUT");
return (int)$timeout ?: 60; return (int)$timeout ?: HTTP_REQUEST_TIMEOUT;
} }
/** /**

View File

@@ -589,36 +589,68 @@ class NotificationContext implements Context {
): void { ): void {
$address = $this->featureContext->getEmailAddressForUser($user); $address = $this->featureContext->getEmailAddressForUser($user);
$this->featureContext->pushEmailRecipientAsMailBox($address); $this->featureContext->pushEmailRecipientAsMailBox($address);
$actualEmailBodyContent = EmailHelper::getBodyOfLastEmail($address, $this->featureContext->getStepLineRef());
if ($ignoreWhiteSpace) { // assert with retries as email delivery might be delayed
$expectedEmailBodyContent = preg_replace('/\s+/', '', $expectedEmailBodyContent); $retried = 0;
$actualEmailBodyContent = preg_replace('/\s+/', '', $actualEmailBodyContent); do {
} $actualEmailBodyContent = EmailHelper::getBodyOfLastEmail(
$address,
$this->featureContext->getStepLineRef()
);
if ($ignoreWhiteSpace) {
$expectedEmailBodyContent = preg_replace('/\s+/', '', $expectedEmailBodyContent);
$actualEmailBodyContent = preg_replace('/\s+/', '', $actualEmailBodyContent);
}
$tryAgain = !\str_contains($actualEmailBodyContent, $expectedEmailBodyContent)
&& $retried <= STANDARD_RETRY_COUNT;
$retried++;
if ($tryAgain) {
$mailBox = EmailHelper::getMailBoxFromEmail($address);
echo "[INFO] Checking last email content for '$mailBox'. (Retry $retried)\n";
// wait for 1 second before trying again
sleep(1);
}
} while ($tryAgain);
Assert::assertStringContainsString( Assert::assertStringContainsString(
$expectedEmailBodyContent, $expectedEmailBodyContent,
$actualEmailBodyContent, $actualEmailBodyContent,
"The email address '$address' should have received an" "The email address '$address' should have received an"
. "email with the body containing $expectedEmailBodyContent . " email with the body containing '$expectedEmailBodyContent'"
but the received email is $actualEmailBodyContent" . " but the received email is '$actualEmailBodyContent'"
); );
} }
/** /**
* Delete all the inbucket emails * Delete all emails from the mailboxes
* *
* @AfterScenario @email * @AfterScenario @email
* *
* @return void * @return void
*/ */
public function clearInbucketMessages(): void { public function clearMailboxes(): void {
$users = \array_keys($this->featureContext->getCreatedUsers());
try { try {
if (!empty($this->featureContext->emailRecipients)) { if (!empty($users)) {
foreach ($this->featureContext->emailRecipients as $emailRecipient) { foreach ($users as $emailRecipient) {
EmailHelper::deleteAllEmailsForAMailbox( $retried = 0;
EmailHelper::getLocalEmailUrl(), do {
$this->featureContext->getStepLineRef(), $res = EmailHelper::deleteAllEmails(
$emailRecipient EmailHelper::getLocalEmailUrl(),
); $emailRecipient,
$this->featureContext->getStepLineRef(),
);
$deleteStatus = $res->getStatusCode();
$mailBox = EmailHelper::getMailboxInformation($emailRecipient);
$tryAgain = ($deleteStatus !== 200 || !empty($mailBox)) && $retried <= STANDARD_RETRY_COUNT;
$retried++;
if ($tryAgain) {
echo "[INFO] Clearing mailbox '$emailRecipient'."
. " Status: $deleteStatus. Emails: " . \count($mailBox) . "."
. " (Retry $retried)\n";
// wait for 1 second before trying again
sleep(1);
}
} while ($tryAgain);
} }
} }
} catch (Exception $e) { } catch (Exception $e) {

View File

@@ -27,51 +27,20 @@ $classLoader->addPsr4("TestHelpers\\", __DIR__ . "/../TestHelpers", true);
$classLoader->register(); $classLoader->register();
// Sleep for 10 milliseconds
if (!\defined('STANDARD_SLEEP_TIME_MILLISEC')) {
\define('STANDARD_SLEEP_TIME_MILLISEC', 10);
}
if (!\defined('STANDARD_SLEEP_TIME_MICROSEC')) {
\define('STANDARD_SLEEP_TIME_MICROSEC', STANDARD_SLEEP_TIME_MILLISEC * 1000);
}
// Long timeout for use in code that needs to wait for known slow UI
if (!\defined('LONG_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('LONG_UI_WAIT_TIMEOUT_MILLISEC', 60000);
}
// Default timeout for use in code that needs to wait for the UI
if (!\defined('STANDARD_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('STANDARD_UI_WAIT_TIMEOUT_MILLISEC', 10000);
}
// Minimum timeout for use in code that needs to wait for the UI
if (!\defined('MINIMUM_UI_WAIT_TIMEOUT_MILLISEC')) {
\define('MINIMUM_UI_WAIT_TIMEOUT_MILLISEC', 500);
}
if (!\defined('MINIMUM_UI_WAIT_TIMEOUT_MICROSEC')) {
\define('MINIMUM_UI_WAIT_TIMEOUT_MICROSEC', MINIMUM_UI_WAIT_TIMEOUT_MILLISEC * 1000);
}
// Minimum timeout for emails
if (!\defined('EMAIL_WAIT_TIMEOUT_SEC')) {
\define('EMAIL_WAIT_TIMEOUT_SEC', 10);
}
if (!\defined('EMAIL_WAIT_TIMEOUT_MILLISEC')) {
\define('EMAIL_WAIT_TIMEOUT_MILLISEC', EMAIL_WAIT_TIMEOUT_SEC * 1000);
}
// Default number of times to retry where retries are useful // Default number of times to retry where retries are useful
if (!\defined('STANDARD_RETRY_COUNT')) { if (!\defined('STANDARD_RETRY_COUNT')) {
\define('STANDARD_RETRY_COUNT', 5); \define('STANDARD_RETRY_COUNT', 10);
} }
// Minimum number of times to retry where retries are useful // Minimum number of times to retry where retries are useful
if (!\defined('MINIMUM_RETRY_COUNT')) { if (!\defined('MINIMUM_RETRY_COUNT')) {
\define('MINIMUM_RETRY_COUNT', 2); \define('MINIMUM_RETRY_COUNT', 2);
} }
// Minimum number of times to retry where retries are useful
if (!\defined('HTTP_REQUEST_TIMEOUT')) {
\define('HTTP_REQUEST_TIMEOUT', 60);
}
// The remote server-under-test might or might not happen to have this directory. // The remote server-under-test might or might not happen to have this directory.
// If it does not exist, then the tests may end up creating it. // If it does not exist, then the tests may end up creating it.
if (!\defined('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER')) { if (!\defined('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER')) {