You are here

EntityShareCronServiceTest.php in Entity Share Cron 8.2

Same filename and directory in other branches
  1. 8 tests/src/Unit/EntityShareCronServiceTest.php

File

tests/src/Unit/EntityShareCronServiceTest.php
View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\entity_share_cron\Unit;

use Drupal\Tests\UnitTestCase;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\entity_share_client\Entity\Remote;
use Drupal\entity_share_cron\EntityShareCronService;
use Drupal\entity_share_cron\EntityShareCronServiceInterface;

/**
 * @coversDefaultClass \Drupal\entity_share_cron\EntityShareCronService
 * @group entity_share_cron
 */
class EntityShareCronServiceTest extends UnitTestCase {
  const REMOTE_ID = 'test_remote';
  const CHANNEL_ID = 'test_channel';
  const CHANNEL_URL = 'https://example.com';

  /**
   * The service under test.
   *
   * @var \Drupal\entity_share_cron\EntityShareCronServiceInterface
   */
  protected $service;

  /**
   * Remote settings.
   *
   * @var array
   */
  protected $remotesConfig = [
    self::REMOTE_ID => [
      'enabled' => TRUE,
      'channels' => [
        self::CHANNEL_ID => [
          'enabled' => TRUE,
          'url' => self::CHANNEL_URL,
          'url_uuid' => self::CHANNEL_URL,
          'operations' => [
            'create' => TRUE,
            'update' => TRUE,
          ],
        ],
      ],
    ],
  ];

  /**
   * Existing nodes.
   *
   * @var array
   */
  protected $nodes = [];

  /**
   * The entities data received by the HTTP client on each request.
   *
   * @var array
   */
  protected $responseData = [
    [
      [
        'type' => 'node--article',
        'id' => 'node1',
      ],
    ],
    [
      [
        'type' => 'node--article',
        'id' => 'node2',
      ],
    ],
    [
      [
        'type' => 'node--article',
        'id' => 'node3',
      ],
      [
        'type' => 'node--article',
        'id' => 'node4',
      ],
    ],
  ];

  /**
   * The number of pages that the response should have.
   *
   * @var int
   */
  protected $responsePages;

  /**
   * Mocked queue.
   *
   * @var \Drupal\Core\Queue\QueueInterface
   */
  protected $queue;

  /**
   * The number of requests made during the test to customize returned bodies.
   *
   * @var int
   */
  protected $requestCount;

  /**
   * Tests the sync() method running through all pages.
   */
  public function testSynchronizationAllPages() {
    $this->responsePages = 3;

    // No page can be enqueued.
    $this->queue
      ->expects($this
      ->never())
      ->method('createItem');
    $info = [
      'url' => self::CHANNEL_URL,
    ];
    $imported = $this->service
      ->sync(self::REMOTE_ID, self::CHANNEL_ID, $info);
    $this
      ->assertCount(4, $imported);
    $this
      ->assertContains($this->responseData[0][0], $imported);
    $this
      ->assertContains($this->responseData[1][0], $imported);
    $this
      ->assertContains($this->responseData[2][0], $imported);
    $this
      ->assertContains($this->responseData[2][1], $imported);
  }

  /**
   * Tests the sync() method with page limit.
   */
  public function testSynchronizationWithPageLimit() {
    $this->responsePages = 3;

    // Sets expectation to get third page enqueued.
    $expected_item = [
      'remote_id' => self::REMOTE_ID,
      'channel_id' => self::CHANNEL_ID,
      'channel_info' => [
        'url' => 'page_2',
      ],
    ];
    $this->queue
      ->expects($this
      ->once())
      ->method('createItem')
      ->with($this
      ->equalTo($expected_item));
    $info = [
      'url' => self::CHANNEL_URL,
    ];
    $imported = $this->service
      ->sync(self::REMOTE_ID, self::CHANNEL_ID, $info, 2);
    $this
      ->assertCount(2, $imported);
    $this
      ->assertContains($this->responseData[0][0], $imported);
    $this
      ->assertContains($this->responseData[1][0], $imported);
  }

