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,57 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\CoreBundle\Entity\IpAddress;
use Mautic\EmailBundle\Entity\Stat;
use Mautic\EmailBundle\Helper\BotRatioHelper;
use PHPUnit\Framework\TestCase;
final class BotRatioHelperTest extends TestCase
{
/**
* @param array<string> $ipDoNotTrackList
* @param array<string> $blockedUserAgents
*/
#[\PHPUnit\Framework\Attributes\DataProvider('hitBotScenariosProvider')]
public function testIsHitByBot(
string $sentBefore,
int $botHelperTimeEmailThreshold,
string $ipAddressString,
array $ipDoNotTrackList,
string $userAgent,
array $blockedUserAgents,
float $botHelperBotRatioThreshold,
bool $isBot,
): void {
// Time
$emailHitDateTime = new \DateTime();
$emailSent = clone $emailHitDateTime;
$emailSent->modify($sentBefore);
$emailStatMock = $this->createMock(Stat::class);
$emailStatMock->expects($this->once())
->method('getDateSent')
->willReturn($emailSent);
// IP
$ipAddress = new IpAddress($ipAddressString);
$botRatioHelper = new BotRatioHelper($botHelperBotRatioThreshold, $botHelperTimeEmailThreshold, $blockedUserAgents, $ipDoNotTrackList);
$isEvaluatedAsBot = $botRatioHelper->isHitByBot($emailStatMock, $emailHitDateTime, $ipAddress, $userAgent);
$this->assertSame($isBot, $isEvaluatedAsBot);
}
/**
* @return iterable<string, array<mixed>>
*/
public static function hitBotScenariosProvider(): iterable
{
// sentBefore, botHelperTimeEmailThreshold, ipAddress, ipDoNotTrackList, userAgent, blockedUserAgents, botHelperBotRatioThreshold, isBot
yield 'Time and IP' => ['-1 second', 2, '217.30.65.82', ['217.30.65.*'], 'Mozilla/5.0', [], 0.6, true];
yield 'Time and User Agent' => ['-1 second', 2, '217.30.65.82', [], 'Mozilla/5.0', ['Mozilla'], 0.6, true];
yield 'Just User Agent' => ['-3 second', 2, '217.30.65.82', [], 'Mozilla/5.0', ['Mozilla'], 0.6, false];
yield 'All' => ['-1 second', 2, '217.30.65.82', ['217.30.65.*'], 'Mozilla/5.0', ['Mozilla'], 1.0, true];
yield 'Just Time and IP' => ['-1 second', 2, '217.30.65.82', ['217.30.65.*'], 'Molla/5.0', ['Mozilla'], 1.0, false];
yield 'Just Time and IP 2' => ['-1 second', 2, '217.30.65.82', ['217.30.65.*'], (string) null, ['Mozilla'], 1.0, false];
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper\DTO;
use Mautic\EmailBundle\Helper\DTO\AddressDTO;
use Mautic\EmailBundle\Helper\Exception\TokenNotFoundOrEmptyException;
use PHPUnit\Framework\TestCase;
class AddressDTOTest extends TestCase
{
public function testNameTokenReturnsTrue(): void
{
$this->assertTrue(AddressDTO::fromAddressArray(['someone@somewhere.com' => '{contactfield=other_name}'])->isNameTokenized());
}
public function testNameTokenReturnsFalse(): void
{
$this->assertFalse((new AddressDTO('someone@somewhere.com', 'Someone Somewhere'))->isNameTokenized());
}
public function testNameTokenEmptyThrowsException(): void
{
$this->expectException(TokenNotFoundOrEmptyException::class);
AddressDTO::fromAddressArray(['someone@somewhere.com' => '{contactfield=other_name}'])->getNameTokenValue([]);
}
public function testNameTokenIsReturned(): void
{
$contact = ['other_name' => 'Thing Two'];
$this->assertEquals(
'Thing Two',
(new AddressDTO('someone@somewhere.com', '{contactfield=other_name}'))->getNameTokenValue($contact)
);
}
public function testEmailTokenReturnsTrue(): void
{
$this->assertTrue((new AddressDTO('{contactfield=other_email}', 'Someone Somewhere'))->isEmailTokenized());
}
public function testEmailTokenReturnsFalse(): void
{
$this->assertFalse((new AddressDTO('someone@somewhere.com', 'Someone Somewhere'))->isEmailTokenized());
}
public function testEmailTokenEmptyThrowsException(): void
{
$this->expectException(TokenNotFoundOrEmptyException::class);
(new AddressDTO('{contactfield=other_email}', 'Thing One'))->getEmailTokenValue([]);
}
public function testEmailTokenIsReturned(): void
{
$contact = ['other_email' => 'other@somewhere.com'];
$this->assertEquals(
'other@somewhere.com',
(new AddressDTO('{contactfield=other_email}', ''))->getEmailTokenValue($contact)
);
}
public function testTokenValuesReturned(): void
{
$contact = [
'other_email' => 'thingtwo@somewhere.com',
'other_name' => 'Thing Two',
];
$addressDTO = new AddressDTO('{contactfield=other_email}', '{contactfield=other_name}');
$this->assertEquals('thingtwo@somewhere.com', $addressDTO->getEmailTokenValue($contact));
$this->assertEquals('Thing Two', $addressDTO->getNameTokenValue($contact));
}
public function testDefaultsAreReturned(): void
{
$addressDTO = new AddressDTO('someone@somewhere.com', 'Someone Somewhere');
$this->assertEquals('someone@somewhere.com', $addressDTO->getEmail());
$this->assertEquals('Someone Somewhere', $addressDTO->getName());
}
public function testSpecialCharactersAreDecoded(): void
{
$addressDTO = new AddressDTO('someone@somewhere.com', 'No Body&#39;s Business');
$this->assertEquals('someone@somewhere.com', $addressDTO->getEmail());
$this->assertEquals("No Body's Business", $addressDTO->getName());
}
}

View File

@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\CoreBundle\Helper\Dsn\Dsn;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\TestCase;
class DsnTest extends TestCase
{
public function testGettersAndSetters(): void
{
$dsn = new Dsn('scheme', 'localhost', 'user', 'password', 3300, 'path', ['ttl' => '200']);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
$newDsn = $dsn->setScheme('mysql');
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('mysql://user:password@localhost:3300/path?ttl=200', (string) $newDsn);
Assert::assertSame('mysql', $newDsn->getScheme());
$newDsn = $dsn->setHost('db');
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://user:password@db:3300/path?ttl=200', (string) $newDsn);
Assert::assertSame('db', $newDsn->getHost());
$newDsn = $dsn->setUser('john');
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://john:password@localhost:3300/path?ttl=200', (string) $newDsn);
Assert::assertSame('john', $newDsn->getUser());
$newDsn = $dsn->setPassword('secret');
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://user:secret@localhost:3300/path?ttl=200', (string) $newDsn);
Assert::assertSame('secret', $newDsn->getPassword());
$newDsn = $dsn->setPort(3301);
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://user:password@localhost:3301/path?ttl=200', (string) $newDsn);
Assert::assertSame(3301, $newDsn->getPort());
$newDsn = $dsn->setPath('folder');
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/folder?ttl=200', (string) $newDsn);
Assert::assertSame('folder', $newDsn->getPath());
$newDsn = $dsn->setOptions(['ttl' => '300', 'timeout' => '10']);
Assert::assertNotSame($newDsn, $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=200', (string) $dsn);
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=300&timeout=10', (string) $newDsn);
Assert::assertSame(['ttl' => '300', 'timeout' => '10'], $newDsn->getOptions());
Assert::assertSame('300', $newDsn->getOption('ttl'));
Assert::assertSame('10', $newDsn->getOption('timeout'));
}
#[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidFromString')]
public function testInvalidFromString(string $dsn, string $exceptionMessage): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage($exceptionMessage);
Dsn::fromString($dsn);
}
/**
* @return iterable<string, array<string|string>>
*/
public static function dataInvalidFromString(): iterable
{
yield 'DSN is invalid.' => [
':', 'The ":" DSN is invalid.',
];
yield 'DSN must contain a scheme.' => [
'://host', 'The "://host" DSN must contain a scheme.',
];
yield 'DSN must contain a host.' => [
'scheme:', 'The "scheme:" DSN must contain a host (use "default" by default).',
];
}
public function testFromStringAllowedDns(): void
{
Assert::assertSame('sync://', (string) Dsn::fromString('sync://'));
}
public function testFromString(): void
{
Assert::assertSame('scheme://user:password@localhost:3300/path?ttl=300&timeout=10', (string) Dsn::fromString('scheme://user:password@localhost:3300/path?ttl=300&timeout=10'));
}
#[\PHPUnit\Framework\Attributes\DataProvider('dataToString')]
public function testToString(Dsn $dsn, string $dsnString): void
{
Assert::assertSame($dsnString, (string) $dsn);
}
/**
* @return iterable<string, array<Dsn|string>>
*/
public static function dataToString(): iterable
{
yield 'With host.' => [
new Dsn('smtp', 'host'), 'smtp://host',
];
yield 'With host and user.' => [
new Dsn('smtp', 'host', 'user'), 'smtp://user@host',
];
yield 'With host, user, password.' => [
new Dsn('smtp', 'host', 'user', 'password'), 'smtp://user:password@host',
];
yield 'With host, port, user, password.' => [
new Dsn('smtp', 'host', 'user', 'password', 25), 'smtp://user:password@host:25',
];
yield 'With host, port, path and query.' => [
new Dsn('smtp', 'host', 'user', 'password', 25, 'test-path', ['encryption' => 'tls', 'auth_mode'=>'login']), 'smtp://user:password@host:25/test-path?encryption=tls&auth_mode=login',
];
}
public function testToStringUrlEncodesProperly(): void
{
$dsn = new Dsn('scheme', 'local+@$#/:*!host', 'us+@$#/:*!er', 'pass+@$#/:*!word', 3300, 'pa+@$#/:*!th', ['type' => 'ty+@$#/:*!pe']);
Assert::assertSame('scheme://'.urlencode('us+@$#/:*!er').':'.urlencode('pass+@$#/:*!word').'@'.urlencode('local+@$#/:*!host').':3300/'.urlencode('pa+@$#/:*!th').'?type='.urlencode('ty+@$#/:*!pe'), (string) $dsn);
$dsnFromString = Dsn::fromString((string) $dsn);
Assert::assertSame('local+@$#/:*!host', $dsnFromString->getHost());
Assert::assertSame('us+@$#/:*!er', $dsnFromString->getUser());
Assert::assertSame('pass+@$#/:*!word', $dsnFromString->getPassword());
Assert::assertSame('pa+@$#/:*!th', $dsnFromString->getPath());
Assert::assertSame('ty+@$#/:*!pe', $dsnFromString->getOption('type'));
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\EmailValidationEvent;
use Mautic\EmailBundle\Exception\InvalidEmailException;
use Mautic\EmailBundle\Helper\EmailValidator;
use Mautic\EmailBundle\Tests\Helper\EventListener\EmailValidationSubscriber;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Contracts\Translation\TranslatorInterface;
class EmailValidatorTest extends \PHPUnit\Framework\TestCase
{
/**
* @var MockObject&TranslatorInterface
*/
private MockObject $translator;
/**
* @var MockObject&EventDispatcherInterface
*/
private MockObject $dispatcher;
/**
* @var MockObject&EmailValidationEvent
*/
private MockObject $event;
private EmailValidator $emailValidator;
protected function setUp(): void
{
parent::setUp();
$this->translator = $this->createMock(TranslatorInterface::class);
$this->dispatcher = $this->createMock(EventDispatcherInterface::class);
$this->event = $this->createMock(EmailValidationEvent::class);
$this->translator->method('trans')->willReturn('some translation');
$this->emailValidator = new EmailValidator($this->translator, $this->dispatcher);
}
public function testValidGmailEmail(): void
{
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->isInstanceOf(EmailValidationEvent::class), EmailEvents::ON_EMAIL_VALIDATION)
->willReturn($this->event);
$this->event->expects($this->once())
->method('isValid')
->willReturn(true);
$this->emailValidator->validate('john@gmail.com');
}
public function testValidGmailEmailWithPeriod(): void
{
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->isInstanceOf(EmailValidationEvent::class), EmailEvents::ON_EMAIL_VALIDATION)
->willReturn($this->event);
$this->event->expects($this->once())
->method('isValid')
->willReturn(true);
$this->emailValidator->validate('john.doe@gmail.com');
}
public function testValidGmailEmailWithPlus(): void
{
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->isInstanceOf(EmailValidationEvent::class), EmailEvents::ON_EMAIL_VALIDATION)
->willReturn($this->event);
$this->event->expects($this->once())
->method('isValid')
->willReturn(true);
$this->emailValidator->validate('john+doe@gmail.com');
}
public function testValidGmailEmailWithNonStandardTld(): void
{
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->isInstanceOf(EmailValidationEvent::class), EmailEvents::ON_EMAIL_VALIDATION)
->willReturn($this->event);
$this->event->expects($this->once())
->method('isValid')
->willReturn(true);
// hopefully this domain remains intact
$this->emailValidator->validate('john@mail.email');
}
public function testValidateNull(): void
{
$this->expectException(UnexpectedValueException::class);
$this->emailValidator->validate(null);
}
public function testValidateEmailWithoutTld(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('john@doe');
}
public function testValidateEmailWithSpaceInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo hn@gmail.com');
}
public function testValidateEmailWithCaretInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo^hn@gmail.com');
}
public function testValidateEmailWithApostropheInTheDomainPortion(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('john@gm\'ail.com');
}
public function testValidateEmailWithSemicolonInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo;hn@gmail.com');
}
public function testValidateEmailWithAmpersandInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo&hn@gmail.com');
}
public function testValidateEmailWithStarInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo*hn@gmail.com');
}
public function testValidateEmailWithPercentInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo%hn@gmail.com');
}
public function testValidateEmailWithDoublePeriodInIt(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('jo..hn@gmail.com');
}
public function testValidateEmailWithBadDNS(): void
{
$this->expectException(InvalidEmailException::class);
$this->emailValidator->validate('john@doe.shouldneverexist', true);
}
public function testIntegrationInvalidatesEmail(): void
{
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new EmailValidationSubscriber());
$emailValidator = new EmailValidator($this->translator, $dispatcher);
$this->expectException(InvalidEmailException::class);
$this->expectExceptionMessage('bad email');
$emailValidator->doPluginValidation('bad@gmail.com');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper\EventListener;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\EmailValidationEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EmailValidationSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
EmailEvents::ON_EMAIL_VALIDATION => ['onEmailValidation', 0],
];
}
public function onEmailValidation(EmailValidationEvent $event): void
{
if ('bad@gmail.com' === $event->getAddress()) {
$event->setInvalid('bad email');
} // defaults to valid
}
}

