xero_sync-8.x-1.x-dev/tests/src/Unit/EntityHandlerTest.php

tests/src/Unit/EntityHandlerTest.php
<?php

namespace Drupal\Tests\xero_sync\Unit;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\Plugin\DataType\StringData;
use Drupal\user\UserInterface;
use Drupal\Tests\xero\Unit\Plugin\DataType\TestBase;
use Drupal\xero_sync\Event\XeroSyncAppropriateEvent;
use Drupal\xero_sync\XeroSyncEntityHandler;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\xero_sync\XeroSyncJobManagerInterface;
use Drupal\xero_sync\XeroSyncSynchronizerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @coversDefaultClass \Drupal\xero_sync\XeroSyncEntityHandler
 * @group xero_sync
 */
class EntityHandlerTest extends TestBase {

  const XERO_TYPE = 'xero_contact';
  const XERO_TYPE_CLASS = '\Drupal\xero\Plugin\DataType\Contact';
  const XERO_DEFINITION_CLASS = '\Drupal\xero\TypedData\Definition\ContactDefinition';

  /**
   * The job manager.
   *
   * @var \Drupal\xero_sync\XeroSyncJobManager|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $jobManager;

  /**
   * The Xero Sync entity handler.
   *
   * @var \Drupal\xero_sync\XeroSyncEntityHandler
   */
  protected $entityHandler;

  /**
   * The Xero Sync synchronizer.
   *
   * @var \Drupal\xero_sync\XeroSyncSynchronizer
   */
  protected $synchronizer;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The event dispatcher.
   *
   * @var \Drupal\xero_sync\Event\XeroSyncPropertiesEvent
   */
  protected $event;

  /**
   * Whether or not sync is appropriate for the entity in contexts.
   *
   * @var array
   */
  protected $appropriatenessFlags = [
    'create' => TRUE,
    'update' => TRUE,
  ];

  /**
   * The entity to sync.
   *
   * @var \Drupal\Core\Entity\EntityInterface
   */
  protected $entity;

  /**
   * {@inheritdoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->jobManager = $this->createMock(XeroSyncJobManagerInterface::class);
    $this->synchronizer = $this->createMock(XeroSyncSynchronizerInterface::class);
    $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
    $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
    $this->entityHandler = new XeroSyncEntityHandler($this->jobManager, $this->synchronizer, $this->typedDataManager, $this->entityTypeManager, $this->eventDispatcher);

    $this->entity = $this->createMock(UserInterface::class);
    $this->entity->expects($this->any())
      ->method('getEntityTypeId')
      ->willReturn('user');

    $this->event = $this->createMock(XeroSyncAppropriateEvent::class);
    $this->eventDispatcher->expects($this->any())
      ->method('dispatch')
      ->with($this->isInstanceOf(XeroSyncAppropriateEvent::class), 'xero_sync.sync_is_appropriate.user')
      ->willReturn($this->event);

    // Because XeroEntityUtilitiesTrait::buildXeroItemWithId calls
    // the item's static getXeroProperty method and then sets a property,
    // we can't use a mock item and so must elaborately prepare a real item.
    $type_class = self::XERO_TYPE_CLASS;
    $item = new $type_class($this->dataDefinition, self::XERO_TYPE);
    $this->typedDataManager->expects($this->any())
      ->method('createDataDefinition')
      ->willReturn($this->createMock(DataDefinitionInterface::class));
    $this->typedDataManager->expects($this->any())
      ->method('create')
      ->willReturn($item);
    $string_def = DataDefinition::create('string');
    $defaultProperty = new StringData($string_def);
    $this->typedDataManager->expects($this->any())
      ->method('getPropertyInstance')
      ->willReturn($defaultProperty);

  }

  /**
   * Test the EntityHandler's getAppropriatenessFlags method.
   *
   * @param array $appropriatenessFlags
   *   Whether syncing is appropriate for the entity in various contexts.
   *
   * @dataProvider appropriatenessProvider
   *
   * @covers \Drupal\xero_sync\XeroSyncEntityHandler::getAppropriatenessFlags
   */
  public function testGetAppropriatenessFlags(array $appropriatenessFlags) {
    $this->event->expects($this->any())
      ->method('getFlags')
      ->willReturn($appropriatenessFlags);
    $actual = $this->entityHandler->getAppropriatenessFlags($this->entity);
    $this->assertSame($appropriatenessFlags, $actual);
  }