  /**
   * Tests synchronization when only entity creation is allowed.
   */
  public function testSynchronizationCreateOnly() {

    // Enables only entity creation.
    $this->remotesConfig[self::REMOTE_ID]['channels'][self::CHANNEL_ID]['operations'] = [
      'create' => TRUE,
      'update' => FALSE,
    ];

    // Creates some nodes.
    $this->nodes[] = $this
      ->createEntity('node1');
    $this->nodes[] = $this
      ->createEntity('node3');
    $info = [
      'url' => self::CHANNEL_URL,
    ];
    $imported = $this->service
      ->sync(self::REMOTE_ID, self::CHANNEL_ID, $info);
    $this
      ->assertCount(2, $imported);
    $this
      ->assertContains($this->responseData[1][0], $imported);
    $this
      ->assertContains($this->responseData[2][1], $imported);
  }

  /**
   * Tests synchronization when only entity update is allowed.
   */
  public function testSynchronizationUpdateOnly() {

    // Enables only entity update.
    $this->remotesConfig[self::REMOTE_ID]['channels'][self::CHANNEL_ID]['operations'] = [
      'create' => FALSE,
      'update' => TRUE,
    ];

    // Creates some nodes.
    $this->nodes[] = $this
      ->createEntity('node1');
    $this->nodes[] = $this
      ->createEntity('node3');
    $info = [
      'url' => self::CHANNEL_URL,
    ];
    $imported = $this->service
      ->sync(self::REMOTE_ID, self::CHANNEL_ID, $info);
    $this
      ->assertCount(2, $imported);
    $this
      ->assertContains($this->responseData[0][0], $imported);
    $this
      ->assertContains($this->responseData[2][0], $imported);
  }

  /**
   * Returns the body of a request for a page.
   *
   * @return \Psr\Http\Message\StreamInterface
   *   The mocked body.
   */
  public function getResponseBody() {
    $this->responsePages -= 1;
    $json = [
      'data' => $this->responseData[$this->requestCount++],
      'links' => [
        'next' => [
          'href' => $this->responsePages > 0 ? 'page_' . $this->requestCount : FALSE,
        ],
      ],
    ];
    $contents = json_encode($json);
    $stream = $this
      ->createMock('Psr\\Http\\Message\\StreamInterface');
    $stream
      ->expects($this
      ->any())
      ->method('getContents')
      ->will($this
      ->returnValue($contents));
    return $stream;
  }

  /**
   * Method used to mock node storage. Loads a node by its UUID.
   *
   * @param array $properties
   *   Array of properties to filter the nodes.
   */
  public function loadNodeByProperties(array $properties) {
    $uuid = $properties['uuid'];
    $filtered_nodes = [];
    foreach ($this->nodes as $node) {
      if (is_array($uuid) && in_array($node
        ->uuid(), $uuid)) {
        $filtered_nodes[] = $node;
      }
      elseif ($node
        ->uuid() == $uuid) {
        $filtered_nodes[] = $node;
      }
    }
    return $filtered_nodes;
  }

  /**
   * Returns the remotes configuration.
   *
   * @return array
   *   Remotes configuration.
   */
  public function getRemotesConfig() {
    return $this->remotesConfig;
  }

  /**
   * Creates a mocked entity with the specified UUID.
   *
   * @param string $uuid
   *   The UUID of the entity to create.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   *   The mocked entity.
   */
  protected function createEntity($uuid) {
    $entity = $this
      ->createMock('Drupal\\Core\\Entity\\EntityInterface');
    $entity
      ->expects($this
      ->any())
      ->method('uuid')
      ->will($this
      ->returnValue($uuid));
    return $entity;
  }

  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    $this->responsePages = count($this->responseData);
    $this->requestCount = 0;

    // Mocks a configuration factory for the module settings.
    $settings = $this
      ->getMockBuilder('Drupal\\Core\\Config\\ImmutableConfig')
      ->disableOriginalConstructor()
      ->getMock();
    $settings
      ->expects($this
      ->any())
      ->method('get')
      ->with('remotes')
      ->will($this
      ->returnCallback([
      $this,
      'getRemotesConfig',
    ]));
    $config_factory = $this
      ->createMock('Drupal\\Core\\Config\\ConfigFactoryInterface');
    $config_factory
      ->expects($this
      ->any())
      ->method('get')
      ->with('entity_share_cron.settings')
      ->will($this
      ->returnValue($settings));