View File

@@ -0,0 +1,772 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\EmailBundle\Entity\Email;
use Mautic\EmailBundle\Helper\DTO\AddressDTO;
use Mautic\EmailBundle\Helper\Exception\OwnerNotFoundException;
use Mautic\EmailBundle\Helper\FromEmailHelper;
use Mautic\LeadBundle\Entity\LeadRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class FromEmailHelperTest extends TestCase
{
/** @var CoreParametersHelper&MockObject */
private $coreParametersHelper;
/** @var LeadRepository&MockObject */
private $leadRepository;
protected function setUp(): void
{
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
$this->leadRepository = $this->createMock(LeadRepository::class);
}
public function testOwnerIsReturnedWhenEmailEntityNotSet(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$defaultFrom = new AddressDTO('someone@somewhere.com', 'Someone');
$contact = ['owner_id' => 1];
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['user@somewhere.com' => 'First Last'], $fromEmail->getAddressArray());
}
public function testOwnerIsReturnedWhenEmailEntityIsSet(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$defaultFrom = new AddressDTO('someone@somewhere.com', 'Someone');
$contact = ['owner_id' => 1];
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$email = new Email();
$email->setUseOwnerAsMailer(true);
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact, $email);
$this->assertEquals(['user@somewhere.com' => 'First Last'], $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsGivenPreference(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = ['other_email' => 'someone@somewhere.com'];
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => null], $fromEmail->getAddressArray());
}
public function testDefaultIsReturnedIfOwnerNotSet(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('someone@somewhere.com', null);
$contact = [];
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => null], $fromEmail->getAddressArray());
}
public function testDefaultIsReturnedWhenOwnerNotFound(): void
{
$this->coreParametersHelper->method('get')
->willReturnMap(
[
['mailer_from_email', null, 'someone@somewhere.com'],
['mailer_from_name', null, 'Someone'],
['mailer_is_owner', null, true],
]
);
$defaultFrom = new AddressDTO('someone@somewhere.com', 'Someone');
$contact = ['owner_id' => 1];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn(null);
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals($defaultFrom->getAddressArray(), $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsReplacedWithOwnerWhenFieldEmptyAndDefaultNotOverriddenAndMailAsOwnerEnabled(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = [
'owner_id' => 1,
'other_email' => '',
];
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['user@somewhere.com' => 'First Last'], $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsReplacedWithSystemDefaultWhenFieldEmptyAndDefaultNotOverriddenAndMailAsOwnerDisabled(): void
{
$matcher = $this->exactly(3);
$this->coreParametersHelper->expects($matcher)
->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_is_owner', $parameters[0]);
return false;
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_email', $parameters[0]);
return 'default@somewhere.com';
}
if (3 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_name', $parameters[0]);
return 'Default';
}
});
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = [
'owner_id' => 1,
'other_email' => '',
];
$fromEmail = $this->getHelper()->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['default@somewhere.com' => 'Default'], $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsReplacedWithOverriddenDefaultWhenFieldEmptyAndMailAsOwnerDisabled(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(false);
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = [
'owner_id' => 1,
'other_email' => '',
];
$helper = $this->getHelper();
$helper->setDefaultFrom(new AddressDTO('overridden@somewhere.com', null));
$fromEmail = $helper->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals(['overridden@somewhere.com' => null], $fromEmail->getAddressArray());
}
public function testMultipleCallsReturnAppropriateEmail(): void
{
$this->coreParametersHelper->expects($this->exactly(2))
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$defaultFrom = new AddressDTO('someone@somewhere.com', 'Someone');
$contacts = [
['owner_id' => 1],
['owner_id' => 2],
];
$users = [
[
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
],
[
'id' => 3,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user2@somewhere.com',
'signature' => 'hello there again',
],
];
$matcher = $this->exactly(2);
$this->leadRepository->expects($matcher)
->method('getLeadOwner')->willReturnCallback(function (...$parameters) use ($matcher, $users) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame(1, $parameters[0]);
return $users[0];
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame(2, $parameters[0]);
return $users[1];
}
});
$helper = $this->getHelper();
foreach ($contacts as $key => $contact) {
$fromEmail = $helper->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals([$users[$key]['email'] => 'First Last'], $fromEmail->getAddressArray());
}
}
public function testTokenizedEmailIsReplacedWithContactField(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}');
$contact = ['other_email' => 'someone@somewhere.com'];
$fromEmail = $this->getHelper()->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => null], $fromEmail->getAddressArray());
}
public function testTokenizedNameIsReplacedWithContactField(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('someone@somewhere.com', '{contactfield=other_name}');
$contact = [
'other_name' => 'Thing One',
];
$fromEmail = $this->getHelper()->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => 'Thing One'], $fromEmail->getAddressArray());
}
public function testTokenizedFromIsReplacedWithContactField(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', '{contactfield=other_name}');
$contact = [
'other_email'=> 'thingone@somewhere.com',
'other_name' => 'Thing One',
];
$fromEmail = $this->getHelper()->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['thingone@somewhere.com' => 'Thing One'], $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsReplacedWithSystemDefaultWhenFieldEmptyAndDefaultNotOverridden(): void
{
$matcher = $this->exactly(2);
$this->coreParametersHelper->expects($matcher)
->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_email', $parameters[0]);
return 'default@somewhere.com';
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_name', $parameters[0]);
return 'Default';
}
});
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = [
'owner_id' => 1,
'other_email' => '',
];
$fromEmail = $this->getHelper()->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['default@somewhere.com' => 'Default'], $fromEmail->getAddressArray());
}
public function testTokenizedNameIsReplacedWithSystemDefaultWhenFieldEmptyAndDefaultNotOverridden(): void
{
$matcher = $this->exactly(2);
$this->coreParametersHelper->expects($matcher)
->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_email', $parameters[0]);
return 'default@somewhere.com';
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_name', $parameters[0]);
return 'Default';
}
});
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('someone@somewhere.com', '{contactfield=other_name}');
$contact = [
'owner_id' => 1,
'other_email' => '',
'other_name' => '',
];
$fromEmail = $this->getHelper()->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => 'Default'], $fromEmail->getAddressArray());
}
public function testTokenizedEmailIsReplacedWithOverriddenDefaultWhenFieldEmpty(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('{contactfield=other_email}', null);
$contact = [
'owner_id' => 1,
'other_email' => '',
];
$helper = $this->getHelper();
$helper->setDefaultFrom(new AddressDTO('overridden@somewhere.com', null));
$fromEmail = $helper->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['overridden@somewhere.com' => null], $fromEmail->getAddressArray());
}
public function testTokenizedNameIsReplacedWithOverriddenDefaultWhenFieldEmpty(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('someone@somewhere.com', '{contactfield=other_name}');
$contact = [
'owner_id' => 1,
'other_email' => '',
'other_name' => '',
];
$helper = $this->getHelper();
$helper->setDefaultFrom(new AddressDTO('overridden@somewhere.com', 'Thing Two'));
$fromEmail = $helper->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => 'Thing Two'], $fromEmail->getAddressArray());
}
public function testTokenizedNameIsReplacedWithSystemDefaultWhenFieldEmptyWithoutDefaultBeingOverriden(): void
{
$matcher = $this->exactly(2);
$this->coreParametersHelper->expects($matcher)
->method('get')->willReturnCallback(function (...$parameters) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_email', $parameters[0]);
return 'default@somewhere.com';
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame('mailer_from_name', $parameters[0]);
return 'Default Name';
}
});
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('someone@somewhere.com', '{contactfield=other_name}');
$contact = [
'owner_id' => 1,
'other_email' => '',
'other_name' => '',
];
$helper = $this->getHelper();
$from = $helper->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals(['someone@somewhere.com' => 'Default Name'], $from->getAddressArray());
}
public function testNullContactReturnsDefaultAddress(): void
{
$this->coreParametersHelper->expects($this->never())
->method('get');
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('default@somewhere.com', 'Default Name');
$contact = null;
$helper = $this->getHelper();
$helper->setDefaultFrom(new AddressDTO('overridden@somewhere.com', null));
$from = $helper->getFromAddressConsideringOwner($defaultFrom, $contact);
$this->assertEquals('default@somewhere.com', $from->getEmail());
$this->assertEquals('Default Name', $from->getName());
}
public function testNullContactReturnsDefaultAddressWhenMailerIsOwnerEnabled(): void
{
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$defaultFrom = new AddressDTO('default@somewhere.com', 'Default Name');
$contact = null;
$helper = $this->getHelper();
$helper->setDefaultFrom(new AddressDTO('overridden@somewhere.com', null));
$from = $helper->getFromAddressDto($defaultFrom, $contact);
$this->assertEquals('default@somewhere.com', $from->getEmail());
$this->assertEquals('Default Name', $from->getName());
}
public function testContactOwnerIsReturnedWhenMailAsOwnerIsEnabled(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$owner = $this->getHelper()->getContactOwner(1);
$this->assertTrue($user === $owner);
}
public function testExceptionIsThrownWhenMailAsOwnerIsDisabled(): void
{
$this->expectException(OwnerNotFoundException::class);
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(false);
$this->leadRepository->expects($this->never())
->method('getLeadOwner');
$owner = $this->getHelper()->getContactOwner(1);
$this->assertEquals(null, $owner);
}
public function testExceptionIsThrownWhenOwnerNotFound(): void
{
$this->expectException(OwnerNotFoundException::class);
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn(null);
$owner = $this->getHelper()->getContactOwner(1);
$this->assertEquals(null, $owner);
}
public function testSignatureOfLastFetchedOwnerReturned(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'hello there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$helper = $this->getHelper();
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$this->assertEquals($user['signature'], $helper->getSignature());
}
public function testSignatureHasUserTokensReplaces(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => '|USER_EMAIL| |USER_FIRST_NAME| there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$helper = $this->getHelper();
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$this->assertEquals('user@somewhere.com First there', $helper->getSignature());
}
public function testEmptySignatureIsReturnedWhenOwnerIsReset(): void
{
$this->coreParametersHelper->expects($this->once())
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => '|USER_EMAIL| |USER_FIRST_NAME| there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$helper = $this->getHelper();
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$helper->getFromAddressDto(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$this->assertEquals('', $helper->getSignature());
}
public function testEmptySignatureIsReturnedWhenOwnerIsNotFound(): void
{
$this->coreParametersHelper->method('get')
->willReturnMap(
[
['mailer_from_email', null, 'someone@somewhere.com'],
['mailer_from_name', null, 'Someone'],
['mailer_is_owner', null, true],
]
);
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn(null);
$helper = $this->getHelper();
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$this->assertEquals('', $helper->getSignature());
}
public function testSignatureIsReturnedForAppropriateUser(): void
{
$this->coreParametersHelper->expects($this->exactly(2))
->method('get')
->with('mailer_is_owner')
->willReturn(true);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user@somewhere.com',
'signature' => 'user 1',
];
$user2 = [
'id' => 2,
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'user2@somewhere.com',
'signature' => 'user 2',
];
$matcher = $this->exactly(2);
$this->leadRepository->expects($matcher)
->method('getLeadOwner')->willReturnCallback(function (...$parameters) use ($matcher, $user, $user2) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame(1, $parameters[0]);
return $user;
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame(2, $parameters[0]);
return $user2;
}
});
$helper = $this->getHelper();
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 2]
);
$this->assertEquals('user 2', $helper->getSignature());
}
public function testOwnerWithEncodedCharactersInName(): void
{
$params = [
['mailer_is_owner', null, true],
];
$this->coreParametersHelper->method('get')->willReturnMap($params);
$user = [
'id' => 1,
'first_name' => 'First',
'last_name' => 'No Body&#39;s Business',
'email' => 'user@somewhere.com',
'signature' => '|USER_EMAIL| |USER_FIRST_NAME| there',
];
$this->leadRepository->expects($this->once())
->method('getLeadOwner')
->with(1)
->willReturn($user);
$helper = $this->getHelper();
$from = $helper->getFromAddressConsideringOwner(
new AddressDTO('someone@somewhere.com', null),
['owner_id' => 1]
);
$this->assertEquals(['user@somewhere.com' => "First No Body's Business"], $from->getAddressArray());
}
private function getHelper(): FromEmailHelper
{
return new FromEmailHelper($this->coreParametersHelper, $this->leadRepository);
}
}

