Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\CampaignRepository;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Entity\Lead as CampaignLead;
|
||||
use Mautic\CampaignBundle\Entity\LeadEventLog;
|
||||
use Mautic\CampaignBundle\Entity\Result\CountResult;
|
||||
use Mautic\CampaignBundle\Executioner\ContactFinder\Limiter\ContactLimiter;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
class CampaignRepositoryFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
private CampaignRepository $repository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->repository = self::getContainer()->get('mautic.campaign.repository.campaign');
|
||||
}
|
||||
|
||||
public function testGetCountsForPendingContactsWithEmptyData(): void
|
||||
{
|
||||
$result = $this->repository->getCountsForPendingContacts(
|
||||
1,
|
||||
[1, 2, 3],
|
||||
new ContactLimiter(100, null, null, null, [1, 2, 3])
|
||||
);
|
||||
|
||||
Assert::assertEquals(
|
||||
new CountResult(0, 0, 0),
|
||||
$result,
|
||||
'There should not be any match as there are no campaign/lead records.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCountsForPendingContactsWithoutEventLogs(): void
|
||||
{
|
||||
$campaign = $this->createCampaign();
|
||||
$eventOne = $this->createEvent($campaign);
|
||||
$eventTwo = $this->createEvent($campaign);
|
||||
$eventThree = $this->createEvent($campaign);
|
||||
$leadOne = $this->createLead($campaign);
|
||||
$leadTwo = $this->createLead($campaign);
|
||||
$leadThree = $this->createLead($campaign);
|
||||
$this->em->flush();
|
||||
|
||||
$result = $this->repository->getCountsForPendingContacts(
|
||||
$campaign->getId(),
|
||||
[$eventOne->getId(), $eventTwo->getId(), $eventThree->getId()],
|
||||
new ContactLimiter(100, null, null, null, [$leadOne->getId(), $leadTwo->getId(), $leadThree->getId()])
|
||||
);
|
||||
|
||||
Assert::assertEquals(
|
||||
new CountResult(3, $leadOne->getId(), $leadThree->getId()),
|
||||
$result,
|
||||
'All three leads should match as none of them have any event logs.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCountsForPendingContactsWithEventLogs(): void
|
||||
{
|
||||
$campaign = $this->createCampaign();
|
||||
$logOne = $this->createEventLog($campaign);
|
||||
$leadOne = $logOne->getLead();
|
||||
$eventOne = $logOne->getEvent();
|
||||
$logTwo = $this->createEventLog($campaign);
|
||||
$leadTwo = $logTwo->getLead();
|
||||
$eventTwo = $logTwo->getEvent();
|
||||
$leadThree = $this->createLead($campaign);
|
||||
$eventThree = $this->createEvent($campaign);
|
||||
$this->em->flush();
|
||||
|
||||
$result = $this->repository->getCountsForPendingContacts(
|
||||
$campaign->getId(),
|
||||
[$eventOne->getId(), $eventTwo->getId(), $eventThree->getId()],
|
||||
new ContactLimiter(100, null, null, null, [$leadOne->getId(), $leadTwo->getId(), $leadThree->getId()])
|
||||
);
|
||||
|
||||
Assert::assertEquals(
|
||||
new CountResult(1, $leadThree->getId(), $leadThree->getId()),
|
||||
$result,
|
||||
'Only lead three should match as it is the only one who does not have any event log.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCountsForPendingContactsWithEventLogsWithNonMatchingRotations(): void
|
||||
{
|
||||
$campaign = $this->createCampaign();
|
||||
$logOne = $this->createEventLog($campaign);
|
||||
$leadOne = $logOne->getLead();
|
||||
$eventOne = $logOne->getEvent();
|
||||
$logTwo = $this->createEventLog($campaign, $campaignLeadTwo);
|
||||
$leadTwo = $logTwo->getLead();
|
||||
$eventTwo = $logTwo->getEvent();
|
||||
$logThree = $this->createEventLog($campaign);
|
||||
$leadThree = $logThree->getLead();
|
||||
$eventThree = $logThree->getEvent();
|
||||
$campaignLeadTwo->setRotation($logTwo->getRotation() + 1);
|
||||
$this->em->flush();
|
||||
|
||||
$result = $this->repository->getCountsForPendingContacts(
|
||||
$campaign->getId(),
|
||||
[$eventOne->getId(), $eventTwo->getId(), $eventThree->getId()],
|
||||
new ContactLimiter(100, null, null, null, [$leadOne->getId(), $leadTwo->getId(), $leadThree->getId()])
|
||||
);
|
||||
|
||||
Assert::assertEquals(
|
||||
new CountResult(1, $leadTwo->getId(), $leadTwo->getId()),
|
||||
$result,
|
||||
'Only lead two should match as it is the only one who has a non-matching rotation.'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetCampaignPublishAndVersionData(): void
|
||||
{
|
||||
$campaign = $this->createCampaign();
|
||||
$this->em->flush();
|
||||
|
||||
$result = $this->repository->getCampaignPublishAndVersionData($campaign->getId());
|
||||
|
||||
Assert::assertIsArray($result);
|
||||
Assert::assertArrayHasKey('is_published', $result);
|
||||
Assert::assertArrayHasKey('version', $result);
|
||||
Assert::assertEquals('1', $result['is_published']);
|
||||
// Version should be a string representation of an integer
|
||||
Assert::assertIsString($result['version']);
|
||||
Assert::assertGreaterThanOrEqual('1', $result['version']);
|
||||
}
|
||||
|
||||
public function testGetCampaignPublishAndVersionDataWithNonExistentCampaign(): void
|
||||
{
|
||||
$nonExistentId = 99999;
|
||||
|
||||
$result = $this->repository->getCampaignPublishAndVersionData($nonExistentId);
|
||||
|
||||
Assert::assertEquals([], $result);
|
||||
}
|
||||
|
||||
private function createLead(Campaign $campaign, ?CampaignLead &$campaignLead = null): Lead // @phpstan-ignore parameterByRef.unusedType
|
||||
{
|
||||
$lead = new Lead();
|
||||
$this->em->persist($lead);
|
||||
|
||||
$campaignLead = new CampaignLead();
|
||||
$campaignLead->setCampaign($campaign);
|
||||
$campaignLead->setLead($lead);
|
||||
$campaignLead->setDateAdded(new \DateTime());
|
||||
$this->em->persist($campaignLead);
|
||||
|
||||
return $lead;
|
||||
}
|
||||
|
||||
private function createCampaign(): Campaign
|
||||
{
|
||||
$campaign = new Campaign();
|
||||
$campaign->setName('Test campaign');
|
||||
$campaign->setIsPublished(true);
|
||||
$this->em->persist($campaign);
|
||||
|
||||
return $campaign;
|
||||
}
|
||||
|
||||
private function createEvent(Campaign $campaign): Event
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setName('Test event');
|
||||
$event->setCampaign($campaign);
|
||||
$event->setType('lead.changepoints');
|
||||
$event->setEventType(Event::TYPE_ACTION);
|
||||
$this->em->persist($event);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function createEventLog(Campaign $campaign, ?CampaignLead &$campaignLead = null): LeadEventLog
|
||||
{
|
||||
$event = $this->createEvent($campaign);
|
||||
$lead = $this->createLead($campaign, $campaignLead);
|
||||
$leadEventLog = new LeadEventLog();
|
||||
$leadEventLog->setLead($lead);
|
||||
$leadEventLog->setEvent($event);
|
||||
$leadEventLog->setTriggerDate(new \DateTime());
|
||||
$this->em->persist($leadEventLog);
|
||||
|
||||
return $leadEventLog;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder as DbalQueryBuilder;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\CampaignRepository;
|
||||
use Mautic\CoreBundle\Test\Doctrine\RepositoryConfiguratorTrait;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class CampaignRepositoryTest extends TestCase
|
||||
{
|
||||
use RepositoryConfiguratorTrait;
|
||||
|
||||
/**
|
||||
* @var MockObject&QueryBuilder
|
||||
*/
|
||||
private MockObject $queryBuilder;
|
||||
|
||||
private CampaignRepository $repository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->queryBuilder = $this->getMockBuilder(QueryBuilder::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['select', 'from', 'where', 'setParameter', 'andWhere', 'getQuery', 'getRootAliases'])
|
||||
->getMock();
|
||||
|
||||
$this->repository = $this->configureRepository(Campaign::class);
|
||||
|
||||
$this->entityManager->method('createQueryBuilder')->willReturn($this->queryBuilder);
|
||||
$this->connection->method('createQueryBuilder')->willReturnCallback(fn () => new DbalQueryBuilder($this->connection));
|
||||
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$translator->method('trans')->willReturnCallback(fn ($id) => match ($id) {
|
||||
'mautic.campaign.campaign.searchcommand.isexpired' => 'is:expired',
|
||||
'mautic.campaign.campaign.searchcommand.ispending' => 'is:pending',
|
||||
default => $id,
|
||||
});
|
||||
$this->repository->setTranslator($translator);
|
||||
}
|
||||
|
||||
public function testFetchEmailIdsById(): void
|
||||
{
|
||||
$id = 2;
|
||||
|
||||
$queryResult = [
|
||||
1 => ['channelId' => 1],
|
||||
2 => ['channelId' => 2],
|
||||
];
|
||||
|
||||
$expectedResult = [1, 2];
|
||||
|
||||
$this->entityManager
|
||||
->method('createQueryBuilder')
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('select')
|
||||
->with('e.channelId')
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('from')
|
||||
->with(Campaign::class, $this->repository->getTableAlias(), $this->repository->getTableAlias().'.id')
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('where')
|
||||
->with($this->repository->getTableAlias().'.id = :id')
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('setParameter')
|
||||
->with('id', $id)
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$this->queryBuilder->method('getRootAliases')
|
||||
->willReturn(['e']);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('andWhere')
|
||||
->with('e.channelId IS NOT NULL')
|
||||
->willReturn($this->queryBuilder);
|
||||
|
||||
$query = $this->getMockBuilder(Query::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['setHydrationMode', 'getResult'])
|
||||
->getMock();
|
||||
|
||||
$query->expects(self::once())
|
||||
->method('setHydrationMode')
|
||||
->with(Query::HYDRATE_ARRAY)
|
||||
->willReturn($query);
|
||||
|
||||
$this->queryBuilder->expects(self::once())
|
||||
->method('getQuery')
|
||||
->willReturn($query);
|
||||
|
||||
$query->expects(self::once())
|
||||
->method('getResult')
|
||||
->willReturn($queryResult);
|
||||
|
||||
$result = $this->repository->fetchEmailIdsById($id);
|
||||
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function testAddSearchCommandWhereClauseHandlesExpirationFilters(): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$filter = (object) ['command' => 'is:expired', 'string' => '', 'not' => false, 'strict' => false];
|
||||
|
||||
$method = new \ReflectionMethod(CampaignRepository::class, 'addSearchCommandWhereClause');
|
||||
$method->setAccessible(true);
|
||||
|
||||
[$expr, $params] = $method->invoke($this->repository, $qb, $filter);
|
||||
|
||||
self::assertSame(
|
||||
'(c.isPublished = :par1) AND (c.publishDown IS NOT NULL) AND (c.publishDown <> \'\') AND (c.publishDown < CURRENT_TIMESTAMP())',
|
||||
(string) $expr
|
||||
);
|
||||
self::assertSame(['par1' => true], $params);
|
||||
}
|
||||
|
||||
public function testAddSearchCommandWhereClauseHandlesPendingFilters(): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$filter = (object) ['command' => 'is:pending', 'string' => '', 'not' => false, 'strict' => false];
|
||||
|
||||
$method = new \ReflectionMethod(CampaignRepository::class, 'addSearchCommandWhereClause');
|
||||
$method->setAccessible(true);
|
||||
|
||||
[$expr, $params] = $method->invoke($this->repository, $qb, $filter);
|
||||
|
||||
self::assertSame(
|
||||
'(c.isPublished = :par1) AND (c.publishUp IS NOT NULL) AND (c.publishUp <> \'\') AND (c.publishUp > CURRENT_TIMESTAMP())',
|
||||
(string) $expr
|
||||
);
|
||||
self::assertSame(['par1' => true], $params);
|
||||
}
|
||||
|
||||
public function testGetSearchCommandsContainsExpirationFilters(): void
|
||||
{
|
||||
$commands = $this->repository->getSearchCommands();
|
||||
self::assertContains('mautic.campaign.campaign.searchcommand.isexpired', $commands);
|
||||
self::assertContains('mautic.campaign.campaign.searchcommand.ispending', $commands);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class CampaignTest extends TestCase
|
||||
{
|
||||
public function testGetEventsByType(): void
|
||||
{
|
||||
$campaign = $this->addSomeEvents(new Campaign());
|
||||
|
||||
Assert::assertCount(2, $campaign->getEventsByType(Event::TYPE_DECISION));
|
||||
Assert::assertCount(1, $campaign->getEventsByType(Event::TYPE_ACTION));
|
||||
Assert::assertCount(1, $campaign->getEventsByType(Event::TYPE_CONDITION));
|
||||
}
|
||||
|
||||
private function addSomeEvents(Campaign $campaign): Campaign
|
||||
{
|
||||
$decisionA = new EventFake(1);
|
||||
$decisionA->setName('Decision A');
|
||||
$decisionA->setEventType(Event::TYPE_DECISION);
|
||||
|
||||
$action = new EventFake(2);
|
||||
$action->setName('Action A');
|
||||
$action->setEventType(Event::TYPE_ACTION);
|
||||
|
||||
$condition = new EventFake(3);
|
||||
$condition->setName('Condition A');
|
||||
$condition->setEventType(Event::TYPE_CONDITION);
|
||||
|
||||
$decisionB = new EventFake(4);
|
||||
$decisionB->setName('Decision B');
|
||||
$decisionB->setEventType(Event::TYPE_DECISION);
|
||||
|
||||
$campaign->addEvent($decisionA->getId(), $decisionA);
|
||||
$campaign->addEvent($action->getId(), $action);
|
||||
$campaign->addEvent($condition->getId(), $condition);
|
||||
$campaign->addEvent($decisionB->getId(), $decisionB);
|
||||
|
||||
return $campaign;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
|
||||
use Doctrine\DBAL\Query\QueryBuilder as DbalQueryBuilder;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder as OrmQueryBuilder;
|
||||
use Mautic\CampaignBundle\Entity\ContactLimiterTrait;
|
||||
use Mautic\CampaignBundle\Executioner\ContactFinder\Limiter\ContactLimiter;
|
||||
use Mautic\CoreBundle\Test\Doctrine\MockedConnectionTrait;
|
||||
|
||||
class ContactLimiterTraitTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
use ContactLimiterTrait;
|
||||
use MockedConnectionTrait;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|Connection
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $connection;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|EntityManagerInterface
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->connection = $this->getMockedConnection();
|
||||
|
||||
$expr = new ExpressionBuilder($this->connection);
|
||||
$this->connection->method('getExpressionBuilder')
|
||||
->willReturn($expr);
|
||||
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$this->entityManager->method('getExpressionBuilder')
|
||||
->willReturn(new Expr());
|
||||
}
|
||||
|
||||
public function testSpecificContactId(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, 1);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id = :contactId LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['contactId' => 1], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) = :contact', $qb->getDQL());
|
||||
$this->assertEquals(1, $qb->getParameter('contact')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testListOfContacts(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, null, null, [1, 2, 3]);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id IN (:contactIds) LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['contactIds' => [1, 2, 3]], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) IN(:contactIds)', $qb->getDQL());
|
||||
$this->assertEquals([1, 2, 3], $qb->getParameter('contactIds')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testMinContactId(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, 4, null);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id >= :minContactId LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['minContactId' => 4], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) >= :minContactId', $qb->getDQL());
|
||||
$this->assertEquals(4, $qb->getParameter('minContactId')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testBatchMinContactId(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, 4, null);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$contactLimiter->setBatchMinContactId(10);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id >= :minContactId LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['minContactId' => 10], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) >= :minContactId', $qb->getDQL());
|
||||
$this->assertEquals(10, $qb->getParameter('minContactId')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testMaxContactId(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, null, 10);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id <= :maxContactId LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['maxContactId' => 10], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) <= :maxContactId', $qb->getDQL());
|
||||
$this->assertEquals(10, $qb->getParameter('maxContactId')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testMinAndMaxContactId(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, 1, 10);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id BETWEEN :minContactId AND :maxContactId LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['minContactId' => 1, 'maxContactId' => 10], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) BETWEEN :minContactId AND :maxContactId', $qb->getDQL());
|
||||
$this->assertEquals(1, $qb->getParameter('minContactId')->getValue());
|
||||
$this->assertEquals(10, $qb->getParameter('maxContactId')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testThreads(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, null, null, null, [], 1, 5);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE MOD((l.lead_id + :threadShift), :maxThreads) = 0 LIMIT 50', $qb->getSQL());
|
||||
$this->assertEquals(['threadShift' => 0, 'maxThreads' => 5], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter);
|
||||
$this->assertEquals('SELECT WHERE MOD((IDENTITY(l.lead) + :threadShift), :maxThreads) = 0', $qb->getDQL());
|
||||
$this->assertEquals(0, $qb->getParameter('threadShift')->getValue());
|
||||
$this->assertEquals(5, $qb->getParameter('maxThreads')->getValue());
|
||||
$this->assertEquals(50, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testMaxResultsIgnoredForCountQueries(): void
|
||||
{
|
||||
$contactLimiter = new ContactLimiter(50, 1);
|
||||
|
||||
$qb = new DbalQueryBuilder($this->connection);
|
||||
$this->updateQueryFromContactLimiter('l', $qb, $contactLimiter, true);
|
||||
$this->assertEquals('SELECT WHERE l.lead_id = :contactId', $qb->getSQL());
|
||||
$this->assertEquals(['contactId' => 1], $qb->getParameters());
|
||||
|
||||
$qb = new OrmQueryBuilder($this->entityManager);
|
||||
$this->updateOrmQueryFromContactLimiter('l', $qb, $contactLimiter, true);
|
||||
$this->assertEquals('SELECT WHERE IDENTITY(l.lead) = :contact', $qb->getDQL());
|
||||
$this->assertEquals(1, $qb->getParameter('contact')->getValue());
|
||||
$this->assertEquals(null, $qb->getMaxResults());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
|
||||
/**
|
||||
* Allows to use the live Event entity and set the ID.
|
||||
*/
|
||||
final class EventFake extends Event
|
||||
{
|
||||
private ?int $id;
|
||||
|
||||
public function __construct(?int $id = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Entity\EventRepository;
|
||||
use Mautic\CampaignBundle\Entity\Lead as CampaignMember;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
class EventRepositoryFunctionalTest extends MauticMysqlTestCase
|
||||
{
|
||||
/**
|
||||
* @return iterable<string, array{?\DateTime, ?\DateTime, int}>
|
||||
*/
|
||||
public static function dataGetContactPendingEventsConsidersCampaignPublishUpAndDown(): iterable
|
||||
{
|
||||
yield 'Publish Up and Down not set' => [null, null, 1];
|
||||
yield 'Publish Up and Down set' => [new \DateTime('-1 day'), new \DateTime('+1 day'), 1];
|
||||
yield 'Publish Up and Down set with Publish Up in the future' => [new \DateTime('+1 day'), new \DateTime('+2 day'), 0];
|
||||
yield 'Publish Up and Down set with Publish Down in the past' => [new \DateTime('-2 day'), new \DateTime('-1 day'), 0];
|
||||
yield 'Publish Up in the past' => [new \DateTime('-1 day'), null, 1];
|
||||
yield 'Publish Up in the future' => [new \DateTime('+1 day'), null, 0];
|
||||
yield 'Publish Down in the past' => [null, new \DateTime('-1 day'), 0];
|
||||
yield 'Publish Down in the future' => [null, new \DateTime('+1 day'), 1];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataGetContactPendingEventsConsidersCampaignPublishUpAndDown')]
|
||||
public function testGetContactPendingEventsConsidersCampaignPublishUpAndDown(?\DateTime $publishUp, ?\DateTime $publishDown, int $expectedCount): void
|
||||
{
|
||||
$repository = static::getContainer()->get('mautic.campaign.repository.event');
|
||||
\assert($repository instanceof EventRepository);
|
||||
|
||||
$campaign = $this->createCampaign();
|
||||
$event = $this->createEvent($campaign);
|
||||
$lead = $this->createLead();
|
||||
$this->createCampaignMember($lead, $campaign);
|
||||
|
||||
$campaign->setPublishUp($publishUp);
|
||||
$campaign->setPublishDown($publishDown);
|
||||
$this->em->persist($campaign);
|
||||
$this->em->flush();
|
||||
|
||||
Assert::assertCount($expectedCount, $repository->getContactPendingEvents($lead->getId(), $event->getType()));
|
||||
}
|
||||
|
||||
private function createLead(): Lead
|
||||
{
|
||||
$lead = new Lead();
|
||||
$lead->setFirstname('Test');
|
||||
$this->em->persist($lead);
|
||||
|
||||
return $lead;
|
||||
}
|
||||
|
||||
private function createCampaign(): Campaign
|
||||
{
|
||||
$campaign = new Campaign();
|
||||
$campaign->setName('Test');
|
||||
$this->em->persist($campaign);
|
||||
|
||||
return $campaign;
|
||||
}
|
||||
|
||||
private function createEvent(Campaign $campaign): Event
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setName('test');
|
||||
$event->setCampaign($campaign);
|
||||
$event->setType('test.type');
|
||||
$event->setEventType('action');
|
||||
$this->em->persist($event);
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function createCampaignMember(Lead $lead, Campaign $campaign): void
|
||||
{
|
||||
$member = new CampaignMember();
|
||||
$member->setLead($lead);
|
||||
$member->setCampaign($campaign);
|
||||
$member->setDateAdded(new \DateTime());
|
||||
$this->em->persist($member);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CoreBundle\Test\Doctrine\RepositoryConfiguratorTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class EventRepositoryTest extends TestCase
|
||||
{
|
||||
use RepositoryConfiguratorTrait;
|
||||
|
||||
public function testDecreaseFailedCount(): void
|
||||
{
|
||||
$emMock = $this->createMock(EntityManager::class);
|
||||
$connMock = $this->createMock(Connection::class);
|
||||
$queryBuilderMock = $this->createMock(QueryBuilder::class);
|
||||
$expressionMock = $this->createMock(Expr::class);
|
||||
|
||||
$queryBuilderMock->expects($this->any())
|
||||
->method('expr')
|
||||
->willReturn($expressionMock);
|
||||
|
||||
$expressionMock->expects($this->once())
|
||||
->method('eq')
|
||||
->with('id', ':id')
|
||||
->willReturn('id = :id');
|
||||
|
||||
$queryBuilderMock->expects($this->any())
|
||||
->method('expr')
|
||||
->willReturn($expressionMock);
|
||||
|
||||
$expressionMock->expects($this->once())
|
||||
->method('gt')
|
||||
->with('failed_count', 0)
|
||||
->willReturn('failed_count > 0');
|
||||
|
||||
$queryBuilderMock->expects($this->once())
|
||||
->method('update')
|
||||
->with(MAUTIC_TABLE_PREFIX.'campaign_events')
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$queryBuilderMock->expects($this->once())
|
||||
->method('set')
|
||||
->with('failed_count', 'failed_count - 1')
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$queryBuilderMock->expects($this->once())
|
||||
->method('where')
|
||||
->with('id = :id')
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$queryBuilderMock->expects($this->once())
|
||||
->method('andWhere')
|
||||
->with('failed_count > 0')
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$queryBuilderMock->expects($this->once())
|
||||
->method('setParameter')
|
||||
->with('id', $this->equalTo(42))
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$connMock->expects($this->once())
|
||||
->method('createQueryBuilder')
|
||||
->willReturn($queryBuilderMock);
|
||||
|
||||
$emMock->expects($this->once())
|
||||
->method('getConnection')
|
||||
->willReturn($connMock);
|
||||
|
||||
$eventRepository = $this->configureRepository(Event::class, $emMock);
|
||||
$this->connection->method('createQueryBuilder')
|
||||
->willReturnCallback(fn () => $queryBuilderMock);
|
||||
|
||||
$eventMock = $this->createMock(Event::class);
|
||||
$eventMock->method('getId')
|
||||
->willReturn(42);
|
||||
|
||||
$eventRepository->decreaseFailedCount($eventMock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class EventTest extends TestCase
|
||||
{
|
||||
private const TEST_NAME = 'Test Name';
|
||||
private const DATE = '2021-10-08 08:00:00';
|
||||
|
||||
public function testSetTriggerHourWhenEmpty(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setName(self::TEST_NAME);
|
||||
$event->setTriggerHour('');
|
||||
$this->assertNull($event->getTriggerHour());
|
||||
}
|
||||
|
||||
public function testSetTriggerHourWhenArray(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setName(self::TEST_NAME);
|
||||
$event->setTriggerHour(['date' => self::DATE]);
|
||||
$this->assertEquals(new \DateTime(self::DATE), $event->getTriggerHour());
|
||||
}
|
||||
|
||||
public function testSetTriggerHourWhenString(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setName(self::TEST_NAME);
|
||||
$event->setTriggerHour(self::DATE);
|
||||
$this->assertEquals(new \DateTime(self::DATE), $event->getTriggerHour());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Entity;
|
||||
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Mautic\CampaignBundle\Entity\FailedLeadEventLog;
|
||||
use Mautic\CampaignBundle\Entity\LeadEventLog;
|
||||
use Mautic\CoreBundle\Test\Doctrine\RepositoryConfiguratorTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class LeadEventLogRepositoryTest extends TestCase
|
||||
{
|
||||
use RepositoryConfiguratorTrait;
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('isLastFailedDataProvider')]
|
||||
public function testIsLastFailed(?LeadEventLog $leadEventLog, bool $expectedResult): void
|
||||
{
|
||||
$emMock = $this->createMock(EntityManager::class);
|
||||
$unitOfWorkMock = $this->createMock(UnitOfWork::class);
|
||||
$emMock->method('getUnitOfWork')
|
||||
->willReturn($unitOfWorkMock);
|
||||
|
||||
$entityPersisterMock = $this->createMock(EntityPersister::class);
|
||||
$unitOfWorkMock->method('getEntityPersister')
|
||||
->willReturn($entityPersisterMock);
|
||||
|
||||
$entityPersisterMock->method('load')
|
||||
->with(['lead' => 42, 'event' => 4242], null, null, [], null, 1, ['dateTriggered' => 'DESC'])
|
||||
->willReturn($leadEventLog);
|
||||
|
||||
$leadEventLogRepository = $this->configureRepository(LeadEventLog::class, $emMock);
|
||||
$this->connection->method('createQueryBuilder')->willReturnCallback(fn () => new QueryBuilder($this->connection));
|
||||
|
||||
$isLastFailed = $leadEventLogRepository->isLastFailed(42, 4242);
|
||||
$this->assertSame($expectedResult, $isLastFailed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,array<mixed>>
|
||||
*/
|
||||
public static function isLastFailedDataProvider(): array
|
||||
{
|
||||
$leadEventLogNoFail = new LeadEventLog();
|
||||
$failedLeadEvent = new FailedLeadEventLog();
|
||||
$leadEventLogFail = new LeadEventLog();
|
||||
$leadEventLogFail->setFailedLog($failedLeadEvent);
|
||||
|
||||
return [
|
||||
'no_last_log' => [null, false],
|
||||
'last_log_no_fail' => [$leadEventLogNoFail, false],
|
||||
'last_log_fail' => [$leadEventLogFail, true],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user