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,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;
}
}

View File

@@ -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']);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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]);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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']
);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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'];
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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']);
}
}

View File

@@ -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);
}
}

View File

@@ -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']);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}