commerce_license-8.x-2.x-dev/tests/src/Kernel/CommerceOrderSyncRenewalTest.php
tests/src/Kernel/CommerceOrderSyncRenewalTest.php
<?php
namespace Drupal\Tests\commerce_license\Kernel;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Tests\commerce_cart\Kernel\CartKernelTestBase;
// cspell:ignore nrpv
/**
* Tests renewable behavior on the license.
*
* @group commerce_license
*/
class CommerceOrderSyncRenewalTest extends CartKernelTestBase {
use LicenseOrderCompletionTestTrait;
/**
* The order type.
*
* @var \Drupal\commerce_order\Entity\OrderType
*/
protected $orderType;
/**
* The product variation type.
*
* @var \Drupal\commerce_product\Entity\ProductVariationType
*/
protected $variationType;
/**
* The product variation type for a non-renewable license.
*
* @var \Drupal\commerce_product\Entity\ProductVariationType
*/
protected $nonRenewableVariationType;
/**
* The variation to test against.
*
* @var \Drupal\commerce_product\Entity\ProductVariation
*/
protected $variation;
/**
* The non-renewable variation to test against.
*
* @var \Drupal\commerce_product\Entity\ProductVariation
*/
protected $nonRenewableVariation;
/**
* The license storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $licenseStorage;
/**
* The customer.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* The license used to test renewable behavior.
*
* @var \Drupal\commerce_license\Entity\LicenseInterface
*/
protected $license;
/**
* The license used to test non-renewable behavior.
*
* @var \Drupal\commerce_license\Entity\LicenseInterface
*/
protected $nonRenewableLicense;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'entity_reference_revisions',
'path',
'profile',
'state_machine',
'system',
'commerce_product',
'commerce_order',
'commerce_license',
'commerce_license_test',
'commerce_number_pattern',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('profile');
$this->installEntitySchema('commerce_product');
$this->installEntitySchema('commerce_product_variation');
$this->installEntitySchema('commerce_order');
$this->installEntitySchema('commerce_order_item');
$this->installEntitySchema('commerce_license');
$this->installConfig('system');
$this->installConfig('commerce_order');
$this->installConfig('commerce_product');
$this->createUser();
$this->licenseStorage = $this->container->get('entity_type.manager')->getStorage('commerce_license');
// Create an order type for licenses which uses the fulfillment workflow.
$this->orderType = $this->createEntity('commerce_order_type', [
'id' => 'license_order_type',
'label' => $this->randomMachineName(),
'workflow' => 'order_default',
]);
// Create an order item type that uses that order type.
$order_item_type = $this->createEntity('commerce_order_item_type', [
'id' => 'license_order_item_type',
'label' => $this->randomMachineName(),
'purchasableEntityType' => 'commerce_product_variation',
'orderType' => 'license_order_type',
'traits' => ['commerce_license_order_item_type'],
]);
$trait_manager = \Drupal::service('plugin.manager.commerce_entity_trait');
$trait = $trait_manager->createInstance('commerce_license_order_item_type');
$trait_manager->installTrait($trait, 'commerce_order_item', $order_item_type->id());
// Create a product variation type with the license trait, using our order
// item type.
$this->variationType = $this->createEntity('commerce_product_variation_type', [
'id' => 'license_pv_type',
'label' => $this->randomMachineName(),
'orderItemType' => 'license_order_item_type',
'traits' => ['commerce_license'],
]);
$trait = $trait_manager->createInstance('commerce_license');
$trait_manager->installTrait($trait, 'commerce_product_variation', $this->variationType->id());
$this->variationType->setThirdPartySetting('commerce_license', 'allow_renewal', TRUE);
$this->variationType->setThirdPartySetting('commerce_license', 'interval', '1');
$this->variationType->setThirdPartySetting('commerce_license', 'period', 'month');
$this->variationType->setThirdPartySetting('commerce_license', 'activate_on_place', TRUE);
$this->variationType->save();
// Create a product variation which grants a license.
$this->variation = $this->createEntity('commerce_product_variation', [
'type' => 'license_pv_type',
'title' => $this->randomMachineName(),
'sku' => $this->randomMachineName(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'license_type' => [
'target_plugin_id' => 'renewable',
'target_plugin_configuration' => [],
],
// Use the rolling interval expiry plugin as it's simple.
'license_expiration' => [
'target_plugin_id' => 'rolling_interval',
'target_plugin_configuration' => [
'interval' => [
'interval' => '1',
'period' => 'year',
],
],
],
]);
// We need a product too otherwise tests complain about the missing
// backreference.
$this->createEntity('commerce_product', [
'type' => 'default',
'title' => $this->randomMachineName(),
'stores' => [$this->store],
'variations' => [$this->variation],
]);
$this->reloadEntity($this->variation);
$this->variation->save();
// Create a product variation with a non-renewable license.
$this->nonRenewableVariationType = $this->createEntity('commerce_product_variation_type', [
'id' => 'license_nrpv_type',
'label' => $this->randomMachineName(),
'orderItemType' => 'license_order_item_type',
'traits' => ['commerce_license'],
]);
$trait_manager->installTrait($trait, 'commerce_product_variation', $this->nonRenewableVariationType->id());
$this->nonRenewableVariationType->setThirdPartySetting('commerce_license', 'allow_renewal', FALSE);
$this->nonRenewableVariationType->save();
// Create a product variation which grants a license.
$this->nonRenewableVariation = $this->createEntity('commerce_product_variation', [
'type' => 'license_nrpv_type',
'sku' => $this->randomMachineName(),
'title' => $this->randomString(),
'price' => [
'number' => 999,
'currency_code' => 'USD',
],
'license_type' => [
'target_plugin_id' => 'renewable',
'target_plugin_configuration' => [],
],
// Use the rolling interval expiry plugin as it's simple.
'license_expiration' => [
'target_plugin_id' => 'rolling_interval',
'target_plugin_configuration' => [
'interval' => [
'interval' => '1',
'period' => 'year',
],
],
],
]);
// We need a product too otherwise tests complain about the missing
// backreference.
$this->createEntity('commerce_product', [
'type' => 'default',
'title' => $this->randomMachineName(),
'stores' => [$this->store],
'variations' => [$this->variation],
]);
$this->reloadEntity($this->variation);
$this->variation->save();
// Create a user to use for orders.
$this->user = $this->createUser();
$this->store = $this->createStore();
// Create a license in the 'active' state.
$this->license = $this->createEntity('commerce_license', [
'type' => 'renewable',
'state' => 'active',
'product_variation' => $this->variation->id(),
'uid' => $this->user->id(),
// 06/01/2015 @ 1:00pm (UTC).
'expires' => '1433163600',
'expiration_type' => [
'target_plugin_id' => 'rolling_interval',
'target_plugin_configuration' => [
'interval' => [
'interval' => '1',
'period' => 'year',
],
],
],
]);
// Create a non-renewable license in the 'active' state.
$this->nonRenewableLicense = $this->createEntity('commerce_license', [
'type' => 'renewable',
'state' => 'active',
'product_variation' => $this->nonRenewableVariation->id(),
'uid' => $this->user->id(),
// 06/01/2015 @ 1:00pm (UTC).
'expires' => '1433163600',
'expiration_type' => [
'target_plugin_id' => 'rolling_interval',
'target_plugin_configuration' => [
'interval' => [
'interval' => '1',
'period' => 'year',
],
],
],
]);
}
/**
* Tests that a license can't be purchased outside the renewable window.
*/
public function testRenewOutsideRenewalWindow(): void {
// Mock the current time service.
$expiration_time_outside_window = strtotime('- 2 months', $this->license->getExpiresTime());
$mock_builder = $this->getMockBuilder(TimeInterface::class)
->disableOriginalConstructor();
$datetime_service = $mock_builder->getMock();
$datetime_service->expects($this->atLeastOnce())
->method('getRequestTime')
->willReturn($expiration_time_outside_window);
$this->container->set('datetime.time', $datetime_service);
// Add a product with license to the cart.
$cart_order = $this->container->get('commerce_cart.cart_provider')->createCart('license_order_type', $this->store, $this->user);
$order_item = $this->cartManager->addEntity($cart_order, $this->variation);
// Assert the order item is NOT in the cart.
$this->assertFalse($cart_order->hasItem($order_item));
}
/**
* Tests that a license is extended when you repurchased it.
*/
public function testRenewInRenewalWindow(): void {
// Mock the current time service.
$expiration_time_inside_window = strtotime('- 1 week', $this->license->getExpiresTime());
$mock_builder = $this->getMockBuilder(TimeInterface::class)
->disableOriginalConstructor();
$datetime_service = $mock_builder->getMock();
$datetime_service->expects($this->atLeastOnce())
->method('getRequestTime')
->willReturn($expiration_time_inside_window);
$this->container->set('datetime.time', $datetime_service);
// Add a product with license to the cart.
$cart_order = $this->container->get('commerce_cart.cart_provider')->createCart('license_order_type', $this->store, $this->user);
$this->cartManager = $this->container->get('commerce_cart.cart_manager');
$order_item = $this->cartManager->addEntity($cart_order, $this->variation);
$order_item = $this->reloadEntity($order_item);
// Check that the order item has the previous license.
$this->assertNotNull($order_item->license->entity, 'The order item has a license set on it.');
$this->assertEquals($this->license->id(), $order_item->license->entity->id(), 'The order item has a reference to the existing license.');
// Assert the order item IS IN the cart.
$this->assertTrue($cart_order->hasItem($order_item), 'The order item IS IN the cart.');
// Take the order through checkout.
$this->completeLicenseOrderCheckout($cart_order);
// Reload the entity because it has been changed.
$this->license = $this->reloadEntity($this->license);
$this->assertEquals(date(DATE_ATOM, strtotime('+1 year', 1433163600)), date(DATE_ATOM, $this->license->getExpiresTime()), 'The license has been extended for a year.');
}
/**
* Tests that a license is active after removing renewing product from cart.
*/
public function testRemovingProductFromCart(): void {
$initial_expiration_time = $this->license->getExpiresTime();
// Mock the current time service.
$expiration_time_inside_window = strtotime('- 1 week', $initial_expiration_time);
$mock_builder = $this->getMockBuilder(TimeInterface::class)
->disableOriginalConstructor();
$datetime_service = $mock_builder->getMock();
$datetime_service->expects($this->atLeastOnce())
->method('getRequestTime')
->willReturn($expiration_time_inside_window);
$this->container->set('datetime.time', $datetime_service);
// Add a product with license to the cart.
$cart_order = $this->container->get('commerce_cart.cart_provider')->createCart('license_order_type', $this->store, $this->user);
$this->cartManager = $this->container->get('commerce_cart.cart_manager');
$order_item = $this->cartManager->addEntity($cart_order, $this->variation);
$order_item = $this->reloadEntity($order_item);
$cart_order = $this->reloadEntity($cart_order);
// Check that the order item has the previous license.
$this->assertNotNull($order_item->license->entity, 'The order item has a license set on it.');
$this->assertEquals($this->license->id(), $order_item->license->entity->id(), 'The order item has a reference to the existing license.');
// Assert the order item IS IN the cart.
$this->assertTrue($cart_order->hasItem($order_item), 'The order item IS IN the cart.');
// Test that the license is in renewal_in_progress state.
$this->assertEquals('renewal_in_progress', $this->license->getState()->value, 'The license is in "renewal in progress" state.');
// Remove the item from the cart.
$order_item = $this->reloadEntity($order_item);
$this->cartManager->removeOrderItem($cart_order, $order_item);
// Assert the order item is NOT in the cart.
$this->assertFALSE($cart_order->hasItem($order_item), 'The order item is NOT in the cart.');
// Reload the entity because it may have been changed.
$this->license = $this->reloadEntity($this->license);
// Test that the license is back to 'active' state,
// without the expiration date extended.
$this->assertEquals('active', $this->license->getState()->value, 'The license is back to the "active" state.');
$this->assertEquals($initial_expiration_time, $this->license->getExpiresTime(), 'The license has still the same expiration time.');
}
/**
* Tests that a non-renewable license can't be purchased if still active.
*/
public function testNonRenewableLicense(): void {
// Add a product with license to the cart.
$cart_order = $this->container->get('commerce_cart.cart_provider')->createCart('license_order_type', $this->store, $this->user);
$this->cartManager = $this->container->get('commerce_cart.cart_manager');
$order_item = $this->cartManager->addEntity($cart_order, $this->nonRenewableVariation);
// Assert the order item is NOT in the cart.
$this->assertFalse($cart_order->hasItem($order_item));
}
/**
* Creates and saves a new entity.
*
* @param string $entity_type
* The entity type to be created.
* @param array $values
* An array of settings.
* Example: 'id' => 'foo'.
*
* @return \Drupal\Core\Entity\EntityInterface
* A new, saved entity.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function createEntity(string $entity_type, array $values): EntityInterface {
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
$storage = \Drupal::service('entity_type.manager')->getStorage($entity_type);
$entity = $storage->create($values);
$status = $entity->save();
$this->assertEquals(SAVED_NEW, $status, new FormattableMarkup('Created %label entity %type.', [
'%label' => $entity->getEntityType()->getLabel(),
'%type' => $entity->id(),
]));
// The newly saved entity isn't identical to a loaded one, and would fail
// comparisons.
$entity = $storage->load($entity->id());
return $entity;
}
}
