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,268 @@
<?php
declare(strict_types=1);
namespace Mautic\CampaignBundle\Tests\Model;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\OptimisticLockException;
use Mautic\CampaignBundle\Entity\Campaign;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CampaignBundle\Entity\Lead as CampaignLead;
use Mautic\CampaignBundle\Model\CampaignModel;
use Mautic\CoreBundle\Entity\IpAddress;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\EmailBundle\Entity\Email;
use Mautic\EmailBundle\Entity\Stat;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\PageBundle\Entity\Hit;
use Mautic\PageBundle\Entity\Redirect;
use Mautic\PageBundle\Entity\Trackable;
class CampaignModelFunctionalTest extends MauticMysqlTestCase
{
/**
* @throws OptimisticLockException
* @throws ORMException
* @throws Exception
*/
public function testGetEmailsCountryStats(): void
{
/** @var CampaignModel $model */
$model = $this->getContainer()->get('mautic.campaign.model.campaign');
$dateFrom = new \DateTimeImmutable('2023-07-21');
$dateTo = new \DateTimeImmutable('2023-07-24');
$leadsPayload = [
[
'email' => 'example1@test.com',
'country' => 'Spain',
'read' => true,
'click' => true,
],
[
'email' => 'example2@test.com',
'country' => 'Spain',
'read' => true,
'click' => false,
],
[
'email' => 'example3@test.com',
'country' => 'Spain',
'read' => false,
'click' => false,
],
[
'email' => 'example4@test.com',
'country' => '',
'read' => true,
'click' => true,
],
[
'email' => 'example5@test.com',
'country' => 'Poland',
'read' => true,
'click' => false,
],
[
'email' => 'example6@test.com',
'country' => 'Poland',
'read' => true,
'click' => true,
],
];
// Create test campaign
$campaign = new Campaign();
$campaign->setName('Test campaign');
$this->em->persist($campaign);
$this->em->flush();
foreach ($leadsPayload as $key => $values) {
// Create lead
$lead = new Lead();
$lead->setEmail($values['email']);
$lead->setCountry($values['country']);
$this->em->persist($lead);
$leadsArr[] = [
'lead' => $lead,
'read' => $values['read'],
'click' => $values['click'],
];
// Create campaign lead and assign to lead
$campaignLead = new CampaignLead();
$campaignLead->setLead($lead);
$campaignLead->setDateAdded(new \DateTime('2023-07-22'));
$campaignLead->setManuallyAdded(true);
$campaignLead->setCampaign($campaign);
$this->em->persist($campaignLead);
$this->em->flush();
$campaign->addLead($key, $campaignLead);
}
for ($i = 0; $i < 4; ++$i) {
// Create email
$email = new Email();
$email->setName('Test email '.$i);
$this->em->persist($email);
$this->em->flush();
// Create email events
$event = new Event();
$event->setName('Send email '.$i);
$event->setType('email.send');
$event->setEventType('action');
$event->setChannel('email');
$event->setChannelId($email->getId());
$event->setCampaign($campaign);
$this->em->persist($event);
$this->em->flush();
// Add events to campaign
$campaign->addEvent($i, $event);
// Create campaign email sending statistics
foreach ($leadsArr as $value) {
$this->emulateEmailStat($value['lead'], $email, $value['read'], $event->getId());
if ($value['read'] && $value['click']) {
$hits = rand(1, 5);
$uniqueHits = rand(1, $hits);
$this->emulateClick($value['lead'], $email, $hits, $uniqueHits);
}
}
}
$results = $model->getCountryStats($campaign, $dateFrom, $dateTo);
$this->assertCount(4, $campaign->getEmailSendEvents());
$this->assertCount(3, $results);
$this->assertSame([
'contacts' => [
[
'country' => '',
'contacts' => '1',
],
[
'country' => 'Poland',
'contacts' => '2',
],
[
'country' => 'Spain',
'contacts' => '3',
],
],
'clicked_through_count' => [
[
'clicked_through_count' => '4',
'country' => '',
],
[
'clicked_through_count' => '4',
'country' => 'Poland',
],
[
'clicked_through_count' => '4',
'country' => 'Spain',
],
],
'read_count' => [
[
'read_count' => '4',
'country' => '',
],
[
'read_count' => '8',
'country' => 'Poland',
],
[
'read_count' => '8',
'country' => 'Spain',
],
],
], $results);
}
/**
* @throws OptimisticLockException
* @throws ORMException
*/
public function testGetContextEntity(): void
{
/** @var CampaignModel $model */
$model = $this->getContainer()->get('mautic.campaign.model.campaign');
$campaign = new Campaign();
$campaign->setName('Test email');
$this->em->persist($campaign);
$this->em->flush();
$id = $campaign->getId();
$result = $model->getEntity($id);
$this->assertSame($campaign, $result);
}
/**
* @throws OptimisticLockException
* @throws ORMException
*/
private function emulateEmailStat(Lead $lead, Email $email, bool $isRead, int $sourceId): void
{
$stat = new Stat();
$stat->setEmailAddress('test@test.com');
$stat->setLead($lead);
$stat->setDateSent(new \DateTime('2023-07-22'));
$stat->setEmail($email);
$stat->setIsRead($isRead);
$stat->setSource('campaign.event');
$stat->setSourceId($sourceId);
$this->em->persist($stat);
$this->em->flush();
}
/**
* @throws OptimisticLockException
* @throws ORMException
*/
private function emulateClick(Lead $lead, Email $email, int $hits, int $uniqueHits): void
{
$ipAddress = new IpAddress();
$ipAddress->setIpAddress('127.0.0.1');
$this->em->persist($ipAddress);
$this->em->flush();
$redirect = new Redirect();
$redirect->setRedirectId(uniqid());
$redirect->setUrl('https://example.com');
$redirect->setUniqueHits($uniqueHits);
$redirect->setHits($hits);
$this->em->persist($redirect);
$trackable = new Trackable();
$trackable->setChannelId($email->getId());
$trackable->setHits($hits);
$trackable->setChannel('email');
$trackable->setUniqueHits($uniqueHits);
$trackable->setRedirect($redirect);
$this->em->persist($trackable);
$pageHit = new Hit();
$pageHit->setRedirect($redirect);
$pageHit->setIpAddress($ipAddress);
$pageHit->setEmail($email);
$pageHit->setLead($lead);
$pageHit->setDateHit(new \DateTime('2023-07-22'));
$pageHit->setCode(200);
$pageHit->setUrl($redirect->getUrl());
$pageHit->setTrackingId($redirect->getRedirectId());
$pageHit->setSource('email');
$pageHit->setSourceId($email->getId());
$this->em->persist($pageHit);
$this->em->flush();
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Mautic\CampaignBundle\Tests\Model;
use Mautic\CampaignBundle\Tests\CampaignTestAbstract;
class CampaignModelTest extends CampaignTestAbstract
{
public function testGetSourceListsWithNull(): void
{
$model = $this->initCampaignModel();
$lists = $model->getSourceLists();
$this->assertTrue(isset($lists['lists']));
$this->assertSame([parent::$mockId => parent::$mockName], $lists['lists']);
$this->assertTrue(isset($lists['forms']));
$this->assertSame([parent::$mockId => parent::$mockName], $lists['forms']);
}
public function testGetSourceListsWithLists(): void
{
$model = $this->initCampaignModel();
$lists = $model->getSourceLists('lists');
$this->assertSame([parent::$mockId => parent::$mockName], $lists);
}
public function testGetSourceListsWithForms(): void
{
$model = $this->initCampaignModel();
$lists = $model->getSourceLists('forms');
$this->assertSame([parent::$mockId => parent::$mockName], $lists);
}
}

View File

@@ -0,0 +1,159 @@
<?php
namespace Mautic\CampaignBundle\Tests\Model;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManager;
use Mautic\CampaignBundle\Entity\Campaign;
use Mautic\CampaignBundle\Entity\CampaignRepository;
use Mautic\CampaignBundle\EventCollector\EventCollector;
use Mautic\CampaignBundle\Membership\MembershipBuilder;
use Mautic\CampaignBundle\Model\CampaignModel;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\FormBundle\Model\FormModel;
use Mautic\LeadBundle\Model\ListModel;
use Mautic\LeadBundle\Tracker\ContactTracker;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class CampaignModelTransactionalTest extends TestCase
{
private MockObject $entityManagerMock;
private MockObject $connectionMock;
private MockObject $userHelperMock;
private MockObject $campaignRepositoryMock;
private MockObject $campaignModel;
protected function setUp(): void
{
$this->connectionMock = $this->createMock(Connection::class);
// Create repository mock
$this->campaignRepositoryMock = $this->createMock(CampaignRepository::class);
$this->campaignRepositoryMock->method('setCurrentUser')
->willReturnSelf();
$this->entityManagerMock = $this->createMock(EntityManager::class);
$this->entityManagerMock->method('getConnection')
->willReturn($this->connectionMock);
$this->entityManagerMock->method('getRepository')
->with(Campaign::class)
->willReturn($this->campaignRepositoryMock);
// Mock user helper
$this->userHelperMock = $this->createMock(UserHelper::class);
// Create all the required dependencies as mocks
$leadListModel = $this->createMock(ListModel::class);
$formModel = $this->createMock(FormModel::class);
$eventCollector = $this->createMock(EventCollector::class);
$membershipBuilder = $this->createMock(MembershipBuilder::class);
$contactTracker = $this->createMock(ContactTracker::class);
$security = $this->createMock(CorePermissions::class);
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$router = $this->createMock(UrlGeneratorInterface::class);
$translator = $this->createMock(Translator::class);
$logger = $this->createMock(LoggerInterface::class);
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
// Create the campaign model mock
$this->campaignModel = $this->getMockBuilder(CampaignModel::class)
->setConstructorArgs([
$leadListModel,
$formModel,
$eventCollector,
$membershipBuilder,
$contactTracker,
$this->entityManagerMock,
$security,
$dispatcher,
$router,
$translator,
$this->userHelperMock,
$logger,
$coreParametersHelper,
])
->onlyMethods(['saveEntity'])
->getMock();
}
/**
* Helper function to set up common campaign mock expectations for unpublish tests.
*
* @param int $campaignId The campaign ID to use
* @param int $version The campaign version to use
* @param bool $isPublished The current published state
*
* @return MockObject&Campaign The configured campaign mock
*/
private function createCampaignMockForUnpublish(
int $campaignId = 5,
int $version = 1,
bool $isPublished = true,
): MockObject&Campaign {
/** @var MockObject&Campaign $campaignMock */
$campaignMock = $this->createMock(Campaign::class);
$campaignMock->expects($this->once())
->method('getId')
->willReturn($campaignId);
// Mock version data from repository
$this->campaignRepositoryMock->expects($this->once())
->method('getCampaignPublishAndVersionData')
->with($campaignId)
->willReturn([
'is_published' => $isPublished ? 1 : 0,
'version' => $version,
]);
$campaignMock->expects($this->once())
->method('getVersion')
->willReturn($version);
// Setting published flag
$campaignMock->expects($this->once())
->method('setIsPublished')
->with(false);
$campaignMock->expects($this->once())
->method('markForVersionIncrement');
return $campaignMock;
}
public function testTransactionalCampaignUnPublish(): void
{
$campaignMock = $this->createCampaignMockForUnpublish();
// Saving the entity
$this->campaignModel->expects($this->once())
->method('saveEntity')
->with($campaignMock);
$this->campaignModel->transactionalCampaignUnPublish($campaignMock);
}
public function testTransactionalCampaignUnPublishWithException(): void
{
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Database error');
$campaignMock = $this->createCampaignMockForUnpublish();
// Saving the entity throws an exception
$this->campaignModel->expects($this->once())
->method('saveEntity')
->with($campaignMock)
->willThrowException(new \Exception('Database error'));
$this->campaignModel->transactionalCampaignUnPublish($campaignMock);
}
}

View File

@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace Mautic\CampaignBundle\Tests\Model;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CampaignBundle\CampaignEvents;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CampaignBundle\Entity\EventRepository;
use Mautic\CampaignBundle\Event\DeleteEvent;
use Mautic\CampaignBundle\Model\EventModel;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class EventModelTest extends TestCase
{
/**
* @var EntityManagerInterface|MockObject
*/
private $entityManagerMock;
/**
* @var EventRepository|MockObject
*/
private $eventRepositoryMock;
/**
* @var EventDispatcherInterface|MockObject
*/
private $dispatcherMock;
private MockObject|EventModel $eventModel;
protected function setUp(): void
{
$this->entityManagerMock = $this->createMock(EntityManagerInterface::class);
$this->eventRepositoryMock = $this->createMock(EventRepository::class);
$this->dispatcherMock = $this->createMock(EventDispatcherInterface::class);
$this->eventModel = new EventModel(
$this->entityManagerMock,
$this->createMock(CorePermissions::class),
$this->dispatcherMock,
$this->createMock(UrlGeneratorInterface::class),
$this->createMock(Translator::class),
$this->createMock(UserHelper::class),
$this->createMock(LoggerInterface::class),
$this->createMock(CoreParametersHelper::class)
);
}
public function testThatClonedEventsDoNotAttemptNullingParentInDeleteEvents(): void
{
$this->entityManagerMock->expects($this->never())
->method('getRepository')
->with(Event::class)
->willReturn($this->eventRepositoryMock);
$currentEvents = [
'new1',
'new2',
'new3',
];
$deletedEvents = [
'new1',
];
$this->eventModel->deleteEvents($currentEvents, $deletedEvents);
}
public function testThatItDeletesEventLogs(): void
{
$idToDelete = 'old1';
$currentEvents = [
'new1',
];
$deletedEvents = [
'new1',
$idToDelete,
];
$this->entityManagerMock->method('getRepository')
->with(Event::class)
->willReturn($this->eventRepositoryMock);
$this->eventRepositoryMock->expects($this->once())
->method('nullEventRelationships')
->with([$idToDelete]);
$this->eventRepositoryMock->expects($this->once())
->method('setEventsAsDeleted')
->with([1 => $idToDelete]);
$this->dispatcherMock->expects($this->once())
->method('dispatch')
->with(new DeleteEvent([$idToDelete]), CampaignEvents::ON_EVENT_DELETE);
$this->eventModel->deleteEvents($currentEvents, $deletedEvents);
}
public function testDeleteEventsByCampaignId(): void
{
/** @var EventModel&MockObject */
$mockModel = $this->getMockBuilder(EventModel::class)
->disableOriginalConstructor()
->onlyMethods(['getRepository', 'deleteEventsByEventIds'])
->getMock();
$mockModel->expects($this->once())
->method('getRepository')
->willReturn($this->eventRepositoryMock);
$campaignEvents = ['1', '2', '3'];
$this->eventRepositoryMock->expects($this->once())
->method('getCampaignEventIds')
->with(1)
->willReturn($campaignEvents);
$mockModel->expects($this->once())->method('deleteEventsByEventIds')
->with($campaignEvents);
$mockModel->deleteEventsByCampaignId(1);
}
}