From 8e27779f098375b818c7f7d883a44edd2fd9da90 Mon Sep 17 00:00:00 2001 From: git <git@787980.no-reply.drupal.org> Date: Thu, 12 Jan 2017 17:22:11 -0500 Subject: [PATCH] Adds MappingsConstants class, moving constant in salesforce_mapping.module to testable constructs. Adds getter methods getDrupalEntityType and getDrupalBundle to SalesforceMapping class to mirror getSalesforceObjectType Fixes referencing errors in PullBase for testability - Interfaces over classes - Constant classes over global constable in *.module files - Removal of t() use and using the implicit calls in the Drupal logger - Protected methods over private methods Adds PullBaseTest class (still needs work) Fixes use list in QueueHandlerTest class Add TestPullBase class to override necessary wrapper functions for PullBaseTest Fixes use list in TestQueueHandler --- .../src/Entity/SalesforceMapping.php | 14 ++ .../src/MappingConstants.php | 43 +++++ .../src/Plugin/QueueWorker/PullBase.php | 39 ++--- tests/src/salesforce_pull/PullBaseTest.php | 147 ++++++++++++++++++ .../src/salesforce_pull/QueueHandlerTest.php | 4 +- tests/src/salesforce_pull/TestPullBase.php | 49 ++++++ .../src/salesforce_pull/TestQueueHandler.php | 5 - 7 files changed, 275 insertions(+), 26 deletions(-) create mode 100644 modules/salesforce_mapping/src/MappingConstants.php create mode 100644 tests/src/salesforce_pull/PullBaseTest.php create mode 100644 tests/src/salesforce_pull/TestPullBase.php diff --git a/modules/salesforce_mapping/src/Entity/SalesforceMapping.php b/modules/salesforce_mapping/src/Entity/SalesforceMapping.php index 34ad582c..4a12ed13 100644 --- a/modules/salesforce_mapping/src/Entity/SalesforceMapping.php +++ b/modules/salesforce_mapping/src/Entity/SalesforceMapping.php @@ -259,6 +259,20 @@ class SalesforceMapping extends ConfigEntityBase implements SalesforceMappingInt return $this->salesforce_object_type; } + /** + * @return string + */ + public function getDrupalEntityType() { + return $this->drupal_entity_type; + } + + /** + * @return string + */ + public function getDrupalBundle() { + return $this->drupal_bundle; + } + /** * @return array */ diff --git a/modules/salesforce_mapping/src/MappingConstants.php b/modules/salesforce_mapping/src/MappingConstants.php new file mode 100644 index 00000000..4b916eeb --- /dev/null +++ b/modules/salesforce_mapping/src/MappingConstants.php @@ -0,0 +1,43 @@ +<?php + +namespace Drupal\salesforce_mapping; + +/** + * Defines events for Salesforce. + * + * @see \Drupal\salesforce\SalesforceEvent + */ +final class MappingConstants { + /** + * Define when a data sync should take place for a given mapping. + */ + const SALESFORCE_MAPPING_SYNC_DRUPAL_CREATE = 'push_create'; + const SALESFORCE_MAPPING_SYNC_DRUPAL_UPDATE = 'push_update'; + const SALESFORCE_MAPPING_SYNC_DRUPAL_DELETE = 'push_delete'; + const SALESFORCE_MAPPING_SYNC_SF_CREATE = 'pull_create'; + const SALESFORCE_MAPPING_SYNC_SF_UPDATE = 'pull_update'; + const SALESFORCE_MAPPING_SYNC_SF_DELETE = 'pull_delete'; + + const SALESFORCE_MAPPING_TRIGGER_MAX_LENGTH = 16; + + /** + * Field mapping direction constants. + */ + const SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF = 'drupal_sf'; + const SALESFORCE_MAPPING_DIRECTION_SF_DRUPAL = 'sf_drupal'; + const SALESFORCE_MAPPING_DIRECTION_SYNC = 'sync'; + + /** + * Delimiter used in Salesforce multipicklists. + */ + const SALESFORCE_MAPPING_ARRAY_DELIMITER =';'; + + /** + * Field mapping maximum name length. + */ + const SALESFORCE_MAPPING_NAME_LENGTH = 128; + + + const SALESFORCE_MAPPING_STATUS_SUCCESS = 1; + const SALESFORCE_MAPPING_STATUS_ERROR = 0; +} diff --git a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php index ef4540b7..c4cfd1be 100644 --- a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php +++ b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php @@ -18,9 +18,10 @@ use Drupal\salesforce\SObject; use Drupal\salesforce\LoggingTrait; use Drupal\salesforce\LoggingLevels; use Drupal\salesforce\Rest\RestClient; -use Drupal\salesforce_mapping\Entity\SalesforceMapping; -use Drupal\salesforce_mapping\Entity\MappedObject; +use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface; +use Drupal\salesforce_mapping\Entity\MappedObjectInterface; use Drupal\salesforce_mapping\PushParams; +use Drupal\salesforce_mapping\MappingConstants; /** * Provides base functionality for the Salesforce Pull Queue Workers. @@ -91,7 +92,7 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi // loadMappingObjects returns an array, but providing salesforce id and mapping guarantees at most one result. $mapped_object = $this->loadMappingObjects([ 'salesforce_id' => (string)$sf_object->id(), - 'salesforce_mapping' => $mapping->id() + 'salesforce_mapping' => $mapping->id ]); $mapped_object = current($mapped_object); $this->updateEntity($mapping, $mapped_object, $sf_object); @@ -112,8 +113,8 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi * @param SObject $sf_object * Current Salesforce record array. */ - private function updateEntity(SalesforceMapping $mapping, MappedObject $mapped_object, SObject $sf_object) { - if (!$mapping->checkTriggers([SALESFORCE_MAPPING_SYNC_SF_UPDATE])) { + protected function updateEntity(SalesforceMappingInterface $mapping, MappedObjectInterface $mapped_object, SObject $sf_object) { + if (!$mapping->checkTriggers([MappingConstants::SALESFORCE_MAPPING_SYNC_SF_UPDATE])) { return; } @@ -158,7 +159,9 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi } catch (\Exception $e) { if ($e instanceof EntityNotFoundException) { - $message = t('Drupal entity existed at one time for Salesforce object %sfobjectid, but does not currently exist. Error: %msg', + $this->log('Salesforce Pull', + LoggingLevels::ERROR, + 'Drupal entity existed at one time for Salesforce object %sfobjectid, but does not currently exist. Error: %msg', [ '%sfobjectid' => (string)$sf_object->id(), '%msg' => $e->getMessage(), @@ -166,7 +169,9 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi ); } else { - $message = t('Failed to update entity %label from Salesforce object %sfobjectid. Error: %msg', + $this->log('Salesforce Pull', + LoggingLevels::ERROR, + 'Failed to update entity %label from Salesforce object %sfobjectid. Error: %msg', [ '%label' => $entity->label(), '%sfobjectid' => (string)$sf_object->id(), @@ -174,7 +179,6 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi ] ); } - $this->log('Salesforce Pull', LoggingLevels::ERROR, $message); $this->watchdogException($e); } } @@ -187,22 +191,19 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi * @param SObject $sf_object * Current Salesforce record array. */ - private function createEntity(SalesforceMapping $mapping, SObject $sf_object) { - if (!$mapping->checkTriggers([SALESFORCE_MAPPING_SYNC_SF_CREATE])) { + protected function createEntity(SalesforceMappingInterface $mapping, SObject $sf_object) { + if (!$mapping->checkTriggers([MappingConstants::SALESFORCE_MAPPING_SYNC_SF_CREATE])) { return; } try { - // Create entity from mapping object and field maps. - $entity_type = $mapping->get('drupal_entity_type'); - $entity_info = $this->etm->getDefinition($entity_type); - // Define values to pass to entity_create(). - $entity_keys = $entity_info->getKeys(); + $entity_type = $mapping->getDrupalEntityType(); + $entity_keys = $this->etm->getDefinition($entity_type)->getKeys(); $values = []; if (isset($entity_keys['bundle']) && !empty($entity_keys['bundle'])) { - $values[$entity_keys['bundle']] = $mapping->get('drupal_bundle'); + $values[$entity_keys['bundle']] = $mapping->getDrupalBundle(); } // See note above about flag. @@ -250,12 +251,14 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi ); } catch (\Exception $e) { - $message = $e->getMessage() . ' ' . t('Pull-create failed for Salesforce Object ID: %sfobjectid', + $this->log('Salesforce Pull', + LoggingLevels::ERROR, + '%msg Pull-create failed for Salesforce Object ID: %sfobjectid', [ + '%msg' => $e->getMessage(), '%sfobjectid' => (string)$sf_object->id(), ] ); - $this->log('Salesforce Pull', LoggingLevels::ERROR, $message); $this->watchdogException($e); } } diff --git a/tests/src/salesforce_pull/PullBaseTest.php b/tests/src/salesforce_pull/PullBaseTest.php new file mode 100644 index 00000000..d9d26900 --- /dev/null +++ b/tests/src/salesforce_pull/PullBaseTest.php @@ -0,0 +1,147 @@ +<?php +namespace Drupal\Tests\salesforce\salesforce_pull; + +use Drupal\Tests\UnitTestCase; +use Drupal\Tests\salesforce\salesforce_pull\TestPullBase; +use Drupal\Core\Config\Entity\ConfigEntityStorage; +use Drupal\Core\Entity\EntityStorageBase; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Component\Plugin\Definition\PluginDefinitionInterface; +use Drupal\salesforce\SelectQueryResult; +use Drupal\salesforce\Rest\RestClient; +use Drupal\salesforce\SObject; +use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface; +use Drupal\salesforce_mapping\Entity\MappedObjectInterface; +use Drupal\salesforce_pull\PullQueueItem; +use Prophecy\Argument; + + +/** + * Test Object instantitation + * + * @group salesforce_pull + */ + +class PullBaseTest extends UnitTestCase { + static $modules = ['salesforce_pull']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + // mock hander for getStorage calls + + + // mock mapping object + $this->mapping = $this->getMockBuilder(SalesforceMappingInterface::CLASS) + ->setMethods(['__construct', '__get', 'checkTriggers', 'getDrupalEntityType', 'getDrupalBundle']) + ->getMock(); + $this->mapping->expects($this->any()) + ->method('__get') + ->with($this->equalTo('id')) + ->willReturn(1); + $this->mapping->expects($this->any()) + ->method('checkTriggers') + ->willReturn(true); + $this->mapping->method('getDrupalEntityType') + ->willReturn('test'); + $this->mapping->method('getDrupalBundle') + ->willReturn('test'); + + // mock mapped object + $prophecy = $this->prophesize(MappedObjectInterface::CLASS); + $this->mappedObject = $prophecy->reveal(); + + // mock ConfigEntityStorage object + $prophecy = $this->prophesize(ConfigEntityStorage::CLASS); + $prophecy->load(Argument::any())->willReturn($this->mapping); + $this->configStorage = $prophecy->reveal(); + + // mock EntityStorage object + $prophecy = $this->prophesize(EntityStorageBase::CLASS); + $prophecy->loadByProperties(Argument::any())->willReturn([$this->mappedObject]); + $this->entityStorage = $prophecy->reveal(); + + // mock Definition + $prophecy = $this->prophesize(PluginDefinitionInterface::CLASS); + $prophecy->getKeys(Argument::any())->willReturn([ + 'bundle' => 'test', + + ]); + $this->entityDefinition = $prophecy->reveal(); + + + // mock EntityTypeManagerInterface + $prophecy = $this->prophesize(EntityTypeManagerInterface::CLASS); + $prophecy->getStorage('salesforce_mapping')->willReturn($this->configStorage); + $prophecy->getStorage('salesforce_mapped_object')->willReturn($this->entityStorage); + $prophecy->getStorage('test')->willReturn($this->newEntityStorage); $prophecy->getDefinition('test')->willReturn($this->entityDefinition); + $this->etm = $prophecy->reveal(); + + // SelectQueryResult for rest client call + $result = [ + 'totalSize' => 1, + 'done' => true, + 'records' => [ + [ + 'Id' => '1234567890abcde', + 'attributes' => ['type' => 'dummy',], + 'name' => 'Example', + ], + ] + ]; + $this->sqr = new SelectQueryResult($result); + + // mock rest cient + $prophecy = $this->prophesize(RestClient::CLASS); + $prophecy->query(Argument::any()) + ->willReturn($this->sqr); + $this->sfapi = $prophecy->reveal(); + + // mock module handler + $prophecy = $this->prophesize(ModuleHandlerInterface::CLASS); + $this->mh = $prophecy->reveal(); + + $this->pullWorker = new TestPullBase($this->etm, $this->sfapi, $this->mh); + } + + /** + * Test object instantiation + */ + public function testObject() { + $this->assertTrue($this->pullWorker instanceof TestPullBase); + } + + /** + * Test handler operation, good data + */ + + public function testProcessItem() { + $sobject = new SObject(['id' => '1234567890abcde', 'attributes' => ['type' => 'dummy',]]); + $item = new PullQueueItem($sobject, $this->mapping); + + $result = $this->pullWorker->ProcessItem($item); + $this->assertTrue($result); + } + + /** + * Test handler operation, too many queue items + */ + /* + public function testTooManyQueueItems() { + // initialize with queue size > 100000 (default) + $prophecy = $this->prophesize(QueueInterface::CLASS); + $prophecy->createItem()->willReturn(1); + $prophecy->numberOfItems()->willReturn(100001); + $this->queue = $prophecy->reveal(); + + $this->qh = TestQueueHandler::create($this->sfapi, [$this->mapping], $this->queue); + $result = $this->qh->getUpdatedRecords(); + $this->assertFalse($result); + } + */ + +} diff --git a/tests/src/salesforce_pull/QueueHandlerTest.php b/tests/src/salesforce_pull/QueueHandlerTest.php index b63b1a9a..394f6507 100644 --- a/tests/src/salesforce_pull/QueueHandlerTest.php +++ b/tests/src/salesforce_pull/QueueHandlerTest.php @@ -3,13 +3,11 @@ namespace Drupal\Tests\salesforce\salesforce_pull; use Drupal\Tests\UnitTestCase; use Drupal\Tests\salesforce\salesforce_pull\TestQueueHandler; -//use Drupal\salesforce_pull\QueueHandler; use Drupal\salesforce\SObject; use Drupal\salesforce\SelectQueryResult; -use Drupal\salesforce\Rest\restClient; +use Drupal\salesforce\Rest\RestClient; use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface; use Drupal\Core\Queue\QueueInterface; -//use Drupal\salesforce_pull\PullQueueItem; use Prophecy\Argument; diff --git a/tests/src/salesforce_pull/TestPullBase.php b/tests/src/salesforce_pull/TestPullBase.php new file mode 100644 index 00000000..a9e926a1 --- /dev/null +++ b/tests/src/salesforce_pull/TestPullBase.php @@ -0,0 +1,49 @@ +<?php + +/** + * @file + * Contains Drupal\salesforce_pull\Plugin\QueueWorker\PullBase.php + */ + +namespace Drupal\Tests\salesforce\salesforce_pull; + +use Drupal\salesforce_pull\Plugin\QueueWorker\PullBase; + +/** + * Provides base functionality for the Salesforce Pull Queue Workers. + */ +class TestPullBase extends PullBase { + + /** + * Overrides parent::loadMapping + * Wrapper for salesforce_mapping_load(); + */ + protected function loadMapping($id) { + // return mapping object + return $this->etm->getStorage('salesforce_mapping')->load($id); + } + + /** + * Overrides parent::loadMappingObjects + * Wrapper for salesforce_mapped_object_load_multiple(); + */ + protected function loadMappingObjects(array $properties) { + // return a mapped object + return $this->etm + ->getStorage('salesforce_mapped_object') + ->loadByProperties($properties); + } + + /** + * Overrides parent::watchdogException + * Wrapper for watchdog_exception() + */ + protected function watchdogException(\Exception $e) { + } + + /** + * Ovverides parent::log() + */ + protected function log($name, $level, $message, array $placeholders = []) { + } +} diff --git a/tests/src/salesforce_pull/TestQueueHandler.php b/tests/src/salesforce_pull/TestQueueHandler.php index 7f717278..cb9a599a 100644 --- a/tests/src/salesforce_pull/TestQueueHandler.php +++ b/tests/src/salesforce_pull/TestQueueHandler.php @@ -4,12 +4,7 @@ namespace Drupal\Tests\salesforce\salesforce_pull; use Drupal\Core\Queue\QueueInterface; use Drupal\salesforce_pull\QueueHandler; -use Drupal\salesforce\SelectQuery; -use Drupal\salesforce\SelectQueryResult; use Drupal\salesforce\Rest\RestClient; -use Drupal\salesforce_mapping\Entity\SalesforceMapping; -use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface; -use Drupal\salesforce\Exception; /** * Handles pull cron queue set up. -- GitLab