Files
piratepoet/mailpoet/tests/integration/Services/BridgeTest.php
Rostislav Wolny cd5a023b35 Move onSettingsChange from Bridge service to SettingsChangeHandler
Bridge is a low level service that processes requests and responses to/from Bridge API.
This change is needed so that we can remove dependency on MailPoet\Util\License\Features\Subscribers
service from the Bridge. The dependecy is a higher level service and may easily cause a circular dependency issue.

The SettingsChangeHandler is service for handling side effects
when saving settings. This feels as a better place to put the functionality.
[MAILPOET-5191]
2023-05-10 13:05:37 +02:00

497 lines
16 KiB
PHP

<?php declare(strict_types = 1);
namespace MailPoet\Test\Services;
use Codeception\Util\Stub;
use MailPoet\Mailer\Mailer;
use MailPoet\Services\Bridge;
use MailPoet\Services\Bridge\API;
use MailPoet\Services\Bridge\BridgeTestMockAPI as MockAPI;
use MailPoet\Settings\SettingsController;
use MailPoet\WP\Functions as WPFunctions;
require_once('BridgeTestMockAPI.php');
class BridgeTest extends \MailPoetTest {
public $usedPremiumKey;
public $expiringPremiumKey;
public $uncheckableKey;
public $underPrivilegedKey;
public $usedKey;
public $expiringKey;
public $invalidKey;
public $validKey;
/** @var SettingsController */
private $settings;
/** @var Bridge */
private $bridge;
public function _before() {
parent::_before();
$this->validKey = 'abcdefghijklmnopqrstuvwxyz';
$this->invalidKey = '401' . $this->validKey;
$this->expiringKey = 'expiring' . $this->validKey;
$this->usedKey = '402' . $this->validKey;
$this->underPrivilegedKey = '403' . $this->validKey;
$this->uncheckableKey = '503' . $this->validKey;
$this->expiringPremiumKey = 'expiring' . $this->validKey;
$this->usedPremiumKey = '402' . $this->validKey;
$this->bridge = new Bridge();
$this->bridge->api = new MockAPI('key');
$this->settings = SettingsController::getInstance();
}
public function testItChecksIfCurrentSendingMethodIsMailpoet() {
$this->setMailPoetSendingMethod();
expect(Bridge::isMPSendingServiceEnabled())->true();
}
public function testMPCheckReturnsFalseWhenMailerThrowsException() {
$this->settings->set(Mailer::MAILER_CONFIG_SETTING_NAME, '');
expect(Bridge::isMPSendingServiceEnabled())->false();
}
public function testItChecksIfPremiumKeyIsSpecified() {
expect(Bridge::isPremiumKeySpecified())->false();
$this->fillPremiumKey();
expect(Bridge::isPremiumKeySpecified())->true();
}
public function testItInstantiatesDefaultAPI() {
$this->bridge->api = null;
expect($this->bridge->getApi('key') instanceof API)->true();
}
public function testItChecksValidMSSKey() {
$result = $this->bridge->checkMSSKey($this->validKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_VALID);
}
public function testItChecksInvalidMSSKey() {
$result = $this->bridge->checkMSSKey($this->invalidKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_INVALID);
}
public function testItChecksExpiringMSSKey() {
$result = $this->bridge->checkMSSKey($this->expiringKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_EXPIRING);
expect($result['data']['expire_at'])->notEmpty();
}
public function testItChecksAlreadyUsed() {
$result = $this->bridge->checkMSSKey($this->usedKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_ALREADY_USED);
}
public function testItChecksForbiddenEndpointMSSKey() {
$result = $this->bridge->checkMSSKey($this->underPrivilegedKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_VALID_UNDERPRIVILEGED);
}
public function testItReturnsErrorStateOnEmptyAPIResponseCodeDuringMSSCheck() {
$api = Stub::make(new API(null), ['checkMSSKey' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->checkMSSKey($this->validKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_CHECK_ERROR);
}
public function testItStoresExpectedMSSKeyStates() {
$states = [
Bridge::KEY_VALID => $this->validKey,
Bridge::KEY_INVALID => $this->invalidKey,
Bridge::KEY_EXPIRING => $this->expiringKey,
Bridge::KEY_ALREADY_USED => $this->usedKey,
Bridge::CHECK_ERROR_UNAVAILABLE => $this->uncheckableKey,
Bridge::KEY_VALID_UNDERPRIVILEGED => $this->underPrivilegedKey,
];
foreach ($states as $state => $key) {
$state = ['state' => $state];
$this->bridge->storeMSSKeyAndState($key, $state);
expect($this->getMSSKey())->equals($key);
expect($this->getMSSKeyState())->equals($state);
}
}
public function testItDoesNotStoreErroneousOrUnexpectedMSSKeyStates() {
$states = [
['state' => Bridge::KEY_CHECK_ERROR],
[],
];
foreach ($states as $state) {
$this->bridge->storeMSSKeyAndState($this->validKey, $state);
expect($this->getMSSKey())->notEquals($this->validKey);
expect($this->getMSSKeyState())->notEquals($state);
}
}
public function testItChecksValidPremiumKey() {
$result = $this->bridge->checkPremiumKey($this->validKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_VALID);
}
public function testItChecksInvalidPremiumKey() {
$result = $this->bridge->checkPremiumKey($this->invalidKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_INVALID);
}
public function testItChecksAlreadyUsedPremiumKey() {
$result = $this->bridge->checkPremiumKey($this->usedPremiumKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_ALREADY_USED);
}
public function testItChecksForbiddenEndpointPremiumKey() {
$result = $this->bridge->checkPremiumKey($this->underPrivilegedKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_VALID_UNDERPRIVILEGED);
}
public function testItChecksExpiringPremiumKey() {
$result = $this->bridge->checkPremiumKey($this->expiringPremiumKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_EXPIRING);
expect($result['data']['expire_at'])->notEmpty();
}
public function testItReturnsErrorStateOnEmptyAPIResponseCodeDuringPremiumCheck() {
$api = Stub::make(new API(null), ['checkPremiumKey' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->checkPremiumKey($this->validKey);
expect($result)->notEmpty();
expect($result['state'])->equals(Bridge::KEY_CHECK_ERROR);
}
public function testItStoresExpectedPremiumKeyStates() {
$states = [
Bridge::KEY_VALID => $this->validKey,
Bridge::KEY_INVALID => $this->invalidKey,
Bridge::KEY_ALREADY_USED => $this->usedPremiumKey,
Bridge::KEY_EXPIRING => $this->expiringKey,
];
foreach ($states as $state => $key) {
$state = ['state' => $state];
$this->bridge->storePremiumKeyAndState($key, $state);
expect($this->getPremiumKey())->equals($key);
expect($this->getPremiumKeyState())->equals($state);
}
}
public function testItDoesNotStoreErroneousOrUnexpectedPremiumKeyStates() {
$states = [
['state' => Bridge::KEY_CHECK_ERROR],
[],
];
foreach ($states as $state) {
$this->bridge->storePremiumKeyAndState($this->validKey, $state);
expect($this->getPremiumKey())->notEquals($this->validKey);
expect($this->getPremiumKeyState())->notEquals($state);
}
}
public function testItInvalidatesMSSKey() {
$this->bridge->storeMSSKeyAndState($this->validKey, ['state' => Bridge::KEY_VALID]);
$storedState = $this->getMssKeyState() ?? [];
expect($storedState['state'])->equals(Bridge::KEY_VALID);
$this->bridge->invalidateMssKey();
$storedState = $this->getMssKeyState() ?? [];
expect($storedState['state'])->equals(Bridge::KEY_INVALID);
}
public function testItPingsBridge() {
if (getenv('WP_TEST_ENABLE_NETWORK_TESTS') !== 'true') $this->markTestSkipped();
expect(Bridge::pingBridge())->true();
}
public function testItAllowsChangingRequestTimeout() {
$wpRemotePostArgs = [];
$wp = Stub::make(new WPFunctions, [
'wpRemotePost' => function() use (&$wpRemotePostArgs) {
$wpRemotePostArgs = func_get_args();
},
]);
$api = new API('test_key', $wp);
// test default request value
$api->sendMessages('test');
expect($wpRemotePostArgs[1]['timeout'])->equals(API::REQUEST_TIMEOUT);
// test custom request value
$customRequestValue = 20;
$filter = function() use ($customRequestValue) {
return $customRequestValue;
};
$wp = new WPFunctions;
$wp->addFilter('mailpoet_bridge_api_request_timeout', $filter);
$api->sendMessages('test');
expect($wpRemotePostArgs[1]['timeout'])->equals($customRequestValue);
$wp->removeFilter('mailpoet_bridge_api_request_timeout', $filter);
}
public function testItReturnsOnlyAuthorizedEmails() {
$array = [
'pending' => ['pending@email.com'],
'authorized' => ['authorized@email.com'],
'main' => 'main@email.com',
];
$api = Stub::make(new API(null), ['getAuthorizedEmailAddresses' => $array], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedEmailAddresses();
expect($result)->same(['authorized@email.com']);
}
public function testItReturnsAllUserEmails() {
$array = [
'pending' => ['pending@email.com'],
'authorized' => ['authorized@email.com'],
'main' => 'main@email.com',
];
$api = Stub::make(new API(null), ['getAuthorizedEmailAddresses' => $array], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedEmailAddresses('all');
expect($result)->same($array);
}
public function testItReturnsAnEmptyArrayIfNoEmailForAllParam() {
$api = Stub::make(new API(null), ['getAuthorizedEmailAddresses' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedEmailAddresses('all');
expect($result)->same([]);
}
public function testItReturnsAnEmptyArrayIfNoEmailForAuthorizedParam() {
$api = Stub::make(new API(null), ['getAuthorizedEmailAddresses' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedEmailAddresses();
expect($result)->same([]);
}
public function testItReturnsAnEmptyArrayIfNoNullForAuthorizedParam() {
$api = Stub::make(new API(null), ['getAuthorizedEmailAddresses' => null], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedEmailAddresses();
expect($result)->same([]);
}
public function testItReturnsTheRightDataForSenderDomains() {
// when API returns null
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => null], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains();
expect($result)->same([]);
// when API returns an empty array []
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains();
expect($result)->same([]);
// when arg param is 'all'
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => []], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains('all');
expect($result)->same([]);
}
public function testItReturnsSenderDomainsDnsRecords() {
$domainData = MockAPI::VERIFIED_DOMAIN_RESPONSE;
$domainData['domain'] = 'example.com';
$data = [$domainData];
// with a custom sender domain param
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => $data], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains('example.com');
expect($result)->same($data[0]['dns']);
// with a custom sender domain param that does not exist
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => $data], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains('mailpoet.com');
expect($result)->same([]);
// when param is all
$returnDataForAllParam = [
'example.com' => $data[0]['dns'],
];
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => $data], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains('all');
expect($result)->same($returnDataForAllParam);
// when param is not provided
$returnDataForNoArgs = [
'example.com' => $data[0]['dns'],
];
$api = Stub::make(new API(null), ['getAuthorizedSenderDomains' => $data], $this);
$this->bridge->api = $api;
$result = $this->bridge->getAuthorizedSenderDomains();
expect($result)->same($returnDataForNoArgs);
}
public function testItCanCreateSenderDomain() {
$result = $this->bridge->createAuthorizedSenderDomain('mailpoet.com');
expect($result)->notEmpty();
expect(isset($result['error']))->false();
expect($result[0]['host'])->equals('mailpoet1._domainkey.example.com');
}
public function testItDoesntCreateSenderDomainThatExists() {
$result = $this->bridge->createAuthorizedSenderDomain('existing.com');
expect($result)->notEmpty();
expect($result['error'])->equals('This domain was already added to the list.');
expect($result['status'])->equals(false);
}
public function testTheSenderDomainApiReturnsValidDataType() {
$result = $this->bridge->getAuthorizedSenderDomains('mailpoet.com');
expect($result)->notEmpty();
expect($result[0]['host'])->equals('mailpoet1._domainkey.example.com');
expect($result[0]['value'])->equals('dkim1.sendingservice.net');
expect($result[0]['type'])->equals('CNAME');
expect($result[0]['status'])->equals('valid');
expect($result[0]['message'])->equals('');
}
public function testItCanVerifySenderDomain() {
$result = $this->bridge->verifyAuthorizedSenderDomain('mailpoet.com');
expect($result)->notEmpty();
expect($result['ok'])->equals(true); // verified
}
public function testItSavesAccessRestrictionForUnderprivilegePremiumKeys() {
$api = $this->createMock(API::class);
// Insufficient privileges
$this->checkKeyAccessRestrictionSetProperly(
'premium',
'Insufficient privileges',
Bridge::KEY_ACCESS_INSUFFICIENT_PRIVILEGES
);
// Email volume limit
$this->checkKeyAccessRestrictionSetProperly(
'premium',
'Email volume limit reached',
Bridge::KEY_ACCESS_EMAIL_VOLUME_LIMIT
);
// Subscribers limit
$this->checkKeyAccessRestrictionSetProperly(
'premium',
'Subscribers limit reached',
Bridge::KEY_ACCESS_SUBSCRIBERS_LIMIT
);
// Unknown value
$this->checkKeyAccessRestrictionSetProperly(
'premium',
'Nonsense message',
null
);
}
public function testItSavesAccessRestrictionForUnderprivilegeMSSKeys() {
$api = $this->createMock(API::class);
// Insufficient privileges
$this->checkKeyAccessRestrictionSetProperly(
'mss',
'Insufficient privileges',
Bridge::KEY_ACCESS_INSUFFICIENT_PRIVILEGES
);
// Email volume limit
$this->checkKeyAccessRestrictionSetProperly(
'mss',
'Email volume limit reached',
Bridge::KEY_ACCESS_EMAIL_VOLUME_LIMIT
);
// Subscribers limit
$this->checkKeyAccessRestrictionSetProperly(
'mss',
'Subscribers limit reached',
Bridge::KEY_ACCESS_SUBSCRIBERS_LIMIT
);
// Unknown value
$this->checkKeyAccessRestrictionSetProperly(
'mss',
'Nonsense message',
null
);
}
private function checkKeyAccessRestrictionSetProperly(string $keyType, string $errorMessage, $expectedAccessRestriction) {
$api = $this->createMock(API::class);
$method = $keyType === 'premium' ? 'checkPremiumKey' : 'checkMSSKey';
$api->method($method)->willReturn([
'code' => 403,
'error_message' => $errorMessage,
]);
$this->bridge->api = $api;
$result = $this->bridge->$method('abc');
expect($result)->notEmpty();
expect($result['data'])->null();
expect($result['access_restriction'])->equals($expectedAccessRestriction);
}
private function setMailPoetSendingMethod() {
$this->settings->set(
Mailer::MAILER_CONFIG_SETTING_NAME,
[
'method' => 'MailPoet',
'mailpoet_api_key' => 'some_key',
]
);
}
private function getMSSKey() {
return $this->settings->get(Bridge::API_KEY_SETTING_NAME);
}
private function getMSSKeyState() {
return $this->settings->get(Bridge::API_KEY_STATE_SETTING_NAME);
}
private function fillPremiumKey() {
$this->settings->set(
Bridge::PREMIUM_KEY_SETTING_NAME,
'123457890abcdef'
);
}
private function getPremiumKey() {
return $this->settings->get(Bridge::PREMIUM_KEY_SETTING_NAME);
}
private function getPremiumKeyState() {
return $this->settings->get(Bridge::PREMIUM_KEY_STATE_SETTING_NAME);
}
}