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,53 @@
<?php
namespace Mautic\LeadBundle\Tests\Deduplicate;
use Mautic\LeadBundle\Deduplicate\CompanyDeduper;
use Mautic\LeadBundle\Entity\CompanyRepository;
use Mautic\LeadBundle\Exception\UniqueFieldNotFoundException;
use Mautic\LeadBundle\Field\FieldsWithUniqueIdentifier;
use Mautic\LeadBundle\Model\FieldModel;
use PHPUnit\Framework\MockObject\MockObject;
class CompanyDeduperTest extends \PHPUnit\Framework\TestCase
{
/**
* @var MockObject&FieldModel
*/
private MockObject $fieldModel;
/**
* @var MockObject&CompanyRepository
*/
private MockObject $companyRepository;
/**
* @var MockObject&FieldsWithUniqueIdentifier
*/
private MockObject $fieldsWithUniqueIdentifier;
protected function setUp(): void
{
$this->fieldModel = $this->createMock(FieldModel::class);
$this->fieldsWithUniqueIdentifier = $this->createMock(FieldsWithUniqueIdentifier::class);
$this->companyRepository = $this->createMock(CompanyRepository::class);
}
public function testUniqueFieldNotFoundException(): void
{
$this->expectException(UniqueFieldNotFoundException::class);
$this->fieldModel->method('getFieldList')->willReturn([]);
$this->getDeduper()->checkForDuplicateCompanies([]);
}
private function getDeduper(): CompanyDeduper
{
return new CompanyDeduper(
$this->fieldModel,
$this->fieldsWithUniqueIdentifier,
$this->companyRepository
);
}
}

View File

@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
namespace Mautic\LeadBundle\Tests\Deduplicate;
use Doctrine\ORM\EntityManager;
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\LeadBundle\Deduplicate\ContactMerger;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Model\LeadModel;
final class ContactMergerFunctionalTest extends MauticMysqlTestCase
{
public function testMergedContactFound(): void
{
$model = static::getContainer()->get('mautic.lead.model.lead');
\assert($model instanceof LeadModel);
$merger = static::getContainer()->get('mautic.lead.merger');
\assert($merger instanceof ContactMerger);
$bob = new Lead();
$bob->setFirstname('Bob')
->setLastname('Smith')
->setEmail('bob.smith@test.com');
$model->saveEntity($bob);
$bobId = $bob->getId();
$jane = new Lead();
$jane->setFirstname('Jane')
->setLastname('Smith')
->setEmail('jane.smith@test.com');
$model->saveEntity($jane);
$janeId = $jane->getId();
$merger->merge($jane, $bob);
// Bob should have been merged into Jane
$jane = $model->getEntity($janeId);
$this->assertEquals($janeId, $jane->getId());
// If Bob is queried, Jane should be returned
$jane = $model->getEntity($bobId);
$this->assertEquals($janeId, $jane->getId());
// Merge Jane into a third contact
$joey = new Lead();
$joey->setFirstname('Joey')
->setLastname('Smith')
->setEmail('joey.smith@test.com');
$model->saveEntity($joey);
$joeyId = $joey->getId();
$merger->merge($joey, $jane);
// Query for Bob which should now return Joey
$joey = $model->getEntity($bobId);
$this->assertEquals($joeyId, $joey->getId());
// If Joey is deleted, querying for Bob or Jane should result in null
$model->deleteEntity($joey);
$bob = $model->getEntity($bobId);
$this->assertNull($bob);
$jane = $model->getEntity($janeId);
$this->assertNull($jane);
}
public function testMergedContactsPointsAreAccurate(): void
{
$model = static::getContainer()->get('mautic.lead.model.lead');
\assert($model instanceof LeadModel);
$em = static::getContainer()->get('doctrine.orm.entity_manager');
\assert($em instanceof EntityManager);
$merger = static::getContainer()->get('mautic.lead.merger');
\assert($merger instanceof ContactMerger);
// Startout Jane with 50 points
$jane = new Lead();
$jane->setFirstname('Jane')
->setLastname('Smith')
->setEmail('jane.smith@test.com')
->setPoints(50);
$model->saveEntity($jane);
$em->detach($jane);
$jane = $model->getEntity($jane->getId());
$this->assertEquals(50, $jane->getPoints());
$janeId = $jane->getId();
// Jane is currently a visitor on a different device with 3 points
$visitor = new Lead();
$visitor->setPoints(3);
$model->saveEntity($visitor);
$em->detach($visitor);
$visitor = $model->getEntity($visitor->getId());
$this->assertEquals(3, $visitor->getPoints());
// Jane submits a form or something that identifies her so the visitor should be merged into Jane giving her 53 points
$jane = $model->getEntity($janeId);
// Jane should start out with 50 points
$this->assertEquals(50, $jane->getPoints());
// Jane should come out of the merge as Jane
$jane = $merger->merge($jane, $visitor);
$this->assertEquals($janeId, $jane->getId());
// Jane should now have 53 points
$this->assertEquals(53, $jane->getPoints());
$em->detach($jane);
$em->detach($visitor);
// Jane should still have 53 points
$jane = $model->getEntity($janeId);
$this->assertEquals(53, $jane->getPoints());
// Jane is on another device again and gets 3 points
$visitor2 = new Lead();
$visitor2->setPoints(3);
$model->saveEntity($visitor2);
$em->detach($visitor2);
$visitor2 = $model->getEntity($visitor2->getId());
$this->assertEquals(3, $visitor2->getPoints());
// Jane again identifies herself, gets merged into the new visitor and so should now have a total of 56 points
$jane = $model->getEntity($janeId);
$jane = $merger->merge($jane, $visitor2);
$this->assertEquals($janeId, $jane->getId());
$em->detach($jane);
$em->detach($visitor2);
$jane = $model->getEntity($jane->getId());
$this->assertEquals(56, $jane->getPoints());
}
}

