contacts_events-8.x-1.x-dev/tests/src/Unit/RecalculateOrderItemsTest.php
tests/src/Unit/RecalculateOrderItemsTest.php
<?php
namespace Drupal\Tests\contacts_events\Unit;
use Drupal\advancedqueue\Job;
use Drupal\advancedqueue\JobResult;
use Drupal\commerce\PurchasableEntityInterface;
use Drupal\commerce_advancedqueue\CommerceOrderJob;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\contacts_events\Entity\SingleUsePurchasableEntityInterface;
use Drupal\contacts_events\Plugin\AdvancedQueue\JobType\RecalculateOrderItems;
use Drupal\contacts_events\PriceCalculator;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Transaction;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Tests the order item price recalculator job.
*
* @covers \Drupal\contacts_events\Plugin\AdvancedQueue\JobType\RecalculateOrderItems::doProcess
* @group contacts_events
*/
class RecalculateOrderItemsTest extends UnitTestCase {
/**
* Test the processing of a recalculate order item job.
*
* @param array $payload
* The payload for the job.
* @param array $order_items
* An array of order items, values an array containing:
* - 'bundle': The bundle of the order item; NULL or missing if it shouldn't
* be checked.
* - 'id': The ID of the order item; NULL or missing if it shouldn't be
* checked.
* - 'valid': Whether the bundle/id checks should succeed; NULL or missing
* if it shouldn't be attempted.
* - 'calc': Whether the calculation should succeed; NULL or missing if it
* shouldn't be attempted.
* - 'save': Whether saving should succeed; NULL or missing if it shouldn't
* be attempted.
* - 'state': Whether to set a state; NULL or missing if it shouldn't
* be set on the order item.
* @param \Drupal\advancedqueue\JobResult $expected_result
* The expected result.
* @param bool $order_needs_save
* Whether the order should be saved.
*
* @dataProvider dataDoProcess
*/
public function testDoProcess(array $payload, array $order_items, JobResult $expected_result, $order_needs_save) {
// Build our prophecies.
$order = $this->prophesize(OrderInterface::class);
$price_calculator = $this->prophesize(PriceCalculator::class);
// Set up our expectations.
$order_item_prophecies = [];
foreach ($order_items as $expected_item) {
$item = $this->prophesize(OrderItemInterface::class);
$method = $item->bundle();
if (isset($expected_item['bundle'])) {
$method
->shouldBeCalledTimes(1)
->willReturn($expected_item['bundle']);
}
else {
$method->shouldNotBeCalled();
}
$method = $item->id();
if (isset($expected_item['id'])) {
$method
->shouldBeCalledTimes(1)
->willReturn($expected_item['id']);
}
else {
$method->shouldNotBeCalled();
}
// If we are expecting valid pre-calc we need to predict state checks.
if (isset($expected_item['valid'])) {
$item->hasField('state')
->shouldBeCalledTimes(1)
->willReturn(TRUE);
// Only check 'state' if 'calc' is expected.
$method = $item->get('state');
if (isset($expected_item['state'])) {
$method
->shouldBeCalledTimes(1)
->willReturn((object) ['value' => $expected_item['state']]);
}
else {
$method
->shouldBeCalledTimes(1)
->willReturn((object) ['value' => NULL]);
}
}
$method = $price_calculator->calculatePrice($item->reveal());
if (isset($expected_item['calc'])) {
$method->shouldBeCalledTimes(1);
if (!$expected_item['calc']) {
$method->willThrow(new \Exception('Calc failed'));
}
}
else {
$method->shouldNotBeCalled();
}
$method = $item->save();
if (isset($expected_item['save'])) {
$method->shouldBeCalledTimes(1);
if (!$expected_item['save']) {
$method->willThrow(new EntityStorageException('Save failed'));
}
else {
if (!isset($expected_item['purchased_entity_type'])) {
$purchased_entity = NULL;
}
else {
$entity = $this->prophesize($expected_item['purchased_entity_type']);
if (!empty($expected_item['purchased_entity_save'])) {
$entity->save()
->shouldBeCalledTimes(1);
}
$purchased_entity = $entity->reveal();
}
$item->get(Argument::exact('purchased_entity'))
->shouldBeCalledTimes(1)
->willReturn((object) ['entity' => $purchased_entity]);
}
}
else {
$method->shouldNotBeCalled();
}
$order_item_prophecies[] = $item;
}
$order->getItems()->willReturn($order_item_prophecies);
$order->save()->shouldNotBeCalled();
// Create our JobType.
$container = $this->buildContainerProphecy(5, $order, $expected_result->getState() == Job::STATE_FAILURE);
$container->get('contacts_events.price_calculator')->willReturn($price_calculator->reveal());
$job_type = RecalculateOrderItems::create($container->reveal(), [], 'contacts_events_recalculate_order_items', []);
// Build our job.
$job = CommerceOrderJob::create('contacts_events_recalculate_order_items', $payload, 5);
$job->setDeferOrderSave();
// Process the job.
$result = $job_type->process($job);
// Check our result.
$this->assertEquals($expected_result, $result, 'Expected result');
$this->assertSame($order_needs_save, $job->orderNeedsSave(), 'Order needs saving');
}
/**
* Data provider for testDoProcess.
*/
public function dataDoProcess() {
$data = [];
$data['invalid-payload'] = [
['other' => 'value'],
[],
new JobResult(Job::STATE_FAILURE, 'Missing payload', 0),
FALSE,
];
$data['bundles-no-items'] = [
['bundles' => ['contacts_ticket']],
[],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['items-no-bundles'] = [
['bundles' => []],
[
[
'bundle' => 'contacts_ticket',
],
],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['diff-items-bundles'] = [
['bundles' => ['contacts_ticket']],
[
[
'bundle' => 'contacts_accommodation',
],
],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['bundles-in-items'] = [
['bundles' => ['contacts_ticket']],
[
[
'bundle' => 'contacts_ticket',
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
[
'bundle' => 'contacts_ticket',
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
$data['ids-no-items'] = [
['ids' => [10]],
[],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['items-no-ids'] = [
['ids' => []],
[
[
'id' => 10,
],
],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['diff-items-ids'] = [
['ids' => [50]],
[
[
'id' => 10,
],
],
new JobResult(Job::STATE_SUCCESS),
FALSE,
];
$data['ids-in-items'] = [
['ids' => [10, 15]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
[
'id' => 15,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
$data['ids-in-items-paid-in-full'] = [
['ids' => [10, 15]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
[
'id' => 15,
'valid' => TRUE,
'state' => 'paid_in_full',
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
$data['calc-failed'] = [
['ids' => [10]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => FALSE,
],
],
new JobResult(Job::STATE_FAILURE, 'Calc failed'),
FALSE,
];
$data['save-failed'] = [
['ids' => [10]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => FALSE,
],
],
new JobResult(Job::STATE_FAILURE, 'Save failed', 5, 60),
FALSE,
];
$data['first-failed'] = [
['ids' => [10, 15]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => FALSE,
],
[
// Nothing check on this item.
],
],
new JobResult(Job::STATE_FAILURE, 'Calc failed'),
FALSE,
];
$data['last-failed'] = [
['ids' => [10, 15]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
],
[
'id' => 15,
'valid' => TRUE,
'calc' => FALSE,
],
],
new JobResult(Job::STATE_FAILURE, 'Calc failed'),
FALSE,
];
$data['no-purchasable-entity'] = [
['ids' => [10]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
'purchased_entity_type' => NULL,
'purchased_entity_save' => FALSE,
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
$data['purchasable-entity'] = [
['ids' => [10]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
'purchased_entity_type' => PurchasableEntityInterface::class,
'purchased_entity_save' => FALSE,
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
$data['single-use-purchasable-entity'] = [
['ids' => [10]],
[
[
'id' => 10,
'valid' => TRUE,
'calc' => TRUE,
'save' => TRUE,
'purchased_entity_type' => SingleUsePurchasableEntityInterface::class,
'purchased_entity_save' => TRUE,
],
],
new JobResult(Job::STATE_SUCCESS),
TRUE,
];
return $data;
}
/**
* Build the container object prophecy.
*
* @param int $order_id
* The order ID that will be retrieved.
* @param \Prophecy\Prophecy\ObjectProphecy $order
* The prophecy of the order to return.
* @param bool $transaction_rollback
* Whether the transaction will be rolled back.
*
* @return \Prophecy\Prophecy\ObjectProphecy
* The prophecy for the container.
*/
protected function buildContainerProphecy($order_id, ObjectProphecy $order, $transaction_rollback = FALSE) {
$order_storage = $this->prophesize(EntityStorageInterface::class);
$order_storage->load($order_id)->willReturn($order->reveal());
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager->getStorage('commerce_order')->willReturn($order_storage->reveal());
$transaction = $this->prophesize(Transaction::class);
$transaction->rollBack()->shouldBeCalledTimes($transaction_rollback ? 1 : 0);
$database = $this->prophesize(Connection::class);
$database->startTransaction()->willReturn($transaction->reveal());
$container = $this->prophesize(ContainerInterface::class);
$container->get('entity_type.manager')->willReturn($entity_type_manager->reveal());
$container->get('database')->willReturn($database->reveal());
return $container;
}
}
