1
0
mirror of https://github.com/owncloud/ocis.git synced 2026-10-23 23:29:02 +03:00
Files
ocis/tests/acceptance/bootstrap/EmailContext.php
Pradip Subedi a66ae6783d [tests-only][full-ci] test: fix flaky grouped email notification (#12882)
* test:start auth-bearer service separately instead of via OCIS_ADD_RUN_SERVICES

* test: fix timezone-dependent expiry assertions and flaky email notification

* test: simplify host timezone detection and log the UTC fallback

* test: oonly run cli commands test suite

* test: run all suites

* test: resolve the expiry timestamp in the timezone the mail was sent in

* test: inline the UTC expiration date conversion and drop the helper

* test: simplify the mail timezone formatter and restore the CI coverage guard
2026-10-01 16:37:56 +05:45

279 lines
7.7 KiB
PHP

<?php declare(strict_types=1);
/**
* ownCloud
*
* @author Amrita Shrestha <amrita@jankaritech.com>
* @copyright Copyright (c) 2025 Amrita Shrestha amrita@jankaritech.com
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Gherkin\Node\PyStringNode;
use PHPUnit\Framework\Assert;
use GuzzleHttp\Exception\GuzzleException;
use TestHelpers\EmailHelper;
use TestHelpers\HttpRequestHelper;
use TestHelpers\GraphHelper;
use TestHelpers\BehatHelper;
require_once 'bootstrap.php';
/**
* Defines application features from the specific context.
*/
class EmailContext implements Context {
private FeatureContext $featureContext;
private SpacesContext $spacesContext;
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
* @throws Exception
*/
public function before(BeforeScenarioScope $scope): void {
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = BehatHelper::getContext($scope, $environment, 'FeatureContext');
$this->spacesContext = BehatHelper::getContext($scope, $environment, 'SpacesContext');
}
/**
* @BeforeScenario @email
* @AfterScenario @email
*
* @return void
* @throws GuzzleException
*/
public function clearAllEmails(): void {
EmailHelper::deleteAllEmails();
}
/**
* @Then user :user should have received the following email from user :sender about the share of project space :spaceName
*
* @param string $user
* @param string $sender
* @param string $spaceName
* @param PyStringNode $content
*
* @return void
* @throws Exception
*/
public function userShouldHaveReceivedTheFollowingEmailFromUserAboutTheShareOfProjectSpace(
string $user,
string $sender,
string $spaceName,
PyStringNode $content,
): void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$this->featureContext->setResponse(
GraphHelper::getMySpaces(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
'',
),
);
$this->assertEmailContains(
$user,
$rawExpectedEmailBodyContent,
false,
$sender,
[
[
"code" => "%space_id%",
"function" =>
[$this->spacesContext, "getSpaceIdByName"],
"parameter" => [$sender, $spaceName],
],
],
);
}
/**
* @Then user :user should have received the following email from user :sender
*
* @param string $user
* @param string $sender
* @param PyStringNode $content
*
* @return void
* @throws Exception
*/
public function userShouldHaveReceivedTheFollowingEmailFromUser(
string $user,
string $sender,
PyStringNode $content,
): void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$this->assertEmailContains($user, $rawExpectedEmailBodyContent, false, $sender);
}
/**
* @Then user :user should have :count emails
*
* @param string $user
* @param int $count
*
* @return void
* @throws GuzzleException
*/
public function userShouldHaveEmail(string $user, int $count): void {
$address = $this->featureContext->getEmailAddressForUser($user);
$query = 'to:' . $address;
$response = EmailHelper::searchEmails($query);
$emails = $this->featureContext->getJsonDecodedResponse($response);
if ($emails["messages_count"] <= $count) {
echo "[INFO] Mailbox is empty. Retrying...\n";
// Wait for 1 second and try again
// the mailbox might not be created yet
sleep(1);
$response = EmailHelper::searchEmails($query);
$emails = $this->featureContext->getJsonDecodedResponse($response);
}
Assert::assertSame(
$count,
$emails["messages_count"],
"Expected '$address' received mail total '$count' email but got " . $emails["messages_count"] . " email",
);
}
/**
* @Then user :user should have received the following email from user :sender ignoring whitespaces
*
* @param string $user
* @param string $sender
* @param PyStringNode $content
*
* @return void
* @throws Exception
*/
public function userShouldHaveReceivedTheFollowingEmailFromUserIgnoringWhitespaces(
string $user,
string $sender,
PyStringNode $content,
): void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$this->assertEmailContains($user, $rawExpectedEmailBodyContent, true, $sender);
}
/***
* @param string $user
* @param string $rawExpectedEmailBodyContent
* @param bool $ignoreWhiteSpace
* @param string|null $sender
* @param array $additionalSubstitutions
*
* @return void
* @throws GuzzleException
*/
public function assertEmailContains(
string $user,
string $rawExpectedEmailBodyContent,
$ignoreWhiteSpace = false,
?string $sender = null,
array $additionalSubstitutions = [],
): void {
$address = $this->featureContext->getEmailAddressForUser($user);
$actualEmailBodyContent = $this->getBodyOfLastEmail($address);
$expectedEmailBodyContent = $this->featureContext->substituteInLineCodes(
$rawExpectedEmailBodyContent,
$sender,
[],
\array_merge(
$additionalSubstitutions,
[
[
"code" => "%expiry_date_in_mail%",
"function" => [$this, "getExpiryDateTimeInEmailTimezone"],
"parameter" => [],
],
],
),
);
if ($ignoreWhiteSpace) {
$expectedEmailBodyContent = preg_replace('/\s+/', '', $expectedEmailBodyContent);
$actualEmailBodyContent = preg_replace('/\s+/', '', $actualEmailBodyContent);
}
Assert::assertStringContainsString(
$expectedEmailBodyContent,
$actualEmailBodyContent,
"The email address '$address' should have received an"
. " email with the body containing $expectedEmailBodyContent
but the received email is $actualEmailBodyContent",
);
}
/**
* Formats the expiry date on the timezone the server used when it sent the mail.
*
* @return string
*/
public function getExpiryDateTimeInEmailTimezone(): string {
return $this->featureContext->getExpiryDateTime()
->setTimezone(HttpRequestHelper::getEmailServerTimezone())
->format('Y-m-d H:i:s');
}
/**
* 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
*
* @param string $emailAddress
* @param int|null $waitTimeSec Time to wait for the email if the email has been delivered
*
* @return string
* @throws GuzzleException
* @throws Exception
*/
public function getBodyOfLastEmail(
string $emailAddress,
?int $waitTimeSec = EMAIL_WAIT_TIMEOUT_SEC,
): string {
$currentTime = \time();
$endTime = $currentTime + $waitTimeSec;
while ($currentTime <= $endTime) {
$query = 'to:' . $emailAddress;
$mailResponse = $this->featureContext->getJsonDecodedResponse(
EmailHelper::searchEmails($query),
);
if ($mailResponse["messages_count"] > 0) {
$lastEmail = $this->featureContext->getJsonDecodedResponse(
EmailHelper::getEmailById("latest", $query),
);
$body = \str_replace(
"\r\n",
"\n",
\quoted_printable_decode($lastEmail["Text"] . "\n" . $lastEmail["HTML"]),
);
return $body;
}
\usleep(STANDARD_SLEEP_TIME_MICROSEC * 50);
$currentTime = \time();
}
throw new Exception("Could not find the email to the address: " . $emailAddress);
}
/**
* @Then user :user should have received the following grouped email
*
* @param string $user
* @param PyStringNode $content
*
* @return void
* @throws Exception|GuzzleException
*/
public function userShouldHaveReceivedTheFollowingGroupedEmail(
string $user,
PyStringNode $content,
): void {
$rawExpectedEmailBodyContent = \str_replace("\r\n", "\n", $content->getRaw());
$this->assertEmailContains($user, $rawExpectedEmailBodyContent);
}
}