Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Command;
use Mautic\ChannelBundle\Entity\MessageQueue;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\CoreBundle\Tests\Functional\CreateTestEntitiesTrait;
use Mautic\EmailBundle\Entity\Email;
use Mautic\EmailBundle\Entity\Stat;
use Mautic\EmailBundle\Entity\StatRepository;
use Mautic\LeadBundle\Entity\Lead;
use PHPUnit\Framework\Assert;
final class ProcessMarketingMessagesQueueCommandFunctionalTest extends MauticMysqlTestCase
{
use CreateTestEntitiesTrait;
public function testIdleCommand(): void
{
$commandTester = $this->testSymfonyCommand('mautic:messages:send');
Assert::assertSame(0, $commandTester->getStatusCode());
}
public function testCommandWithEmailQueue(): void
{
$email = $this->createEmail('Test Email');
$this->em->flush();
$scheduledDate = new \DateTime('-10 minutes');
$datePublished = new \DateTime('-1 day');
// Create 60 different leads and message queue items
$leads = [];
$messages = [];
for ($i = 0; $i < 60; ++$i) {
$leads[$i] = $this->createLead("John{$i}", "Doe{$i}", "jd{$i}@example.com");
$this->em->persist($leads[$i]);
}
$this->em->flush();
// Create a message for each lead
foreach ($leads as $lead) {
$messages[] = $this->createMessageQueue($email, $lead, $scheduledDate, $datePublished);
}
foreach ($messages as $message) {
$this->em->persist($message);
}
$this->em->flush();
$commandTester = $this->testSymfonyCommand('mautic:messages:send');
Assert::assertSame(0, $commandTester->getStatusCode());
Assert::assertStringContainsString('Messages sent: 60', $commandTester->getDisplay());
// Verify that stats were created for a sample of leads
$this->assertEmailStatCreated($email, $leads[0]);
$this->assertEmailStatCreated($email, $leads[29]);
$this->assertEmailStatCreated($email, $leads[59]);
}
public function testCommandWithLimitParameter(): void
{
$lead = $this->createLead('John', 'Doe', 'jd@example.com');
$email1 = $this->createEmail('Test Email 1');
$email2 = $this->createEmail('Test Email 2');
$email3 = $this->createEmail('Test Email 3');
$this->em->flush();
$scheduledDate = new \DateTime('-10 minutes');
$datePublished = new \DateTime('-1 day');
$messages = [
$this->createMessageQueue($email1, $lead, $scheduledDate, $datePublished),
$this->createMessageQueue($email2, $lead, $scheduledDate, $datePublished),
$this->createMessageQueue($email3, $lead, $scheduledDate, $datePublished),
];
foreach ($messages as $message) {
$this->em->persist($message);
}
$this->em->flush();
$commandTester = $this->testSymfonyCommand('mautic:messages:send', ['--limit' => 2]);
Assert::assertSame(0, $commandTester->getStatusCode());
Assert::assertStringContainsString('Messages sent: 2', $commandTester->getDisplay());
}
private function createMessageQueue(Email $email, Lead $lead, \DateTime $scheduledDate, \DateTime $datePublished): MessageQueue
{
$message = new MessageQueue();
$message->setScheduledDate($scheduledDate);
$message->setDatePublished($datePublished);
$message->setChannel('email');
$message->setChannelId($email->getId());
$message->setLead($lead);
$message->setPriority(MessageQueue::PRIORITY_NORMAL);
$message->setMaxAttempts(3);
$message->setAttempts(0);
$message->setStatus(MessageQueue::STATUS_PENDING);
return $message;
}
private function assertEmailStatCreated(Email $email, Lead $lead): void
{
/** @var StatRepository $emailStatRepository */
$emailStatRepository = $this->em->getRepository(Stat::class);
/** @var Stat|null $emailStat */
$emailStat = $emailStatRepository->findOneBy([
'email' => $email->getId(),
'lead' => $lead->getId(),
]);
Assert::assertNotNull($emailStat, "Email stat not created for email ID {$email->getId()} and lead ID {$lead->getId()}");
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Command;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use PHPUnit\Framework\Assert;
final class SendChannelBroadcastCommandTest extends MauticMysqlTestCase
{
public function testBroadcastCommand(): void
{
$commandTester = $this->testSymfonyCommand('mautic:broadcasts:send');
Assert::assertSame(0, $commandTester->getStatusCode());
}
public function testBroadcastCommandWithLimit(): void
{
$commandTester = $this->testSymfonyCommand('mautic:broadcasts:send', ['--limit' => 1]);
Assert::assertSame(0, $commandTester->getStatusCode());
}
}

View File

@@ -0,0 +1,208 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Controller\Api;
use Mautic\ChannelBundle\Entity\Channel;
use Mautic\ChannelBundle\Entity\Message;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use PHPUnit\Framework\Assert;
final class MessageApiControllerTest extends MauticMysqlTestCase
{
public function testCreateMessage(): void
{
$payloadJson = <<<'JSON'
{
"name": "API message",
"description": "Marketing message created via API functional test",
"channels": {
"email": {
"channel": "email",
"channelId": 12,
"isEnabled": true
}
}
}
JSON;
$payloadArray = json_decode($payloadJson, true);
$this->client->request('POST', '/api/messages/new', $payloadArray);
$responseJson = $this->client->getResponse()->getContent();
self::assertResponseStatusCodeSame(201, $responseJson);
$this->assertMessagePayload($payloadArray, json_decode($responseJson, true)['message'], $responseJson);
}
/**
* @param mixed[] $payload
* @param mixed[] $expectedResponsePayload
*/
#[\PHPUnit\Framework\Attributes\DataProvider('patchProvider')]
public function testEditMessageWithPatch(array $payload, array $expectedResponsePayload): void
{
$channel = new Channel();
$channel->setChannel('email');
$channel->setChannelId(12);
$channel->setIsEnabled(true);
$message = new Message();
$message->setName('API message');
$message->addChannel($channel);
$this->em->persist($channel);
$this->em->persist($message);
$this->em->flush();
$this->em->detach($channel);
$this->em->detach($message);
$patchPayload = ['id' => $message->getId()] + $payload;
$this->client->request('PATCH', "/api/messages/{$message->getId()}/edit", $patchPayload);
$responseJson = $this->client->getResponse()->getContent();
self::assertResponseIsSuccessful($responseJson);
$this->assertMessagePayload(
['id' => $message->getId()] + $expectedResponsePayload,
json_decode($responseJson, true)['message'],
$responseJson
);
}
/**
* Note: the ID is added to the payload automatically in the test.
*
* @return iterable<mixed[]>
*/
public static function patchProvider(): iterable
{
yield [
[
'name' => 'API message (updated)',
],
[
'name' => 'API message (updated)',
'description' => null,
'channels' => [
'email' => [
'channel' => 'email',
'channelId' => 12,
'isEnabled' => true,
],
],
],
];
yield [
[
'description' => 'Description (updated)',
'channels' => [
'email' => [
'channel' => 'email',
'channelId' => 13,
'isEnabled' => false,
],
],
],
[
'name' => 'API message',
'description' => 'Description (updated)',
'channels' => [
'email' => [
'channel' => 'email',
'channelId' => 13,
'isEnabled' => false,
],
],
],
];
}
public function testEditMessagesWithPatch(): void
{
$channel1 = new Channel();
$channel1->setChannel('email');
$channel1->setChannelId(12);
$channel1->setIsEnabled(true);
$message1 = new Message();
$message1->setName('API message 1');
$message1->addChannel($channel1);
$channel2 = new Channel();
$channel2->setChannel('email');
$channel2->setChannelId(13);
$channel2->setIsEnabled(true);
$message2 = new Message();
$message2->setName('API message 2');
$message2->addChannel($channel2);
$this->em->persist($channel1);
$this->em->persist($channel2);
$this->em->persist($message1);
$this->em->persist($message2);
$this->em->flush();
$this->em->detach($channel1);
$this->em->detach($channel2);
$this->em->detach($message1);
$this->em->detach($message2);
$patchPayload = [
['id' => $message1->getId(), 'name' => 'API message 1 (updated)'],
['id' => $message2->getId(), 'channels' => ['email' => ['channelId' => 14, 'isEnabled' => false]]],
];
$this->client->request('PATCH', '/api/messages/batch/edit', $patchPayload);
$responseJson = $this->client->getResponse()->getContent();
self::assertResponseIsSuccessful($responseJson);
$responseArray = json_decode($responseJson, true);
$this->assertMessagePayload(
[
'id' => $message1->getId(),
'name' => 'API message 1 (updated)',
'description' => null,
'channels' => [
'email' => [
'channel' => 'email',
'channelId' => 12,
'isEnabled' => true,
],
],
],
$responseArray['messages'][0],
$responseJson
);
$this->assertMessagePayload(
[
'id' => $message2->getId(),
'name' => 'API message 2',
'description' => null,
'channels' => [
'email' => [
'channel' => 'email',
'channelId' => 14,
'isEnabled' => false,
],
],
],
$responseArray['messages'][1],
$responseJson
);
}
/**
* @param mixed[] $expectedPayload
* @param mixed[] $actualPayload
*/
private function assertMessagePayload(array $expectedPayload, array $actualPayload, string $deliveredPayloadJson): void
{
Assert::assertSame($expectedPayload['name'], $actualPayload['name'], $deliveredPayloadJson);
Assert::assertSame($expectedPayload['description'], $actualPayload['description'], $deliveredPayloadJson);
Assert::assertCount(count($expectedPayload['channels']), $actualPayload['channels'], $deliveredPayloadJson);
Assert::assertGreaterThan(0, $actualPayload['id'], $deliveredPayloadJson);
Assert::assertSame($expectedPayload['channels']['email']['channel'], $actualPayload['channels'][0]['channel'], $deliveredPayloadJson);
Assert::assertSame($expectedPayload['channels']['email']['channelId'], $actualPayload['channels'][0]['channelId'], $deliveredPayloadJson);
Assert::assertSame($expectedPayload['channels']['email']['isEnabled'], $actualPayload['channels'][0]['isEnabled'], $deliveredPayloadJson);
Assert::assertGreaterThan(0, $actualPayload['channels'][0]['id'], $deliveredPayloadJson);
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Controller;
use Mautic\ChannelBundle\Entity\Message;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\ProjectBundle\Entity\Project;
use PHPUnit\Framework\Assert;
class MessageControllerFunctionalTest extends MauticMysqlTestCase
{
public function testFormWithProject(): void
{
$message = new Message();
$message->setName('Test message');
$this->em->persist($message);
$project = new Project();
$project->setName('Test Project');
$this->em->persist($project);
$this->em->flush();
$this->em->clear();
$crawler = $this->client->request('GET', '/s/messages/edit/'.$message->getId());
$form = $crawler->selectButton('Save')->form();
$form['message[projects]']->setValue((string) $project->getId());
$this->client->submit($form);
$this->assertResponseIsSuccessful();
$savedMessage = $this->em->find(Message::class, $message->getId());
Assert::assertSame($project->getId(), $savedMessage->getProjects()->first()->getId());
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Controller;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Symfony\Component\HttpFoundation\Request;
final class MessageControllerTest extends MauticMysqlTestCase
{
public function testMMUiWorkflow(): void
{
$crawler = $this->client->request(Request::METHOD_GET, '/s/messages/new');
$this->assertResponseIsSuccessful();
$form = $crawler->selectButton('Save & Close')->form([
'message[name]' => 'Test message',
'message[description]' => 'Test message description',
]);
$this->client->submit($form);
$this->assertResponseIsSuccessful();
}
}

View File

@@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Controller;
use Mautic\ChannelBundle\Entity\Channel;
use Mautic\ChannelBundle\Entity\Message;
use Mautic\ProjectBundle\Tests\Functional\AbstractProjectSearchTestCase;
final class MessageProjectSearchFunctionalTest extends AbstractProjectSearchTestCase
{
#[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
public function testProjectSearch(string $searchTerm, array $expectedEntities, array $unexpectedEntities): void
{
$projectOne = $this->createProject('Project One');
$projectTwo = $this->createProject('Project Two');
$projectThree = $this->createProject('Project Three');
$messageAlpha = $this->createMessage('Message Alpha');
$messageBeta = $this->createMessage('Message Beta');
$this->createMessage('Message Gamma');
$this->createMessage('Message Delta');
$messageAlpha->addProject($projectOne);
$messageAlpha->addProject($projectTwo);
$messageBeta->addProject($projectTwo);
$messageBeta->addProject($projectThree);
$this->em->flush();
$this->em->clear();
$this->searchAndAssert($searchTerm, $expectedEntities, $unexpectedEntities, ['/api/messages', '/s/messages']);
}
/**
* @return \Generator<string, array{searchTerm: string, expectedEntities: array<string>, unexpectedEntities: array<string>}>
*/
public static function searchDataProvider(): \Generator
{
yield 'search by one project' => [
'searchTerm' => 'project:"Project Two"',
'expectedEntities' => ['Message Alpha', 'Message Beta'],
'unexpectedEntities' => ['Message Gamma', 'Message Delta'],
];
yield 'search by one project AND message name' => [
'searchTerm' => 'project:"Project Two" AND Beta',
'expectedEntities' => ['Message Beta'],
'unexpectedEntities' => ['Message Alpha', 'Message Gamma', 'Message Delta'],
];
yield 'search by one project OR message name' => [
'searchTerm' => 'project:"Project Two" OR Gamma',
'expectedEntities' => ['Message Alpha', 'Message Beta', 'Message Gamma'],
'unexpectedEntities' => ['Message Delta'],
];
yield 'search by NOT one project' => [
'searchTerm' => '!project:"Project Two"',
'expectedEntities' => ['Message Gamma', 'Message Delta'],
'unexpectedEntities' => ['Message Alpha', 'Message Beta'],
];
yield 'search by two projects with AND' => [
'searchTerm' => 'project:"Project Two" AND project:"Project Three"',
'expectedEntities' => ['Message Beta'],
'unexpectedEntities' => ['Message Alpha', 'Message Gamma', 'Message Delta'],
];
yield 'search by two projects with NOT AND' => [
'searchTerm' => '!project:"Project Two" AND !project:"Project Three"',
'expectedEntities' => ['Message Gamma', 'Message Delta'],
'unexpectedEntities' => ['Message Alpha', 'Message Beta'],
];
yield 'search by two projects with OR' => [
'searchTerm' => 'project:"Project Two" OR project:"Project Three"',
'expectedEntities' => ['Message Alpha', 'Message Beta'],
'unexpectedEntities' => ['Message Gamma', 'Message Delta'],
];
yield 'search by two projects with NOT OR' => [
'searchTerm' => '!project:"Project Two" OR !project:"Project Three"',
'expectedEntities' => ['Message Alpha', 'Message Gamma', 'Message Delta'],
'unexpectedEntities' => ['Message Beta'],
];
}
private function createMessage(string $name): Message
{
$message = new Message();
$message->setName($name);
$message->addChannel((new Channel())
->setChannel('email')
->setMessage($message));
$this->em->persist($message);
return $message;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Mautic\ChannelBundle\Tests\Entity;
use Mautic\CategoryBundle\Entity\Category;
use Mautic\ChannelBundle\Entity\Message;
use PHPUnit\Framework\TestCase;
class MessageTest extends TestCase
{
public function testMessageUpdatesReflectsInChanges(): void
{
$category = new Category();
$category->setTitle('New Category');
$category->setAlias('category');
$category->setBundle('bundle');
$message = new Message();
$message->setName('New Message');
$message->setDescription('random text string for description');
$message->setCategory($category);
$message->setPublishDown(new \DateTime());
$message->setPublishUp(new \DateTime());
$this->assertIsArray($message->getChanges());
$this->assertNotEmpty($message->getChanges());
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Mautic\ChannelBundle\Tests\Event;
use Mautic\ChannelBundle\Event\ChannelBroadcastEvent;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
class ChannelBroadcastEventTest extends TestCase
{
private string $channel;
private int $channelId;
private OutputInterface $output;
protected function setUp(): void
{
$this->channel = 'email';
$this->channelId = 1;
$this->output = new BufferedOutput();
}
public function testConstructorAndGetters(): void
{
$event = new ChannelBroadcastEvent($this->channel, $this->channelId, $this->output);
$this->assertSame($this->channel, $event->getChannel());
$this->assertSame($this->channelId, $event->getId());
$this->assertSame($this->output, $event->getOutput());
}
public function testResults(): void
{
$event = new ChannelBroadcastEvent($this->channel, $this->channelId, $this->output);
$successCount = 10;
$failedCount = 2;
$failedRecipientsByList = ['list1' => ['user1@example.com', 'user2@example.com']];
$event->setResults($this->channel, $successCount, $failedCount, $failedRecipientsByList);
$this->assertSame([
$this->channel => [
'success' => $successCount,
'failed' => $failedCount,
'failedRecipientsByList' => $failedRecipientsByList,
],
], $event->getResults());
}
public function testCheckContext(): void
{
$event = new ChannelBroadcastEvent($this->channel, $this->channelId, $this->output);
$this->assertTrue($event->checkContext('email'));
$this->assertFalse($event->checkContext('sms'));
}
}

View File

@@ -0,0 +1,368 @@
<?php
namespace Mautic\ChannelBundle\Tests\EventListener;
use Doctrine\Common\Collections\ArrayCollection;
use Mautic\CampaignBundle\Entity\Campaign;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CampaignBundle\Entity\LeadEventLog;
use Mautic\CampaignBundle\Event\CampaignExecutionEvent;
use Mautic\CampaignBundle\Event\PendingEvent;
use Mautic\CampaignBundle\EventCollector\Accessor\Event\ActionAccessor;
use Mautic\CampaignBundle\EventCollector\EventCollector;
use Mautic\CampaignBundle\Executioner\Dispatcher\ActionDispatcher;
use Mautic\CampaignBundle\Executioner\Dispatcher\LegacyEventDispatcher;
use Mautic\CampaignBundle\Executioner\Scheduler\EventScheduler;
use Mautic\ChannelBundle\ChannelEvents;
use Mautic\ChannelBundle\EventListener\CampaignSubscriber;
use Mautic\ChannelBundle\Form\Type\MessageSendType;
use Mautic\ChannelBundle\Model\MessageModel;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Form\Type\EmailListType;
use Mautic\EmailBundle\Form\Type\EmailSendType;
use Mautic\LeadBundle\Entity\DoNotContact;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Tracker\ContactTracker;
use Mautic\SmsBundle\Form\Type\SmsSendType;
use Mautic\SmsBundle\SmsEvents;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcher;
class CampaignSubscriberTest extends \PHPUnit\Framework\TestCase
{
private EventDispatcher $dispatcher;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|MessageModel
*/
private \PHPUnit\Framework\MockObject\MockObject $messageModel;
private ActionDispatcher $eventDispatcher;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|EventCollector
*/
private \PHPUnit\Framework\MockObject\MockObject $eventCollector;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|Translator
*/
private \PHPUnit\Framework\MockObject\MockObject $translator;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|EventScheduler
*/
private \PHPUnit\Framework\MockObject\MockObject $scheduler;
private LegacyEventDispatcher $legacyDispatcher;
protected function setUp(): void
{
$this->dispatcher = new EventDispatcher();
$this->messageModel = $this->createMock(MessageModel::class);
$this->messageModel->method('getChannels')
->willReturn(
[
'email' => [
'campaignAction' => 'email.send',
'campaignDecisionsSupported' => [
'email.open',
'page.pagehit',
'asset.download',
'form.submit',
],
'lookupFormType' => EmailListType::class,
],
'sms' => [
'campaignAction' => 'sms.send_text_sms',
'campaignDecisionsSupported' => [
'page.pagehit',
'asset.download',
'form.submit',
],
'lookupFormType' => 'sms_list',
'repository' => \Mautic\SmsBundle\Entity\Sms::class,
],
]
);
$this->messageModel->method('getMessageChannels')
->willReturn(
[
'email' => [
'id' => 2,
'channel' => 'email',
'channel_id' => 2,
'properties' => [],
],
'sms' => [
'id' => 1,
'channel' => 'sms',
'channel_id' => 1,
'properties' => [],
],
]
);
$this->scheduler = $this->createMock(EventScheduler::class);
$contactTracker = $this->createMock(ContactTracker::class);
$this->legacyDispatcher = new LegacyEventDispatcher(
$this->dispatcher,
$this->scheduler,
new NullLogger(),
$contactTracker
);
$this->eventDispatcher = new ActionDispatcher(
$this->dispatcher,
new NullLogger(),
$this->scheduler,
$this->legacyDispatcher
);
$this->eventCollector = $this->createMock(EventCollector::class);
$this->eventCollector->method('getEventConfig')
->willReturnCallback(
function (Event $event) {
switch ($event->getType()) {
case 'email.send':
return new ActionAccessor(
[
'label' => 'mautic.email.campaign.event.send',
'description' => 'mautic.email.campaign.event.send_descr',
'batchEventName' => EmailEvents::ON_CAMPAIGN_BATCH_ACTION,
'formType' => EmailSendType::class,
'formTypeOptions' => ['update_select' => 'campaignevent_properties_email', 'with_email_types' => true],
'formTheme' => 'MauticEmailBundle:FormTheme\EmailSendList',
'channel' => 'email',
'channelIdField' => 'email',
]
);
case 'sms.send_text_sms':
return new ActionAccessor(
[
'label' => 'mautic.campaign.sms.send_text_sms',
'description' => 'mautic.campaign.sms.send_text_sms.tooltip',
'eventName' => SmsEvents::ON_CAMPAIGN_TRIGGER_ACTION,
'formType' => SmsSendType::class,
'formTypeOptions' => ['update_select' => 'campaignevent_properties_sms'],
'formTheme' => 'MauticSmsBundle:FormTheme\SmsSendList',
'timelineTemplate' => '@MauticSms/SubscribedEvents/Timeline/index.html.twig',
'channel' => 'sms',
'channelIdField' => 'sms',
]
);
}
}
);
$this->translator = $this->createMock(Translator::class);
$campaignSubscriber = new CampaignSubscriber(
$this->messageModel,
$this->eventDispatcher,
$this->eventCollector,
new NullLogger(),
$this->translator
);
$this->dispatcher->addSubscriber($campaignSubscriber);
$this->dispatcher->addListener(EmailEvents::ON_CAMPAIGN_BATCH_ACTION, [$this, 'sendMarketingMessageEmail']);
$this->dispatcher->addListener(SmsEvents::ON_CAMPAIGN_TRIGGER_ACTION, [$this, 'sendMarketingMessageSms']);
}
public function testCorrectChannelIsUsed(): void
{
$event = $this->getEvent();
$config = new ActionAccessor(
[
'label' => 'mautic.channel.message.send.marketing.message',
'description' => 'mautic.channel.message.send.marketing.message.descr',
'batchEventName' => ChannelEvents::ON_CAMPAIGN_BATCH_ACTION,
'formType' => MessageSendType::class,
'formTheme' => 'MauticChannelBundle:FormTheme\MessageSend',
'channel' => 'channel.message',
'channelIdField' => 'marketingMessage',
'connectionRestrictions' => [
'target' => [
'decision' => [
'email.open',
'page.pagehit',
'asset.download',
'form.submit',
],
],
],
'timelineTemplate' => '@MauticChannel/SubscribedEvents/Timeline/index.html.twig',
'timelineTemplateVars' => [
'messageSettings' => [],
],
]
);
$logs = $this->getLogs();
$pendingEvent = new PendingEvent($config, $event, $logs);
$this->dispatcher->dispatch($pendingEvent, ChannelEvents::ON_CAMPAIGN_BATCH_ACTION);
$this->assertCount(0, $pendingEvent->getFailures());
$successful = $pendingEvent->getSuccessful();
// SMS should be noted as DNC
$this->assertFalse(empty($successful->get(2)->getMetadata()['sms']['dnc']));
// Nothing recorded for success
$this->assertTrue(empty($successful->get(1)->getMetadata()));
}
public function sendMarketingMessageEmail(PendingEvent $event): void
{
$contacts = $event->getContacts();
$logs = $event->getPending();
$this->assertCount(1, $logs);
if (1 === $contacts->first()->getId()) {
// Processing priority 1 for contact 1, let's fail this one so that SMS is used
$event->fail($logs->first(), 'just because');
return;
}
if (2 === $contacts->first()->getId()) {
// Processing priority 1 for contact 2 so let's pass it
$event->pass($logs->first());
return;
}
}
/**
* BC support for old campaign.
*/
public function sendMarketingMessageSms(CampaignExecutionEvent $event): void
{
$lead = $event->getLead();
if (1 === $lead->getId()) {
$event->setResult(true);
return;
}
if (2 === $lead->getId()) {
$this->fail('Lead ID 2 is unsubscribed from SMS so this shouldn not have happened.');
}
}
/**
* @return Event|\PHPUnit\Framework\MockObject\MockObject
*/
private function getEvent()
{
$event = $this->getMockBuilder(Event::class)
->onlyMethods(['getId'])
->getMock();
$event->method('getId')
->willReturn(1);
$event->setEventType(Event::TYPE_ACTION);
$event->setType('message.send');
$event->setChannel('channel.message');
$event->setChannelId('1');
$event->setProperties(
[
'canvasSettings' => [
'droppedX' => '337',
'droppedY' => '155',
],
'name' => '',
'triggerMode' => 'immediate',
'triggerDate' => null,
'triggerInterval' => '1',
'triggerIntervalUnit' => 'd',
'anchor' => 'leadsource',
'properties' => [
'marketingMessage' => '1',
],
'type' => 'message.send',
'eventType' => 'action',
'anchorEventType' => 'source',
'campaignId' => '1',
'_token' => 'q7FpcDX7iye6fBuBzsqMvQWKqW75lcD77jSmuNAEDXg',
'buttons' => [
'save' => '',
],
'marketingMessage' => '1',
]
);
$campaign = $this->createMock(Campaign::class);
$campaign->method('getId')
->willReturn(1);
$event->setCampaign($campaign);
return $event;
}
/**
* @return ArrayCollection
*/
private function getLogs()
{
$lead = $this->createMock(Lead::class);
$lead->method('getId')
->willReturn(1);
$lead->expects($this->once())
->method('getChannelRules')
->willReturn(
[
'sms' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
'email' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
]
);
$log = $this->getMockBuilder(LeadEventLog::class)
->onlyMethods(['getLead', 'getId'])
->getMock();
$log->method('getLead')
->willReturn($lead);
$log->method('getId')
->willReturn(1);
$lead2 = $this->createMock(Lead::class);
$lead2->method('getId')
->willReturn(2);
$lead2->expects($this->once())
->method('getChannelRules')
->willReturn(
[
'email' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
'sms' => [
'dnc' => DoNotContact::UNSUBSCRIBED,
],
]
);
$log2 = $this->getMockBuilder(LeadEventLog::class)
->onlyMethods(['getLead', 'getId'])
->getMock();
$log2->method('getLead')
->willReturn($lead2);
$log2->method('getId')
->willReturn(2);
return new ArrayCollection([1 => $log, 2 => $log2]);
}
}

View File

@@ -0,0 +1,197 @@
<?php
declare(strict_types=1);
namespace Mautic\ChannelBundle\Tests\Model;
use Mautic\ChannelBundle\Model\ChannelActionModel;
use Mautic\LeadBundle\Entity\DoNotContact as DNC;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Model\DoNotContact;
use Mautic\LeadBundle\Model\LeadModel;
use Symfony\Contracts\Translation\TranslatorInterface;
class ChannelActionModelTest extends \PHPUnit\Framework\TestCase
{
private \PHPUnit\Framework\MockObject\MockObject $contactMock5;
private \PHPUnit\Framework\MockObject\MockObject $contactMock6;
private \PHPUnit\Framework\MockObject\MockObject $contactModelMock;
private \PHPUnit\Framework\MockObject\MockObject $doNotContactMock;
private \PHPUnit\Framework\MockObject\MockObject $translatorMock;
private ChannelActionModel $actionModel;
protected function setUp(): void
{
parent::setUp();
$this->contactMock5 = $this->createMock(Lead::class);
$this->contactMock6 = $this->createMock(Lead::class);
$this->contactModelMock = $this->createMock(LeadModel::class);
$this->doNotContactMock = $this->createMock(DoNotContact::class);
$this->translatorMock = $this->createMock(TranslatorInterface::class);
$this->actionModel = new ChannelActionModel(
$this->contactModelMock,
$this->doNotContactMock,
$this->translatorMock
);
$this->contactMock5->method('getId')->willReturn(5);
}
public function testUpdateEntityAccess(): void
{
$contacts = [5, 6];
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5, $this->contactMock6]);
$matcher = $this->exactly(2);
$this->contactModelMock->expects($matcher)
->method('canEditContact')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame($this->contactMock5, $parameters[0]);
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame($this->contactMock6, $parameters[0]);
}
return false;
});
$this->contactModelMock->expects($this->never())
->method('getContactChannels');
$this->actionModel->update($contacts, []);
}
public function testSubscribeContactToEmailChannel(): void
{
$contacts = [5];
$subscribedChannels = ['email', 'sms']; // Subscribe contact to these channels
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5]);
$this->contactModelMock->expects($this->once())
->method('canEditContact')
->with($this->contactMock5)
->willReturn(true);
// Contact is already subscribed to the SMS channel but not to email
$this->contactModelMock->expects($this->once())
->method('getContactChannels')
->with($this->contactMock5)
->willReturn(['sms' => 'sms']);
$this->doNotContactMock->expects($this->once())
->method('isContactable')
->with($this->contactMock5, 'email')
->willReturn(DNC::IS_CONTACTABLE);
$this->doNotContactMock->expects($this->once())
->method('removeDncForContact')
->with(5, 'email');
$this->contactModelMock->expects($this->once())
->method('getPreferenceChannels')
->willReturn(['Email' => 'email', 'Text Message' => 'sms']);
$this->doNotContactMock->expects($this->never())
->method('addDncForContact');
$this->actionModel->update($contacts, $subscribedChannels);
}
public function testSubscribeContactWhoUnsubscribedToEmailChannel(): void
{
$contacts = [5];
$subscribedChannels = ['email', 'sms']; // Subscribe contact to these channels
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5]);
$this->contactModelMock->expects($this->once())
->method('canEditContact')
->with($this->contactMock5)
->willReturn(true);
// Contact is already subscribed to the SMS channel but not to email
$this->contactModelMock->expects($this->once())
->method('getContactChannels')
->with($this->contactMock5)
->willReturn(['sms' => 'sms']);
$this->doNotContactMock->expects($this->once())
->method('isContactable')
->with($this->contactMock5, 'email')
->willReturn(DNC::UNSUBSCRIBED);
$this->doNotContactMock->expects($this->never())
->method('removeDncForContact');
$this->contactModelMock->expects($this->once())
->method('getPreferenceChannels')
->willReturn(['Email' => 'email', 'Text Message' => 'sms']);
$this->doNotContactMock->expects($this->never())
->method('addDncForContact');
$this->actionModel->update($contacts, $subscribedChannels);
}
public function testUnsubscribeContactFromSmsChannel(): void
{
$contacts = [5];
$subscribedChannels = []; // Unsubscribe contact from missing
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5]);
$this->contactModelMock->expects($this->once())
->method('canEditContact')
->with($this->contactMock5)
->willReturn(true);
$this->contactModelMock->expects($this->once())
->method('getContactChannels')
->with($this->contactMock5)
->willReturn(['sms' => 'sms']);
$this->doNotContactMock->expects($this->never())
->method('isContactable');
$this->contactModelMock->expects($this->once())
->method('getPreferenceChannels')
->willReturn(['Email' => 'email', 'Text Message' => 'sms']);
$matcher = $this->exactly(2);
$this->doNotContactMock->expects($matcher)
->method('addDncForContact')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame(5, $parameters[0]);
$this->assertSame('email', $parameters[1]);
$this->assertSame(DNC::MANUAL, $parameters[2]);
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame(5, $parameters[0]);
$this->assertSame('sms', $parameters[1]);
$this->assertSame(DNC::MANUAL, $parameters[2]);
}
});
$this->actionModel->update($contacts, $subscribedChannels);
}
}