  /**
   * Data provider for tests that need to test the appropriateness flags.
   */
  public function appropriatenessProvider() {
    return [
      [
        [
          'create' => TRUE,
          'update' => TRUE,
        ],
      ],
      [
        [
          'create' => TRUE,
          'update' => FALSE,
        ],
      ],
      [
        [
          'create' => FALSE,
          'update' => TRUE,
        ],
      ],
      [
        [
          'create' => FALSE,
          'update' => FALSE,
        ],
      ],
    ];

  }

  /**
   * Test the EntityHandler's unsyncIfAppropriate method.
   *
   * @param bool $isUpdateAppropriate
   *   Whether or not the entity should be synced.
   * @param bool $isAlreadySynced
   *   Whether or not the entity has already been synced.
   * @param bool $expectUnsync
   *   Whether or not to expect the entity to be unsynced.
   *
   * @dataProvider unsyncIfAppropriateProvider
   *
   * @covers \Drupal\xero_sync\XeroSyncEntityHandler::unsyncIfAppropriate
   */
  public function testUnsyncIfAppropriate($isUpdateAppropriate, $isAlreadySynced, $expectUnsync) {
    $flags = [
      'create' => FALSE,
      'update' => $isUpdateAppropriate,
    ];
    $this->event->expects($this->any())
      ->method('getFlags')
      ->willReturn($flags);
    $xeroId = $isAlreadySynced ? 'A' : NULL;
    $xeroType = $this->randomMachineName();
    $fieldName = $this->entityHandler->getFieldName($this->entity->getEntityTypeId());
    $this->entity->expects($this->any())
      ->method('hasField')
      ->with($fieldName)
      ->willReturn(TRUE);
    $this->entity->expects($this->any())
      ->method('get')
      ->with($fieldName)
      ->willReturn($this->getMockXeroField($xeroType, $xeroId));
    if ($expectUnsync) {
      $this->jobManager->expects($this->once())
        ->method('queueEntityUnsync')
        ->with($this->entity, NULL, NULL, FALSE);
    }
    else {
      $this->jobManager->expects($this->never())
        ->method('queueEntityUnsync');
    }

    $this->entityHandler->unsyncIfAppropriate($this->entity);
  }

  /**
   * Data provider for testUnsyncIfAppropriate.
   */
  public function unsyncIfAppropriateProvider() {
    return [
      [
        'isUpdateAppropriate' => TRUE,
        'isAlreadySynced' => TRUE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => TRUE,
        'isAlreadySynced' => FALSE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => FALSE,
        'isAlreadySynced' => TRUE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => FALSE,
        'isAlreadySynced' => FALSE,
        'expectUnsync' => FALSE,
      ],
    ];
  }

  /**
   * Test the EntityHandler's handleInsert method.
   *
   * @param array $appropriatenessFlags
   *   Whether syncing is appropriate for the entity.
   *
   * @dataProvider appropriatenessProvider
   *
   * @covers \Drupal\xero_sync\XeroSyncEntityHandler::handleInsert
   */
  public function testHandleInsert(array $appropriatenessFlags) {
    $this->event->expects($this->any())
      ->method('getFlags')
      ->willReturn($appropriatenessFlags);
    if ($appropriatenessFlags['create'] || $appropriatenessFlags['update']) {
      $this->jobManager->expects($this->once())
        ->method('queueEntitySync')
        ->with($this->entity);
    }
    else {
      $this->jobManager->expects($this->never())
        ->method('queueEntitySync');
    }

    $this->entityHandler->handleInsert($this->entity);
  }

