1 | <?php |
||
2 | |||
3 | /* |
||
4 | * @copyright 2016 Mautic Contributors. All rights reserved |
||
5 | * @author Mautic, Inc. |
||
6 | * |
||
7 | * @link https://mautic.org |
||
8 | * |
||
9 | * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html |
||
10 | */ |
||
11 | |||
12 | namespace MauticPlugin\MauticCrmBundle\Tests\Integration; |
||
13 | |||
14 | use Mautic\CoreBundle\Entity\AuditLogRepository; |
||
15 | use Mautic\LeadBundle\Entity\Company; |
||
16 | use Mautic\LeadBundle\Entity\Lead; |
||
17 | use Mautic\PluginBundle\Entity\Integration; |
||
18 | use Mautic\PluginBundle\Entity\IntegrationEntity; |
||
19 | use Mautic\PluginBundle\Entity\IntegrationEntityRepository; |
||
20 | use Mautic\PluginBundle\Event\PluginIntegrationKeyEvent; |
||
21 | use Mautic\PluginBundle\Exception\ApiErrorException; |
||
22 | use Mautic\PluginBundle\Model\IntegrationEntityModel; |
||
23 | use Mautic\PluginBundle\Tests\Integration\AbstractIntegrationTestCase; |
||
24 | use MauticPlugin\MauticCrmBundle\Integration\SalesforceIntegration; |
||
25 | use PHPUnit\Framework\MockObject\MockObject; |
||
26 | use ReflectionClass; |
||
27 | |||
28 | class SalesforceIntegrationTest extends AbstractIntegrationTestCase |
||
29 | { |
||
30 | const SC_MULTIPLE_SF_LEADS = 'multiple_sf_leads'; |
||
31 | const SC_MULTIPLE_SF_CONTACTS = 'multiple_sf_contacts'; |
||
32 | const SC_CONVERTED_SF_LEAD = 'converted_sf_lead'; |
||
33 | const SC_EMAIL_WITH_APOSTROPHE = 'email_with_apostrophe'; |
||
34 | const SC_MULTIPLE_MAUTIC_CONTACTS = 'multiple_mautic_contacts'; |
||
35 | |||
36 | /** |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $maxInvocations = []; |
||
40 | |||
41 | /** |
||
42 | * @var string|null |
||
43 | */ |
||
44 | protected $specialSfCase; |
||
45 | |||
46 | /** |
||
47 | * @var array |
||
48 | */ |
||
49 | protected $persistedIntegrationEntities = []; |
||
50 | |||
51 | /** |
||
52 | * @var array |
||
53 | */ |
||
54 | protected $returnedSfEntities = []; |
||
55 | |||
56 | /** |
||
57 | * @var array |
||
58 | */ |
||
59 | protected $mauticContacts = [ |
||
60 | 'Contact' => [], |
||
61 | 'Lead' => [], |
||
62 | ]; |
||
63 | |||
64 | /** |
||
65 | * @var array |
||
66 | */ |
||
67 | protected $sfObjects = [ |
||
68 | 'Lead', |
||
69 | 'Contact', |
||
70 | 'company', |
||
71 | ]; |
||
72 | |||
73 | /** |
||
74 | * @var array |
||
75 | */ |
||
76 | protected $sfMockMethods = [ |
||
77 | 'makeRequest', |
||
78 | ]; |
||
79 | |||
80 | /** |
||
81 | * @var array |
||
82 | */ |
||
83 | protected $sfMockResetMethods = [ |
||
84 | 'makeRequest', |
||
85 | ]; |
||
86 | |||
87 | /** |
||
88 | * @var array |
||
89 | */ |
||
90 | protected $sfMockResetObjects = [ |
||
91 | 'Lead', |
||
92 | 'Contact', |
||
93 | 'company', |
||
94 | ]; |
||
95 | |||
96 | /** |
||
97 | * @var int |
||
98 | */ |
||
99 | protected $idCounter = 1; |
||
100 | |||
101 | /** |
||
102 | * @var array |
||
103 | */ |
||
104 | protected $leadsUpdatedCounter = [ |
||
105 | 'Lead' => 0, |
||
106 | 'Contact' => 0, |
||
107 | ]; |
||
108 | |||
109 | /** |
||
110 | * @var int |
||
111 | */ |
||
112 | protected $leadsCreatedCounter = 0; |
||
113 | |||
114 | protected function setUp(): void |
||
115 | { |
||
116 | parent::setUp(); |
||
117 | |||
118 | defined('MAUTIC_ENV') or define('MAUTIC_ENV', 'test'); |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Reset. |
||
123 | */ |
||
124 | public function tearDown(): void |
||
125 | { |
||
126 | $this->returnedSfEntities = []; |
||
127 | $this->persistedIntegrationEntities = []; |
||
128 | $this->sfMockMethods = $this->sfMockResetMethods; |
||
129 | $this->sfObjects = $this->sfMockResetObjects; |
||
130 | $this->specialSfCase = null; |
||
131 | $this->idCounter = 1; |
||
132 | $this->leadsCreatedCounter = 0; |
||
133 | $this->leadsUpdatedCounter = [ |
||
134 | 'Lead' => 0, |
||
135 | 'Contact' => 0, |
||
136 | ]; |
||
137 | $this->mauticContacts = []; |
||
138 | } |
||
139 | |||
140 | public function testPushLeadsUpdateAndCreateCorrectNumbers() |
||
141 | { |
||
142 | $sf = $this->getSalesforceIntegration(); |
||
143 | $stats = $sf->pushLeads(); |
||
144 | |||
145 | $this->assertCount(400, $this->getPersistedIntegrationEntities()); |
||
146 | $this->assertEquals(300, $stats[0], var_export($stats, true)); // update |
||
147 | $this->assertEquals(100, $stats[1], var_export($stats, true)); // create |
||
148 | } |
||
149 | |||
150 | public function testThatMultipleSfLeadsReturnedAreUpdatedButOnlyOneIntegrationRecordIsCreated() |
||
151 | { |
||
152 | $this->specialSfCase = self::SC_MULTIPLE_SF_LEADS; |
||
153 | $sf = $this->getSalesforceIntegration(2, 0, 2, 0, 'Lead'); |
||
154 | $sf->pushLeads(); |
||
155 | |||
156 | // Validate there are only two integration entities (two contacts with same email) |
||
157 | $integrationEntities = $this->getPersistedIntegrationEntities(); |
||
158 | $this->assertCount(2, $integrationEntities); |
||
159 | |||
160 | // Validate that there were 4 found entries (two duplicate leads) |
||
161 | $sfEntities = $this->getReturnedSfEntities(); |
||
162 | $this->assertCount(4, $sfEntities); |
||
163 | } |
||
164 | |||
165 | public function testThatMultipleSfContactsReturnedAreUpdatedButOnlyOneIntegrationRecordIsCreated() |
||
166 | { |
||
167 | $this->specialSfCase = self::SC_MULTIPLE_SF_CONTACTS; |
||
168 | $sf = $this->getSalesforceIntegration(2, 0, 0, 2, 'Contact'); |
||
169 | $sf->pushLeads(); |
||
170 | |||
171 | // Validate there are only two integration entities (two contacts with same email) |
||
172 | $integrationEntities = $this->getPersistedIntegrationEntities(); |
||
173 | $this->assertEquals(2, count($integrationEntities)); |
||
174 | |||
175 | // Validate that there were 4 found entries (two duplciate leads) |
||
176 | $sfEntities = $this->getReturnedSfEntities(); |
||
177 | $this->assertEquals(4, count($sfEntities)); |
||
178 | } |
||
179 | |||
180 | public function testThatLeadsAreOnlyCreatedIfEnabled() |
||
181 | { |
||
182 | $this->sfObjects = ['Contact']; |
||
183 | $this->sfMockMethods = ['makeRequest', 'findLeadsToCreate', 'getMauticContactsToCreate']; |
||
184 | |||
185 | $sf = $this->getSalesforceIntegration(); |
||
186 | $sf->expects($this->never()) |
||
187 | ->method('findLeadsToCreate'); |
||
188 | |||
189 | $sf->expects($this->never()) |
||
190 | ->method('getMauticContactsToCreate'); |
||
191 | |||
192 | $sf->pushLeads(); |
||
193 | } |
||
194 | |||
195 | public function testThatLeadsAreOnlyCreatedIfLimitIsAppropriate() |
||
196 | { |
||
197 | $this->sfMockMethods = ['makeRequest', 'getMauticContactsToCreate', 'getMauticContactsToUpdate', 'getSalesforceSyncLimit']; |
||
198 | |||
199 | $sf = $this->getSalesforceIntegration(); |
||
200 | $counter = 0; |
||
201 | $sf->expects($this->exactly(2)) |
||
202 | ->method('getMauticContactsToUpdate') |
||
203 | ->will( |
||
204 | $this->returnCallback( |
||
205 | function () use (&$counter) { |
||
206 | ++$counter; |
||
207 | |||
208 | return true; |
||
209 | } |
||
210 | ) |
||
211 | ); |
||
212 | |||
213 | $sf->method('getSalesforceSyncLimit') |
||
214 | ->willReturn(50); |
||
215 | |||
216 | $sf->expects($this->exactly(1)) |
||
217 | ->method('getMauticContactsToCreate'); |
||
218 | |||
219 | $sf->pushLeads(); |
||
220 | } |
||
221 | |||
222 | public function testThatLeadsAreNotCreatedIfCountIsLessThanLimit() |
||
223 | { |
||
224 | $this->sfMockMethods = ['makeRequest', 'getMauticContactsToCreate', 'getMauticContactsToUpdate', 'getSalesforceSyncLimit']; |
||
225 | |||
226 | $sf = $this->getSalesforceIntegration(); |
||
227 | $counter = 0; |
||
228 | $sf->expects($this->exactly(2)) |
||
229 | ->method('getMauticContactsToUpdate') |
||
230 | ->will( |
||
231 | $this->returnCallback( |
||
232 | function () use (&$counter) { |
||
233 | ++$counter; |
||
234 | |||
235 | return true; |
||
236 | } |
||
237 | ) |
||
238 | ); |
||
239 | |||
240 | $counter = 0; |
||
241 | $sf->method('getSalesforceSyncLimit') |
||
242 | ->will( |
||
243 | $this->returnCallback( |
||
244 | function () use (&$counter) { |
||
245 | ++$counter; |
||
246 | |||
247 | return (1 === $counter) ? 100 : 0; |
||
248 | } |
||
249 | ) |
||
250 | ); |
||
251 | |||
252 | $sf->expects($this->never()) |
||
253 | ->method('getMauticContactsToCreate'); |
||
254 | |||
255 | $sf->pushLeads(); |
||
256 | } |
||
257 | |||
258 | public function testLastSyncDate() |
||
259 | { |
||
260 | $class = new \ReflectionClass(SalesforceIntegration::class); |
||
261 | $lastSyncMethod = $class->getMethod('getLastSyncDate'); |
||
262 | $lastSyncMethod->setAccessible(true); |
||
263 | |||
264 | $sf = $this->getSalesforceIntegration(); |
||
265 | |||
266 | // should be a DateTime |
||
267 | $lastSync = $lastSyncMethod->invokeArgs($sf, []); |
||
268 | $this->assertTrue($lastSync instanceof \DateTime); |
||
269 | |||
270 | // Set the override set by fetch command |
||
271 | define('MAUTIC_DATE_MODIFIED_OVERRIDE', time()); |
||
272 | |||
273 | /** @var \DateTime $lastSync */ |
||
274 | $lastSync = $lastSyncMethod->invokeArgs($sf, []); |
||
275 | |||
276 | // should be teh same as MAUTIC_DATE_MODIFIED_OVERRIDE override |
||
277 | $this->assertTrue($lastSync instanceof \DateTime); |
||
278 | $this->assertEquals(MAUTIC_DATE_MODIFIED_OVERRIDE, $lastSync->format('U')); |
||
279 | |||
280 | $lead = new Lead(); |
||
281 | $reflectedLead = new \ReflectionObject($lead); |
||
282 | $reflectedId = $reflectedLead->getProperty('id'); |
||
283 | $reflectedId->setAccessible(true); |
||
284 | $reflectedId->setValue($lead, 1); |
||
285 | |||
286 | $modified = new \DateTime('-15 minutes'); |
||
287 | $lead->setDateModified($modified); |
||
288 | // Set it twice to get an original and updated datetime |
||
289 | $now = new \DateTime(); |
||
290 | $lead->setDateModified($now); |
||
291 | |||
292 | $params = [ |
||
293 | 'start' => $modified->format('c'), |
||
294 | ]; |
||
295 | |||
296 | // Should be null due to the contact was updated since last sync |
||
297 | $lastSync = $lastSyncMethod->invokeArgs($sf, [$lead, $params, false]); |
||
298 | $this->assertNull($lastSync); |
||
299 | |||
300 | // Should be a DateTime object |
||
301 | $lead = new Lead(); |
||
302 | $lastSync = $lastSyncMethod->invokeArgs($sf, [$lead, $params]); |
||
303 | $this->assertTrue($lastSync instanceof \DateTime); |
||
304 | } |
||
305 | |||
306 | public function testLeadsAreNotCreatedInSfIfFoundToAlreadyExistAsContacts() |
||
307 | { |
||
308 | $this->sfObjects = ['Lead', 'Contact']; |
||
309 | $this->sfMockMethods = ['makeRequest', 'getSalesforceObjectsByEmails', 'prepareMauticContactsToCreate']; |
||
310 | $sf = $this->getSalesforceIntegration(0, 2); |
||
311 | |||
312 | /* |
||
313 | * This forces the integration to think the contact exists in SF, |
||
314 | * and removes those emails from the array for creation. |
||
315 | */ |
||
316 | $sf->method('getSalesforceObjectsByEmails') |
||
317 | ->willReturnCallback( |
||
318 | function () { |
||
319 | $args = func_get_args(); |
||
320 | $emails = array_column($args[1], 'email'); |
||
321 | |||
322 | return $this->getSalesforceObjects($emails, 0, 1); |
||
323 | } |
||
324 | ); |
||
325 | |||
326 | $sf->expects($this->never()) |
||
327 | ->method('prepareMauticContactsToCreate'); |
||
328 | |||
329 | $sf->pushLeads(); |
||
330 | } |
||
331 | |||
332 | public function testLeadsAreNotCreatedInSfIfFoundToAlreadyExistAsLeads() |
||
333 | { |
||
334 | $this->sfObjects = ['Lead']; |
||
335 | $this->sfMockMethods = ['makeRequest', 'getSalesforceObjectsByEmails', 'prepareMauticContactsToCreate']; |
||
336 | $sf = $this->getSalesforceIntegration(0, 1); |
||
337 | |||
338 | /* |
||
339 | * This forces the integration to think the contact exists in SF, |
||
340 | * and removes those emails from the array for creation. |
||
341 | */ |
||
342 | $sf->method('getSalesforceObjectsByEmails') |
||
343 | ->willReturnCallback( |
||
344 | function () { |
||
345 | $args = func_get_args(); |
||
346 | $emails = array_column($args[1], 'email'); |
||
347 | |||
348 | return $this->getSalesforceObjects($emails, 0, 1); |
||
349 | } |
||
350 | ); |
||
351 | |||
352 | $sf->expects($this->never()) |
||
353 | ->method('prepareMauticContactsToCreate'); |
||
354 | |||
355 | $sf->pushLeads(); |
||
356 | } |
||
357 | |||
358 | public function testExceptionIsThrownIfSfReturnsErrorOnEmailLookup() |
||
359 | { |
||
360 | $this->sfObjects = ['Lead']; |
||
361 | $this->sfMockMethods = ['makeRequest', 'getSalesforceObjectsByEmails']; |
||
362 | $sf = $this->getSalesforceIntegration(); |
||
363 | |||
364 | $sf->method('getSalesforceObjectsByEmails') |
||
365 | ->willReturn('Some Error'); |
||
366 | |||
367 | $this->expectException(ApiErrorException::class); |
||
368 | |||
369 | $sf->pushLeads(); |
||
370 | } |
||
371 | |||
372 | public function testGetCampaigns() |
||
373 | { |
||
374 | $this->sfObjects = ['Contact']; |
||
375 | $this->sfMockMethods = ['makeRequest']; |
||
376 | $sf = $this->getSalesforceIntegration(); |
||
377 | |||
378 | $sf->expects($this->once()) |
||
379 | ->method('makeRequest') |
||
380 | ->with( |
||
381 | 'https://sftest.com/services/data/v34.0/query', |
||
382 | [ |
||
383 | 'q' => 'Select Id, Name from Campaign where isDeleted = false', |
||
384 | ] |
||
385 | ); |
||
386 | |||
387 | $sf->getCampaigns(); |
||
388 | } |
||
389 | |||
390 | public function testGetCampaignMembers() |
||
391 | { |
||
392 | $this->sfObjects = ['Contact']; |
||
393 | $this->sfMockMethods = ['makeRequest']; |
||
394 | $sf = $this->getSalesforceIntegration(); |
||
395 | |||
396 | $sf->expects($this->once()) |
||
397 | ->method('makeRequest') |
||
398 | ->with( |
||
399 | 'https://sftest.com/services/data/v34.0/query', |
||
400 | [ |
||
401 | 'q' => "Select CampaignId, ContactId, LeadId, isDeleted from CampaignMember where CampaignId = '1'", |
||
402 | ] |
||
403 | ); |
||
404 | |||
405 | $sf->getCampaignMembers(1); |
||
406 | } |
||
407 | |||
408 | public function testGetCampaignMemberStatus() |
||
409 | { |
||
410 | $this->sfObjects = ['Contact']; |
||
411 | $this->sfMockMethods = ['makeRequest']; |
||
412 | $sf = $this->getSalesforceIntegration(); |
||
413 | |||
414 | $sf->expects($this->once()) |
||
415 | ->method('makeRequest') |
||
416 | ->with( |
||
417 | 'https://sftest.com/services/data/v34.0/query', |
||
418 | [ |
||
419 | 'q' => "Select Id, Label from CampaignMemberStatus where isDeleted = false and CampaignId='1'", |
||
420 | ] |
||
421 | ); |
||
422 | |||
423 | $sf->getCampaignMemberStatus(1); |
||
424 | } |
||
425 | |||
426 | public function testPushToCampaign() |
||
427 | { |
||
428 | $this->sfObjects = ['Contact']; |
||
429 | $this->sfMockMethods = ['makeRequest']; |
||
430 | $sf = $this->getSalesforceIntegration(); |
||
431 | |||
432 | $lead = new Lead(); |
||
433 | |||
434 | $lead->setFirstname('Lead1'); |
||
435 | $lead->setEmail('[email protected]'); |
||
436 | $lead->setId(1); |
||
437 | |||
438 | $sf->expects($this->any()) |
||
439 | ->method('makeRequest') |
||
440 | ->willReturnCallback( |
||
441 | function () { |
||
442 | $args = func_get_args(); |
||
443 | |||
444 | // Checking for campaign members should return empty array for testing purposes |
||
445 | if (false !== strpos($args[0], '/query') && false !== strpos($args[1]['q'], 'CampaignMember')) { |
||
446 | return []; |
||
447 | } |
||
448 | |||
449 | if (false !== strpos($args[0], '/composite')) { |
||
450 | $this->assertSame( |
||
451 | '1-CampaignMemberNew-null-1', |
||
452 | $args[1]['compositeRequest'][0]['referenceId'], |
||
453 | 'The composite request when pushing a campaign member should contain the correct referenceId.' |
||
454 | ); |
||
455 | |||
456 | return true; |
||
457 | } |
||
458 | } |
||
459 | ); |
||
460 | |||
461 | $sf->pushLeadToCampaign($lead, 1, 'Active', ['Lead' => [1]]); |
||
462 | } |
||
463 | |||
464 | public function testPushCompany() |
||
465 | { |
||
466 | $this->sfObjects = ['Account']; |
||
467 | $this->sfMockMethods = ['makeRequest']; |
||
468 | $sf = $this->getSalesforceIntegration(); |
||
469 | |||
470 | $company = new Company(); |
||
471 | |||
472 | $company->setName('MyCompanyName'); |
||
473 | |||
474 | $sf->expects($this->any()) |
||
475 | ->method('makeRequest') |
||
476 | ->willReturnCallback( |
||
477 | function () { |
||
478 | $args = func_get_args(); |
||
479 | |||
480 | // Checking for campaign members should return empty array for testing purposes |
||
481 | if (false !== strpos($args[0], '/query') && false !== strpos($args[1]['q'], 'Account')) { |
||
482 | return []; |
||
483 | } |
||
484 | |||
485 | if (false !== strpos($args[0], '/composite')) { |
||
486 | $this->assertSame( |
||
487 | '1-Account-null-1', |
||
488 | $args[1]['compositeRequest'][0]['referenceId'], |
||
489 | 'The composite request when pushing an account should contain the correct referenceId.' |
||
490 | ); |
||
491 | |||
492 | return true; |
||
493 | } |
||
494 | } |
||
495 | ); |
||
496 | |||
497 | $this->assertFalse($sf->pushCompany($company)); |
||
498 | } |
||
499 | |||
500 | public function testExportingContactActivity() |
||
501 | { |
||
502 | $this->sfObjects = ['Contact']; |
||
503 | $this->sfMockMethods = ['makeRequest', 'getSalesforceObjectsByEmails', 'isAuthorized', 'getFetchQuery', 'getLeadData']; |
||
504 | $sf = $this->getSalesforceIntegration(2, 2); |
||
505 | |||
506 | $sf->expects($this->once()) |
||
507 | ->method('isAuthorized') |
||
508 | ->willReturn(true); |
||
509 | |||
510 | $sf->expects($this->once()) |
||
511 | ->method('getFetchQuery') |
||
512 | ->with([]) |
||
513 | ->willReturnCallback( |
||
514 | function () { |
||
515 | return [ |
||
516 | 'start' => '-1 week', |
||
517 | 'end' => 'now', |
||
518 | ]; |
||
519 | } |
||
520 | ); |
||
521 | |||
522 | $this->setMaxInvocations('getIntegrationsEntityId', 1); |
||
523 | |||
524 | $sf->expects($this->once()) |
||
525 | ->method('getLeadData') |
||
526 | ->willReturnCallback( |
||
527 | function () { |
||
528 | $leadIds = func_get_arg(2); |
||
529 | $data = []; |
||
530 | |||
531 | foreach ($leadIds as $i => $id) { |
||
532 | ++$i; |
||
533 | |||
534 | $data[$id] = [ |
||
535 | 'records' => [ |
||
536 | [ |
||
537 | 'eventType' => 'email', |
||
538 | 'name' => 'Email Name', |
||
539 | 'description' => 'Email sent', |
||
540 | 'dateAdded' => new \DateTime(), |
||
541 | 'id' => 'pointChange'.$i, |
||
542 | ], |
||
543 | ], |
||
544 | ]; |
||
545 | } |
||
546 | |||
547 | return $data; |
||
548 | } |
||
549 | ); |
||
550 | |||
551 | /* |
||
552 | * Ensures that makeRequest is called with the mautic_timeline__c endpoint. |
||
553 | * If it is, then we've successfully exported contact activity. |
||
554 | */ |
||
555 | $sf->expects($this->exactly(1)) |
||
556 | ->method('makeRequest') |
||
557 | ->with('https://sftest.com/services/data/v38.0/composite/') |
||
558 | ->willReturnCallback( |
||
559 | function () { |
||
560 | return $this->getSalesforceCompositeResponse(func_get_arg(1)); |
||
561 | } |
||
562 | ); |
||
563 | |||
564 | $sf->pushLeadActivity(); |
||
565 | } |
||
566 | |||
567 | public function testMauticContactTimelineLinkPopulatedsPayload() |
||
568 | { |
||
569 | $this->sfObjects = ['Contact']; |
||
570 | $this->sfMockMethods = ['makeRequest', 'getSalesforceObjectsByEmails']; |
||
571 | $sf = $this->getSalesforceIntegration(2, 2); |
||
572 | |||
573 | /* |
||
574 | * When checking if contacts need to be created, the mauticContactTimelineLink |
||
575 | * has been populated at this point. If populated here, the test passes. |
||
576 | * We return the salesforce objects so as not to throw an error. |
||
577 | */ |
||
578 | $sf->method('getSalesforceObjectsByEmails') |
||
579 | ->willReturnCallback( |
||
580 | function () { |
||
581 | $args = func_get_args(); |
||
582 | $emails = array_column($args[1], 'email'); |
||
583 | |||
584 | return $this->getSalesforceObjects($emails, 0, 1); |
||
585 | } |
||
586 | ); |
||
587 | |||
588 | /* |
||
589 | * With the given SF integration setup, the `getSalesforceObjectsByEmails` method |
||
590 | * will be called once, and have the given parameters, which contains the contact |
||
591 | * timeline link. |
||
592 | */ |
||
593 | $sf->expects($this->once()) |
||
594 | ->method('getSalesforceObjectsByEmails') |
||
595 | ->with( |
||
596 | 'Contact', |
||
597 | [ |
||
598 | '[email protected]' => [ |
||
599 | 'integration_entity_id' => 'SF1', |
||
600 | 'integration_entity' => 'Contact', |
||
601 | 'id' => 1, |
||
602 | 'internal_entity_id' => 1, |
||
603 | 'firstname' => 'Contact1', |
||
604 | 'lastname' => 'Contact1', |
||
605 | 'company' => 'Contact1', |
||
606 | 'email' => '[email protected]', |
||
607 | 'mauticContactTimelineLink' => 'mautic_plugin_timeline_view', |
||
608 | 'mauticContactIsContactableByEmail' => 1, |
||
609 | 'mauticContactId' => 1, |
||
610 | ], |
||
611 | '[email protected]' => [ |
||
612 | 'integration_entity_id' => 'SF2', |
||
613 | 'integration_entity' => 'Contact', |
||
614 | 'id' => 2, |
||
615 | 'internal_entity_id' => 2, |
||
616 | 'firstname' => 'Contact2', |
||
617 | 'lastname' => 'Contact2', |
||
618 | 'company' => 'Contact2', |
||
619 | 'email' => '[email protected]', |
||
620 | 'mauticContactTimelineLink' => 'mautic_plugin_timeline_view', |
||
621 | 'mauticContactIsContactableByEmail' => 1, |
||
622 | 'mauticContactId' => 2, |
||
623 | ], |
||
624 | ], |
||
625 | 'FirstName,LastName,Email' |
||
626 | ); |
||
627 | |||
628 | $sf->pushLeads(); |
||
629 | } |
||
630 | |||
631 | public function testUpdateDncBySfDate() |
||
632 | { |
||
633 | $this->sfMockMethods = ['makeRequest', 'updateDncByDate', 'getDncHistory']; |
||
634 | |||
635 | $objects = ['Contact', 'Lead']; |
||
636 | foreach ($objects as $object) { |
||
637 | $mappedData = [ |
||
638 | '[email protected]' => [ |
||
639 | 'integration_entity_id' => 'SF1', |
||
640 | 'integration_entity' => $object, |
||
641 | 'id' => 1, |
||
642 | 'internal_entity_id' => 1, |
||
643 | 'firstname' => 'Contact1', |
||
644 | 'lastname' => 'Contact1', |
||
645 | 'company' => 'Contact1', |
||
646 | 'email' => '[email protected]', |
||
647 | 'mauticContactTimelineLink' => 'mautic_plugin_timeline_view', |
||
648 | 'mauticContactIsContactableByEmail' => 1, |
||
649 | ], |
||
650 | '[email protected]' => [ |
||
651 | 'integration_entity_id' => 'SF2', |
||
652 | 'integration_entity' => $object, |
||
653 | 'id' => 2, |
||
654 | 'internal_entity_id' => 2, |
||
655 | 'firstname' => 'Contact2', |
||
656 | 'lastname' => 'Contact2', |
||
657 | 'company' => 'Contact2', |
||
658 | 'email' => '[email protected]', |
||
659 | 'mauticContactTimelineLink' => 'mautic_plugin_timeline_view', |
||
660 | 'mauticContactIsContactableByEmail' => 0, |
||
661 | ], |
||
662 | ]; |
||
663 | |||
664 | $sf = $this->getSalesforceIntegration(2, 2); |
||
665 | $sf->expects($this->any())->method('updateDncByDate')->willReturn(true); |
||
666 | $sf->expects($this->any()) |
||
667 | ->method('getDncHistory') |
||
668 | ->willReturn( |
||
669 | $this->getSalesforceDNCHistory($object, 'SF') |
||
670 | ); |
||
671 | $sf->pushLeadDoNotContactByDate('email', $mappedData, $object, ['start' => '2017-10-16 13:00:00.000000']); |
||
672 | |||
673 | foreach ($mappedData as $assertion) { |
||
674 | $this->assertArrayHasKey('mauticContactIsContactableByEmail', $assertion); |
||
675 | } |
||
676 | } |
||
677 | } |
||
678 | |||
679 | public function testUpdateDncByMauticDate() |
||
680 | { |
||
681 | $this->sfMockMethods = ['makeRequest', 'updateDncByDate', 'getDncHistory']; |
||
682 | |||
683 | $objects = ['Contact', 'Lead']; |
||
684 | foreach ($objects as $object) { |
||
685 | $mappedData = [ |
||
686 | '[email protected]' => [ |
||
687 | 'integration_entity_id' => 'SF1', |
||
688 | 'integration_entity' => $object, |
||
689 | 'id' => 1, |
||
690 | 'internal_entity_id' => 1, |
||
691 | 'firstname' => 'Contact1', |
||
692 | 'lastname' => 'Contact1', |
||
693 | 'company' => 'Contact1', |
||
694 | 'email' => '[email protected]', |
||
695 | 'mauticContactTimelineLink' => 'mautic_plugin_timeline_view', |
||
696 | 'mauticContactIsContactableByEmail' => 1, |
||
697 | ], |
||
698 | ]; |
||
699 | |||
700 | $sf = $this->getSalesforceIntegration(2, 2); |
||
701 | $sf->expects($this->any())->method('updateDncByDate')->willReturn(true); |
||
702 | $sf->expects($this->any())->method('getDncHistory')->willReturn($this->getSalesforceDNCHistory($object, 'Mautic')); |
||
703 | |||
704 | $sf->pushLeadDoNotContactByDate('email', $mappedData, $object, ['start' => '2017-10-15T10:00:00.000000']); |
||
705 | foreach ($mappedData as $assertion) { |
||
706 | $this->assertArrayNotHasKey('mauticContactIsContactableByEmail', $assertion); |
||
707 | } |
||
708 | } |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * @param string $name |
||
713 | * @param int $max |
||
714 | * |
||
715 | * @return $this |
||
716 | */ |
||
717 | protected function setMaxInvocations($name, $max) |
||
718 | { |
||
719 | $this->maxInvocations[$name] = $max; |
||
720 | |||
721 | return $this; |
||
722 | } |
||
723 | |||
724 | /** |
||
725 | * @param $name |
||
726 | * |
||
727 | * @return int |
||
728 | */ |
||
729 | protected function getMaxInvocations($name) |
||
730 | { |
||
731 | if (isset($this->maxInvocations[$name])) { |
||
732 | return $this->maxInvocations[$name]; |
||
733 | } |
||
734 | |||
735 | return 1; |
||
736 | } |
||
737 | |||
738 | protected function setMocks() |
||
739 | { |
||
740 | $integrationEntityRepository = $this->getMockBuilder(IntegrationEntityRepository::class) |
||
741 | ->disableOriginalConstructor() |
||
742 | ->getMock(); |
||
743 | |||
744 | // we need insight into the entities persisted |
||
745 | $integrationEntityRepository->method('saveEntities') |
||
746 | ->willReturnCallback( |
||
747 | function () { |
||
748 | $this->persistedIntegrationEntities = array_merge($this->persistedIntegrationEntities, func_get_arg(0)); |
||
749 | } |
||
750 | ); |
||
751 | |||
752 | $integrationEntityRepository |
||
753 | ->expects($spy = $this->any()) |
||
754 | ->method('getIntegrationsEntityId') |
||
755 | ->willReturnCallback( |
||
756 | function () use ($spy) { |
||
757 | // WARNING: this is using a PHPUnit undocumented workaround: |
||
758 | // https://github.com/sebastianbergmann/phpunit/issues/3888 |
||
759 | $spyParentProperties = self::getParentPrivateProperties($spy); |
||
760 | $invocations = $spyParentProperties['invocations']; |
||
761 | |||
762 | if (count($invocations) > $this->getMaxInvocations('getIntegrationsEntityId')) { |
||
763 | return null; |
||
764 | } |
||
765 | |||
766 | // Just return some bogus entities for testing |
||
767 | return $this->getLeadsToUpdate('Lead', 2, 2, 'Lead')['Lead']; |
||
768 | } |
||
769 | ); |
||
770 | $auditLogRepo = $this->getMockBuilder(AuditLogRepository::class) |
||
771 | ->disableOriginalConstructor() |
||
772 | ->getMock(); |
||
773 | |||
774 | $auditLogRepo |
||
775 | ->expects($this->any()) |
||
776 | ->method('getAuditLogsForLeads') |
||
777 | ->willReturn( |
||
778 | [ |
||
779 | [ |
||
780 | 'userName' => 'Salesforce', |
||
781 | 'userId' => 0, |
||
782 | 'bundle' => 'lead', |
||
783 | 'object' => 'lead', |
||
784 | 'objectId' => 1, |
||
785 | 'action' => 'update', |
||
786 | 'details' => [ |
||
787 | 'dnc_channel_status' => [ |
||
788 | 'email' => [ |
||
789 | 'reason' => 3, |
||
790 | 'comments' => 'Set by Salesforce', |
||
791 | ], |
||
792 | ], |
||
793 | 'dnc_status' => [ |
||
794 | 'manual', |
||
795 | 'Set by Salesforce', |
||
796 | ], |
||
797 | ], |
||
798 | 'dateAdded' => new \DateTime('2017-10-16 15:00:36.000000', new \DateTimeZone('UTC')), |
||
799 | 'ipAddress' => '127.0.0.1', |
||
800 | ], |
||
801 | ] |
||
802 | ); |
||
803 | |||
804 | $this->em->method('getRepository') |
||
805 | ->willReturnMap( |
||
806 | [ |
||
807 | ['MauticPluginBundle:IntegrationEntity', $integrationEntityRepository], |
||
808 | ['MauticCoreBundle:AuditLog', $auditLogRepo], |
||
809 | ] |
||
810 | ); |
||
811 | |||
812 | $this->em->method('getReference') |
||
813 | ->willReturnCallback( |
||
814 | function () { |
||
815 | switch (func_get_arg(0)) { |
||
816 | case 'MauticPluginBundle:IntegrationEntity': |
||
817 | return new IntegrationEntity(); |
||
818 | } |
||
819 | } |
||
820 | ); |
||
821 | |||
822 | $this->router->method('generate') |
||
823 | ->willReturnArgument(0); |
||
824 | |||
825 | $this->leadModel->method('getEntity') |
||
826 | ->willReturn(new Lead()); |
||
827 | |||
828 | $this->companyModel->method('getEntity') |
||
829 | ->willReturn(new Company()); |
||
830 | $this->companyModel->method('getEntities') |
||
831 | ->willReturn([]); |
||
832 | |||
833 | $leadFields = [ |
||
834 | 'Id__Lead' => [ |
||
835 | 'type' => 'string', |
||
836 | 'label' => 'Lead-Lead ID', |
||
837 | 'required' => false, |
||
838 | 'group' => 'Lead', |
||
839 | 'optionLabel' => 'Lead ID', |
||
840 | ], |
||
841 | 'LastName__Lead' => [ |
||
842 | 'type' => 'string', |
||
843 | 'label' => 'Lead-Last Name', |
||
844 | 'required' => true, |
||
845 | 'group' => 'Lead', |
||
846 | 'optionLabel' => 'Last Name', |
||
847 | ], |
||
848 | 'FirstName__Lead' => [ |
||
849 | 'type' => 'string', |
||
850 | 'label' => 'Lead-First Name', |
||
851 | 'required' => false, |
||
852 | 'group' => 'Lead', |
||
853 | 'optionLabel' => 'First Name', |
||
854 | ], |
||
855 | 'Company__Lead' => [ |
||
856 | 'type' => 'string', |
||
857 | 'label' => 'Lead-Company', |
||
858 | 'required' => true, |
||
859 | 'group' => 'Lead', |
||
860 | 'optionLabel' => 'Company', |
||
861 | ], |
||
862 | 'Email__Lead' => [ |
||
863 | 'type' => 'string', |
||
864 | 'label' => 'Lead-Email', |
||
865 | 'required' => false, |
||
866 | 'group' => 'Lead', |
||
867 | 'optionLabel' => 'Email', |
||
868 | ], |
||
869 | ]; |
||
870 | $contactFields = [ |
||
871 | 'Id__Contact' => [ |
||
872 | 'type' => 'string', |
||
873 | 'label' => 'Contact-Contact ID', |
||
874 | 'required' => false, |
||
875 | 'group' => 'Contact', |
||
876 | 'optionLabel' => 'Contact ID', |
||
877 | ], |
||
878 | 'LastName__Contact' => [ |
||
879 | 'type' => 'string', |
||
880 | 'label' => 'Contact-Last Name', |
||
881 | 'required' => true, |
||
882 | 'group' => 'Contact', |
||
883 | 'optionLabel' => 'Last Name', |
||
884 | ], |
||
885 | 'FirstName__Contact' => [ |
||
886 | 'type' => 'string', |
||
887 | 'label' => 'Contact-First Name', |
||
888 | 'required' => false, |
||
889 | 'group' => 'Contact', |
||
890 | 'optionLabel' => 'First Name', |
||
891 | ], |
||
892 | 'Email__Contact' => [ |
||
893 | 'type' => 'string', |
||
894 | 'label' => 'Contact-Email', |
||
895 | 'required' => false, |
||
896 | 'group' => 'Contact', |
||
897 | 'optionLabel' => 'Email', |
||
898 | ], |
||
899 | ]; |
||
900 | |||
901 | $this->cache |
||
902 | ->method('get') |
||
903 | ->willReturnMap( |
||
904 | [ |
||
905 | ['leadFields.Lead', null, $leadFields], |
||
906 | ['leadFields.Contact', null, $contactFields], |
||
907 | ] |
||
908 | ); |
||
909 | |||
910 | $this->cache->method('getCache') |
||
911 | ->willReturn($this->cache); |
||
912 | } |
||
913 | |||
914 | /** |
||
915 | * @param int $maxUpdate |
||
916 | * @param int $maxCreate |
||
917 | * @param int $maxSfLeads |
||
918 | * @param int $maxSfContacts |
||
919 | * @param null $updateObject |
||
920 | * |
||
921 | * @return SalesforceIntegration|\PHPUnit\Framework\MockObject\MockObject |
||
922 | */ |
||
923 | protected function getSalesforceIntegration($maxUpdate = 100, $maxCreate = 200, $maxSfLeads = 25, $maxSfContacts = 25, $updateObject = null) |
||
924 | { |
||
925 | $this->setMocks(); |
||
926 | |||
927 | $featureSettings = [ |
||
928 | 'sandbox' => [ |
||
929 | ], |
||
930 | 'updateOwner' => [ |
||
931 | ], |
||
932 | 'objects' => $this->sfObjects, |
||
933 | 'namespace' => null, |
||
934 | 'leadFields' => [ |
||
935 | 'Company__Lead' => 'company', |
||
936 | 'FirstName__Lead' => 'firstname', |
||
937 | 'LastName__Lead' => 'lastname', |
||
938 | 'Email__Lead' => 'email', |
||
939 | 'FirstName__Contact' => 'firstname', |
||
940 | 'LastName__Contact' => 'lastname', |
||
941 | 'Email__Contact' => 'email', |
||
942 | ], |
||
943 | 'update_mautic' => [ |
||
944 | 'Company__Lead' => '0', |
||
945 | 'FirstName__Lead' => '0', |
||
946 | 'LastName__Lead' => '0', |
||
947 | 'Email__Lead' => '0', |
||
948 | 'FirstName__Contact' => '0', |
||
949 | 'LastName__Contact' => '0', |
||
950 | 'Email__Contact' => '0', |
||
951 | ], |
||
952 | 'companyFields' => [ |
||
953 | 'Name' => 'companyname', |
||
954 | ], |
||
955 | 'update_mautic_company' => [ |
||
956 | 'Name' => '0', |
||
957 | ], |
||
958 | ]; |
||
959 | |||
960 | $integration = new Integration(); |
||
961 | $integration->setIsPublished(true) |
||
962 | ->setName('Salesforce') |
||
963 | ->setPlugin('MauticCrmBundle') |
||
964 | ->setApiKeys( |
||
965 | [ |
||
966 | 'access_token' => '123', |
||
967 | 'instance_url' => 'https://sftest.com', |
||
968 | ] |
||
969 | ) |
||
970 | ->setFeatureSettings($featureSettings) |
||
971 | ->setSupportedFeatures( |
||
972 | [ |
||
973 | 'get_leads', |
||
974 | 'push_lead', |
||
975 | 'push_leads', |
||
976 | ] |
||
977 | ); |
||
978 | |||
979 | $integrationEntityModelMock = $this->getMockBuilder(IntegrationEntityModel::class) |
||
980 | ->disableOriginalConstructor() |
||
981 | ->getMock(); |
||
982 | |||
983 | $integrationEntityModelMock->method('getEntityByIdAndSetSyncDate') |
||
984 | ->willReturn(new IntegrationEntity()); |
||
985 | |||
986 | $sf = $this->getMockBuilder(SalesforceIntegration::class) |
||
987 | ->setConstructorArgs([ |
||
988 | $this->dispatcher, |
||
989 | $this->cache, |
||
990 | $this->em, |
||
991 | $this->session, |
||
992 | $this->request, |
||
993 | $this->router, |
||
994 | $this->translator, |
||
995 | $this->logger, |
||
996 | $this->encryptionHelper, |
||
997 | $this->leadModel, |
||
998 | $this->companyModel, |
||
999 | $this->pathsHelper, |
||
1000 | $this->notificationModel, |
||
1001 | $this->fieldModel, |
||
1002 | $integrationEntityModelMock, |
||
1003 | $this->doNotContact, |
||
1004 | ]) |
||
1005 | ->setMethods($this->sfMockMethods) |
||
1006 | ->getMock(); |
||
1007 | |||
1008 | $sf->method('makeRequest') |
||
1009 | ->will( |
||
1010 | $this->returnCallback( |
||
1011 | function () use ($maxSfContacts, $maxSfLeads, $updateObject) { |
||
1012 | $args = func_get_args(); |
||
1013 | // Determine what to return by analyzing the URL and query parameters |
||
1014 | switch (true) { |
||
1015 | case false !== strpos($args[0], '/query'): |
||
1016 | if (isset($args[1]['q']) && false !== strpos($args[0], 'from CampaignMember')) { |
||
1017 | return []; |
||
1018 | } elseif (isset($args[1]['q']) && false !== strpos($args[1]['q'], 'from Campaign')) { |
||
1019 | return [ |
||
1020 | 'totalSize' => 0, |
||
1021 | 'records' => [], |
||
1022 | ]; |
||
1023 | } elseif (isset($args[1]['q']) && false !== strpos($args[1]['q'], 'from Account')) { |
||
1024 | return [ |
||
1025 | 'totalSize' => 0, |
||
1026 | 'records' => [], |
||
1027 | ]; |
||
1028 | } elseif (isset($args[1]['q']) && 'SELECT CreatedDate from Organization' === $args[1]['q']) { |
||
1029 | return [ |
||
1030 | 'records' => [ |
||
1031 | ['CreatedDate' => '2012-10-30T17:56:50.000+0000'], |
||
1032 | ], |
||
1033 | ]; |
||
1034 | } elseif (isset($args[1]['q']) && false !== strpos($args[1]['q'], 'from '.$updateObject.'History')) { |
||
1035 | return $this->getSalesforceDNCHistory($updateObject, 'Mautic'); |
||
1036 | } else { |
||
1037 | // Extract emails |
||
1038 | $found = preg_match('/Email in \(\'(.*?)\'\)/', $args[1]['q'], $match); |
||
1039 | if ($found) { |
||
1040 | $emails = explode("','", $match[1]); |
||
1041 | |||
1042 | return $this->getSalesforceObjects($emails, $maxSfContacts, $maxSfLeads); |
||
1043 | } else { |
||
1044 | return $this->getSalesforceObjects([], $maxSfContacts, $maxSfLeads); |
||
1045 | } |
||
1046 | } |
||
1047 | // no break |
||
1048 | case false !== strpos($args[0], '/composite'): |
||
1049 | return $this->getSalesforceCompositeResponse($args[1]); |
||
1050 | } |
||
1051 | } |
||
1052 | ) |
||
1053 | ); |
||
1054 | |||
1055 | /* @var \PHPUnit\Framework\MockObject\MockObject $this->>dispatcher */ |
||
1056 | $this->dispatcher->method('dispatch') |
||
1057 | ->will( |
||
1058 | $this->returnCallback( |
||
1059 | function () use ($sf, $integration) { |
||
1060 | $args = func_get_args(); |
||
1061 | |||
1062 | switch ($args[0]) { |
||
1063 | default: |
||
1064 | return new PluginIntegrationKeyEvent($sf, $integration->getApiKeys()); |
||
1065 | } |
||
1066 | } |
||
1067 | ) |
||
1068 | ); |
||
1069 | |||
1070 | $sf->setIntegrationSettings($integration); |
||
1071 | |||
1072 | $repo = $sf->getIntegrationEntityRepository(); |
||
1073 | $this->setLeadsToUpdate($repo, $maxUpdate, $maxSfContacts, $maxSfLeads, $updateObject); |
||
1074 | $this->setLeadsToCreate($repo, $maxCreate); |
||
1075 | |||
1076 | return $sf; |
||
1077 | } |
||
1078 | |||
1079 | /** |
||
1080 | * @param $max |
||
1081 | * @param $maxSfContacts |
||
1082 | * @param $maxSfLeads |
||
1083 | * @param $specificObject |
||
1084 | */ |
||
1085 | protected function setLeadsToUpdate(MockObject $mockRepository, $max, $maxSfContacts, $maxSfLeads, $specificObject) |
||
1086 | { |
||
1087 | $mockRepository->method('findLeadsToUpdate') |
||
1088 | ->willReturnCallback( |
||
1089 | function () use ($max, $specificObject) { |
||
1090 | $args = func_get_args(); |
||
1091 | $object = $args[6]; |
||
1092 | |||
1093 | // determine whether to return a count or records |
||
1094 | $results = []; |
||
1095 | if (false === $args[3]) { |
||
1096 | foreach ($object as $object) { |
||
1097 | if ($specificObject && $specificObject !== $object) { |
||
1098 | continue; |
||
1099 | } |
||
1100 | |||
1101 | // Should be 100 contacts and 100 leads |
||
1102 | $results[$object] = $max; |
||
1103 | } |
||
1104 | |||
1105 | return $results; |
||
1106 | } |
||
1107 | |||
1108 | $results = $this->getLeadsToUpdate($object, $args[3], $max, $specificObject); |
||
1109 | |||
1110 | return $results; |
||
1111 | } |
||
1112 | ); |
||
1113 | } |
||
1114 | |||
1115 | /** |
||
1116 | * @param int $max |
||
1117 | */ |
||
1118 | protected function setLeadsToCreate(MockObject $mockRepository, $max = 200) |
||
1119 | { |
||
1120 | $mockRepository->method('findLeadsToCreate') |
||
1121 | ->willReturnCallback( |
||
1122 | function () use ($max) { |
||
1123 | $args = func_get_args(); |
||
1124 | |||
1125 | if (false === $args[2]) { |
||
1126 | return $max; |
||
1127 | } |
||
1128 | |||
1129 | $createLeads = $this->getLeadsToCreate($args[2], $max); |
||
1130 | |||
1131 | // determine whether to return a count or records |
||
1132 | if (false === $args[2]) { |
||
1133 | return count($createLeads); |
||
1134 | } |
||
1135 | |||
1136 | return $createLeads; |
||
1137 | } |
||
1138 | ); |
||
1139 | } |
||
1140 | |||
1141 | /** |
||
1142 | * Simulate looping over Mautic leads to update. |
||
1143 | * |
||
1144 | * @param $object |
||
1145 | * @param $limit |
||
1146 | * @param $max |
||
1147 | * @param $specificObject |
||
1148 | * |
||
1149 | * @return array |
||
1150 | */ |
||
1151 | protected function getLeadsToUpdate($object, $limit, $max, $specificObject) |
||
1152 | { |
||
1153 | $entities = [ |
||
1154 | $object => [], |
||
1155 | ]; |
||
1156 | |||
1157 | // Should be 100 each |
||
1158 | if (($this->leadsUpdatedCounter[$object] >= $max) || ($specificObject && $specificObject !== $object)) { |
||
1159 | return $entities; |
||
1160 | } |
||
1161 | |||
1162 | if ($limit > $max) { |
||
1163 | $limit = $max; |
||
1164 | } |
||
1165 | |||
1166 | $counter = 0; |
||
1167 | while ($counter < $limit) { |
||
1168 | $entities[$object][$this->idCounter] = [ |
||
1169 | 'integration_entity_id' => 'SF'.$this->idCounter, |
||
1170 | 'integration_entity' => $object, |
||
1171 | 'id' => $this->idCounter, |
||
1172 | 'internal_entity_id' => $this->idCounter, |
||
1173 | 'firstname' => $object.$this->idCounter, |
||
1174 | 'lastname' => $object.$this->idCounter, |
||
1175 | 'company' => $object.$this->idCounter, |
||
1176 | 'email' => $object.$this->idCounter.'@sftest.com', |
||
1177 | ]; |
||
1178 | |||
1179 | ++$counter; |
||
1180 | ++$this->idCounter; |
||
1181 | ++$this->leadsUpdatedCounter[$object]; |
||
1182 | } |
||
1183 | |||
1184 | $this->mauticContacts = array_merge($entities[$object], $this->mauticContacts); |
||
1185 | |||
1186 | return $entities; |
||
1187 | } |
||
1188 | |||
1189 | /** |
||
1190 | * Simulate looping over Mautic leads to create. |
||
1191 | * |
||
1192 | * @param $limit |
||
1193 | * @param $max |
||
1194 | * |
||
1195 | * @return array |
||
1196 | */ |
||
1197 | protected function getLeadsToCreate($limit, $max = 200) |
||
1198 | { |
||
1199 | $entities = []; |
||
1200 | |||
1201 | if ($this->leadsCreatedCounter > $max) { |
||
1202 | return $entities; |
||
1203 | } |
||
1204 | |||
1205 | if ($limit > $max) { |
||
1206 | $limit = $max; |
||
1207 | } |
||
1208 | |||
1209 | $counter = 0; |
||
1210 | while ($counter < $limit) { |
||
1211 | //Start after the update |
||
1212 | $entities[$this->idCounter] = [ |
||
1213 | 'id' => $this->idCounter, |
||
1214 | 'internal_entity_id' => $this->idCounter, |
||
1215 | 'firstname' => 'Lead'.$this->idCounter, |
||
1216 | 'lastname' => 'Lead'.$this->idCounter, |
||
1217 | 'company' => 'Lead'.$this->idCounter, |
||
1218 | 'email' => 'Lead'.$this->idCounter.'@sftest.com', |
||
1219 | ]; |
||
1220 | |||
1221 | ++$this->idCounter; |
||
1222 | ++$counter; |
||
1223 | ++$this->leadsCreatedCounter; |
||
1224 | } |
||
1225 | |||
1226 | $this->mauticContacts = array_merge($entities, $this->mauticContacts); |
||
1227 | |||
1228 | return $entities; |
||
1229 | } |
||
1230 | |||
1231 | /** |
||
1232 | * Mock SF response. |
||
1233 | * |
||
1234 | * @return array |
||
1235 | */ |
||
1236 | protected function getSalesforceObjects($emails, $maxContacts, $maxLeads) |
||
1237 | { |
||
1238 | // Let's find around $max records |
||
1239 | $records = []; |
||
1240 | $contactCount = 0; |
||
1241 | $leadCount = 0; |
||
1242 | |||
1243 | if (!empty($emails)) { |
||
1244 | foreach ($emails as $email) { |
||
1245 | // Extact ID |
||
1246 | preg_match('/(Lead|Contact)([0-9]*)@sftest\.com/', $email, $match); |
||
1247 | $object = $match[1]; |
||
1248 | |||
1249 | if ('Lead' === $object) { |
||
1250 | if ($leadCount >= $maxLeads) { |
||
1251 | continue; |
||
1252 | } |
||
1253 | ++$leadCount; |
||
1254 | } else { |
||
1255 | if ($contactCount >= $maxContacts) { |
||
1256 | continue; |
||
1257 | } |
||
1258 | ++$contactCount; |
||
1259 | } |
||
1260 | |||
1261 | $id = $match[2]; |
||
1262 | $records[] = [ |
||
1263 | 'attributes' => [ |
||
1264 | 'type' => $object, |
||
1265 | 'url' => "/services/data/v34.0/sobjects/$object/SF$id", |
||
1266 | ], |
||
1267 | 'Id' => 'SF'.$id, |
||
1268 | 'FirstName' => $object.$id, |
||
1269 | 'LastName' => $object.$id, |
||
1270 | 'Email' => $object.$id.'@sftest.com', |
||
1271 | ]; |
||
1272 | |||
1273 | $this->addSpecialCases($id, $records); |
||
1274 | } |
||
1275 | } |
||
1276 | |||
1277 | $this->returnedSfEntities = array_merge($this->returnedSfEntities, $records); |
||
1278 | |||
1279 | return [ |
||
1280 | 'totalSize' => count($records), |
||
1281 | 'done' => true, |
||
1282 | 'records' => $records, |
||
1283 | ]; |
||
1284 | } |
||
1285 | |||
1286 | /** |
||
1287 | * Mock SF response. |
||
1288 | * |
||
1289 | * @return array |
||
1290 | */ |
||
1291 | protected function getSalesforceDNCHistory($object, $priority) |
||
1292 | { |
||
1293 | $records = []; |
||
0 ignored issues
–
show
Unused Code
introduced
by
Loading history...
|
|||
1294 | $datePriority = [ |
||
1295 | 'SF' => '2017-10-16T00:43:43.000+0000', |
||
1296 | 'Mautic' => '2017-10-16T18:43:43.000+0000', |
||
1297 | ]; |
||
1298 | |||
1299 | $records = [ |
||
1300 | 'totalSize' => 3, |
||
1301 | 'done' => 1, |
||
1302 | 'records' => [ |
||
1303 | [ |
||
1304 | 'attributes' => [ |
||
1305 | 'type' => 'ContactHistory', |
||
1306 | 'url' => '/services/data/v34.0/sobjects/'.$object.'History/0170SFH1', |
||
1307 | ], |
||
1308 | 'Field' => 'HasOptedOutOfEmail', |
||
1309 | $object.'Id' => 'SF1', |
||
1310 | 'CreatedDate' => $datePriority[$priority], |
||
1311 | 'IsDeleted' => false, |
||
1312 | 'NewValue' => true, |
||
1313 | ], |
||
1314 | [ |
||
1315 | 'attributes' => [ |
||
1316 | 'type' => 'ContactHistory', |
||
1317 | 'url' => '/services/data/v34.0/sobjects/'.$object.'History/0170SFH3', |
||
1318 | ], |
||
1319 | 'Field' => 'HasOptedOutOfEmail', |
||
1320 | $object.'Id' => 'SF2', |
||
1321 | 'CreatedDate' => $datePriority[$priority], |
||
1322 | 'IsDeleted' => false, |
||
1323 | 'NewValue' => true, |
||
1324 | ], |
||
1325 | ], |
||
1326 | ]; |
||
1327 | |||
1328 | return $records; |
||
1329 | } |
||
1330 | |||
1331 | /** |
||
1332 | * @param $id |
||
1333 | * @param $records |
||
1334 | */ |
||
1335 | protected function addSpecialCases($id, &$records) |
||
1336 | { |
||
1337 | switch ($this->specialSfCase) { |
||
1338 | case self::SC_MULTIPLE_SF_LEADS: |
||
1339 | $records[] = [ |
||
1340 | 'attributes' => [ |
||
1341 | 'type' => 'Lead', |
||
1342 | 'url' => '/services/data/v34.0/sobjects/Lead/SF'.$id.'b', |
||
1343 | ], |
||
1344 | 'Id' => 'SF'.$id.'b', |
||
1345 | 'FirstName' => 'Lead'.$id, |
||
1346 | 'LastName' => 'Lead'.$id, |
||
1347 | 'Email' => 'Lead'.$id.'@sftest.com', |
||
1348 | 'Company' => 'Lead'.$id, |
||
1349 | 'ConvertedContactId' => null, |
||
1350 | ]; |
||
1351 | break; |
||
1352 | |||
1353 | case self::SC_MULTIPLE_SF_CONTACTS: |
||
1354 | $records[] = [ |
||
1355 | 'attributes' => [ |
||
1356 | 'type' => 'Contact', |
||
1357 | 'url' => '/services/data/v34.0/sobjects/Contact/SF'.$id.'b', |
||
1358 | ], |
||
1359 | 'Id' => 'SF'.$id.'b', |
||
1360 | 'FirstName' => 'Contact'.$id, |
||
1361 | 'LastName' => 'Contact'.$id, |
||
1362 | 'Email' => 'Contact'.$id.'@sftest.com', |
||
1363 | 'Company' => 'Contact'.$id, |
||
1364 | 'ConvertedContactId' => null, |
||
1365 | ]; |
||
1366 | break; |
||
1367 | } |
||
1368 | } |
||
1369 | |||
1370 | /** |
||
1371 | * Mock SF response. |
||
1372 | * |
||
1373 | * @param $data |
||
1374 | * |
||
1375 | * @return array |
||
1376 | */ |
||
1377 | protected function getSalesforceCompositeResponse($data) |
||
1378 | { |
||
1379 | $response = []; |
||
1380 | foreach ($data['compositeRequest'] as $subrequest) { |
||
1381 | if ('PATCH' === $subrequest['method']) { |
||
1382 | $response[] = [ |
||
1383 | 'body' => null, |
||
1384 | 'httpHeaders' => [], |
||
1385 | 'httpStatusCode' => 204, |
||
1386 | 'referenceId' => $subrequest['referenceId'], |
||
1387 | ]; |
||
1388 | } else { |
||
1389 | $contactId = ''; |
||
1390 | $parts = explode('-', $subrequest['referenceId']); |
||
1391 | |||
1392 | if (3 === count($parts)) { |
||
1393 | list($contactId, $sfObject, $id) = $parts; |
||
1394 | } elseif (2 === count($parts)) { |
||
1395 | list($contactId, $sfObject) = $parts; |
||
1396 | } elseif (4 === count($parts)) { |
||
1397 | list($contactId, $sfObject, $empty, $campaignId) = $parts; |
||
1398 | } |
||
1399 | $response[] = [ |
||
1400 | 'body' => [ |
||
1401 | 'id' => 'SF'.$contactId, |
||
1402 | 'success' => true, |
||
1403 | 'errors' => [], |
||
1404 | ], |
||
1405 | 'httpHeaders' => [ |
||
1406 | 'Location' => '/services/data/v38.0/sobjects/'.$sfObject.'/SF'.$contactId, |
||
1407 | ], |
||
1408 | 'httpStatusCode' => 201, |
||
1409 | 'referenceId' => $subrequest['referenceId'], |
||
1410 | ]; |
||
1411 | } |
||
1412 | } |
||
1413 | |||
1414 | return ['compositeResponse' => $response]; |
||
1415 | } |
||
1416 | |||
1417 | /** |
||
1418 | * @return array |
||
1419 | */ |
||
1420 | protected function getPersistedIntegrationEntities() |
||
1421 | { |
||
1422 | $entities = $this->persistedIntegrationEntities; |
||
1423 | $this->persistedIntegrationEntities = []; |
||
1424 | |||
1425 | return $entities; |
||
1426 | } |
||
1427 | |||
1428 | protected function getReturnedSfEntities() |
||
1429 | { |
||
1430 | $entities = $this->returnedSfEntities; |
||
1431 | $this->returnedSfEntities = []; |
||
1432 | |||
1433 | return $entities; |
||
1434 | } |
||
1435 | |||
1436 | protected function getMauticContacts() |
||
1437 | { |
||
1438 | $contacts = $this->mauticContacts; |
||
1439 | $this->mauticContacts = [ |
||
1440 | 'Contact' => [], |
||
1441 | 'Lead' => [], |
||
1442 | ]; |
||
1443 | |||
1444 | return $contacts; |
||
1445 | } |
||
1446 | |||
1447 | /** |
||
1448 | * This function determines the parent of the class instance provided, and returns all properties of its parent. |
||
1449 | * Inspired from https://github.com/sebastianbergmann/phpunit/issues/3888#issuecomment-559513371. |
||
1450 | * |
||
1451 | * Result structure: |
||
1452 | * Array =>[ |
||
1453 | * 'parentPropertyName1' => 'value1' |
||
1454 | * 'parentPropertyName2' => 'value2' |
||
1455 | * ... |
||
1456 | * ] |
||
1457 | * |
||
1458 | * @param $instance |
||
1459 | * |
||
1460 | * @throws \ReflectionException |
||
1461 | */ |
||
1462 | private static function getParentPrivateProperties($instance): array |
||
1463 | { |
||
1464 | $reflectionClass = new ReflectionClass(get_class($instance)); |
||
1465 | $parentReflectionClass = $reflectionClass->getParentClass(); |
||
1466 | |||
1467 | $parentProperties = []; |
||
1468 | |||
1469 | foreach ($parentReflectionClass->getProperties() as $p) { |
||
1470 | $p->setAccessible(true); |
||
1471 | $parentProperties[$p->getName()] = $p->getValue($instance); |
||
1472 | } |
||
1473 | |||
1474 | return $parentProperties; |
||
1475 | } |
||
1476 | } |
||
1477 |