View File

@@ -0,0 +1,759 @@
<?php
namespace Mautic\LeadBundle\Tests\Deduplicate;
use Doctrine\Common\Collections\ArrayCollection;
use Mautic\CoreBundle\Entity\IpAddress;
use Mautic\LeadBundle\Deduplicate\ContactMerger;
use Mautic\LeadBundle\Deduplicate\Exception\SameContactException;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Entity\LeadRepository;
use Mautic\LeadBundle\Entity\MergeRecordRepository;
use Mautic\LeadBundle\Entity\Tag;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\UserBundle\Entity\User;
use Monolog\Logger;
use Symfony\Component\EventDispatcher\EventDispatcher;
class ContactMergerTest extends \PHPUnit\Framework\TestCase
{
/**
* @var \PHPUnit\Framework\MockObject\MockObject|LeadModel
*/
private \PHPUnit\Framework\MockObject\MockObject $leadModel;
/**
* @var \PHPUnit\Framework\MockObject\MockObject&LeadRepository
*/
private \PHPUnit\Framework\MockObject\MockObject $leadRepo;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|MergeRecordRepository
*/
private \PHPUnit\Framework\MockObject\MockObject $mergeRecordRepo;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|EventDispatcher
*/
private \PHPUnit\Framework\MockObject\MockObject $dispatcher;
/**
* @var \PHPUnit\Framework\MockObject\MockObject|Logger
*/
private \PHPUnit\Framework\MockObject\MockObject $logger;
protected function setUp(): void
{
$this->leadModel = $this->createMock(LeadModel::class);
$this->leadRepo = $this->createMock(LeadRepository::class);
$this->mergeRecordRepo = $this->createMock(MergeRecordRepository::class);
$this->dispatcher = $this->createMock(EventDispatcher::class);
$this->logger = $this->createMock(Logger::class);
$this->leadModel->method('getRepository')->willReturn($this->leadRepo);
}
public function testMergeTimestamps(): void
{
$oldestDateTime = new \DateTime('-60 minutes');
$latestDateTime = new \DateTime('-30 minutes');
$winner = new Lead();
$winner->setLastActive($oldestDateTime);
$winner->setDateIdentified($latestDateTime);
$loser = new Lead();
$loser->setLastActive($latestDateTime);
$loser->setDateIdentified($oldestDateTime);
$this->getMerger()->mergeTimestamps($winner, $loser);
$this->assertEquals($latestDateTime, $winner->getLastActive());
$this->assertEquals($oldestDateTime, $winner->getDateIdentified());
// Test with null date identified loser
$winner->setDateIdentified($latestDateTime);
$loser->setDateIdentified(null);
$this->getMerger()->mergeTimestamps($winner, $loser);
$this->assertEquals($latestDateTime, $winner->getDateIdentified());
// Test with null date identified winner
$winner->setDateIdentified(null);
$loser->setDateIdentified($latestDateTime);
$this->getMerger()->mergeTimestamps($winner, $loser);
$this->assertEquals($latestDateTime, $winner->getDateIdentified());
}
public function testMergeIpAddresses(): void
{
$winner = new Lead();
$winner->addIpAddress((new IpAddress('1.2.3.4'))->setIpDetails(['extra' => 'from winner']));
$winner->addIpAddress((new IpAddress('4.3.2.1'))->setIpDetails(['extra' => 'from winner']));
$winner->addIpAddress((new IpAddress('5.6.7.8'))->setIpDetails(['extra' => 'from winner']));
$loser = new Lead();
$loser->addIpAddress((new IpAddress('5.6.7.8'))->setIpDetails(['extra' => 'from loser']));
$loser->addIpAddress((new IpAddress('8.7.6.5'))->setIpDetails(['extra' => 'from loser']));
$this->getMerger()->mergeIpAddressHistory($winner, $loser);
$ipAddresses = $winner->getIpAddresses();
$this->assertCount(4, $ipAddresses);
$ipAddressArray = $ipAddresses->toArray();
$expectedIpAddressArray = [
'1.2.3.4' => ['extra' => 'from winner'],
'4.3.2.1' => ['extra' => 'from winner'],
'5.6.7.8' => ['extra' => 'from winner'],
'8.7.6.5' => ['extra' => 'from loser'],
];
foreach ($expectedIpAddressArray as $ipAddress => $ipId) {
$this->assertSame($ipAddress, $ipAddressArray[$ipAddress]->getIpAddress());
$this->assertSame($ipId, $ipAddressArray[$ipAddress]->getIpDetails());
}
}
public function testMergeFieldDataWithLoserAsNewlyUpdated(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 1,
'points' => 10,
'email' => 'winner@test.com',
]
);
$loser = $this->createMock(Lead::class);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 2,
'points' => 20,
'email' => 'loser@test.com',
]
);
$merger = $this->getMerger();
$winnerDateModified = new \DateTime('-30 minutes');
$loserDateModified = new \DateTime();
$winner->expects($this->exactly(1))
->method('getDateModified')
->willReturn($winnerDateModified);
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn($loserDateModified);
$winner->expects($this->once())
->method('getFieldValue')
->with('email')
->willReturn('winner@test.com');
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn([
'value' => 'winner@test.com',
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
]);
$winner->expects($this->exactly(3))
->method('getId')
->willReturn(1);
$loser->expects($this->exactly(2))
->method('getId')
->willReturn(2);
// Loser values are newest so should be kept
// id and points should not be set addUpdatedField should only be called once for email
$winner->expects($this->once())
->method('addUpdatedField')
->with('email', 'loser@test.com');
$merger->mergeFieldData($winner, $loser);
}
public function testMergeFieldDataWithWinnerAsNewlyUpdated(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 1,
'points' => 10,
'email' => 'winner@test.com',
]
);
$loser = $this->createMock(Lead::class);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 2,
'points' => 20,
'email' => 'loser@test.com',
]
);
$merger = $this->getMerger();
$winnerDateModified = new \DateTime();
$loserDateModified = new \DateTime('-30 minutes');
$winner->expects($this->exactly(1))
->method('getDateModified')
->willReturn($winnerDateModified);
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn([
'value' => 'winner@test.com',
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
]);
$winner->expects($this->once())
->method('getFieldValue')
->with('email')
->willReturn('winner@test.com');
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn($loserDateModified);
$winner->expects($this->exactly(4))
->method('getId')
->willReturn(1);
$loser->expects($this->once())
->method('getId');
// Winner values are newest so should be kept
// addUpdatedField should never be called as they aren't different values
$winner->expects($this->never())
->method('addUpdatedField');
$merger->mergeFieldData($winner, $loser);
}
public function testMergeFieldDataWithLoserAsNewlyCreated(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 1,
'points' => 10,
'email' => 'winner@test.com',
]
);
$loser = $this->createMock(Lead::class);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 2,
'points' => 20,
'email' => 'loser@test.com',
]
);
$merger = $this->getMerger();
$winnerDateModified = new \DateTime('-30 minutes');
$loserDateModified = new \DateTime();
$winner->expects($this->exactly(1))
->method('getDateModified')
->willReturn($winnerDateModified);
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn([
'value' => 'winner@test.com',
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
]);
$winner->expects($this->once())
->method('getFieldValue')
->with('email')
->willReturn('winner@test.com');
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn(null);
$loser->expects($this->once())
->method('getDateAdded')
->willReturn($loserDateModified);
$winner->expects($this->exactly(3))
->method('getId')
->willReturn(1);
$loser->expects($this->exactly(2))
->method('getId')
->willReturn(2);
// Loser values are newest so should be kept
// id and points should not be set addUpdatedField should only be called once for email
$winner->expects($this->once())
->method('addUpdatedField')
->with('email', 'loser@test.com');
$merger->mergeFieldData($winner, $loser);
}
public function testMergeFieldDataWithWinnerAsNewlyCreated(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 1,
'points' => 10,
'email' => 'winner@test.com',
]
);
$loser = $this->createMock(Lead::class);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 2,
'points' => 20,
'email' => 'loser@test.com',
]
);
$merger = $this->getMerger();
$winnerDateModified = new \DateTime();
$loserDateModified = new \DateTime('-30 minutes');
$winner->expects($this->once())
->method('getDateModified')
->willReturn(null);
$winner->expects($this->once())
->method('getDateAdded')
->willReturn($winnerDateModified);
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn([
'value' => 'winner@test.com',
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
]);
$winner->expects($this->once())
->method('getFieldValue')
->with('email')
->willReturn('winner@test.com');
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn($loserDateModified);
$winner->expects($this->exactly(4))
->method('getId')
->willReturn(1);
$loser->expects($this->once())
->method('getId');
// Winner values are newest so should be kept
// addUpdatedField should never be called as they aren't different values
$winner->expects($this->never())
->method('addUpdatedField');
$merger->mergeFieldData($winner, $loser);
}
/**
* Scenario: A contact clicks on a tracked email link that goes to a tracked page.
* The browser must contain no Mautic cookies. A new contact is created with only default values.
* If default values from the new contact overwrite the values of the original contact then data are lost.
*/
public function testMergeFieldDataWithDefaultValues(): void
{
$winner = $this->createMock(Lead::class);
$loser = $this->createMock(Lead::class);
$merger = $this->getMerger();
$winnerDateModified = new \DateTime('-30 minutes');
$loserDateModified = new \DateTime();
$winner->expects($this->once())
->method('getProfileFields')
->willReturn([
'id' => 1,
'email' => 'winner@test.com',
'consent' => 'Yes',
'boolean' => 1,
]);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn([
'id' => 2,
'email' => null,
'consent' => 'No',
'boolean' => 0,
]);
$winner->method('getDateModified')->willReturn($winnerDateModified);
$winner->method('getId')->willReturn(1);
$loser->method('getDateModified')->willReturn($loserDateModified);
$loser->method('getId')->willReturn(2);
$loser->method('isAnonymous')->willReturn(true);
$matcher = $this->exactly(3);
$winner->expects($matcher)
->method('getFieldValue')
->willReturnCallback(function ($parameter) use ($matcher) {
if (1 === $matcher->numberOfInvocations()) {
$this->assertSame('email', $parameter);
return 'winner@test.com';
}
if (2 === $matcher->numberOfInvocations()) {
$this->assertSame('consent', $parameter);
return 'Yes';
}
if (3 === $matcher->numberOfInvocations()) {
$this->assertSame('boolean', $parameter);
return 1;
}
});
$matcher2 = $this->exactly(3);
$winner->expects($matcher2)
->method('getField')
->willReturnCallback(function ($parameter) use ($matcher2) {
if (1 === $matcher2->numberOfInvocations()) {
$this->assertSame('email', $parameter);
return [
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
];
}
if (2 === $matcher2->numberOfInvocations()) {
$this->assertSame('consent', $parameter);
return [
'id' => 44,
'label' => 'Email Consent',
'alias' => 'consent',
'type' => 'select',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => 'No',
];
}
if (3 === $matcher2->numberOfInvocations()) {
$this->assertSame('boolean', $parameter);
return [
'id' => 45,
'label' => 'Boolean Field',
'alias' => 'boolean',
'type' => 'boolean',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => 0,
];
}
});
$matcher3 = $this->exactly(3);
$winner->expects($matcher3)
->method('addUpdatedField')->willReturnCallback(function (...$parameters) use ($matcher3) {
if (1 === $matcher3->numberOfInvocations()) {
$this->assertSame('email', $parameters[0]);
$this->assertSame('winner@test.com', $parameters[1]);
}
if (2 === $matcher3->numberOfInvocations()) {
$this->assertSame('consent', $parameters[0]);
$this->assertSame('Yes', $parameters[1]);
}
if (3 === $matcher3->numberOfInvocations()) {
$this->assertSame('boolean', $parameters[0]);
$this->assertSame(1, $parameters[1]);
}
});
$merger->mergeFieldData($winner, $loser);
}
public function testMergeOwners(): void
{
$winner = new Lead();
$loser = new Lead();
$winnerOwner = new User();
$winnerOwner->setUsername('bob');
$winner->setOwner($winnerOwner);
$loserOwner = new User();
$loserOwner->setUsername('susan');
$loser->setOwner($loserOwner);
// Should not have been merged due to winner already having one
$this->getMerger()->mergeOwners($winner, $loser);
$this->assertEquals($winnerOwner->getUserIdentifier(), $winner->getOwner()->getUserIdentifier());
$winner->setOwner(null);
$this->getMerger()->mergeOwners($winner, $loser);
// Should be set to loser owner since winner owner was null
$this->assertEquals($loserOwner->getUserIdentifier(), $winner->getOwner()->getUserIdentifier());
}
public function testMergePoints(): void
{
$winner = new Lead();
$loser = new Lead();
$winner->setPoints(100);
$loser->setPoints(50);
$this->getMerger()->mergePoints($winner, $loser);
$this->assertEquals(150, $winner->getPoints());
}
public function testMergeTags(): void
{
$winner = new Lead();
$loser = new Lead();
$loser->addTag(new Tag('loser'));
$loser->addTag(new Tag('loser2'));
$this->leadModel->expects($this->once())
->method('modifyTags')
->with($winner, ['loser', 'loser2'], null, false);
$this->getMerger()->mergeTags($winner, $loser);
}
public function testFullMergeThrowsSameContactException(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->once())
->method('getId')
->willReturn(1);
$loser = $this->createMock(Lead::class);
$loser->expects($this->once())
->method('getId')
->willReturn(1);
$this->expectException(SameContactException::class);
$this->getMerger()->merge($winner, $loser);
}
public function testFullMerge(): void
{
$winner = $this->createMock(Lead::class);
$winner->expects($this->any())
->method('getId')
->willReturn(1);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 1,
'points' => 10,
'email' => 'winner@test.com',
]
);
$winner->expects($this->exactly(1))
->method('getDateModified')
->willReturn(new \DateTime('-30 minutes'));
$loser = $this->createMock(Lead::class);
$loser->expects($this->any())
->method('getId')
->willReturn(2);
$loser->expects($this->once())
->method('getProfileFields')
->willReturn(
[
'id' => 2,
'points' => 20,
'email' => 'loser@test.com',
]
);
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn(new \DateTime());
// updateMergeRecords
$this->mergeRecordRepo->expects($this->once())
->method('moveMergeRecord')
->with(2, 1);
// mergeIpAddresses
$ip = new IpAddress('1.2.3..4');
$loser->expects($this->once())
->method('getIpAddresses')
->willReturn(new ArrayCollection([$ip]));
$winner->expects($this->once())
->method('addIpAddress')
->with($ip);
// mergeFieldData
$winner->expects($this->once())
->method('getFieldValue')
->with('email')
->willReturn('winner@test.com');
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn([
'value' => 'winner@test.com',
'id' => 22,
'label' => 'Email',
'alias' => 'email',
'type' => 'email',
'group' => 'core',
'object' => 'lead',
'is_fixed' => true,
'default_value' => null,
]);
$winner->expects($this->once())
->method('addUpdatedField')
->with('email', 'loser@test.com');
// mergeOwners
$winner->expects($this->never())
->method('setOwner');
// mergePoints
$loser->expects($this->once())
->method('getPoints')
->willReturn(100);
$winner->expects($this->once())
->method('adjustPoints')
->with(100);
// mergeTags
$loser->expects($this->once())
->method('getTags')
->willReturn(new ArrayCollection());
$this->leadModel->expects($this->once())
->method('modifyTags')
->with($winner, [], null, false);
$this->getMerger()->merge($winner, $loser);
}
public function testMergeFieldWithEmptyFieldData(): void
{
$loser = $this->createMock(Lead::class);
$winner = $this->createMock(Lead::class);
$loser->expects($this->exactly(1))
->method('getDateModified')
->willReturn(new \DateTime('-10 minutes'));
$winner->expects($this->exactly(1))
->method('getDateModified')
->willReturn(new \DateTime());
$winner->expects($this->exactly(4))
->method('getId')
->willReturn(1);
$loser->expects($this->once())
->method('getId')
->willReturn(2);
$winner->expects($this->once())
->method('getProfileFields')
->willReturn([
'email' => 'winner@test.com',
]);
$winner->expects($this->once())
->method('getField')
->with('email')
->willReturn(false);
$this->logger->expects($this->once())
->method('info')
->with('CONTACT: email is not mergeable for 1 - ');
$this->getMerger()->mergeFieldData($winner, $loser);
}
/**
* @return ContactMerger
*/
private function getMerger()
{
return new ContactMerger(
$this->leadModel,
$this->mergeRecordRepo,
$this->dispatcher,
$this->logger
);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Mautic\LeadBundle\Tests\Deduplicate\Helper;
use Mautic\LeadBundle\Deduplicate\Exception\ValueNotMergeableException;
use Mautic\LeadBundle\Deduplicate\Helper\MergeValueHelper;
class MergeValueHelperTest extends \PHPUnit\Framework\TestCase
{
public function testGetMergeValueWhenNewAndOldValuesAreIdentical(): void
{
$newerValue = 'bbb';
$olderValue = 'bbb';
$winnerValue = null;
$defaultValue = null;
$newIsAnonymous = false;
$this->expectException(ValueNotMergeableException::class);
MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
}
public function testGetMergeValueWhenNewAndWinnerValuesAreIdentical(): void
{
$newerValue = 'bbb';
$olderValue = 'aaa';
$winnerValue = 'bbb';
$defaultValue = null;
$newIsAnonymous = false;
$this->expectException(ValueNotMergeableException::class);
MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
}
public function testGetMergeValueWhenNewerValueIsNotNull(): void
{
$newerValue = 'aaa';
$olderValue = 'bbb';
$winnerValue = 'bbb';
$defaultValue = null;
$newIsAnonymous = false;
$value = MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
$this->assertSame('aaa', $value);
}
public function testGetMergeValueWhenNewerValueIsNotNullAndSameAsDefaultValueForAnonymousContact(): void
{
$newerValue = 'aaa';
$olderValue = 'bbb';
$winnerValue = 'bbb';
$defaultValue = 'aaa';
$newIsAnonymous = true;
$value = MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
$this->assertSame('bbb', $value);
}
public function testGetMergeValueWhenNewerValueIsNotNullAndSameAsDefaultValueForIdentifiedContact(): void
{
$newerValue = 'aaa';
$olderValue = 'bbb';
$winnerValue = 'bbb';
$defaultValue = 'aaa';
$newIsAnonymous = false;
$value = MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
$this->assertSame('aaa', $value);
}
public function testGetMergeValueWhenNewerValueIsNull(): void
{
$newerValue = null;
$olderValue = 'bbb';
$winnerValue = 'bbb';
$defaultValue = null;
$newIsAnonymous = false;
$value = MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
$this->assertSame('bbb', $value);
}
public function testGetMergeValueWhenNewerValueIsNotNullAndDefaultValueIsZero(): void
{
$newerValue = 0;
$olderValue = 1;
$winnerValue = 1;
$defaultValue = 0;
$newIsAnonymous = true;
$value = MergeValueHelper::getMergeValue($newerValue, $olderValue, $winnerValue, $defaultValue, $newIsAnonymous);
$this->assertSame($winnerValue, $value);
}
}