xero_sync-8.x-1.x-dev/tests/src/Unit/QueueJobTest.php
tests/src/Unit/QueueJobTest.php
<?php
namespace Drupal\Tests\xero_sync\Unit;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\xero\TypedData\XeroComplexItemInterface;
use Drupal\xero_sync\XeroSyncEntityHandlerInterface;
use Drupal\xero_sync\XeroSyncSynchronizer;
use Drupal\xero_sync\XeroSyncQueueJobTrait;
use Psr\Log\LoggerInterface;
use Drupal\xero_sync\Exception\SyncFailureException;
use Symfony\Component\Serializer\SerializerInterface;
/**
* @coversDefaultClass \Drupal\xero_sync\XeroSyncEntityHandler
* @group xero_sync
*/
class QueueJobTest extends UnitTestCase {
use XeroSyncQueueJobTrait;
/**
* The Xero Sync Synchronizer.
*
* @var \Drupal\xero_sync\XeroSyncSynchronizer
*/
protected $synchronizer;
/**
* The logger.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\SerializerInterface
*/
protected $serializer;
/**
* The entity type object.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* The id of the queue being tested.
*
* @var string
*/
protected $queueId;
/**
* A Xero item.
*
* @var \Drupal\xero\TypedData\XeroComplexItemInterface
*/
protected $item;
/**
* An entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
$this->synchronizer = $this->createMock(XeroSyncSynchronizer::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->item = $this->createMock(XeroComplexItemInterface::class);
$this->entity = $this->createMock(EntityInterface::class);
$this->storage = $this->createMock(EntityStorageInterface::class);
$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
$this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$this->entityType = $this->createMock(EntityTypeInterface::class);
$this->entityHandler = $this->createMock(XeroSyncEntityHandlerInterface::class);
}
/**
* Test the doProcessItem method from XeroSyncQueueJobTrait.
*
* @param string $queueId
* The id of the queue being tested.
* @param string $synchronizerCall
* The synchronizer method that is expected to be called.
* @param array $data
* The test data to provide to doProcessItem.
* @param string|false $exception
* The exception to expect to be thrown, or FALSE if none expected.
* @param bool $hasItem
* Whether the synchronizer call should expect an item to be passed.
* @param array $flagsPassedOn
* The create & update flags expected to be passed to the synchronizer call.
*
* @dataProvider doProcessItemProvider
*/
public function testDoProcessItem($queueId, $synchronizerCall, array $data, $exception, $hasItem, array $flagsPassedOn) {
$this->queueId = $queueId;
// @todo When #2570593 lands, expand test to test for (a) no bundle in data,
// (b) invalid bundle, (c) no bundle class, (d) bundle class.
$this->entityTypeBundleInfo->expects($this->any())
->method('getBundleInfo')
->with('valid')
->willReturn(NULL);
if (isset($data['entity_type'])) {
if ($data['entity_type'] !== 'valid') {
if (isset($data['entity'])) {
$this->entityTypeManager->expects($this->once())
->method('getDefinition', TRUE)
->with('invalid')
->willThrowException(new PluginNotFoundException('invalid'));
}
else {
$this->entityTypeManager->expects($this->once())
->method('getStorage')
->with('invalid')
->willThrowException(new InvalidPluginDefinitionException('invalid'));
}
}
else {
// Has set and valid entity type.
$this->entity->expects($this->any())
->method('getEntityTypeId')
->willReturn('valid');
$entityType = $this->createMock(EntityTypeInterface::class);
$this->entity->expects($this->any())
->method('getEntityType')
->willReturn($entityType);
// Setup doProcessXeroSyncUnsync.
$query = $this->createMock(QueryInterface::class);
$query->expects($this->any())
->method('accessCheck')
->willReturn($query);
$query->expects($this->any())
->method('condition');
$query->expects($this->any())
->method('execute')
->willReturn([]);
$this->storage->expects($this->any())
->method('getQuery')
->willReturn($query);
$this->entityHandler->expects($this->any())
->method('getFieldName')
->willReturn('xero_sync');
$this->entityTypeManager->expects($this->any())
->method('getStorage')
->with('valid')
->willReturn($this->storage);
if (isset($data['entity'])) {
$this->entityType->expects($this->once())
->method('getClass')
->willReturn('fqcn');
$this->entityTypeManager->expects($this->once())
->method('getDefinition')
->with('valid')
->willReturn($this->entityType);
$this->serializer->expects($this->once())
->method('deserialize')
->with($data['entity'], 'fqcn', 'json')
->willReturn($this->entity);
}
else {
// Entity needs loading, not serialized.
if (isset($data['entity_id'])) {
if ($data['entity_id'] === 'valid') {
$this->storage->expects($this->once())
->method('load')
->with('valid')
->willReturn($this->entity);
}
else {
$this->storage->expects($this->once())
->method('load')
->with('invalid')
->willReturn(NULL);
}
}
}
}
}
$item = NULL;
if ($hasItem) {
$item = $this->item;
}
if ($exception) {
$this->expectException($exception);
}
if ($exception === \InvalidArgumentException::class) {
$this->synchronizer->expects($this->never())
->method($synchronizerCall);
}
else {
if ($synchronizerCall === 'syncEntityWithItem') {
if ($exception) {
$this->synchronizer->expects($this->once())
->method($synchronizerCall)
->with($this->entity, $item, $flagsPassedOn['create'], $flagsPassedOn['update'], NULL)
->willThrowException(new $exception());
}
else {
$this->synchronizer->expects($this->once())
->method($synchronizerCall)
->with($this->entity, $item, $flagsPassedOn['create'], $flagsPassedOn['update'], NULL);
}
}
else {
if ($exception) {
$this->synchronizer->expects($this->once())
->method($synchronizerCall)
->with($this->entity, $item)
->willThrowException(new $exception());
}
else {
$this->synchronizer->expects($this->once())
->method($synchronizerCall)
->with($this->entity, $item);
}
}
}
$this->doProcessItem($data);
}
/**
* Data provider for testDoProcessItem.
*/
public function doProcessItemProvider() {
$queues = [
[
'id' => 'xero_sync_sync',
'synchronizer_call' => 'syncEntityWithItem',
],
[
'id' => 'xero_sync_unsync',
'synchronizer_call' => 'unsyncEntityWithItem',
],
];
$entity_args = [
[
'data' => [
'entity_type' => 'valid',
'entity_id' => 'valid',
],
'exception' => FALSE,
],
[
'data' => [
'entity_type' => 'invalid',
'entity_id' => 'valid',
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity_type' => 'valid',
'entity_id' => 'invalid',
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity_type' => 'invalid',
'entity_id' => 'invalid',
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity_id' => 'invalid',
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity_type' => 'valid',
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity_type' => 'valid',
'entity_id' => 'valid',
'entity' => ['serialized entity'],
],
'exception' => FALSE,
],
[
'data' => [
'entity_type' => 'valid',
'entity_id' => 'valid',
'entity_bundle' => 'valid',
'entity' => ['serialized entity'],
],
'exception' => FALSE,
],
[
'data' => [
'entity_type' => 'valid',
'entity' => ['serialized entity'],
],
'exception' => FALSE,
],
[
'data' => [
'entity_type' => 'invalid',
'entity' => ['serialized entity'],
],
'exception' => \InvalidArgumentException::class,
],
[
'data' => [
'entity' => ['serialized entity'],
],
'exception' => \InvalidArgumentException::class,
],
];
$item_args = [
[
'data' => [
'item_type' => 'some item',
'item_guid' => 'some guid',
],
'has_item' => TRUE,
],
[
'data' => [
'item_type' => 'some item',
],
'has_item' => FALSE,
],
[
'data' => [
'item_type' => 'some item',
],
'has_item' => FALSE,
],
[
'data' => [],
'has_item' => FALSE,
],
];
// 'data' is what is set in the job data array for a create/update flag.
// 'flag_passed_on' is whether the flag is passed to the synchronizer.
$flag_versions =
[
[
'data' => TRUE,
'flag_passed_on' => TRUE,
],
[
'data' => FALSE,
'flag_passed_on' => FALSE,
],
[
'data' => NULL,
'flag_passed_on' => TRUE,
],
[
'data' => 'missing',
'flag_passed_on' => TRUE,
],
];
$flag_args = [];
foreach ($flag_versions as $create_version) {
foreach ($flag_versions as $update_version) {
$flags_supplied = [];
if ($create_version['data'] !== 'missing') {
$flags_supplied['create'] = $create_version['data'];
}
if ($update_version['data'] !== 'missing') {
$flags_supplied['update'] = $update_version['data'];
}
$flags_passed_on = [
'create' => $create_version['flag_passed_on'],
'update' => $update_version['flag_passed_on'],
];
$flag_args[] = [
'data' => $flags_supplied,
'flags_passed_on' => $flags_passed_on,
];
}
}
$synchronizer_exceptions = [
SyncFailureException::class,
FALSE,
];
$tests = [];
foreach ($queues as $queue) {
foreach ($entity_args as $entity_arg) {
foreach ($item_args as $item_arg) {
foreach ($flag_args as $flag_arg) {
foreach ($synchronizer_exceptions as $synchronizer_exception) {
$data = [] + $entity_arg['data'] + $item_arg['data'] + $flag_arg['data'];
$exception = $entity_arg['exception'] ? $entity_arg['exception'] : $synchronizer_exception;
$tests[] = [
'queue_id' => $queue['id'],
'synchronizer_call' => $queue['synchronizer_call'],
'data' => $data,
'exception' => $exception,
'has_item' => $item_arg['has_item'],
'flags_passed_on' => $flag_arg['flags_passed_on'],
];
}
}
}
}
}
return $tests;
}
/**
* {@inheritDoc}
*/
protected function buildXeroItemWithId($itemType, $itemId) {
// Override buildXeroItemWithId from XeroSyncUtilitiesTrait, so
// that we don't need to mock the Typed Data Manager.
return $this->item;
}
/**
* Identify which queue we are simulating being within.
*
* @return string
* The queue plugin id.
*/
protected function getPluginId() {
return $this->queueId;
}
}