  /**
   * Test the EntityHandler's handleDelete method.
   *
   * @param bool $isUpdateAppropriate
   *   Whether or not the entity should be synced.
   * @param bool $isAlreadySynced
   *   Whether or not the entity has already been synced.
   * @param bool $expectUnsync
   *   Whether or not to expect the entity to be unsynced.
   *
   * @dataProvider handleDeleteProvider
   *
   * @covers \Drupal\xero_sync\XeroSyncEntityHandler::handleDelete
   */
  public function testHandleDelete($isUpdateAppropriate, $isAlreadySynced, $expectUnsync) {
    $flags = [
      'create' => FALSE,
      'update' => $isUpdateAppropriate,
    ];
    $this->event->expects($this->any())
      ->method('getFlags')
      ->willReturn($flags);
    $xeroId = $isAlreadySynced ? 'A' : NULL;
    $xeroType = $this->randomMachineName();
    $fieldName = $this->entityHandler->getFieldName($this->entity->getEntityTypeId());
    $this->entity->expects($this->any())
      ->method('hasField')
      ->with($fieldName)
      ->willReturn(TRUE);
    $this->entity->expects($this->any())
      ->method('get')
      ->with($fieldName)
      ->willReturn($this->getMockXeroField($xeroType, $xeroId));
    if ($expectUnsync) {
      $this->jobManager->expects($this->once())
        ->method('queueEntityUnsync')
        ->with($this->entity, NULL, NULL, TRUE);
    }
    else {
      $this->jobManager->expects($this->never())
        ->method('queueEntityUnsync');
    }

    $this->entityHandler->handleDelete($this->entity);
  }

  /**
   * Data provider for testHandleDelete.
   */
  public function handleDeleteProvider() {
    return [
      [
        'isUpdateAppropriate' => TRUE,
        'isAlreadySynced' => TRUE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => TRUE,
        'isAlreadySynced' => FALSE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => FALSE,
        'isAlreadySynced' => TRUE,
        'expectUnsync' => TRUE,
      ],
      [
        'isUpdateAppropriate' => FALSE,
        'isAlreadySynced' => FALSE,
        'expectUnsync' => FALSE,
      ],
    ];
  }

  /**
   * Test how handleUpdate handles changes in a entity's stored Xero id.
   *
   * @param bool|null $hasOriginal
   *   Whether the entity has the 'original' property.
   * @param string $previousId
   *   The Xero id stored on the entity before this update.
   * @param string $currentId
   *   The Xero id stored on the entity which has just been updated.
   * @param bool|null $isItemUpdateNeeded
   *   If bool, then a call to synchronizer::isItemUpdateNeeded should return
   *   this value, if NULL then there should be no such call.
   * @param array $expectedCalls
   *   The methods on the job manager that should be called.
   * @param bool $isUpdateAppropriate
   *   Whether or not syncing is appropriate for the entity.
   *
   * @dataProvider handleUpdateProvider
   *
   * @covers \Drupal\xero_sync\XeroSyncEntityHandler::handleUpdate
   */
  public function testHandleUpdate($hasOriginal, $previousId, $currentId, $isItemUpdateNeeded, array $expectedCalls, $isUpdateAppropriate) {
    $flags = [
      'create' => FALSE,
      'update' => $isUpdateAppropriate,
    ];
    $this->event->expects($this->any())
      ->method('getFlags')
      ->willReturn($flags);

    $xeroType = $this->randomMachineName();

    $original = $this->createMock(UserInterface::class);
    $fieldName = $this->entityHandler->getFieldName($original->getEntityTypeId());

    $original->expects($this->any())
      ->method('get')
      ->with($fieldName)
      ->willReturn($this->getMockXeroField($xeroType, $previousId));

    $user = $this->entity;
    $user->expects($this->any())
      ->method('get')
      ->with($fieldName)
      ->willReturn($this->getMockXeroField($xeroType, $currentId));
    $user->expects($this->any())
      ->method('hasField')
      ->with($fieldName)
      ->willReturn(TRUE);

    if ($hasOriginal !== FALSE) {
      $user->original = $original;
    }
    if (is_null($hasOriginal)) {
      $user->original = NULL;
    }

    if (is_null($isItemUpdateNeeded)) {
      $this->synchronizer->expects($this->never())
        ->method('isItemUpdateNeeded');
    }
    else {
      $this->synchronizer->expects($this->once())
        ->method('isItemUpdateNeeded')
        ->willReturn($isItemUpdateNeeded);
    }

    foreach ($expectedCalls as $index => $call) {
      if ($call === 'queueEntityUnsync') {
        $this->jobManager->expects($this->once())
          ->method($call)
          ->with(
            $this->isInstanceOf(UserInterface::class),
            $this->equalTo($xeroType),
            $this->equalTo($previousId)
          );
      }
      elseif ($call === 'queueEntitySync') {
        $this->jobManager->expects($this->once())
          ->method($call)
          ->with($user);
      }
    }

    $this->entityHandler->handleUpdate($user);
  }

