Files
piratepoet/mailpoet/tests/integration/API/MP/APITest.php
2022-07-14 11:29:09 +02:00

568 lines
21 KiB
PHP

<?php
namespace MailPoet\Test\API\MP;
use Codeception\Stub;
use Codeception\Stub\Expected;
use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
use MailPoet\API\MP\v1\API;
use MailPoet\API\MP\v1\CustomFields;
use MailPoet\API\MP\v1\Segments;
use MailPoet\API\MP\v1\Subscribers;
use MailPoet\Config\Changelog;
use MailPoet\CustomFields\CustomFieldsRepository;
use MailPoet\Entities\CustomFieldEntity;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Features\FeaturesController;
use MailPoet\Models\ScheduledTask;
use MailPoet\Models\SendingQueue;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\RequiredCustomFieldValidator;
use MailPoet\Subscribers\SubscriberSegmentRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tasks\Sending;
use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Idiorm\ORM;
class APITest extends \MailPoetTest {
const VERSION = 'v1';
/** @var CustomFieldsRepository */
private $customFieldRepository;
/** @var SubscriberFactory */
private $subscriberFactory;
/** @var SegmentsRepository */
private $segmentRepository;
public function _before(): void {
parent::_before();
$settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', true);
$this->customFieldRepository = $this->diContainer->get(CustomFieldsRepository::class);
$this->subscriberFactory = new SubscriberFactory();
$this->segmentRepository = $this->diContainer->get(SegmentsRepository::class);
}
private function getSubscribers() {
return new Subscribers(
Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmail']),
Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
$this->diContainer->get(SegmentsRepository::class),
SettingsController::getInstance(),
$this->diContainer->get(SubscriberSegmentRepository::class),
$this->diContainer->get(SubscribersRepository::class),
$this->diContainer->get(SubscribersResponseBuilder::class),
Stub::makeEmpty(WelcomeScheduler::class),
$this->diContainer->get(FeaturesController::class),
$this->diContainer->get(WPFunctions::class)
);
}
private function getApi($subscriberActions = null): API {
if (!$subscriberActions) {
$subscriberActions = $this->getSubscribers();
}
return new API(
$this->diContainer->get(RequiredCustomFieldValidator::class),
$this->diContainer->get(CustomFields::class),
$this->diContainer->get(Segments::class),
$subscriberActions,
$this->diContainer->get(Changelog::class)
);
}
private function getSegment($name = 'Default', $type = SegmentEntity::TYPE_DEFAULT) {
$segment = $this->segmentRepository->createOrUpdate($name);
$segment->setType($type);
$this->segmentRepository->flush();
return $segment;
}
public function testItUsesMultipleListsSubscribeMethodWhenSubscribingToSingleList() {
// subscribing to single list = converting list ID to an array and using
// multiple lists subscription method
$API = Stub::make($this->getApi(), [
'subscribeToLists' => function() {
return func_get_args();
},
]);
expect($API->subscribeToList(1, 2))->equals(
[
1,
[
2,
],
[],
]
);
}
public function testItRequiresEmailAddressToAddSubscriber() {
try {
$this->getApi()->addSubscriber([]);
$this->fail('Subscriber email address required exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('Subscriber email address is required.');
}
}
public function testItOnlyAcceptsWhitelistedProperties() {
$subscriber = [
'email' => 'test-ignore-status@example.com',
'first_name' => '',
'last_name' => '',
'status' => 'bounced',
];
$result = $this->getApi()->addSubscriber($subscriber);
expect($result['status'])->equals('unconfirmed');
}
public function testItDoesNotAddExistingSubscriber() {
$subscriber = $this->subscriberFactory->create();
try {
$this->getApi()->addSubscriber(['email' => $subscriber->getEmail()]);
$this->fail('Subscriber exists exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This subscriber already exists.');
}
}
public function testItThrowsExceptionWhenSubscriberCannotBeAdded() {
$subscriber = [
'email' => 'test', // invalid email
];
try {
$this->getApi()->addSubscriber($subscriber);
$this->fail('Failed to add subscriber exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->stringContainsString('Failed to add subscriber:');
// error message (converted to lowercase) returned by the model
expect($e->getMessage())->stringContainsString('your email address is invalid!');
}
}
public function testItAddsSubscriber() {
$customField = $this->customFieldRepository->createOrUpdate([
'name' => 'test custom field',
'type' => CustomFieldEntity::TYPE_TEXT,
]);
$subscriber = [
'email' => 'test@example.com',
'cf_' . $customField->getId() => 'test',
];
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$result = $this->getApi()->addSubscriber($subscriber);
expect($result['id'])->greaterThan(0);
expect($result['email'])->equals($subscriber['email']);
expect($result['cf_' . $customField->getId()])->equals('test');
expect($result['source'])->equals('api');
expect($result['subscribed_ip'])->equals($_SERVER['REMOTE_ADDR']);
expect(strlen($result['unsubscribe_token']))->equals(15);
}
public function testItAllowsToOverrideSubscriberIPAddress() {
$subscriber = [
'email' => 'test-ip-2@example.com',
'subscribed_ip' => '1.2.3.4',
];
$result = $this->getApi()->addSubscriber($subscriber);
expect($result['subscribed_ip'])->equals($subscriber['subscribed_ip']);
}
public function testItChecksForMandatoryCustomFields() {
$this->customFieldRepository->createOrUpdate([
'name' => 'custom field',
'type' => CustomFieldEntity::TYPE_TEXT,
'params' => ['required' => '1'],
]);
$subscriber = [
'email' => 'test@example.com',
];
$this->expectException('Exception');
$this->getApi()->addSubscriber($subscriber);
}
public function testItSubscribesToSegmentsWhenAddingSubscriber() {
$segment = $this->getSegment();
$subscriber = [
'email' => 'test@example.com',
];
$result = $this->getApi()->addSubscriber($subscriber, [$segment->getId()]);
expect($result['id'])->greaterThan(0);
expect($result['email'])->equals($subscriber['email']);
expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
}
public function testItSchedulesWelcomeNotificationByDefaultAfterAddingSubscriber() {
$segment = $this->getSegment();
$settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', false);
$subscriberActions = Stub::make(
Subscribers::class,
[
'_scheduleWelcomeNotification' => Expected::once(),
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
'confirmationEmailMailer' => Stub::makeEmpty(ConfirmationEmailMailer::class),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => $settings,
],
$this);
$API = Stub::make(
API::class,
[
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
'subscribers' => $subscriberActions,
],
$this
);
$subscriber = [
'email' => 'test@example.com',
];
$segments = [$segment->getId()];
$API->addSubscriber($subscriber, $segments);
}
public function testItThrowsIfWelcomeEmailFails() {
$settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', false);
$task = ScheduledTask::create();
$task->type = 'sending';
$task->setError("Big Error");
$sendingStub = Sending::create($task, SendingQueue::create());
$segment = $this->getSegment();
$subscribers = Stub::copy($this->getSubscribers(), [
'welcomeScheduler' => $this->make('MailPoet\Newsletter\Scheduler\WelcomeScheduler', [
'scheduleSubscriberWelcomeNotification' => [$sendingStub],
]),
]);
$API = $this->getApi($subscribers);
$subscriber = [
'email' => 'test@example.com',
];
$segments = [$segment->getId()];
$this->expectException('\Exception');
$API->addSubscriber($subscriber, $segments, ['schedule_welcome_email' => true, 'send_confirmation_email' => false]);
}
public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberIfStatusIsNotSubscribed() {
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::never(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
], $this);
$subscriber = [
'email' => 'test@example.com',
];
$segments = [1];
$API->addSubscriber($subscriber, $segments);
}
public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberWhenDisabledByOption() {
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::never(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
], $this);
$subscriber = [
'email' => 'test@example.com',
'status' => SubscriberEntity::STATUS_SUBSCRIBED,
];
$segments = [1];
$options = ['schedule_welcome_email' => false];
$API->addSubscriber($subscriber, $segments, $options);
}
public function testByDefaultItSendsConfirmationEmailAfterAddingSubscriber() {
$API = $this->makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribeToLists' => Expected::once(function ($subscriberId, $segmentsIds, $options) {
expect($options)->contains('send_confirmation_email');
expect($options['send_confirmation_email'])->equals(true);
}),
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
]
);
$subscriber = [
'email' => 'test@example.com',
];
$segments = [1];
$API->addSubscriber($subscriber, $segments);
}
public function testItThrowsWhenConfirmationEmailFailsToSend() {
$confirmationMailer = $this->createMock(ConfirmationEmailMailer::class);
$confirmationMailer->expects($this->once())
->method('sendConfirmationEmailOnce')
->willThrowException(new \Exception('Something went wrong with your subscription. Please contact the website owner.'));
$subscribers = Stub::copy($this->getSubscribers(), [
'confirmationEmailMailer' => $confirmationMailer,
]);
$API = $this->getApi($subscribers);
$segment = $this->getSegment();
$subscriber = [
'email' => 'test@example.com',
];
$this->expectException('\Exception');
$this->expectExceptionMessage('Subscriber added to lists, but confirmation email failed to send: something went wrong with your subscription. please contact the website owner.');
$API->addSubscriber($subscriber, [$segment->getId()], ['send_confirmation_email' => true]);
}
public function testItDoesNotSendConfirmationEmailAfterAddingSubscriberWhenOptionIsSet() {
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_sendConfirmationEmail' => Expected::never(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
], $this);
$subscriber = [
'email' => 'test@example.com',
];
$segments = [1];
$options = ['send_confirmation_email' => false];
$API->addSubscriber($subscriber, $segments, $options);
}
public function testItRequiresNameToAddList() {
try {
$this->getApi()->addList([]);
$this->fail('List name required exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('List name is required.');
}
}
public function testItDoesOnlySaveWhiteListedPropertiesWhenAddingList() {
$result = $this->getApi()->addList([
'name' => 'Test segment123',
'description' => 'Description',
'type' => 'ignore this field',
]);
expect($result['id'])->greaterThan(0);
expect($result['name'])->equals('Test segment123');
expect($result['description'])->equals('Description');
expect($result['type'])->equals('default');
}
public function testItDoesNotAddExistingList() {
$segment = $this->getSegment('Test Segment');
try {
$this->getApi()->addList(['name' => $segment->getName()]);
$this->fail('List exists exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This list already exists.');
}
}
public function testItAddsList() {
$segment = [
'name' => 'Test segment',
];
$result = $this->getApi()->addList($segment);
expect($result['id'])->greaterThan(0);
expect($result['name'])->equals($segment['name']);
}
public function testItDoesNotUnsubscribeMissingSubscriberFromLists() {
try {
$this->getApi()->unsubscribeFromLists(false, [1,2,3]);
$this->fail('Subscriber does not exist exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This subscriber does not exist.');
}
}
public function testItDoesNotUnsubscribeSubscriberFromMissingLists() {
$subscriber = $this->subscriberFactory->create();
// multiple lists error message
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [1,2,3]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('These lists do not exist.');
}
// single list error message
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [1]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This list does not exist.');
}
}
public function testItDoesNotUnsubscribeSubscriberFromListsWhenOneOrMoreListsAreMissing() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
// multiple lists error message
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId(), 90, 100]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.');
}
// single list error message
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId(), 90]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('List with ID 90 does not exist.');
}
}
public function testItDoesNotUnsubscribeSubscriberFromWPUsersList() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS);
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WP Users segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't unsubscribe from a WordPress Users list with ID {$segment->getId()}.");
}
}
public function testItDoesNotUnsubscribeSubscriberFromWooCommerceCustomersList() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment('Default', SegmentEntity::TYPE_WC_USERS);
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WooCommerce Customers segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't unsubscribe from a WooCommerce Customers list with ID {$segment->getId()}.");
}
}
public function testItUsesMultipleListsUnsubscribeMethodWhenUnsubscribingFromSingleList() {
// unsubscribing from single list = converting list ID to an array and using
// multiple lists unsubscribe method
$API = Stub::make(API::class, [
'unsubscribeFromLists' => function() {
return func_get_args();
},
]);
expect($API->unsubscribeFromList(1, 2))
->equals([
1,
[
2,
],
]
);
}
public function testItUnsubscribesSubscriberFromMultipleLists() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
// test if segments are specified
try {
$this->getApi()->unsubscribeFromLists($subscriber->getId(), []);
$this->fail('Segments are required exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('At least one segment ID is required.');
}
$result = $this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]);
expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_SUBSCRIBED);
$result = $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_UNSUBSCRIBED);
}
public function testItGetsSubscriber() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
$this->getApi()->subscribeToList($subscriber->getId(), $segment->getId());
// successful response
$result = $this->getApi()->getSubscriber($subscriber->getEmail());
expect($result['email'])->equals($subscriber->getEmail());
expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
// error response
try {
$this->getApi()->getSubscriber('some_fake_email');
$this->fail('Subscriber does not exist exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This subscriber does not exist.');
}
}
public function _after() {
$this->truncateEntity(SubscriberEntity::class);
$this->truncateEntity(CustomFieldEntity::class);
$this->truncateEntity(SegmentEntity::class);
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
}
}