Skip to content
Snippets Groups Projects
Commit fb3a7c57 authored by Rich's avatar Rich Committed by mattwire
Browse files

Existing StripeIPN test now fully mocked (code needs cleanup still)

parent 65aeebcd
Branches
Tags
1 merge request!1526.6 to master
......@@ -42,7 +42,9 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
*/
public function testIPNContribution() {
// Mock the Stripe API. xxx
PropertySpy::$buffer = 'none';
// Set this to 'print' or 'log' maybe more helpful in development.
PropertySpy::$outputMode = 'exception';
$this->assertInstanceOf('CRM_Core_Payment_Stripe', $this->paymentObject);
// Create a mock stripe client.
......@@ -55,7 +57,7 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$mockPaymentMethod = $this->createMock('Stripe\\PaymentMethod');
$mockPaymentMethod->method('__get')
->will($this->returnValueMap([
[ 'id', 'PM_ID_MOCK']
[ 'id', 'pm_mock']
]));
$stripeClient->paymentMethods = $this->createMock('Stripe\\Service\\PaymentMethodService');
......@@ -63,7 +65,7 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$stripeClient->paymentMethods
->method('create')
->willReturn($mockPaymentMethod);
// new PropertySpy('paymentMethod.create', ['id' => 'PM_ID_MOCK']));
// new PropertySpy('paymentMethod.create', ['id' => 'pm_mock']));
$stripeClient->paymentMethods
->method('retrieve')
......@@ -75,37 +77,43 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$stripeClient->customers
->method('create')
->willReturn(
new PropertySpy('customers.create', ['id' => 'CU_ID_MOCK'])
new PropertySpy('customers.create', ['id' => 'cus_mock'])
);
$stripeClient->customers
->method('retrieve')
->willReturn(
new PropertySpy('customers.create', ['id' => 'CU_ID_MOCK'])
new PropertySpy('customers.retrieve', ['id' => 'cus_mock'])
);
$mockPlan = $this->createMock('Stripe\\Plan');
$mockPlan
->method('__get')
->will($this->returnValueMap([
['id', 'every-1-month-40000-usd-test']
]));
$stripeClient->plans = $this->createMock('Stripe\\Service\\PlanService');
$stripeClient->plans
->method('retrieve')
->willReturn($mockPlan);
// $stripeSubscription = $this->stripeClient->subscriptions->create($subscriptionParams);
// Need a mock intent with id and status, and
$mockCharge = $this->createMock('Stripe\\Charge');
$mockCharge
->method('__get')
->will($this->returnValueMap([
['id', 'CH_ID_MOCK'],
['balance_transaction', NULL],
['id', 'ch_mock'],
['captured', TRUE],
['status', 'succeeded'],
['balance_transaction', 'txn_mock'],
]));
$mockPaymentIntent = $this->createMock('Stripe\\PaymentIntent');
$mockPaymentIntent
->method('__get')
->will($this->returnValueMap([
['id', 'PI_ID_MOCK'],
['status', 'requires_capture'],
['id', 'pi_mock'],
['status', 'succeeded'],
['charges', (object) ['data' => [ $mockCharge ]]]
]));
......@@ -113,8 +121,12 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$stripeClient->subscriptions
->method('create')
->willReturn(new PropertySpy('subscription.create', [
'id' => 'SB_ID_MOCK',
'latest_invoice' => ['id' => 'IN_ID_MOCK', 'payment_intent' => $mockPaymentIntent],
'id' => 'sub_mock',
'current_period_end' => time(),
'latest_invoice' => [
'id' => 'in_mock',
'payment_intent' => $mockPaymentIntent,
],
'pending_setup_intent' => '',
]));
......@@ -122,9 +134,11 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$stripeClient->balanceTransactions
->method('retrieve')
->willReturn(new PropertySpy('balanceTransaction', [
'fee' => 1,
'currency' => 'USD',
'exchange_rate' => 1,
'id' => 'txn_mock',
'fee' => 1190, /* means $11.90 */
'currency' => 'usd',
'exchange_rate' => NULL,
'object' => 'charge',
]));
$stripeClient->paymentIntents = $this->createMock('Stripe\\Service\\PaymentIntentService');
......@@ -134,15 +148,52 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$stripeClient->invoices = $this->createMock('Stripe\\Service\\InvoiceService');
$stripeClient->invoices
->method('all')
->willReturn(['data' => (object) ['charge' => 'CH_ID_MOCK', 'created' => date('Y-m-d H:i:s')]]);
//$invoices = $processor->stripeClient->invoices->all(['subscription' => $subscription]);
->willReturn(['data' => new PropertySpy('Invoice', [
'amount_due' => 40000,
'charge' => 'ch_mock',
'created' => time(),
'currency' => 'usd',
'customer' => 'cus_mock',
'id' => 'in_mock',
'object' => 'invoice',
'subscription' => 'sub_mock',
])]);
// Mock Event service.
$stripeClient->events = $this->createMock('Stripe\\Service\\EventService');
// @todo create Mock events.
$mockEvent = [
'id' => 'evt_mock',
'object' => 'event',
'livemode' => false,
'pending_webhooks' => 0,
'request' => [ 'id' => NULL ],
'type' => 'invoice.payment_succeeded',
'data' => [
'object' => [
'id' => 'in_mock',
'object' => 'invoice',
'subscription' => 'sub_mock',
'customer' => 'cus_mock',
'charge' => 'ch_mock',
'created' => time(),
'amount_due' => 40000,
]
],
];
$stripeClient->events
->method('all')
->willReturn(['data' => [$mockEvent]]);
->willReturn(new PropertySpy('events.all',
[
'data' => [ $mockEvent ]
]));
$stripeClient->events
->method('retrieve')
->willReturn(new PropertySpy('events.retrieve', $mockEvent));
$stripeClient->charges = $this->createMock('Stripe\\Service\\ChargeService');
$stripeClient->charges
->method('retrieve')
->willReturn($mockCharge);
// Setup a recurring contribution for $200 per month.
$this->setupRecurringTransaction();
......@@ -163,7 +214,6 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$this->assertEquals(2, $status_id);
// Now check to see if an event was triggered and if so, process it.
// xxx
$payment_object = $this->getEvent('invoice.payment_succeeded');
if ($payment_object) {
$this->ipn($payment_object);
......@@ -193,10 +243,10 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
$params['output'] = 'raw';
// Now try to retrieve this transaction.
// Give it a few seconds to be processed...
sleep(5);
$transactions = civicrm_api3('Stripe', 'listevents', $params);
foreach($transactions['values']['data'] as $transaction) {
$_ = $transaction->data;
$_ = $_->object;
if ($transaction->data->object->$property == $this->processorID) {
return $transaction;
}
......@@ -258,18 +308,159 @@ class CRM_Stripe_IpnTest extends CRM_Stripe_BaseTest {
}
class PropertySpy {
/**
* This class provides a data structure for mocked stripe responses, and will detect
* if a property is requested that is not already mocked.
*
* This enables us to only need to mock the things we actually use, which
* hopefully makes the code more readable/maintainable.
*
* It implements the same interfaces as StripeObject does.
*
*
*/
class PropertySpy implements ArrayAccess, Iterator, Countable {
/**
* @var string $outputMode print|log|exception
*
* log means Civi::log()->debug()
* exception means throw a RuntimeException. Use this once your tests are passing,
* so that in future if the code starts relying on something we have not
* mocked we can figure it out quickly.
*/
public static $outputMode = 'print';
/**
* @var string $buffer
*
* - 'none' output immediately.
* - 'global' tries to output things chronologically at end when all objects have been killed.
* - 'local' outputs everything that happened to this object on destruction
*/
public static $buffer = 'none'; /* none|global|local */
protected $_name;
protected $_props;
protected $localLog = [];
public static $globalLog = [];
public static $globalObjects = 0;
protected $iteratorIdx=0;
// Iterator
public function current() {
// $this->warning("Iterating " . array_keys($this->_props)[$this->key()]);
return current($this->_props);
}
/**
* Implemetns Countable
*/
public function count() {
return \count($this->_props);
}
public function key ( ) {
return key($this->_props);
}
public function next() {
return next($this->_props);
}
public function rewind() {
return reset($this->_props);
}
public function valid() {
return array_key_exists(key($this->_props), $this->_props);
}
public function __construct($name, $props) {
$this->_name = $name;
$this->_props = $props;
foreach ($props as $k => $v) {
if (is_array($v)) {
// Iterative spies.
$v = new static("$name{" . "$k}", $v);
}
$this->_props[$k] = $v;
}
static::$globalObjects++;
}
public function __destruct() {
static::$globalObjects--;
if (static::$buffer === 'local') {
$msg = "PropertySpy: $this->_name\n"
. json_encode($this->localLog, JSON_PRETTY_PRINT) . "\n";
if (static::$outputMode === 'print') {
print $msg;
}
elseif (static::$outputMode === 'log') {
\Civi::log()->debug($msg);
}
elseif (static::$outputMode === 'exception') {
throw new \RuntimeException($msg);
}
}
elseif (static::$buffer === 'global' && static::$globalObjects === 0) {
// End of run.
$msg = "PropertySpy:\n" . json_encode(static::$globalLog, JSON_PRETTY_PRINT) . "\n";
if (static::$outputMode === 'print') {
print $msg;
}
elseif (static::$outputMode === 'log') {
\Civi::log()->debug($msg);
}
elseif (static::$outputMode === 'exception') {
throw new \RuntimeException($msg);
}
}
}
protected function warning($msg) {
if (static::$buffer === 'none') {
// Immediate output
if (static::$outputMode === 'print') {
print "$this->_name $msg\n";
}
elseif (static::$outputMode === 'log') {
Civi::log()->debug("$this->_name $msg\n");
}
}
elseif (static::$buffer === 'global') {
static::$globalLog[] = "$this->_name $msg";
}
elseif (static::$buffer === 'local') {
$this->localLog[] = $msg;
}
}
public function __get($prop) {
if ($prop === 'log') {
throw new \Exception("stop");
}
if (array_key_exists($prop, $this->_props)) {
return $this->_props[$prop];
}
print "$this->_name property '$prop' requested\n";
$this->warning("->$prop requested but not defined");
return NULL;
}
public function offsetGet($prop) {
if (array_key_exists($prop, $this->_props)) {
return $this->_props[$prop];
}
$this->warning("['$prop'] requested but not defined");
}
public function offsetExists($prop) {
if (!array_key_exists($prop, $this->_props)) {
$this->warning("['$prop'] offsetExists requested but not defined");
return FALSE;
}
return TRUE;
}
public function __isset($prop) {
if (!array_key_exists($prop, $this->_props)) {
$this->warning("isset(->$prop) but not defined");
}
return isset($this->_props[$prop]);
}
public function offsetSet($prop, $value) {
$this->warning("['$prop'] offsetSet");
$this->_props[$prop] = $value;
}
public function offsetUnset($prop) {
$this->warning("['$prop'] offsetUnset");
unset($this->_props[$prop]);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment