You are here

public function RetryTest::testRetry in Commerce Recurring Framework 8

@covers ::process @covers ::handleDecline @covers ::updateSubscriptions

File

tests/src/Kernel/Plugin/AdvancedQueue/JobType/RetryTest.php, line 62

Class

RetryTest
@coversDefaultClass \Drupal\commerce_recurring\Plugin\AdvancedQueue\JobType\RecurringJobTypeBase @group commerce_recurring

Namespace

Drupal\Tests\commerce_recurring\Kernel\Plugin\AdvancedQueue\JobType

Code

public function testRetry() {

  // A subscription without a payment method, to ensure a decline.

  /** @var \Drupal\commerce_recurring\Entity\SubscriptionInterface $subscription */
  $subscription = Subscription::create([
    'type' => 'product_variation',
    'store_id' => $this->store
      ->id(),
    'billing_schedule' => $this->billingSchedule,
    'uid' => $this->user,
    'purchased_entity' => $this->variation,
    'title' => $this->variation
      ->getOrderItemTitle(),
    'unit_price' => new Price('2', 'USD'),
    'state' => 'active',
    'starts' => strtotime('2019-02-24 17:00'),
  ]);
  $subscription
    ->save();
  $order = $this->recurringOrderManager
    ->startRecurring($subscription);

  // Rewind time to the end of the first subscription.
  $new_time = strtotime('2019-03-01 00:00');
  $this
    ->rewindTime($new_time);
  $job = Job::create('commerce_recurring_order_close', [
    'order_id' => $order
      ->id(),
  ]);
  $this->queue
    ->enqueueJob($job);
  $job = $this->queue
    ->getBackend()
    ->claimJob();

  /** @var \Drupal\advancedqueue\ProcessorInterface $processor */
  $processor = \Drupal::service('advancedqueue.processor');
  $result = $processor
    ->processJob($job, $this->queue);

  // Confirm that the order was placed.
  $order = $this
    ->reloadEntity($order);
  $this
    ->assertEquals('needs_payment', $order
    ->getState()
    ->getId());

  // Confirm that the job result is correct.
  $this
    ->assertEquals(Job::STATE_FAILURE, $result
    ->getState());
  $this
    ->assertEquals('Payment method not found.', $result
    ->getMessage());
  $this
    ->assertEquals(3, $result
    ->getMaxRetries());
  $this
    ->assertEquals(86400, $result
    ->getRetryDelay());

  // Confirm that the job was re-queued.
  $this
    ->assertEquals(1, $job
    ->getNumRetries());
  $this
    ->assertEquals(Job::STATE_QUEUED, $job
    ->getState());
  $this
    ->assertEquals(strtotime('2019-03-02 00:00'), $job
    ->getAvailableTime());

  // Confirm dunning email.
  $this
    ->assertMailString('subject', 'Payment declined - Order #1.', 1);
  $this
    ->assertMailString('body', 'We regret to inform you that the most recent charge attempt on your card failed.', 1);
  $this
    ->assertMailString('body', Url::fromRoute('entity.commerce_payment_method.collection', [
    'user' => 1,
  ], [
    'absolute' => TRUE,
  ])
    ->toString(), 1);
  $next_retry_time = strtotime('+1 day', $new_time);
  $this
    ->assertMailString('body', 'Our next charge attempt will be on: ' . date('F d', $next_retry_time), 1);

  // Run the first retry.
  $new_time = strtotime('2019-03-02 00:00');
  $this
    ->rewindTime($new_time);
  $job = $this->queue
    ->getBackend()
    ->claimJob();
  $result = $processor
    ->processJob($job, $this->queue);
  $this
    ->assertEquals(Job::STATE_FAILURE, $result
    ->getState());
  $this
    ->assertEquals('Payment method not found.', $result
    ->getMessage());
  $this
    ->assertEquals(3, $result
    ->getMaxRetries());
  $this
    ->assertEquals(86400 * 3, $result
    ->getRetryDelay());

  // Confirm that the job was re-queued.
  $this
    ->assertEquals(2, $job
    ->getNumRetries());
  $this
    ->assertEquals(Job::STATE_QUEUED, $job
    ->getState());
  $this
    ->assertEquals(strtotime('2019-03-05 00:00'), $job
    ->getAvailableTime());

  // Confirm dunning email.
  $next_retry_time = strtotime('+3 days', $new_time);
  $this
    ->assertMailString('body', 'Our next charge attempt will be on: ' . date('F d', $next_retry_time), 1);

  // Run the second retry.
  $new_time = strtotime('2019-03-05 00:00');
  $this
    ->rewindTime($new_time);
  $job = $this->queue
    ->getBackend()
    ->claimJob();
  $result = $processor
    ->processJob($job, $this->queue);
  $this
    ->assertEquals(Job::STATE_FAILURE, $result
    ->getState());
  $this
    ->assertEquals('Payment method not found.', $result
    ->getMessage());
  $this
    ->assertEquals(3, $result
    ->getMaxRetries());
  $this
    ->assertEquals(86400 * 5, $result
    ->getRetryDelay());

  // Confirm that the job was re-queued.
  $this
    ->assertEquals(3, $job
    ->getNumRetries());
  $this
    ->assertEquals(Job::STATE_QUEUED, $job
    ->getState());
  $this
    ->assertEquals(strtotime('2019-03-10 00:00'), $job
    ->getAvailableTime());

  // Confirm dunning email.
  $next_retry_time = strtotime('+5 days', $new_time);
  $this
    ->assertMailString('body', 'Our final charge attempt will be on: ' . date('F d', $next_retry_time), 1);

  // Run the last retry.
  $new_time = strtotime('2019-03-10 00:00');
  $this
    ->rewindTime($new_time);
  $job = $this->queue
    ->getBackend()
    ->claimJob();
  $result = $processor
    ->processJob($job, $this->queue);

  // Confirm that the order was marked as failed.
  $order = $this
    ->reloadEntity($order);
  $this
    ->assertEquals('failed', $order
    ->getState()
    ->getId());

  // Confirm that the job result is correct.
  $this
    ->assertEquals(Job::STATE_SUCCESS, $result
    ->getState());
  $this
    ->assertEquals('Dunning complete, recurring order not paid.', $result
    ->getMessage());

  // Confirm that the job was not requeued.
  $this
    ->assertEquals(3, $job
    ->getNumRetries());
  $this
    ->assertEquals(Job::STATE_SUCCESS, $job
    ->getState());

  // Confirm that the subscription was canceled.
  $subscription = $this
    ->reloadEntity($subscription);
  $this
    ->assertEquals('canceled', $subscription
    ->getState()
    ->getId());

  // Confirm dunning email.
  $this
    ->assertMailString('body', 'This was our final charge attempt.', 1);
}