  /**
   * Get a mock Xero reference field object.
   *
   * @param string $xeroType
   *   The Xero type stored in the field.
   * @param string $xeroId
   *   The Xero guid stored in the field.
   *
   * @return \PHPUnit_Framework_MockObject_MockObject
   *   The mock field obect.
   */
  protected function getMockXeroField($xeroType, $xeroId) {
    $field = $this->createMock(FieldItemListInterface::class);
    if (empty($xeroId)) {
      $field->expects($this->any())
        ->method('isEmpty')
        ->willReturn(TRUE);
      $field->expects($this->never())
        ->method('first');
    }
    else {
      $field->expects($this->any())
        ->method('isEmpty')
        ->willReturn(FALSE);
      $field->expects($this->any())
        ->method('first')
        ->willReturn((object) [
          'type' => $xeroType,
          'guid' => $xeroId,
        ]);
    }
    return $field;
  }

  /**
   * Data provider for testHandleUpdate.
   */
  public function handleUpdateProvider() {
    $cases = [
      [
        'has original' => TRUE,
        'previous Xero id' => NULL,
        'current Xero id' => NULL,
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => TRUE,
        'previous Xero id' => 'A',
        'current Xero id' => NULL,
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntityUnsync'],
      ],
      [
        'has original' => TRUE,
        'previous Xero id' => 'A',
        'current Xero id' => 'B',
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => [
          'queueEntityUnsync',
          'queueEntitySync',
        ],
      ],
      [
        'has original' => TRUE,
        'previous Xero id' => NULL,
        'current Xero id' => 'B',
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => TRUE,
        'previous Xero id' => 'A',
        'current Xero id' => 'A',
        'isItemUpdateNeeded' => TRUE,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => TRUE,
        'previous Xero id' => 'A',
        'current Xero id' => 'A',
        'isItemUpdateNeeded' => FALSE,
        'expected job manager calls' => [],
      ],
      [
        'has original' => NULL,
        'previous Xero id' => NULL,
        'current Xero id' => NULL,
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => NULL,
        'previous Xero id' => NULL,
        'current Xero id' => 'A',
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => FALSE,
        'previous Xero id' => NULL,
        'current Xero id' => NULL,
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
      [
        'has original' => FALSE,
        'previous Xero id' => NULL,
        'current Xero id' => 'A',
        'isItemUpdateNeeded' => NULL,
        'expected job manager calls' => ['queueEntitySync'],
      ],
    ];

    $syncAppropriateCases = [];
    $syncInappropriateCases = [];
    foreach ($cases as $case) {
      $case['isUpdateAppropriate'] = TRUE;
      $syncAppropriateCases[] = $case;
      $case['isUpdateAppropriate'] = FALSE;
      $case['expected job manager calls'] = [];
      $syncInappropriateCases[] = $case;
    }

    return array_merge($syncAppropriateCases, $syncInappropriateCases);

  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc