Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests;
|
||||
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\SmsBundle\Sms\TransportInterface;
|
||||
|
||||
class ArrayTransport implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var array<array{'contact': Lead, 'content': string}>
|
||||
*/
|
||||
public array $smses = [];
|
||||
|
||||
/**
|
||||
* @var array<array{'contact': Lead, 'content': string}>
|
||||
*/
|
||||
public array $mmses = [];
|
||||
|
||||
public function sendSms(Lead $lead, $content): bool
|
||||
{
|
||||
$this->smses[] = ['contact' => $lead, 'content' => $content];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class AjaxControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
public function testGetBuilderTokensAction(): void
|
||||
{
|
||||
$this->client->request(Request::METHOD_POST, '/s/ajax?action=sms:getBuilderTokens');
|
||||
Assert::assertTrue($this->client->getResponse()->isOk());
|
||||
$tokens = json_decode($this->client->getResponse()->getContent(), true);
|
||||
$this->assertArrayHasKey('tokens', $tokens);
|
||||
$this->assertArrayHasKey('{contactfield=email}', $tokens['tokens']);
|
||||
$this->assertArrayHasKey('{ownerfield=email}', $tokens['tokens']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Controller;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\ProjectBundle\Entity\Project;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class SMSControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->configParams['site_url'] = 'https://localhost';
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testSmsWithProject(): void
|
||||
{
|
||||
$sms = $this->CreateSms();
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('Test Project');
|
||||
$this->em->persist($project);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$crawler = $this->client->request('GET', '/s/sms/edit/'.$sms->getId());
|
||||
$form = $crawler->selectButton('Save')->form();
|
||||
$form['sms[projects]']->setValue((string) $project->getId());
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$savedSms = $this->em->find(Sms::class, $sms->getId());
|
||||
Assert::assertSame($project->getId(), $savedSms->getProjects()->first()->getId());
|
||||
}
|
||||
|
||||
private function CreateSms(string $name = 'sms', string $message = 'sms body'): Sms
|
||||
{
|
||||
$sms = new Sms();
|
||||
$sms->setName($name);
|
||||
$sms->setMessage($message);
|
||||
$sms->setSmsType('template');
|
||||
$this->em->persist($sms);
|
||||
$this->em->flush();
|
||||
|
||||
return $sms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Controller;
|
||||
|
||||
use Mautic\ProjectBundle\Tests\Functional\AbstractProjectSearchTestCase;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
|
||||
final class SmsProjectSearchFunctionalTest 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');
|
||||
|
||||
$smsAlpha = $this->createSms('Sms Alpha');
|
||||
$smsBeta = $this->createSms('Sms Beta');
|
||||
$this->createSms('Sms Gamma');
|
||||
$this->createSms('Sms Delta');
|
||||
|
||||
$smsAlpha->addProject($projectOne);
|
||||
$smsAlpha->addProject($projectTwo);
|
||||
$smsBeta->addProject($projectTwo);
|
||||
$smsBeta->addProject($projectThree);
|
||||
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
$this->searchAndAssert($searchTerm, $expectedEntities, $unexpectedEntities, ['/api/smses', '/s/sms']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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' => ['Sms Alpha', 'Sms Beta'],
|
||||
'unexpectedEntities' => ['Sms Gamma', 'Sms Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project AND sms name' => [
|
||||
'searchTerm' => 'project:"Project Two" AND Beta',
|
||||
'expectedEntities' => ['Sms Beta'],
|
||||
'unexpectedEntities' => ['Sms Alpha', 'Sms Gamma', 'Sms Delta'],
|
||||
];
|
||||
|
||||
yield 'search by one project OR sms name' => [
|
||||
'searchTerm' => 'project:"Project Two" OR Gamma',
|
||||
'expectedEntities' => ['Sms Alpha', 'Sms Beta', 'Sms Gamma'],
|
||||
'unexpectedEntities' => ['Sms Delta'],
|
||||
];
|
||||
|
||||
yield 'search by NOT one project' => [
|
||||
'searchTerm' => '!project:"Project Two"',
|
||||
'expectedEntities' => ['Sms Gamma', 'Sms Delta'],
|
||||
'unexpectedEntities' => ['Sms Alpha', 'Sms Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with AND' => [
|
||||
'searchTerm' => 'project:"Project Two" AND project:"Project Three"',
|
||||
'expectedEntities' => ['Sms Beta'],
|
||||
'unexpectedEntities' => ['Sms Alpha', 'Sms Gamma', 'Sms Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT AND' => [
|
||||
'searchTerm' => '!project:"Project Two" AND !project:"Project Three"',
|
||||
'expectedEntities' => ['Sms Gamma', 'Sms Delta'],
|
||||
'unexpectedEntities' => ['Sms Alpha', 'Sms Beta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with OR' => [
|
||||
'searchTerm' => 'project:"Project Two" OR project:"Project Three"',
|
||||
'expectedEntities' => ['Sms Alpha', 'Sms Beta'],
|
||||
'unexpectedEntities' => ['Sms Gamma', 'Sms Delta'],
|
||||
];
|
||||
|
||||
yield 'search by two projects with NOT OR' => [
|
||||
'searchTerm' => '!project:"Project Two" OR !project:"Project Three"',
|
||||
'expectedEntities' => ['Sms Alpha', 'Sms Gamma', 'Sms Delta'],
|
||||
'unexpectedEntities' => ['Sms Beta'],
|
||||
];
|
||||
}
|
||||
|
||||
private function createSms(string $name): Sms
|
||||
{
|
||||
$sms = new Sms();
|
||||
$sms->setName($name);
|
||||
$sms->setMessage('Message for '.$name);
|
||||
$this->em->persist($sms);
|
||||
|
||||
return $sms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\DependencyInjection\Compiler;
|
||||
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\SmsBundle\DependencyInjection\Compiler\SmsTransportPass;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class SmsTransportPassTest extends TestCase
|
||||
{
|
||||
public function testProcess(): void
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->addCompilerPass(new SmsTransportPass());
|
||||
$container
|
||||
->register('foo')
|
||||
->setPublic(true)
|
||||
->setAbstract(true)
|
||||
->addTag('mautic.sms_transport', ['alias'=>'fakeAliasDefault', 'integrationAlias' => 'fakeIntegrationDefault']);
|
||||
|
||||
$container
|
||||
->register('chocolate')
|
||||
->setPublic(true)
|
||||
->setAbstract(true);
|
||||
|
||||
$container
|
||||
->register('bar')
|
||||
->setPublic(true)
|
||||
->setAbstract(true)
|
||||
->addTag('mautic.sms_transport');
|
||||
|
||||
$transport = $this->getMockBuilder(TransportChain::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['addTransport'])
|
||||
->getMock();
|
||||
|
||||
$container
|
||||
->register('mautic.sms.transport_chain')
|
||||
->setClass($transport::class)
|
||||
->setArguments(['foo', $this->createMock(IntegrationHelper::class)])
|
||||
->setShared(false)
|
||||
->setSynthetic(true)
|
||||
->setAbstract(true);
|
||||
|
||||
$pass = new SmsTransportPass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertEquals(2, count($container->findTaggedServiceIds('mautic.sms_transport')));
|
||||
|
||||
$methodCalls = $container->getDefinition('mautic.sms.transport_chain')->getMethodCalls();
|
||||
$this->assertCount(count($methodCalls), $container->findTaggedServiceIds('mautic.sms_transport'));
|
||||
|
||||
// Translation string
|
||||
$this->assertEquals('fakeAliasDefault', $methodCalls[0][1][2]);
|
||||
// Integration name/alias
|
||||
$this->assertEquals('fakeIntegrationDefault', $methodCalls[0][1][3]);
|
||||
|
||||
// Translation string is set as service ID by default
|
||||
$this->assertEquals('bar', $methodCalls[1][1][2]);
|
||||
// Integration name/alias is set to service ID by default
|
||||
$this->assertEquals('bar', $methodCalls[1][1][3]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\CampaignBundle\Event\CampaignExecutionEvent;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use Mautic\SmsBundle\EventListener\CampaignSendSubscriber;
|
||||
use Mautic\SmsBundle\Model\SmsModel;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class CampaignSendSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $args;
|
||||
|
||||
/**
|
||||
* @var MockObject|SmsModel
|
||||
*/
|
||||
private MockObject $smsModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|TransportChain
|
||||
*/
|
||||
private MockObject $transportChain;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->smsModel = $this->createMock(SmsModel::class);
|
||||
$this->transportChain = $this->createMock(TransportChain::class);
|
||||
|
||||
$lead = new Lead();
|
||||
$lead->setId(1);
|
||||
$this->args = [
|
||||
'lead' => $lead,
|
||||
'event' => [
|
||||
'type' => 'sms.send_text_sms',
|
||||
'properties' => ['sms' => 1],
|
||||
],
|
||||
'eventDetails' => [],
|
||||
'systemTriggered' => true,
|
||||
'eventSettings' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSendDeletedSms(): void
|
||||
{
|
||||
$this->smsModel->expects(self::once())->method('getEntity')->willReturn(null);
|
||||
|
||||
$event = new CampaignExecutionEvent($this->args, false, null);
|
||||
|
||||
$this->CampaignSendSubscriber()->onCampaignTriggerAction($event);
|
||||
self::assertTrue((bool) $event->getResult()['failed']);
|
||||
self::assertSame('mautic.sms.campaign.failed.missing_entity', $event->getResult()['reason']);
|
||||
}
|
||||
|
||||
public function testSendUnpublishedSms(): void
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setId(1);
|
||||
$sms = new Sms();
|
||||
$sms->setIsPublished(false);
|
||||
$this->smsModel->expects(self::once())->method('getEntity')->willReturn($sms);
|
||||
|
||||
$event = new CampaignExecutionEvent($this->args, false, null);
|
||||
|
||||
$this->CampaignSendSubscriber()->onCampaignTriggerAction($event);
|
||||
self::assertTrue((bool) $event->getResult()['failed']);
|
||||
self::assertSame('mautic.sms.campaign.failed.unpublished', $event->getResult()['reason']);
|
||||
}
|
||||
|
||||
private function CampaignSendSubscriber(): CampaignSendSubscriber
|
||||
{
|
||||
return new CampaignSendSubscriber($this->smsModel, $this->transportChain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\CoreBundle\Event\TokenReplacementEvent;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Model\AuditLogModel;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PageBundle\Entity\Trackable;
|
||||
use Mautic\PageBundle\Helper\TokenHelper;
|
||||
use Mautic\PageBundle\Model\TrackableModel;
|
||||
use Mautic\SmsBundle\EventListener\SmsSubscriber;
|
||||
use Mautic\SmsBundle\Helper\SmsHelper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SmsSubscriberTest extends TestCase
|
||||
{
|
||||
private CoreParametersHelper|\PHPUnit\Framework\MockObject\MockObject $coreParametersHelper;
|
||||
|
||||
private $messageText = 'custom http://mautic.com text';
|
||||
|
||||
private $messageUrl = 'http://mautic.com';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testOnTokenReplacementWithTrackableUrls(): void
|
||||
{
|
||||
$mockAuditLogModel = $this->createMock(AuditLogModel::class);
|
||||
|
||||
$mockTrackableModel = $this->createMock(TrackableModel::class);
|
||||
$mockTrackableModel->expects($this->any())->method('parseContentForTrackables')->willReturn([
|
||||
$this->messageUrl,
|
||||
new Trackable(),
|
||||
]);
|
||||
$mockTrackableModel->expects($this->any())->method('generateTrackableUrl')->willReturn('custom');
|
||||
|
||||
$mockPageTokenHelper = $this->createMock(TokenHelper::class);
|
||||
$mockPageTokenHelper->expects($this->any())->method('findPageTokens')->willReturn([]);
|
||||
|
||||
$mockAssetTokenHelper = $this->createMock(\Mautic\AssetBundle\Helper\TokenHelper::class);
|
||||
$mockAssetTokenHelper->expects($this->any())->method('findAssetTokens')->willReturn([]);
|
||||
|
||||
$mockSmsHelper = $this->createMock(SmsHelper::class);
|
||||
$mockSmsHelper->expects($this->any())->method('getDisableTrackableUrls')->willReturn(false);
|
||||
|
||||
$lead = new Lead();
|
||||
$tokenReplacementEvent = new TokenReplacementEvent($this->messageText, $lead, ['channel' => [1 => 'sms']]);
|
||||
$subscriber = new SmsSubscriber(
|
||||
$mockAuditLogModel,
|
||||
$mockTrackableModel,
|
||||
$mockPageTokenHelper,
|
||||
$mockAssetTokenHelper,
|
||||
$mockSmsHelper,
|
||||
$this->coreParametersHelper
|
||||
);
|
||||
$subscriber->onTokenReplacement($tokenReplacementEvent);
|
||||
$this->assertNotSame($this->messageText, $tokenReplacementEvent->getContent());
|
||||
}
|
||||
|
||||
public function testOnTokenReplacementWithDisableTrackableUrls(): void
|
||||
{
|
||||
$mockAuditLogModel = $this->createMock(AuditLogModel::class);
|
||||
|
||||
$mockTrackableModel = $this->createMock(TrackableModel::class);
|
||||
$mockTrackableModel->expects($this->any())->method('parseContentForTrackables')->willReturn([
|
||||
$this->messageUrl,
|
||||
new Trackable(),
|
||||
]);
|
||||
$mockTrackableModel->expects($this->any())->method('generateTrackableUrl')->willReturn('custom');
|
||||
|
||||
$mockPageTokenHelper = $this->createMock(TokenHelper::class);
|
||||
$mockPageTokenHelper->expects($this->any())->method('findPageTokens')->willReturn([]);
|
||||
|
||||
$mockAssetTokenHelper = $this->createMock(\Mautic\AssetBundle\Helper\TokenHelper::class);
|
||||
$mockAssetTokenHelper->expects($this->any())->method('findAssetTokens')->willReturn([]);
|
||||
|
||||
$mockSmsHelper = $this->createMock(SmsHelper::class);
|
||||
$mockSmsHelper->expects($this->any())->method('getDisableTrackableUrls')->willReturn(true);
|
||||
|
||||
$lead = new Lead();
|
||||
$tokenReplacementEvent = new TokenReplacementEvent($this->messageText, $lead, ['channel' => ['sms', 1]]);
|
||||
$subscriber = new SmsSubscriber(
|
||||
$mockAuditLogModel,
|
||||
$mockTrackableModel,
|
||||
$mockPageTokenHelper,
|
||||
$mockAssetTokenHelper,
|
||||
$mockSmsHelper,
|
||||
$this->coreParametersHelper
|
||||
);
|
||||
$subscriber->onTokenReplacement($tokenReplacementEvent);
|
||||
$this->assertSame($this->messageText, $tokenReplacementEvent->getContent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\AssetBundle\Entity\Asset;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Mautic\PageBundle\Entity\Page;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use Mautic\SmsBundle\Model\SmsModel;
|
||||
use Mautic\SmsBundle\Tests\SmsTestHelperTrait;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class SmsSubscriberTokenTest extends MauticMysqlTestCase
|
||||
{
|
||||
use SmsTestHelperTrait;
|
||||
|
||||
public function testSmsTokenReplacement(): void
|
||||
{
|
||||
$transport = $this->configureTwilioWithArrayTransport();
|
||||
$smsModel = $this->getContainer()->get('mautic.sms.model.sms');
|
||||
\assert($smsModel instanceof SmsModel);
|
||||
|
||||
$contactModel = $this->getContainer()->get('mautic.lead.model.lead');
|
||||
\assert($contactModel instanceof LeadModel);
|
||||
|
||||
$page = new Page();
|
||||
$page->setTitle('Test Page');
|
||||
$page->setAlias('test-page');
|
||||
|
||||
$this->em->persist($page);
|
||||
|
||||
$asset = new Asset();
|
||||
$asset->setPath('test.jpg');
|
||||
$asset->setTitle('test');
|
||||
$asset->setAlias('test');
|
||||
|
||||
$this->em->persist($asset);
|
||||
|
||||
$contact = new Lead();
|
||||
$contact->setFirstname('John');
|
||||
$contact->setPhone('1234567890');
|
||||
|
||||
$this->em->persist($contact);
|
||||
$this->em->flush();
|
||||
|
||||
$sms = new Sms();
|
||||
$sms->setName('Test SMS');
|
||||
$sms->setMessage("Hello {contactfield=firstname}, download {assetlink={$asset->getId()}} or visit {pagelink={$page->getId()}} or https://mautic.org");
|
||||
|
||||
$smsModel->saveEntity($sms);
|
||||
$smsModel->sendSms($sms, $contactModel->getEntity($contact->getId()));
|
||||
|
||||
Assert::assertCount(1, $transport->smses);
|
||||
|
||||
$ctRegex = 'ct=([a-zA-Z0-9%]+)';
|
||||
$domainRegex = 'https?:\/\/([a-zA-Z0-9.-]+)';
|
||||
$assetLinkRegex = $domainRegex.'\/asset\/'.$asset->getId().':test\?'.$ctRegex;
|
||||
$pageLinkregex = $domainRegex.'\/test-page\?'.$ctRegex;
|
||||
$trackingRegex = $domainRegex.'\/r\/([a-zA-Z0-9]+)\?'.$ctRegex;
|
||||
|
||||
Assert::assertMatchesRegularExpression(
|
||||
"/Hello John, download {$assetLinkRegex} or visit {$pageLinkregex} or {$trackingRegex}/",
|
||||
$transport->smses[0]['content']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\LeadBundle\Entity\DoNotContact;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Model\DoNotContact as DoNotContactModel;
|
||||
use Mautic\SmsBundle\Event\ReplyEvent;
|
||||
use Mautic\SmsBundle\EventListener\StopSubscriber;
|
||||
|
||||
class StopSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject&DoNotContactModel
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $doNotContactModel;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->doNotContactModel = $this->createMock(DoNotContactModel::class);
|
||||
}
|
||||
|
||||
public function testLeadAddedToDNC(): void
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setId(1);
|
||||
$event = new ReplyEvent($lead, 'stop');
|
||||
|
||||
$this->doNotContactModel->expects($this->once())
|
||||
->method('addDncForContact')
|
||||
->with(1, 'sms', DoNotContact::UNSUBSCRIBED);
|
||||
|
||||
$this->StopSubscriber()->onReply($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StopSubscriber
|
||||
*/
|
||||
private function StopSubscriber()
|
||||
{
|
||||
return new StopSubscriber($this->doNotContactModel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\EmailBundle\Entity\Email;
|
||||
use Mautic\EmailBundle\Entity\Stat;
|
||||
use Mautic\EmailBundle\Entity\StatRepository;
|
||||
use Mautic\EmailBundle\EventListener\TrackingSubscriber;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Event\ContactIdentificationEvent;
|
||||
|
||||
class TrackingSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|StatRepository
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $statRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->statRepository = $this->createMock(StatRepository::class);
|
||||
}
|
||||
|
||||
public function testIdentifyContactByStat(): void
|
||||
{
|
||||
$ct = [
|
||||
'lead' => 2,
|
||||
'channel' => [
|
||||
'email' => 1,
|
||||
],
|
||||
'stat' => 'abc123',
|
||||
];
|
||||
|
||||
$email = $this->createMock(Email::class);
|
||||
$email->method('getId')
|
||||
->willReturn(1);
|
||||
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->method('getId')
|
||||
->willReturn(2);
|
||||
|
||||
$stat = new Stat();
|
||||
$stat->setEmail($email);
|
||||
$stat->setLead($lead);
|
||||
|
||||
$this->statRepository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with(['trackingHash' => 'abc123'])
|
||||
->willReturn($stat);
|
||||
|
||||
$event = new ContactIdentificationEvent($ct);
|
||||
|
||||
$this->getSubscriber()->onIdentifyContact($event);
|
||||
|
||||
$this->assertEquals($lead->getId(), $event->getIdentifiedContact()->getId());
|
||||
}
|
||||
|
||||
public function testChannelMismatchDoesNotIdentify(): void
|
||||
{
|
||||
$ct = [
|
||||
'lead' => 2,
|
||||
'channel' => [
|
||||
'sms' => 1,
|
||||
],
|
||||
'stat' => 'abc123',
|
||||
];
|
||||
|
||||
$event = new ContactIdentificationEvent($ct);
|
||||
|
||||
$this->getSubscriber()->onIdentifyContact($event);
|
||||
|
||||
$this->assertNull($event->getIdentifiedContact());
|
||||
}
|
||||
|
||||
public function testChannelIdMismatchDoesNotIdentify(): void
|
||||
{
|
||||
$ct = [
|
||||
'lead' => 2,
|
||||
'channel' => [
|
||||
'email' => 2,
|
||||
],
|
||||
'stat' => 'abc123',
|
||||
];
|
||||
|
||||
$email = $this->createMock(Email::class);
|
||||
$email->method('getId')
|
||||
->willReturn(1);
|
||||
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->method('getId')
|
||||
->willReturn(2);
|
||||
|
||||
$stat = new Stat();
|
||||
$stat->setEmail($email);
|
||||
$stat->setLead($lead);
|
||||
|
||||
$this->statRepository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with(['trackingHash' => 'abc123'])
|
||||
->willReturn($stat);
|
||||
|
||||
$event = new ContactIdentificationEvent($ct);
|
||||
|
||||
$this->getSubscriber()->onIdentifyContact($event);
|
||||
|
||||
$this->assertNull($event->getIdentifiedContact());
|
||||
}
|
||||
|
||||
public function testStatEmptyLeadDoesNotIdentify(): void
|
||||
{
|
||||
$ct = [
|
||||
'lead' => 2,
|
||||
'channel' => [
|
||||
'email' => 2,
|
||||
],
|
||||
'stat' => 'abc123',
|
||||
];
|
||||
|
||||
$email = $this->createMock(Email::class);
|
||||
$email->method('getId')
|
||||
->willReturn(1);
|
||||
|
||||
$stat = new Stat();
|
||||
$stat->setEmail($email);
|
||||
|
||||
$this->statRepository->expects($this->once())
|
||||
->method('findOneBy')
|
||||
->with(['trackingHash' => 'abc123'])
|
||||
->willReturn($stat);
|
||||
|
||||
$event = new ContactIdentificationEvent($ct);
|
||||
|
||||
$this->getSubscriber()->onIdentifyContact($event);
|
||||
|
||||
$this->assertNull($event->getIdentifiedContact());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TrackingSubscriber
|
||||
*/
|
||||
private function getSubscriber()
|
||||
{
|
||||
return new TrackingSubscriber($this->statRepository);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\EventListener;
|
||||
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\SmsBundle\Event\SmsSendEvent;
|
||||
use Mautic\SmsBundle\EventListener\WebhookSubscriber;
|
||||
use Mautic\SmsBundle\SmsEvents;
|
||||
use Mautic\WebhookBundle\Event\WebhookBuilderEvent;
|
||||
use Mautic\WebhookBundle\Model\WebhookModel;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
final class WebhookSubscriberTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|WebhookModel
|
||||
*/
|
||||
private MockObject $webhookModel;
|
||||
|
||||
private WebhookSubscriber $subscriber;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->webhookModel = $this->createMock(WebhookModel::class);
|
||||
$this->subscriber = new WebhookSubscriber($this->webhookModel);
|
||||
}
|
||||
|
||||
public function testOnWebhookBuild(): void
|
||||
{
|
||||
$event = $this->createMock(WebhookBuilderEvent::class);
|
||||
|
||||
$event->expects($this->once())
|
||||
->method('addEvent')
|
||||
->with(
|
||||
SmsEvents::SMS_ON_SEND,
|
||||
[
|
||||
'label' => 'mautic.sms.webhook.event.send',
|
||||
'description' => 'mautic.sms.webhook.event.send_desc',
|
||||
]
|
||||
);
|
||||
|
||||
$this->subscriber->onWebhookBuild($event);
|
||||
}
|
||||
|
||||
public function testOnSend(): void
|
||||
{
|
||||
$event = $this->createMock(SmsSendEvent::class);
|
||||
$contact = $this->createMock(Lead::class);
|
||||
|
||||
$event->expects($this->once())
|
||||
->method('getSmsId')
|
||||
->willReturn(343);
|
||||
|
||||
$event->expects($this->once())
|
||||
->method('getLead')
|
||||
->willReturn($contact);
|
||||
|
||||
$event->expects($this->once())
|
||||
->method('getContent')
|
||||
->willReturn('The SMS content.');
|
||||
|
||||
$this->webhookModel->expects($this->once())
|
||||
->method('queueWebhooksByType')
|
||||
->with(
|
||||
SmsEvents::SMS_ON_SEND,
|
||||
[
|
||||
'smsId' => 343,
|
||||
'contact' => $contact,
|
||||
'content' => 'The SMS content.',
|
||||
]
|
||||
);
|
||||
|
||||
$this->subscriber->onSend($event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Functional;
|
||||
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
|
||||
trait CreateEntitiesTrait
|
||||
{
|
||||
private function createAnSms(string $name, string $message, bool $isPublished = true, string $locale = 'en'): Sms
|
||||
{
|
||||
$sms = new Sms();
|
||||
$sms->setName($name);
|
||||
$sms->setMessage($message);
|
||||
$sms->setLanguage($locale);
|
||||
$sms->setIsPublished($isPublished);
|
||||
|
||||
return $sms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
final class SmsControllerFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
use CreateEntitiesTrait;
|
||||
|
||||
public function testSmsCanBeCreatedWithTranslationParent(): void
|
||||
{
|
||||
// Arrange
|
||||
$parentSms = $this->createAndPersistSms('Parent SMS', 'Parent SMS message');
|
||||
|
||||
// Act
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/new');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$form = $crawler->selectButton('Save')->form();
|
||||
$form['sms[name]'] = 'Child SMS';
|
||||
$form['sms[message]'] = 'Child SMS message';
|
||||
$form['sms[translationParentSelector]'] = (string) $parentSms->getId();
|
||||
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Assert
|
||||
$childSms = $this->em->getRepository(Sms::class)->findOneBy(['name' => 'Child SMS']);
|
||||
$this->assertInstanceOf(Sms::class, $childSms);
|
||||
$this->assertInstanceOf(Sms::class, $childSms->getTranslationParent());
|
||||
$this->assertSame($parentSms->getId(), $childSms->getTranslationParent()->getId());
|
||||
}
|
||||
|
||||
public function testSmsCannotBeItsOwnTranslationParent(): void
|
||||
{
|
||||
// Arrange
|
||||
$sms = $this->createAndPersistSms('Test SMS', 'Test SMS message');
|
||||
|
||||
// Act
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/edit/'.$sms->getId());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Assert
|
||||
$options = $crawler->filter('#sms_translationParentSelector option');
|
||||
$this->assertCount(2, $options);
|
||||
$this->assertSame('Choose a translated item...', $options->eq(0)->text());
|
||||
$this->assertSame('Create new...', $options->eq(1)->text());
|
||||
|
||||
// Ensure the SMS itself is not in the dropdown
|
||||
$optionValues = $options->each(fn ($node) => $node->attr('value'));
|
||||
$this->assertNotContains((string) $sms->getId(), $optionValues);
|
||||
}
|
||||
|
||||
public function testSmsWithTranslationParentCanBeEdited(): void
|
||||
{
|
||||
// Arrange
|
||||
$parentSms = $this->createAndPersistSms('Parent SMS', 'Parent SMS message');
|
||||
$childSms = $this->createAndPersistSms('Child SMS', 'Child SMS message');
|
||||
$childSms->setTranslationParent($parentSms);
|
||||
|
||||
$newParentSms = $this->createAndPersistSms('New Parent SMS', 'New Parent SMS message');
|
||||
$this->em->flush();
|
||||
|
||||
// Act
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/edit/'.$childSms->getId());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Assert original parent is selected
|
||||
$this->assertSame(
|
||||
(string) $parentSms->getId(),
|
||||
$crawler->filter('#sms_translationParentSelector option[selected]')->attr('value')
|
||||
);
|
||||
|
||||
// Change parent
|
||||
$form = $crawler->selectButton('Save')->form();
|
||||
$form['sms[translationParentSelector]'] = (string) $newParentSms->getId();
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Assert parent updated
|
||||
$this->em->refresh($childSms);
|
||||
$this->assertInstanceOf(Sms::class, $childSms->getTranslationParent());
|
||||
$this->assertSame($newParentSms->getId(), $childSms->getTranslationParent()->getId());
|
||||
}
|
||||
|
||||
public function testTranslationParentCanBeRemovedFromSms(): void
|
||||
{
|
||||
// Arrange
|
||||
$parentSms = $this->createAndPersistSms('Parent SMS', 'Parent SMS message');
|
||||
$childSms = $this->createAndPersistSms('Child SMS', 'Child SMS message');
|
||||
$childSms->setTranslationParent($parentSms);
|
||||
$this->em->flush();
|
||||
|
||||
// Act
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/edit/'.$childSms->getId());
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$form = $crawler->selectButton('Save')->form();
|
||||
$form['sms[translationParentSelector]'] = '';
|
||||
$this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Assert
|
||||
$this->em->refresh($childSms);
|
||||
$this->assertNull($childSms->getTranslationParent());
|
||||
}
|
||||
|
||||
public function testTranslationsAreDisplayedOnViewPage(): void
|
||||
{
|
||||
// Arrange
|
||||
$parentSms = $this->createAndPersistSms('Parent SMS', 'Parent SMS message', 'en');
|
||||
$childSms = $this->createAndPersistSms('Child SMS', 'Child SMS message', 'fr');
|
||||
$childSms->setTranslationParent($parentSms);
|
||||
$parentSms->addTranslationChild($childSms);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
// Act & Assert - Parent view
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/view/'.$parentSms->getId());
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertCount(1, $crawler->filter('a[href="#translation-container"]'));
|
||||
$this->client->click($crawler->selectLink('Translations')->link());
|
||||
$this->assertSelectorTextContains('#translation-container', 'Child SMS');
|
||||
|
||||
// Act & Assert - Child view
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/s/sms/view/'.$childSms->getId());
|
||||
$this->assertResponseIsSuccessful();
|
||||
$this->assertCount(1, $crawler->filter('a[href="#translation-container"]'));
|
||||
$this->client->click($crawler->selectLink('Translations')->link());
|
||||
$this->assertSelectorTextContains('#translation-container', 'Parent SMS');
|
||||
}
|
||||
|
||||
private function createAndPersistSms(string $name, string $message, string $locale = 'en'): Sms
|
||||
{
|
||||
$sms = $this->createAnSms($name, $message, true, $locale);
|
||||
$this->em->persist($sms);
|
||||
$this->em->flush();
|
||||
|
||||
return $sms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use Mautic\SmsBundle\Model\SmsModel;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
|
||||
final class SmsModelFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
use CreateEntitiesTrait;
|
||||
|
||||
#[DataProvider('smsTranslationDataProvider')]
|
||||
public function testSmsTranslationBasedOnLocale(string $contactLocale, string $expectedMessage): void
|
||||
{
|
||||
// 1. Create SMS with translation
|
||||
$sms = $this->createAnSms('English SMS', 'Hello');
|
||||
$smsFr = $this->createAnSms('French SMS', 'Bonjour', true, 'fr_FR');
|
||||
$smsFr->setTranslationParent($sms);
|
||||
|
||||
$this->em->persist($sms);
|
||||
$this->em->persist($smsFr);
|
||||
|
||||
// 2. Create contact
|
||||
$contact = new Lead();
|
||||
$contact->setFirstname('Test');
|
||||
$contact->setLastname('Contact');
|
||||
$contact->setMobile('123456789');
|
||||
$this->em->persist($contact);
|
||||
$this->em->flush();
|
||||
|
||||
$contactId = $contact->getId();
|
||||
|
||||
// Clear the EM and fetch the entities
|
||||
$this->em->clear();
|
||||
$contact = $this->em->find(Lead::class, $contactId);
|
||||
$sms = $this->em->find(Sms::class, $sms->getId());
|
||||
|
||||
// Set locale
|
||||
$contact->addUpdatedField('preferred_locale', $contactLocale);
|
||||
$this->em->persist($contact);
|
||||
$this->em->flush();
|
||||
|
||||
// 3. Mock transport
|
||||
$transportMock = $this->createMock(TransportChain::class);
|
||||
$transportMock->expects($this->once())
|
||||
->method('sendSms')
|
||||
->with(
|
||||
$this->anything(),
|
||||
$this->callback(function (string $message) use ($expectedMessage) {
|
||||
$this->assertSame($expectedMessage, $message);
|
||||
|
||||
return true;
|
||||
}),
|
||||
$this->anything()
|
||||
)
|
||||
->willReturn(true);
|
||||
|
||||
$this->getContainer()->set('mautic.sms.transport_chain', $transportMock);
|
||||
|
||||
/** @var SmsModel $smsModel */
|
||||
$smsModel = $this->getContainer()->get('mautic.sms.model.sms');
|
||||
|
||||
// 4. Send SMS
|
||||
$smsModel->sendSms($sms, $contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return iterable<string, string[]>
|
||||
*/
|
||||
public static function smsTranslationDataProvider(): iterable
|
||||
{
|
||||
yield 'translation exists' => ['fr_FR', 'Bonjour'];
|
||||
yield 'translation not available (fallback)' => ['de_DE', 'Hello'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Helper;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Tracker\ContactTracker;
|
||||
use Mautic\SmsBundle\Callback\CallbackInterface;
|
||||
use Mautic\SmsBundle\Exception\NumberNotFoundException;
|
||||
use Mautic\SmsBundle\Helper\ReplyHelper;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ReplyHelperTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $eventDispatcher;
|
||||
|
||||
private NullLogger $logger;
|
||||
|
||||
/**
|
||||
* @var ContactTracker|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $contactTracker;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$this->logger = new NullLogger();
|
||||
$this->contactTracker = $this->createMock(ContactTracker::class);
|
||||
}
|
||||
|
||||
public function testFoundContactsDispatchEvent(): void
|
||||
{
|
||||
$handler = $this->createMock(CallbackInterface::class);
|
||||
$handler->expects($this->once())
|
||||
->method('getContacts')
|
||||
->willReturn(new ArrayCollection([new Lead()]));
|
||||
|
||||
$handler->method('getMessage')->willReturn('some message');
|
||||
|
||||
$this->contactTracker->expects($this->once())
|
||||
->method('setSystemContact');
|
||||
|
||||
$this->eventDispatcher->expects($this->once())
|
||||
->method('dispatch');
|
||||
|
||||
$this->getHelper()->handleRequest($handler, new Request());
|
||||
}
|
||||
|
||||
public function testContactsNotFoundDoesNotDispatchEvent(): void
|
||||
{
|
||||
$handler = $this->createMock(CallbackInterface::class);
|
||||
$handler->expects($this->once())
|
||||
->method('getContacts')
|
||||
->willReturnCallback(
|
||||
function (): void {
|
||||
throw new NumberNotFoundException('');
|
||||
}
|
||||
);
|
||||
|
||||
$this->contactTracker->expects($this->never())
|
||||
->method('setSystemContact');
|
||||
|
||||
$this->eventDispatcher->expects($this->never())
|
||||
->method('dispatch');
|
||||
|
||||
$this->getHelper()->handleRequest($handler, new Request());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReplyHelper
|
||||
*/
|
||||
private function getHelper()
|
||||
{
|
||||
return new ReplyHelper($this->eventDispatcher, $this->logger, $this->contactTracker);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Integration\Twilio;
|
||||
|
||||
use Mautic\PluginBundle\Entity\Integration;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\PluginBundle\Integration\AbstractIntegration;
|
||||
use Mautic\SmsBundle\Integration\Twilio\Configuration;
|
||||
use Twilio\Exceptions\ConfigurationException;
|
||||
|
||||
class ConfigurationTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var IntegrationHelper|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $integrationHelper;
|
||||
|
||||
/**
|
||||
* @var AbstractIntegration|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $integrationObject;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
|
||||
$integrationSettings = new Integration();
|
||||
$integrationSettings->setIsPublished(true);
|
||||
$integrationSettings->setFeatureSettings(['messaging_service_sid' => '123']);
|
||||
$this->integrationObject = $this->createMock(AbstractIntegration::class);
|
||||
$this->integrationObject->method('getIntegrationSettings')
|
||||
->willReturn($integrationSettings);
|
||||
|
||||
$this->integrationHelper->method('getIntegrationObject')
|
||||
->with('Twilio')
|
||||
->willReturn($this->integrationObject);
|
||||
}
|
||||
|
||||
public function testGetMessagingServiceSid(): void
|
||||
{
|
||||
$this->integrationObject->method('getDecryptedApiKeys')
|
||||
->willReturn(
|
||||
[
|
||||
'username' => 'username',
|
||||
'password' => 'password',
|
||||
]
|
||||
);
|
||||
$this->assertEquals('123', $this->getConfiguration()->getMessagingServiceSid());
|
||||
}
|
||||
|
||||
public function testGetAccountSid(): void
|
||||
{
|
||||
$this->integrationObject->method('getDecryptedApiKeys')
|
||||
->willReturn(
|
||||
[
|
||||
'username' => 'username',
|
||||
'password' => 'password',
|
||||
]
|
||||
);
|
||||
$this->assertEquals('username', $this->getConfiguration()->getAccountSid());
|
||||
}
|
||||
|
||||
public function testGetAuthToken(): void
|
||||
{
|
||||
$this->integrationObject->method('getDecryptedApiKeys')
|
||||
->willReturn(
|
||||
[
|
||||
'username' => 'username',
|
||||
'password' => 'password',
|
||||
]
|
||||
);
|
||||
$this->assertEquals('password', $this->getConfiguration()->getAuthToken());
|
||||
}
|
||||
|
||||
public function testConfigurationExceptionThrownIfNotPublished(): void
|
||||
{
|
||||
$this->expectException(ConfigurationException::class);
|
||||
|
||||
$integrationSettings = new Integration();
|
||||
$integrationSettings->setIsPublished(false);
|
||||
$integrationSettings->setFeatureSettings(['messaging_service_sid' => '123']);
|
||||
|
||||
$this->integrationObject->method('getIntegrationSettings')
|
||||
->willReturn($integrationSettings);
|
||||
|
||||
$this->getConfiguration()->getMessagingServiceSid();
|
||||
}
|
||||
|
||||
public function testConfigurationExceptionThrownWithoutMessagingServiceSId(): void
|
||||
{
|
||||
$this->expectException(ConfigurationException::class);
|
||||
|
||||
$this->integrationObject->getIntegrationSettings()->setFeatureSettings(['messaging_service_sid' => '']);
|
||||
|
||||
$this->getConfiguration()->getMessagingServiceSid();
|
||||
}
|
||||
|
||||
public function testConfigurationExceptionThrownWithoutUsername(): void
|
||||
{
|
||||
$this->expectException(ConfigurationException::class);
|
||||
$this->integrationObject->method('getDecryptedApiKeys')
|
||||
->willReturn(
|
||||
[
|
||||
'username' => '',
|
||||
'password' => 'password',
|
||||
]
|
||||
);
|
||||
$this->getConfiguration()->getMessagingServiceSid();
|
||||
}
|
||||
|
||||
public function testConfigurationExceptionThrownWithoutPassword(): void
|
||||
{
|
||||
$this->expectException(ConfigurationException::class);
|
||||
$this->integrationObject->method('getDecryptedApiKeys')
|
||||
->willReturn(
|
||||
[
|
||||
'username' => 'username',
|
||||
'password' => '',
|
||||
]
|
||||
);
|
||||
$this->getConfiguration()->getMessagingServiceSid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Configuration
|
||||
*/
|
||||
private function getConfiguration()
|
||||
{
|
||||
return new Configuration($this->integrationHelper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Integration\Twilio;
|
||||
|
||||
use Mautic\SmsBundle\Helper\ContactHelper;
|
||||
use Mautic\SmsBundle\Integration\Twilio\Configuration;
|
||||
use Mautic\SmsBundle\Integration\Twilio\TwilioCallback;
|
||||
use Symfony\Component\HttpFoundation\InputBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class TwilioCallbackTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var ContactHelper|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $contactHelper;
|
||||
|
||||
/**
|
||||
* @var Configuration|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $configuration;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->contactHelper = $this->createMock(ContactHelper::class);
|
||||
$this->configuration = $this->createMock(Configuration::class);
|
||||
$this->configuration->method('getAccountSid')
|
||||
->willReturn('123');
|
||||
}
|
||||
|
||||
public function testMissingFromThrowsBadRequestException(): void
|
||||
{
|
||||
$this->expectException(BadRequestHttpException::class);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$inputBag = new InputBag([
|
||||
'AccountSid' => '123',
|
||||
'From' => '',
|
||||
]);
|
||||
|
||||
$request->request = $inputBag;
|
||||
|
||||
$this->getCallback()->getMessage($request);
|
||||
}
|
||||
|
||||
public function testMissingBodyThrowsBadRequestException(): void
|
||||
{
|
||||
$this->expectException(BadRequestHttpException::class);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$inputBag = new InputBag([
|
||||
'AccountSid' => '123',
|
||||
'From' => '321',
|
||||
'Body' => '',
|
||||
]);
|
||||
|
||||
$request->request = $inputBag;
|
||||
|
||||
$this->getCallback()->getMessage($request);
|
||||
}
|
||||
|
||||
public function testMismatchedAccountSidThrowsBadRequestException(): void
|
||||
{
|
||||
$this->expectException(BadRequestHttpException::class);
|
||||
|
||||
$request = $this->createMock(Request::class);
|
||||
$inputBag = new InputBag([
|
||||
'AccountSid' => '321',
|
||||
]);
|
||||
|
||||
$request->request = $inputBag;
|
||||
|
||||
$this->getCallback()->getMessage($request);
|
||||
}
|
||||
|
||||
public function testMessageIsReturned(): void
|
||||
{
|
||||
$request = $this->createMock(Request::class);
|
||||
$request->method('get')
|
||||
->willReturn('Hello');
|
||||
|
||||
$inputBag = new InputBag([
|
||||
'AccountSid' => '123',
|
||||
'From' => '321',
|
||||
'Body' => 'Hello',
|
||||
]);
|
||||
|
||||
$request->request = $inputBag;
|
||||
|
||||
$this->assertEquals('Hello', $this->getCallback()->getMessage($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwilioCallback
|
||||
*/
|
||||
private function getCallback()
|
||||
{
|
||||
return new TwilioCallback($this->contactHelper, $this->configuration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Integration\Twilio;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\PluginBundle\Entity\Integration;
|
||||
use Mautic\SmsBundle\Integration\TwilioIntegration;
|
||||
use Mautic\SmsBundle\Tests\SmsTestHelperTrait;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
final class TwilioConfigurationFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
use SmsTestHelperTrait;
|
||||
|
||||
public function testSaveTwilioConfig(): void
|
||||
{
|
||||
$this->configureTwilioWithArrayTransport();
|
||||
|
||||
$integration = $this->getContainer()->get('mautic.integration.twilio');
|
||||
\assert($integration instanceof TwilioIntegration);
|
||||
|
||||
$integrationRepository = $this->em->getRepository(Integration::class);
|
||||
|
||||
$integrationConfig = $integrationRepository->findOneBy(['name' => $integration->getName()]);
|
||||
\assert($integrationConfig instanceof Integration);
|
||||
Assert::assertSame('messaging_sid', $integrationConfig->getFeatureSettings()['messaging_service_sid']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Integration\Twilio;
|
||||
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\SmsBundle\Integration\Twilio\Configuration;
|
||||
use Mautic\SmsBundle\Integration\Twilio\TwilioTransport;
|
||||
use Monolog\Logger;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TwilioTransportTest extends TestCase
|
||||
{
|
||||
private TwilioTransport $twilioTransport;
|
||||
|
||||
/**
|
||||
* @var MockObject&Logger
|
||||
*/
|
||||
private MockObject $logger;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->logger = $this->createMock(Logger::class);
|
||||
$integrationHelper = $this->createMock(IntegrationHelper::class);
|
||||
$configuration = new Configuration($integrationHelper);
|
||||
|
||||
$this->twilioTransport = new TwilioTransport($configuration, $this->logger);
|
||||
}
|
||||
|
||||
public function testSendSMS(): void
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setMobile('123456');
|
||||
$this->logger->expects($this->once())
|
||||
->method('warning')
|
||||
->with('mautic.sms.transport.twilio.not_configured');
|
||||
|
||||
$this->twilioTransport->sendSms($lead, 'some_content');
|
||||
}
|
||||
|
||||
public function testCreatePayload(): void
|
||||
{
|
||||
$reflection = new \ReflectionClass($this->twilioTransport::class);
|
||||
$method = $reflection->getMethod('createPayload');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$payload = ['messagingServiceSid' => 'MS1234', 'body' => 'some_content'];
|
||||
|
||||
$result = $method->invokeArgs($this->twilioTransport, array_values($payload));
|
||||
Assert::assertSame($payload, $result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Model;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\ChannelBundle\Model\MessageQueueModel;
|
||||
use Mautic\CoreBundle\Helper\CacheStorageHelper;
|
||||
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\Model\LeadModel;
|
||||
use Mautic\PageBundle\Model\TrackableModel;
|
||||
use Mautic\SmsBundle\Entity\Sms;
|
||||
use Mautic\SmsBundle\Entity\SmsRepository;
|
||||
use Mautic\SmsBundle\Form\Type\SmsType;
|
||||
use Mautic\SmsBundle\Model\SmsModel;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class SmsModelTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject|CacheStorageHelper
|
||||
*/
|
||||
private MockObject $cacheStorageHelper;
|
||||
|
||||
/**
|
||||
* @var MockObject|EntityManager
|
||||
*/
|
||||
private MockObject $entityManger;
|
||||
|
||||
/**
|
||||
* @var MockObject|LeadModel
|
||||
*/
|
||||
private MockObject $leadModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|MessageQueueModel
|
||||
*/
|
||||
private MockObject $messageQueueModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|TrackableModel
|
||||
*/
|
||||
private MockObject $pageTrackableModel;
|
||||
|
||||
/**
|
||||
* @var MockObject|TransportChain
|
||||
*/
|
||||
private MockObject $transport;
|
||||
|
||||
/**
|
||||
* @var MockObject&CorePermissions
|
||||
*/
|
||||
private MockObject $security;
|
||||
|
||||
private SmsModel $smsModel;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->pageTrackableModel = $this->createMock(TrackableModel::class);
|
||||
$this->leadModel = $this->createMock(LeadModel::class);
|
||||
$this->messageQueueModel = $this->createMock(MessageQueueModel::class);
|
||||
$this->transport = $this->createMock(TransportChain::class);
|
||||
$this->cacheStorageHelper = $this->createMock(CacheStorageHelper::class);
|
||||
$this->entityManger = $this->createMock(EntityManager::class);
|
||||
$this->security = $this->createMock(CorePermissions::class);
|
||||
$this->smsModel = new SmsModel(
|
||||
$this->pageTrackableModel,
|
||||
$this->leadModel,
|
||||
$this->messageQueueModel,
|
||||
$this->transport,
|
||||
$this->cacheStorageHelper,
|
||||
$this->entityManger,
|
||||
$this->security,
|
||||
$this->createMock(EventDispatcherInterface::class),
|
||||
$this->createMock(UrlGeneratorInterface::class),
|
||||
$this->createMock(Translator::class),
|
||||
$this->createMock(UserHelper::class),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$this->createMock(CoreParametersHelper::class)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to get lookup results when class name is sent as a parameter.
|
||||
*/
|
||||
public function testGetLookupResultsWhenTypeIsClass(): void
|
||||
{
|
||||
$entities = [['name' => 'Mautic', 'id' => 1, 'language' => 'cs']];
|
||||
|
||||
/** @var MockObject|SmsRepository $repositoryMock */
|
||||
$repositoryMock = $this->createMock(SmsRepository::class);
|
||||
$repositoryMock->method('getSmsList')
|
||||
->with('', 10, 0, true, false)
|
||||
->willReturn($entities);
|
||||
|
||||
$this->entityManger->method('getRepository')
|
||||
->with(Sms::class)
|
||||
->willReturn($repositoryMock);
|
||||
|
||||
$this->security->method('isGranted')
|
||||
->with('sms:smses:viewother')
|
||||
->willReturn(true);
|
||||
|
||||
$textMessages = $this->smsModel->getLookupResults(SmsType::class);
|
||||
$this->assertSame('Mautic', $textMessages['cs'][1], 'Mautic is the right text message name');
|
||||
}
|
||||
|
||||
public function testSendSmsNotPublished(): void
|
||||
{
|
||||
$sms = new Sms();
|
||||
$sms->setIsPublished(false);
|
||||
$lead = new Lead();
|
||||
$lead->setId(1);
|
||||
$results = $this->smsModel->sendSms($sms, $lead);
|
||||
self::assertFalse((bool) $results[1]['sent']);
|
||||
self::assertSame('mautic.sms.campaign.failed.unpublished', $results[1]['status']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\SmsBundle\Tests\Sms;
|
||||
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\SmsBundle\Integration\Twilio\TwilioTransport;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use Mautic\SmsBundle\Sms\TransportInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class TransportChainTest extends MauticMysqlTestCase
|
||||
{
|
||||
private TransportChain $transportChain;
|
||||
|
||||
/**
|
||||
* @var TransportInterface|MockObject
|
||||
*/
|
||||
private MockObject $twilioTransport;
|
||||
|
||||
/**
|
||||
* Call protected/private method of a class.
|
||||
*
|
||||
* @param object &$object Instantiated object that we will run method on
|
||||
* @param string $methodName Method name to call
|
||||
* @param array $parameters array of parameters to pass into method
|
||||
*
|
||||
* @return mixed method return
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function invokeMethod(&$object, $methodName, array $parameters = [])
|
||||
{
|
||||
$reflection = new \ReflectionClass($object::class);
|
||||
$method = $reflection->getMethod($methodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invokeArgs($object, $parameters);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->transportChain = new TransportChain(
|
||||
'mautic.test.twilio.mock',
|
||||
static::getContainer()->get('mautic.helper.integration')
|
||||
);
|
||||
|
||||
$this->twilioTransport = $this->createMock(TwilioTransport::class);
|
||||
|
||||
$this->twilioTransport
|
||||
->method('sendSMS')
|
||||
->willReturn('lol');
|
||||
}
|
||||
|
||||
public function testAddTransport(): void
|
||||
{
|
||||
$count = count($this->transportChain->getTransports());
|
||||
|
||||
$this->transportChain->addTransport('mautic.transport.test', static::getContainer()->get('mautic.sms.twilio.transport'), 'mautic.transport.test', 'Twilio');
|
||||
|
||||
$this->assertCount($count + 1, $this->transportChain->getTransports());
|
||||
}
|
||||
|
||||
public function testSendSms(): void
|
||||
{
|
||||
$this->testAddTransport();
|
||||
|
||||
$this->transportChain->addTransport('mautic.test.twilio.mock', $this->twilioTransport, 'mautic.test.twilio.mock', 'Twilio');
|
||||
|
||||
$lead = new Lead();
|
||||
$lead->setMobile('+123456789');
|
||||
|
||||
try {
|
||||
$this->transportChain->sendSms($lead, 'Yeah');
|
||||
} catch (\Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
$this->assertEquals('Primary SMS transport is not enabled', $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\SmsBundle\Tests;
|
||||
|
||||
use Mautic\SmsBundle\Integration\TwilioIntegration;
|
||||
use Mautic\SmsBundle\Sms\TransportChain;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
trait SmsTestHelperTrait
|
||||
{
|
||||
private function configureTwilioWithArrayTransport(): ArrayTransport
|
||||
{
|
||||
$this->testSymfonyCommand('mautic:plugins:install');
|
||||
$messagingServiceSid = 'messaging_sid';
|
||||
|
||||
$integration = $this->getContainer()->get('mautic.integration.twilio');
|
||||
\assert($integration instanceof TwilioIntegration);
|
||||
|
||||
$crawler = $this->client->request(Request::METHOD_GET, 's/plugins/config/'.$integration->getName());
|
||||
$response = $this->client->getResponse();
|
||||
|
||||
Assert::assertSame(Response::HTTP_OK, $response->getStatusCode(), $response->getContent());
|
||||
|
||||
$saveButton = $crawler->selectButton('integration_details[buttons][save]');
|
||||
$form = $saveButton->form();
|
||||
|
||||
$form['integration_details[apiKeys][username]']->setValue('test_username');
|
||||
$form['integration_details[apiKeys][password]']->setValue('test_password');
|
||||
$form['integration_details[isPublished]']->setValue('1');
|
||||
$form['integration_details[featureSettings][messaging_service_sid]']->setValue($messagingServiceSid);
|
||||
|
||||
$this->client->submit($form);
|
||||
|
||||
$response = $this->client->getResponse();
|
||||
Assert::assertSame(Response::HTTP_OK, $response->getStatusCode(), $response->getContent());
|
||||
|
||||
$transportChain = $this->getContainer()->get('mautic.sms.transport_chain');
|
||||
\assert($transportChain instanceof TransportChain);
|
||||
|
||||
// Replaces Twilio transport with ArrayTransport
|
||||
$transport = new ArrayTransport();
|
||||
$transportChain->addTransport('mautic.sms.twilio.transport', $transport, 'Array SMS Transport', 'Twilio');
|
||||
|
||||
return $transport;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user