    // Mocks storages to load remotes and nodes.
    $remote = new Remote([
      'id' => self::REMOTE_ID,
    ], 'remote');
    $remote_storage = $this
      ->createMock('Drupal\\Core\\Config\\Entity\\ConfigEntityStorageInterface');
    $remote_storage
      ->expects($this
      ->any())
      ->method('load')
      ->with($this
      ->equalTo(self::REMOTE_ID))
      ->will($this
      ->returnValue($remote));
    $node_storage = $this
      ->createMock('Drupal\\Core\\Entity\\ContentEntityStorageInterface');
    $node_storage
      ->expects($this
      ->any())
      ->method('loadByProperties')
      ->will($this
      ->returnCallback([
      $this,
      'loadNodeByProperties',
    ]));

    // Mocks node entity type definition.
    $node_definition = $this
      ->createMock('Drupal\\Core\\Entity\\EntityTypeInterface');
    $node_definition
      ->expects($this
      ->any())
      ->method('getKey')
      ->with($this
      ->equalTo('uuid'))
      ->will($this
      ->returnValue('uuid'));

    // Mocks entity type manager and repository and add them to the container.
    $entity_type_manager = $this
      ->createMock('Drupal\\Core\\Entity\\EntityTypeManagerInterface');
    $entity_type_manager
      ->expects($this
      ->any())
      ->method('getStorage')
      ->will($this
      ->returnValueMap([
      [
        'node',
        $node_storage,
      ],
      [
        'remote',
        $remote_storage,
      ],
    ]));
    $entity_type_manager
      ->expects($this
      ->any())
      ->method('getDefinition')
      ->with($this
      ->equalTo('node'))
      ->will($this
      ->returnValue($node_definition));
    $entity_type_repository = $this
      ->createMock('Drupal\\Core\\Entity\\EntityTypeRepositoryInterface');
    $entity_type_repository
      ->expects($this
      ->any())
      ->method('getEntityTypeFromClass')
      ->will($this
      ->returnValue('remote'));
    $container = new ContainerBuilder();
    $container
      ->set('entity_type.manager', $entity_type_manager);
    $container
      ->set('entity_type.repository', $entity_type_repository);
    \Drupal::setContainer($container);

    // Mocks a remote manager.
    $response = $this
      ->createMock('Psr\\Http\\Message\\ResponseInterface');
    $response
      ->expects($this
      ->any())
      ->method('getBody')
      ->will($this
      ->returnCallback([
      $this,
      'getResponseBody',
    ]));
    $http_client = $this
      ->createMock('GuzzleHttp\\Client');
    $http_client
      ->expects($this
      ->atLeastOnce())
      ->method('__call')
      ->with($this
      ->equalTo('get'))
      ->will($this
      ->returnValue($response));
    $remote_manager = $this
      ->createMock('Drupal\\entity_share_client\\Service\\RemoteManagerInterface');
    $remote_manager
      ->expects($this
      ->any())
      ->method('prepareJsonApiClient')
      ->will($this
      ->returnValue($http_client));

    // Mocks a JSON API Helper.
    $jsonapi_helper = $this
      ->createMock('Drupal\\entity_share_client\\Service\\JsonapiHelperInterface');
    $jsonapi_helper
      ->expects($this
      ->once())
      ->method('importEntityListData')
      ->will($this
      ->returnArgument(0));

    // Mocks a queue factory.
    $this->queue = $this
      ->createMock('Drupal\\Core\\Queue\\QueueInterface');
    $queue_factory = $this
      ->getMockBuilder('Drupal\\Core\\Queue\\QueueFactory')
      ->disableOriginalConstructor()
      ->setMethods([
      'get',
    ])
      ->getMock();
    $queue_factory
      ->expects($this
      ->any())
      ->method('get')
      ->with($this
      ->equalTo(EntityShareCronServiceInterface::PENDING_QUEUE_NAME))
      ->will($this
      ->returnValue($this->queue));

    // Instantiates a service to test.
    $this->service = new EntityShareCronService($config_factory, $remote_manager, $jsonapi_helper, $queue_factory, $entity_type_manager);
  }

}

Classes

Namesort descending Description
EntityShareCronServiceTest @coversDefaultClass \Drupal\entity_share_cron\EntityShareCronService @group entity_share_cron