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

View File

@@ -57,7 +57,7 @@ class HttpRequestHelper {
public static function numRetriesOnHttpTooEarly(): int {
// Currently reva and OpenCloud may return HTTP_TOO_EARLY
// 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 {
$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 {
$address = $this->featureContext->getEmailAddressForUser($user);
$this->featureContext->pushEmailRecipientAsMailBox($address);
$actualEmailBodyContent = EmailHelper::getBodyOfLastEmail($address, $this->featureContext->getStepLineRef());
if ($ignoreWhiteSpace) {
$expectedEmailBodyContent = preg_replace('/\s+/', '', $expectedEmailBodyContent);
$actualEmailBodyContent = preg_replace('/\s+/', '', $actualEmailBodyContent);
}
// assert with retries as email delivery might be delayed
$retried = 0;
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(
$expectedEmailBodyContent,
$actualEmailBodyContent,
"The email address '$address' should have received an"
. "email with the body containing $expectedEmailBodyContent
but the received email is $actualEmailBodyContent"
. " email with the body containing '$expectedEmailBodyContent'"
. " but the received email is '$actualEmailBodyContent'"
);
}
/**
* Delete all the inbucket emails
* Delete all emails from the mailboxes
*
* @AfterScenario @email
*
* @return void
*/
public function clearInbucketMessages(): void {
public function clearMailboxes(): void {
$users = \array_keys($this->featureContext->getCreatedUsers());
try {
if (!empty($this->featureContext->emailRecipients)) {
foreach ($this->featureContext->emailRecipients as $emailRecipient) {
EmailHelper::deleteAllEmailsForAMailbox(
EmailHelper::getLocalEmailUrl(),
$this->featureContext->getStepLineRef(),
$emailRecipient
);
if (!empty($users)) {
foreach ($users as $emailRecipient) {
$retried = 0;
do {
$res = EmailHelper::deleteAllEmails(
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) {

View File

@@ -27,51 +27,20 @@ $classLoader->addPsr4("TestHelpers\\", __DIR__ . "/../TestHelpers", true);
$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
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
if (!\defined('MINIMUM_RETRY_COUNT')) {
\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.
// If it does not exist, then the tests may end up creating it.
if (!\defined('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER')) {