View File

@@ -0,0 +1,203 @@
<?php
namespace Mautic\ChannelBundle\Tests\Model;
use Doctrine\Common\Collections\AbstractLazyCollection;
use Mautic\ChannelBundle\Model\FrequencyActionModel;
use Mautic\LeadBundle\Entity\FrequencyRule;
use Mautic\LeadBundle\Entity\FrequencyRuleRepository;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Model\LeadModel;
use PHPUnit\Framework\MockObject\MockObject;
class FrequencyActionModelTest extends \PHPUnit\Framework\TestCase
{
/**
* @var MockObject|Lead
*/
private MockObject $contactMock5;
/**
* @var MockObject|LeadModel
*/
private MockObject $contactModelMock;
/**
* @var MockObject|FrequencyRuleRepository
*/
private MockObject $frequencyRepoMock;
/**
* @var MockObject|FrequencyRule
*/
private MockObject $frequencyRuleEmailMock;
/**
* @var MockObject|FrequencyRule
*/
private MockObject $frequencyRuleSmsMock;
private FrequencyActionModel $actionModel;
protected function setUp(): void
{
parent::setUp();
$this->contactMock5 = $this->createMock(Lead::class);
$this->contactModelMock = $this->createMock(LeadModel::class);
$this->frequencyRepoMock = $this->createMock(FrequencyRuleRepository::class);
$this->frequencyRuleEmailMock = $this->createMock(FrequencyRule::class);
$this->frequencyRuleSmsMock = $this->createMock(FrequencyRule::class);
$collectionMock = $this->createMock(AbstractLazyCollection::class);
$this->actionModel = new FrequencyActionModel(
$this->contactModelMock,
$this->frequencyRepoMock
);
$collectionMock->method('toArray')
->willReturn([
'email' => $this->frequencyRuleEmailMock,
'sms' => $this->frequencyRuleSmsMock,
]);
$this->contactMock5->method('getFrequencyRules')->willReturn($collectionMock);
}
public function testUpdateWhenEntityAccess(): void
{
$contacts = [5];
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5]);
$this->contactModelMock->expects($this->once())
->method('canEditContact')
->with($this->contactMock5)
->willReturn(false);
$this->contactModelMock->expects($this->never())
->method('getPreferenceChannels');
$this->actionModel->update($contacts, [], '');
}
public function testUpdate(): void
{
$contacts = [5];
$params = [
'subscribed_channels' => ['email', 'sms'],
'frequency_number_email' => '2',
'frequency_time_email' => 'WEEK',
'preferred_channel' => 'email',
'contact_pause_start_date_email' => '2018-05-13',
'contact_pause_end_date_email' => '2018-05-26',
'frequency_number_sms' => '',
'frequency_time_sms' => '',
'contact_pause_start_date_sms' => '',
'contact_pause_end_date_sms' => '',
];
$this->contactModelMock->expects($this->once())
->method('getLeadsByIds')
->with($contacts)
->willReturn([$this->contactMock5]);
$this->contactModelMock->expects($this->once())
->method('canEditContact')
->with($this->contactMock5)
->willReturn(true);
$this->contactModelMock->expects($this->once())
->method('getPreferenceChannels')
->willReturn([
'Email' => 'email',
'Text Message' => 'sms',
]);
$this->frequencyRuleEmailMock->expects($this->once())
->method('setChannel')
->with('email');
$this->frequencyRuleEmailMock->expects($this->once())
->method('setLead')
->with($this->contactMock5);
$this->frequencyRuleEmailMock->expects($this->once())
->method('setDateAdded');
$this->frequencyRuleEmailMock->expects($this->once())
->method('setFrequencyNumber')
->with('2');
$this->frequencyRuleEmailMock->expects($this->once())
->method('setFrequencyTime')
->with('WEEK');
$this->frequencyRuleEmailMock->expects($this->once())
->method('setPauseFromDate')
->with(new \DateTime('2018-05-13T00:00:00.000000+0000'));
$this->frequencyRuleEmailMock->expects($this->once())
->method('setPauseToDate')
->with(new \DateTime('2018-05-26T00:00:00.000000+0000'));
$this->frequencyRuleEmailMock->expects($this->once())
->method('setPreferredChannel')
->with(true);
$matcher = $this->exactly(2);
$this->contactMock5->expects($matcher)
->method('addFrequencyRule')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertEquals($this->frequencyRuleEmailMock, $parameters[0]);
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertEquals($this->frequencyRuleEmailMock, $parameters[0]);
}
});
$matcher = $this->exactly(2);
$this->frequencyRepoMock->expects($matcher)
->method('saveEntity')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame($this->frequencyRuleEmailMock, $parameters[0]);
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame($this->frequencyRuleSmsMock, $parameters[0]);
}
});
$this->frequencyRuleSmsMock->expects($this->once())
->method('setChannel')
->with('sms');
$this->frequencyRuleSmsMock->expects($this->once())
->method('setLead')
->with($this->contactMock5);
$this->frequencyRuleSmsMock->expects($this->once())
->method('setDateAdded');
$this->frequencyRuleSmsMock->expects($this->once())
->method('setFrequencyNumber')
->with(null);
$this->frequencyRuleSmsMock->expects($this->once())
->method('setFrequencyTime')
->with(null);
$this->frequencyRuleSmsMock->expects($this->never())
->method('setPauseFromDate');
$this->frequencyRuleSmsMock->expects($this->never())
->method('setPauseToDate');
$this->frequencyRuleSmsMock->expects($this->once())
->method('setPreferredChannel')
->with(false);
$this->actionModel->update($contacts, $params, 'email');
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace Mautic\ChannelBundle\Tests\Model;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\ChannelBundle\Entity\MessageQueue;
use Mautic\ChannelBundle\Entity\MessageQueueRepository;
use Mautic\ChannelBundle\Model\MessageQueueModel;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Entity\LeadRepository;
use Mautic\LeadBundle\Model\CompanyModel;
use Mautic\LeadBundle\Model\LeadModel;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class MessageQueueModelTest extends \PHPUnit\Framework\TestCase
{
/**
* @var string
*/
public const DATE = '2019-07-07 15:00:00';
/**
* @var MessageQueueModel
*/
protected $messageQueue;
/**
* @var MessageQueue
*/
protected $message;
/** @var MockObject|LeadModel */
protected $leadModel;
/** @var MockObject|CompanyModel */
protected $companyModel;
/** @var MockObject|EntityManagerInterface */
protected $entityManager;
/** @var MockObject|MessageQueueRepository */
protected $messageQueueRepository;
protected function setUp(): void
{
$this->leadModel = $this->createMock(LeadModel::class);
$this->companyModel = $this->createMock(CompanyModel::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->messageQueueRepository = $this->createMock(MessageQueueRepository::class);
$coreHelper = $this->createMock(CoreParametersHelper::class);
$this->messageQueue = new MessageQueueModel(
$this->leadModel,
$this->companyModel,
$coreHelper,
$this->entityManager,
$this->createMock(CorePermissions::class),
$this->createMock(EventDispatcherInterface::class),
$this->createMock(UrlGeneratorInterface::class),
$this->createMock(Translator::class),
$this->createMock(UserHelper::class),
$this->createMock(LoggerInterface::class)
);
$this->entityManager->method('getRepository')->willReturn($this->messageQueueRepository);
$message = new MessageQueue();
$scheduleDate = new \DateTime(self::DATE);
$message->setScheduledDate($scheduleDate);
$this->message = $message;
}
public function testRescheduleMessageIntervalDay(): void
{
$interval = new \DateInterval('P2D');
$this->prepareRescheduleMessageIntervalTest($interval);
}
public function testRescheduleMessageIntervalWeek(): void
{
$interval = new \DateInterval('P4W');
$this->prepareRescheduleMessageIntervalTest($interval);
}
public function testRescheduleMessageIntervalMonth(): void
{
$interval = new \DateInterval('P8M');
$this->prepareRescheduleMessageIntervalTest($interval);
}
public function testRescheduleMessageNoInterval(): void
{
$interval = new \DateInterval('PT0S');
$this->prepareRescheduleMessageIntervalTest($interval);
}
protected function prepareRescheduleMessageIntervalTest(\DateInterval $interval)
{
$oldScheduleDate = $this->message->getScheduledDate();
$this->messageQueue->reschedule($this->message, $interval);
$scheduleDate = $this->message->getScheduledDate();
/** @var \DateTime $oldScheduleDate */
$oldScheduleDate->add($interval);
$this->assertEquals($oldScheduleDate, $scheduleDate);
$this->assertNotSame($oldScheduleDate, $scheduleDate);
}
public function testSendMessagesWithNullEvent(): void
{
$queue = $this->message;
$lead = new Lead();
$lead->setId(1);
$queue->setLead($lead);
$contactData = [
1 => [
'firstname' => 'John',
'email' => 'john.doe@example.com',
],
];
$leadRepository = $this->createMock(LeadRepository::class);
$this->leadModel->method('getRepository')->willReturn($leadRepository);
$leadRepository->method('getContacts')->willReturn($contactData);
$this->entityManager->expects($this->exactly(1))
->method('detach');
$this->messageQueueRepository->method('getQueuedMessages')
->willReturn([$queue]);
$this->messageQueue->sendMessages('email', 1);
}
public function testProcessMessageQueueLeadFieldsShouldNotContainCompany(): void
{
$queue = $this->message;
$lead = new Lead();
$lead->setId(1);
$queue->setLead($lead);
$contactData = [
1 => [
'firstname' => 'John',
'email' => 'john.doe@example.com',
],
];
$leadRepository = $this->createMock(LeadRepository::class);
$this->leadModel->method('getRepository')->willReturn($leadRepository);
$leadRepository->method('getContacts')->willReturn($contactData);
$this->messageQueue->processMessageQueue($queue);
$this->assertArrayNotHasKey('companies', $queue->getLead()->getFields());
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Mautic\ChannelBundle\Tests\PreferenceBuilder;
use Mautic\CampaignBundle\Entity\Campaign;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CampaignBundle\Entity\LeadEventLog;
use Mautic\ChannelBundle\PreferenceBuilder\ChannelPreferences;
class ChannelPreferencesTest extends \PHPUnit\Framework\TestCase
{
public function testLogsAreOrganizedByPriority(): void
{
$campaign = new Campaign();
$event = new Event();
$event->setCampaign($campaign);
$channelPreferences = $this->getChannelPreference('email', $event);
$log1 = new LeadEventLog();
$log1->setEvent($event);
$log1->setCampaign($campaign);
$log1->setMetadata(['log' => 1]);
$channelPreferences->addLog($log1, 1);
$log2 = new LeadEventLog();
$log2->setEvent($event);
$log2->setCampaign($campaign);
$log2->setMetadata(['log' => 2]);
$channelPreferences->addLog($log2, 2);
$organized = $channelPreferences->getLogsByPriority(1);
$this->assertEquals($organized->first()->getMetadata()['log'], 1);
$organized = $channelPreferences->getLogsByPriority(2);
$this->assertEquals($organized->first()->getMetadata()['log'], 2);
}
/**
* @return ChannelPreferences
*/
private function getChannelPreference($channel, Event $event)
{
return new ChannelPreferences($event);
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace Mautic\ChannelBundle\Tests\PreferenceBuilder;
use Doctrine\Common\Collections\ArrayCollection;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CampaignBundle\Entity\LeadEventLog;
use Mautic\ChannelBundle\PreferenceBuilder\ChannelPreferences;
use Mautic\ChannelBundle\PreferenceBuilder\PreferenceBuilder;
use Mautic\LeadBundle\Entity\DoNotContact;
use Mautic\LeadBundle\Entity\Lead;
use Psr\Log\NullLogger;
class PreferenceBuilderTest extends \PHPUnit\Framework\TestCase
{
public function testChannelsArePrioritized(): void
{
$lead = $this->createMock(Lead::class);
$lead->expects($this->once())
->method('getChannelRules')
->willReturn(
[
'sms' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
'email' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
]
);
$log = $this->createMock(LeadEventLog::class);
$log->method('getLead')
->willReturn($lead);
$log->method('getId')
->willReturn(1);
$lead2 = $this->createMock(Lead::class);
$lead2->expects($this->once())
->method('getChannelRules')
->willReturn(
[
'email' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
'sms' => [
'dnc' => DoNotContact::UNSUBSCRIBED,
],
]
);
$log2 = $this->createMock(LeadEventLog::class);
$log2->method('getLead')
->willReturn($lead2);
$log2->method('getId')
->willReturn(2);
$logs = new ArrayCollection([$log, $log2]);
$event = new Event();
$builder = new PreferenceBuilder($logs, $event, ['email' => [], 'sms' => [], 'push' => []], new NullLogger());
$preferences = $builder->getChannelPreferences();
$this->assertCount(3, $preferences);
$this->assertTrue(isset($preferences['email']));
$this->assertTrue(isset($preferences['sms']));
$this->assertTrue(isset($preferences['push']));
/** @var ChannelPreferences $emailLogs */
$email = $preferences['email'];
// First priority
$emailLogs = $email->getLogsByPriority(1);
$this->assertCount(1, $emailLogs);
$this->assertEquals(2, $emailLogs->first()->getId());
// Second priority
$emailLogs = $email->getLogsByPriority(2);
$this->assertCount(1, $emailLogs);
$this->assertEquals(1, $emailLogs->first()->getId());
// First priority for SMS which should just be one
/** @var ChannelPreferences $smsLogs */
$sms = $preferences['sms'];
$smsLogs = $sms->getLogsByPriority(1);
$this->assertCount(1, $smsLogs);
$this->assertEquals(1, $smsLogs->first()->getId());
// None for second priority because of DNC
$smsLogs = $sms->getLogsByPriority(2);
$this->assertCount(0, $smsLogs);
// No one had push enabled but it should be defined
$push = $preferences['push'];
$pushLogs = $push->getLogsByPriority(1);
$this->assertCount(0, $pushLogs);
}
public function testLogIsRemovedFromAllChannels(): void
{
$lead = $this->createMock(Lead::class);
$lead->expects($this->once())
->method('getChannelRules')
->willReturn(
[
'sms' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
'email' => [
'dnc' => DoNotContact::IS_CONTACTABLE,
],
]
);
$log = $this->createMock(LeadEventLog::class);
$log->method('getLead')
->willReturn($lead);
$log->method('getId')
->willReturn(1);
$logs = new ArrayCollection([$log]);
$event = new Event();
$builder = new PreferenceBuilder($logs, $event, ['email' => [], 'sms' => [], 'push' => []], new NullLogger());
$preferences = $builder->getChannelPreferences();
/** @var ChannelPreferences $sms */
$sms = $preferences['sms'];
$smsLogs = $sms->getLogsByPriority(1);
$this->assertCount(1, $smsLogs);
/** @var ChannelPreferences $email */
$email = $preferences['email'];
$emailLogs = $email->getLogsByPriority(2);
$this->assertCount(1, $emailLogs);
$builder->removeLogFromAllChannels($log);
$preferences = $builder->getChannelPreferences();
/** @var ChannelPreferences $sms */
$sms = $preferences['sms'];
$smsLogs = $sms->getLogsByPriority(1);
$this->assertCount(0, $smsLogs);
/** @var ChannelPreferences $email */
$email = $preferences['email'];
$emailLogs = $email->getLogsByPriority(2);
$this->assertCount(0, $emailLogs);
}
}