View File

@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\EmailBundle\Helper\PlainTextHelper;
use PHPUnit\Framework\TestCase;
class PlainTextHelperTest extends TestCase
{
#[\PHPUnit\Framework\Attributes\DataProvider('emailContentProvider')]
public function testGetText(string $htmlContent, string $expectedPlainText): void
{
$plainTextHelper = new PlainTextHelper();
$plainTextHelper->setHtml($htmlContent);
$actualPlainText = $plainTextHelper->getText();
$this->assertEquals($expectedPlainText, $actualPlainText);
}
/**
* @return array<int, array<int, string>>
*/
public static function emailContentProvider(): array
{
return [
// Test case 1: Simple paragraph
[
'<p>This is a simple paragraph.</p>',
'This is a simple paragraph.',
],
// Test case 2: Line breaks
[
'<p>This is line one.<br>This is line two.</p>',
"This is line one.\nThis is line two.",
],
// Test case 3: Links
[
'<p>Check this <a href="http://example.com">link</a>.</p>',
'Check this link [http://example.com].',
],
// Test case 4: Bold text
[
'<p>This is <strong>bold</strong> text.</p>',
'This is bold text.',
],
// Test case 5: Full html body
[
'<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Email</title>
</head>
<body>
<h1>Welcome to Our Newsletter</h1>
<p>This is an example paragraph in our email content.</p>
<!-- More HTML content here -->
</body>
</html>',
"WELCOME TO OUR NEWSLETTER\n\nThis is an example paragraph in our email content.",
],
[
<<<HTML
<a href="https://example.com">text</a>
HTML
,
<<<HTML
text [https://example.com]
HTML,
],
[
<<<HTML
<a href="https://example.com">link 1</a>
<a href="https://examples.com">link 2</a>
HTML
,
<<<HTML
link 1 [https://example.com] link 2 [https://examples.com]
HTML,
],
[
<<<HTML
<a href="https://example.com">text<br></a>
HTML
,
<<<HTML
text
[https://example.com]
HTML,
],
[
<<<HTML
<h1>something</h1>
<h2>another something</h2>
HTML
,
<<<HTML
SOMETHING
ANOTHER SOMETHING
HTML,
],
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('getPreviewProvider')]
public function testGetPreview(?int $previewLength, string $htmlContent, string $expectedPlainText): void
{
$options = [];
if ($previewLength) {
$options['preview_length'] = $previewLength;
}
$plainTextHelper = new PlainTextHelper($options);
$plainTextHelper->setHtml($htmlContent);
$actualPlainText = $plainTextHelper->getPreview();
$this->assertEquals($expectedPlainText, $actualPlainText);
}
/**
* @return array<int, array<int, string|int|null>>
*/
public static function getPreviewProvider(): array
{
return [
// Test case 1: Simple paragraph, with default options
[
null,
'<p>This is a simple paragraph.</p>',
'This is a simple paragraph.',
],
// Test case 2: Simple paragraph, with length set to 10 (whitespace truncated)
[
10,
'<p>This is a simple paragraph.</p>',
'This is a...',
],
// Test case 3: Full html body
[
25,
'<h1>Welcome to Our Newsletter</h1>
<p>This is an example paragraph in our email content.</p>',
'WELCOME TO OUR NEWSLETTER...',
],
];
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\EmailBundle\Entity\Email;
use Mautic\EmailBundle\Helper\PointEventHelper;
use Mautic\EmailBundle\Model\EmailModel;
use Mautic\LeadBundle\Entity\Lead;
use PHPUnit\Framework\MockObject\MockObject;
class PointEventHelperTest extends \PHPUnit\Framework\TestCase
{
public function testSendEmail(): void
{
$lead = new Lead();
$lead->setFields([
'core' => [
'email' => [
'value' => 'test@test.com',
],
],
]);
$event = [
'id' => 1,
'properties' => [
'email' => 1,
],
];
$emailModel = $this->getMockEmail(true, true);
$helper = new PointEventHelper($emailModel);
$result = $helper->sendEmail($event, $lead);
$this->assertEquals(true, $result);
$emailModel = $this->getMockEmail(false, true);
$helper = new PointEventHelper($emailModel);
$result = $helper->sendEmail($event, $lead);
$this->assertEquals(false, $result);
$emailModel = $this->getMockEmail(true, false);
$helper = new PointEventHelper($emailModel);
$result = $helper->sendEmail($event, $lead);
$this->assertEquals(false, $result);
$emailModel = $this->getMockEmail(true, false);
$helper = new PointEventHelper($emailModel);
$result = $helper->sendEmail($event, new Lead());
$this->assertEquals(false, $result);
}
private function getMockEmail(bool $published = true, bool $success = true): EmailModel&MockObject
{
$sendEmail = $success ? true : ['error' => 1];
$mock = $this->getMockBuilder(EmailModel::class)
->disableOriginalConstructor()
->onlyMethods(['getEntity', 'sendEmail'])
->getMock();
$mock->expects($this->any())
->method('getEntity')
->willReturnCallback(function ($id) use ($published) {
$email = new Email();
$email->setIsPublished($published);
return $email;
});
$mock->expects($this->any())
->method('sendEmail')
->willReturn($sendEmail);
return $mock;
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Mautic\EmailBundle\Tests\Helper\Transport;
use Mautic\EmailBundle\Mailer\Message\MauticMessage;
use Mautic\EmailBundle\Mailer\Transport\TokenTransportInterface;
use Mautic\EmailBundle\Mailer\Transport\TokenTransportTrait;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
class BatchTransport extends AbstractTransport implements TokenTransportInterface
{
use TokenTransportTrait;
/**
* @var array<string, mixed>
*/
private $transports = []; // @phpstan-ignore-line
private $metadatas = [];
/**
* @var string[]
*/
private array $fromAddresses = [];
/**
* @var string[]
*/
private array $fromNames = [];
private ?MauticMessage $message = null;
public function __construct(private bool $validate = false, private int $maxRecipients = 4, private int $numberToFail = 1)
{
$this->transports['main'] = $this;
parent::__construct();
}
public function __toString(): string
{
return 'batch://';
}
protected function doSend(SentMessage $message): void
{
$message = $message->getOriginalMessage();
if (!$message instanceof MauticMessage) {
return;
}
$this->metadatas[] = $message->getMetadata();
if ($this->validate && $this->numberToFail) {
--$this->numberToFail;
if (!$message->getSubject()) {
throw new TransportException('Subject empty');
}
}
$this->fromAddresses[] = !empty($message->getFrom()) ? $message->getFrom()[0]->getAddress() : null;
$this->fromNames[] = !empty($message->getFrom()) ? $message->getFrom()[0]->getName() : null;
$this->message = $message;
}
public function getMaxBatchLimit(): int
{
return $this->maxRecipients;
}
public function getMetadatas(): array
{
return $this->metadatas;
}
/**
* @return string[]
*/
public function getFromAddresses(): array
{
return $this->fromAddresses;
}
/**
* @return string[]
*/
public function getFromNames(): array
{
return $this->fromNames;
}
public function getMessage(): ?MauticMessage
{
return $this->message;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper\Transport;
use Mautic\EmailBundle\Mailer\Message\MauticMessage;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\RawMessage;
class BcInterfaceTokenTransport implements TransportInterface
{
/**
* @var array<string, mixed>
*/
private $transports = []; // @phpstan-ignore-line
/**
* @var string[]
*/
private $fromAddresses = [];
/**
* @var string[]
*/
private $fromNames = [];
private $numberToFail;
/**
* @var mixed[]
*/
private array $metadatas = [];
/**
* @var RawMessage
*/
private $message;
/**
* @param bool $validate
*/
public function __construct(
private $validate = false,
$numberToFail = 1,
) {
$this->numberToFail = (int) $numberToFail;
$this->transports['main'] = $this;
}
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
if ($message instanceof Email) {
$this->fromAddresses[] = !empty($message->getFrom()) ? $message->getFrom()[0]->getAddress() : null;
$this->fromNames[] = !empty($message->getFrom()) ? $message->getFrom()[0]->getName() : null;
}
$this->message = $message;
$this->metadatas[] = $this->getMetadata();
return null;
}
/**
* @return string[]
*/
public function getFromAddresses(): array
{
return $this->fromAddresses;
}
/**
* @return string[]
*/
public function getFromNames(): array
{
return $this->fromNames;
}
/**
* @return mixed[]
*/
public function getMetadatas(): array
{
return $this->metadatas;
}
public function getMetadata(): array
{
return ($this->message instanceof MauticMessage) ? $this->message->getMetadata() : [];
}
public function __toString(): string
{
return 'BcInterface';
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper\Transport;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\RawMessage;
class SmtpTransport implements TransportInterface
{
/**
* @var array<string, mixed>
*/
private $transports = []; // @phpstan-ignore-line
public Email $sentMessage;
public function __construct()
{
$this->transports['main'] = $this;
}
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
{
if ($message instanceof Email) {
$this->sentMessage = clone $message;
}
return null;
}
public function __toString(): string
{
return 'null://';
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Mautic\EmailBundle\Tests\Helper;
use Mautic\EmailBundle\Helper\UrlMatcher;
class UrlMatcherTest extends \PHPUnit\Framework\TestCase
{
public function testUrlIsFound(): void
{
$urls = [
'google.com',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'google.com'));
}
public function testUrlWithSlashIsMatched(): void
{
$urls = [
'https://google.com',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com'));
}
public function testUrlWithEscapedSlashesIsMatched(): void
{
$urls = [
'https:\/\/google.com\/hello',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com/hello'));
}
public function testUrlWithEndingSlash(): void
{
$urls = [
'https://google.com/hello/',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com/hello'));
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com/hello/'));
}
public function testUrlWithoutHttpPrefix(): void
{
$urls = [
'google.com/hello',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com/hello'));
$this->assertTrue(UrlMatcher::hasMatch($urls, 'http://google.com/hello/'));
}
public function testUrlWithoutHttp(): void
{
$urls = [
'//google.com/hello',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'https://google.com/hello'));
$this->assertTrue(UrlMatcher::hasMatch($urls, '//google.com/hello'));
}
public function testUrlMismatch(): void
{
$urls = [
'http://google.com',
];
$this->assertFalse(UrlMatcher::hasMatch($urls, 'https://yahoo.com'));
}
public function testFTPSchemeMisMatch(): void
{
$urls = [
'ftp://google.com',
];
$this->assertFalse(UrlMatcher::hasMatch($urls, 'https://google.com'));
}
public function testFTPSchemeMatch(): void
{
$urls = [
'ftp://google.com',
];
$this->assertTrue(UrlMatcher::hasMatch($urls, 'ftp://google.com'));
}
}