xero_sync-8.x-1.x-dev/tests/src/Unit/SynchronizerTest.php
tests/src/Unit/SynchronizerTest.php
<?php
namespace Drupal\Tests\xero_sync\Unit;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\xero_sync\Exception\SyncFailureException;
use Drupal\xero_sync\XeroSyncItemFinder;
use Drupal\xero\XeroItemManager;
use Drupal\xero_sync\XeroSyncSynchronizer;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\xero\TypedData\XeroComplexItemInterface;
use Drupal\xero_sync\Event\XeroSyncPropertiesEvent;
use Drupal\xero_sync\XeroSyncInvokedAtIndex as CustomInvokedAtIndexMatcher;
/**
* @coversDefaultClass \Drupal\xero_sync\XeroSyncSynchronizer
* @group xero_sync
*/
class SynchronizerTest extends UnitTestCase {
/**
* The item finder.
*
* @var \Drupal\xero_sync\XeroSyncItemFinder
*/
protected $itemFinder;
/**
* The item manager.
*
* @var \Drupal\xero\XeroItemManager
*/
protected $itemManager;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The typed data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
*/
protected $typedDataManager;
/**
* An entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entityA;
/**
* An entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entityB;
/**
* A Xero item.
*
* @var \Drupal\xero\TypedData\XeroComplexItemInterface
*/
protected $itemA;
/**
* A Xero item.
*
* @var \Drupal\xero\TypedData\XeroComplexItemInterface
*/
protected $itemB;
/**
* The update item event.
*
* @var \Drupal\xero_sync\Event\XeroSyncPropertiesEvent
*/
protected $updateItemEvent;
/**
* The update entity event.
*
* @var \Drupal\xero_sync\Event\XeroSyncPropertiesEvent
*/
protected $updateEntityEvent;
/**
* The synchronizer.
*
* @var \Drupal\xero_sync\XeroSyncSynchronizer
*/
protected $synchronizer;
/**
* The entity type.
*
* @var string
*/
protected $testEntityType;
/**
* The entity id.
*
* @var string|int
*/
protected $testEntityId;
/**
* How many events to have been dispatched so far.
*
* @var int
*/
protected $eventsDispatched = 0;
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->testEntityType = $this->randomMachineName();
$this->testEntityId = $this->randomMachineName();
$this->itemFinder = $this->createMock(XeroSyncItemFinder::class);
$this->itemManager = $this->createMock(XeroItemManager::class);
$this->itemManager->expects($this->any())
->method('throwErrors')
->willReturn($this->itemManager);
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$this->typedDataManager = $this->createMock(TypedDataManagerInterface::class);
$this->entityA = $this->createMock(EntityInterface::class);
$this->entityA->expects($this->any())->method('getEntityTypeId')->willReturn($this->testEntityType);
$this->entityA->expects($this->any())->method('id')->willReturn($this->testEntityId);
$this->expectNoEntitySave($this->entityA);
$this->itemA = $this->createMock(XeroComplexItemInterface::class);
$this->entityB = $this->createMock(EntityInterface::class);
$this->entityB->expects($this->any())->method('getEntityTypeId')->willReturn($this->testEntityType);
$this->entityB->expects($this->any())->method('id')->willReturn($this->testEntityId);
$this->itemB = $this->createMock(XeroComplexItemInterface::class);
$this->updateItemEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->updateEntityEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->synchronizer = new XeroSyncSynchronizer($this->itemFinder, $this->eventDispatcher, $this->typedDataManager, $this->itemManager);
}
/**
* Expect an item will be found.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to find the item from.
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* The found item.
*/
protected function expectItemFoundOnXero(EntityInterface $entity, XeroComplexItemInterface $item) {
$this->itemFinder->expects($this->once())
->method('getItem')
->with($entity)
->willReturn($item);
}
/**
* Expect an item will be uploaded on Xero.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* The item.
*/
protected function expectItemUpdatedOnXero(XeroComplexItemInterface $item) {
$this->itemManager->expects($this->once())
->method('updateItem')
->with($item)
->willReturn($item);
}
/**
* Expect an item to upload to Xero will return FALSE.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* The item.
*/
protected function expectItemUpdateOnXeroFailed(XeroComplexItemInterface $item) {
$this->itemManager->expects($this->once())
->method('updateItem')
->with($item)
->willReturn(FALSE);
}
/**
* Expect no item will be updated on Xero.
*/
protected function expectItemNotUpdatedOnXero() {
$this->itemManager->expects($this->never())
->method('updateItem');
}
/**
* Expect an item will be created on Xero.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* Expect the item to create.
*/
protected function expectItemCreatedOnXero(XeroComplexItemInterface $item) {
$this->itemManager->expects($this->once())
->method('createItem')
->with($item)
->willReturn($item);
}
/**
* Expect an item creation on Xero fails.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface $item
* Expect the item to create.
*/
protected function expectItemCreationOnXeroFailed(XeroComplexItemInterface $item) {
$this->itemManager->expects($this->once())
->method('createItem')
->with($item)
->willReturn(FALSE);
}
/**
* Expect no item will be created on Xero.
*/
protected function expectItemNotCreatedOnXero() {
$this->itemManager->expects($this->never())
->method('createItem');
}
/**
* Expect an entity will be saved.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*/
protected function expectEntitySave(EntityInterface $entity) {
$entity->expects($this->once())
->method('save');
}
/**
* Expect an entity will not be saved.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*/
protected function expectNoEntitySave(EntityInterface $entity) {
$entity->expects($this->never())
->method('save');
}
/**
* Returns a matcher that matches when the method is executed
* at the given index.
*
*/
public static function custom_at(int $index): CustomInvokedAtIndexMatcher
{
return new CustomInvokedAtIndexMatcher($index);
}
/**
* Set expectations for an item sync.
*
* @param \Drupal\xero\TypedData\XeroComplexItemInterface|bool $modifiedItem
* The modified item to be returned form the sync event.
* @param string $eventId
* The id of the event to dispatch.
* @param \Drupal\xero_sync\Event\XeroSyncPropertiesEvent|null $event
* (optional) The event object, in case multiple events are needed.
*/
protected function expectUpdateItemEventDispatched($modifiedItem, $eventId, XeroSyncPropertiesEvent $event = NULL) {
$event = $event ?? $this->updateItemEvent;
if ($modifiedItem) {
$event->expects($this->any())
->method('getItem')
->willReturn($modifiedItem);
$event->expects($this->any())
->method('isModified')
->willReturn(TRUE);
}
else {
$event->expects($this->any())
->method('getItem')
->willReturn($this->itemA);
$event->expects($this->any())
->method('isModified')
->willReturn(FALSE);
}
if ($this->eventsDispatched === 0) {
$this->eventDispatcher->expects($this->custom_at(0))
->method('dispatch')
->with(
$this->isInstanceOf(XeroSyncPropertiesEvent::class),
$this->equalTo('xero_sync.' . $eventId . '.' . $this->testEntityType)
)
->willReturn($event);
}
if ($this->eventsDispatched === 1) {
$dispatch_arguments = [$this->isInstanceOf(XeroSyncPropertiesEvent::class),
$this->equalTo('xero_sync.' . $eventId . '.' . $this->testEntityType),
];
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch')
->willReturnOnConsecutiveCalls($dispatch_arguments, $event);
}
$this->eventsDispatched++;
}
/**
* Set expectations for an entity sync.
*
* @param \Drupal\Core\Entity\EntityInterface|bool $modifiedEntity
* The modified entity to be returned from the sync event.
* @param string $eventId
* The sync event to dispatch.
*/
protected function expectUpdateEntityEventDispatched($modifiedEntity, $eventId) {
if ($modifiedEntity) {
$this->updateEntityEvent->expects($this->any())
->method('getEntity')
->willReturn($modifiedEntity);
$this->updateEntityEvent->expects($this->any())
->method('isModified')
->willReturn(TRUE);
$this->expectEntitySave($modifiedEntity);
}
else {
$this->updateEntityEvent->expects($this->any())
->method('isModified')
->willReturn(FALSE);
}
if ($this->eventsDispatched === 0) {
$this->eventDispatcher->expects($this->once())
->method('dispatch')
->with(
$this->isInstanceOf(XeroSyncPropertiesEvent::class),
$this->equalTo('xero_sync.' . $eventId . '.' . $this->testEntityType)
)
->willReturn($this->updateEntityEvent);
}
elseif ($this->eventsDispatched === 1) {
$dispatch_arguments = [$this->isInstanceOf(XeroSyncPropertiesEvent::class),
$this->equalTo('xero_sync.' . $eventId . '.' . $this->testEntityType),
];
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch')
->willReturnOnConsecutiveCalls($dispatch_arguments, $this->updateEntityEvent);
}
$this->eventsDispatched++;
}
/**
* Test syncEntityWithItem with default arguments.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncEntityWithItem() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectUpdateEntityEventDispatched($this->entityB, 'sync_to_entity_from_item');
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test syncEntityWithItem when Xero API fails to update item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncEntityWithItemUnsuccessful() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdateOnXeroFailed($this->itemB);
$this->expectUpdateEntityEventDispatched($this->entityB, 'sync_to_entity_from_item');
$this->expectNoEntitySave($this->entityA);
$this->expectEntitySave($this->entityB);
$this->expectException(SyncFailureException::class);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test syncEntityWithItem when no corresponding item is found.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncEntityWithNoItemFound() {
$this->itemFinder->expects($this->once())
->method('getItem')
->with($this->entityA)
->willReturn(FALSE);
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test syncEntityWithItem with the update argument set to FALSE.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncItemUpdateProhibited() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectItemNotUpdatedOnXero();
$this->expectUpdateEntityEventDispatched($this->entityB, 'sync_to_entity_from_item');
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->syncEntityWithItem($this->entityA, NULL, TRUE, FALSE);
}
/**
* Test syncEntityWithItem when the found item does not need update.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncItemNotModifiedByEvent() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectUpdateEntityEventDispatched($this->entityB, 'sync_to_entity_from_item');
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test syncEntityWithItem when the entity does not need updating from item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncEntityNotModifiedByEvent() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectUpdateEntityEventDispatched(FALSE, 'sync_to_entity_from_item');
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test the recursion prevention feature of syncEntityWithItem.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::syncEntityWithItem
*/
public function testSyncRecursion() {
$this->expectItemFoundOnXero($this->entityA, $this->itemA);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->updateEntityEvent->expects($this->once())
->method('getEntity')
->willReturn($this->entityB);
$this->updateEntityEvent->expects($this->any())
->method('isModified')
->willReturn(TRUE);
$this->expectNoEntitySave($this->entityA);
$this->expectEntitySave($this->entityB);
$this->expectItemUpdatedOnXero($this->itemB);
// Invoke a sync from inside a sync, to see if recursion is successfully
// prevented.
$entity = $this->entityB;
$synchronizer = $this->synchronizer;
$event = $this->updateEntityEvent;
$callback = $this->returnCallback(function ($arg1, $arg2) use ($entity, $synchronizer, $event) {
$synchronizer->syncEntityWithItem($entity);
return $event;
});
$dispatch_arguments = [$this->isInstanceOf(XeroSyncPropertiesEvent::class),
$this->equalTo('xero_sync.sync_to_entity_from_item.' . $this->testEntityType),
];
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch')
->willReturnOnConsecutiveCalls($dispatch_arguments, $callback);
$this->synchronizer->syncEntityWithItem($this->entityA);
}
/**
* Test unsyncEntityWithItem with an event that modifies the item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::unsyncEntityWithItem
*/
public function testUnsyncEntityWithItem() {
$this->itemFinder->expects($this->never())
->method('getItem');
$this->expectUpdateItemEventDispatched($this->itemB, 'unsync_item');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->unsyncEntityWithItem($this->entityA, $this->itemA);
}
/**
* Test unsyncEntityWithItem with an event that does not modify the item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::unsyncEntityWithItem
*/
public function testUnsyncEntityWithItemNotModifiedByEvent() {
$this->itemFinder->expects($this->never())
->method('getItem');
$this->expectUpdateItemEventDispatched(FALSE, 'unsync_item');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->unsyncEntityWithItem($this->entityA, $this->itemA);
}
/**
* Test createSyncedItem.
*
* @todo Find a way of testing this - currently it would need the complex
* business of mocking the typed data manager to allow the Synchronizer
* to manufacture a stubbed item, which seems overkill here.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::createSyncedItem
*/
public function todoTestCreateSyncedItem() {
$this->expectUpdateItemEventDispatched($this->itemA, 'sync_to_item_from_entity');
$this->expectItemCreated($this->itemA);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->createSyncedItem($this->entityA, NULL, 'xero_contact');
}
/**
* Test createSyncedItem with a supplied item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::createSyncedItem
*/
public function testCreateSyncedItemWithItemSupplied() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemCreatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->createSyncedItem($this->entityA, $this->itemA);
}
/**
* Test createSyncedItem with a supplied item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::createSyncedItem
*/
public function testCreateSyncedItemUnsuccessful() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemCreationOnXeroFailed($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->expectException(SyncFailureException::class);
$this->synchronizer->createSyncedItem($this->entityA, $this->itemA);
}
/**
* Test update necessity with an item that needs updating.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedItemNeeded() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA);
}
/**
* Test update necessity with an item that does not need updating.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedItemNotNeeded() {
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA);
}
/**
* Test updating a synced stub without previous state that needs updating.
*
* Unless a previous entity state is supplied, it doesn't matter that the
* item is synced or a stub.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedStubNeeded() {
$this->itemA->stub = TRUE;
$this->itemA->synced = TRUE;
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA);
}
/**
* Test updating when a previous state is supplied but not a synced stub.
*
* Unless the item is a synced stub, it doesn't matter that a previous state
* is supplied.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedNeededWithIrrelevantPreviousState() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA, FALSE, $this->entityB);
}
/**
* Test updating a synced stub when a previous state is supplied.
*
* And the previous state did not modify the item.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedNeededWithRelevantPassivePreviousState() {
$this->itemA->stub = TRUE;
$this->itemA->synced = TRUE;
$previousStateEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity', $previousStateEvent);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA, FALSE, $this->entityB);
}
/**
* Test updating a synced stub when a previous state is supplied.
*
* And the previous state did modify the item, but the current state modifies
* it further.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedNeededWithRelevantActivePreviousState() {
$this->itemA->stub = TRUE;
$this->itemA->synced = TRUE;
$previousStateEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->expectUpdateItemEventDispatched($this->itemA, 'sync_to_item_from_entity', $previousStateEvent);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdatedOnXero($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA, FALSE, $this->entityB);
}
/**
* Test updating a synced stub when a previous state is supplied.
*
* And the previous state did modify the item, and the current state does not
* modify it further.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedNotNeededWithRelevantActivePreviousState() {
$this->itemA->stub = TRUE;
$this->itemA->synced = TRUE;
$previousStateEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity', $previousStateEvent);
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA, FALSE, $this->entityB);
}
/**
* Test update necessity with an item that does not need updating.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::updateSyncedItemIfNecessary
*/
public function testUpdateSyncedItemFailed() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemUpdateOnXeroFailed($this->itemB);
$this->expectNoEntitySave($this->entityA);
$this->expectException(SyncFailureException::class);
$this->synchronizer->updateSyncedItemIfNecessary($this->entityA, $this->itemA);
}
/**
* Test isItemUpdateNeeded with a simple case of needing update.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::isItemUpdateNeeded
*/
public function testIsItemUpdateNeededTrue() {
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$result = $this->synchronizer->isItemUpdateNeeded($this->entityA, $this->itemA);
$this->assertTrue($result);
}
/**
* Test isItemUpdateNeeded with a simple case of not needing update.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::isItemUpdateNeeded
*/
public function testIsItemUpdateNeededFalse() {
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$result = $this->synchronizer->isItemUpdateNeeded($this->entityA, $this->itemA);
$this->assertFalse($result);
}
/**
* Test isItemUpdateNeeded when the result is determined by a previous state.
*
* @covers \Drupal\xero_sync\XeroSyncSynchronizer::isItemUpdateNeeded
*/
public function testIsItemUpdateNeededBaseOnPreviousState() {
$this->itemA->stub = TRUE;
$this->itemA->synced = TRUE;
$previousStateEvent = $this->createMock(XeroSyncPropertiesEvent::class);
$this->expectUpdateItemEventDispatched($this->itemB, 'sync_to_item_from_entity', $previousStateEvent);
$this->expectUpdateItemEventDispatched(FALSE, 'sync_to_item_from_entity');
$this->expectItemNotUpdatedOnXero();
$this->expectNoEntitySave($this->entityA);
$this->expectNoEntitySave($this->entityB);
$result = $this->synchronizer->isItemUpdateNeeded($this->entityA, $this->itemA, $this->entityB);
// The current entity state did not modify the previous item state, so no
// update needed.
$this->assertFalse($result);
}
}
