jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/343421 )
Change subject: Split out queue tests ...................................................................... Split out queue tests Separate files for one-time donations, recurring, and refunds TODO: better cleanup Change-Id: I1ddbcd72d62c0085e11def6d8a410914a40b8c27 --- A sites/all/modules/queue2civicrm/tests/phpunit/DonationQueueTest.php D sites/all/modules/queue2civicrm/tests/phpunit/ProcessMessageTest.php A sites/all/modules/queue2civicrm/tests/phpunit/RecurringQueueTest.php A sites/all/modules/queue2civicrm/tests/phpunit/RefundQueueTest.php 4 files changed, 702 insertions(+), 577 deletions(-) Approvals: jenkins-bot: Verified Awight: Looks good to me, approved diff --git a/sites/all/modules/queue2civicrm/tests/phpunit/DonationQueueTest.php b/sites/all/modules/queue2civicrm/tests/phpunit/DonationQueueTest.php new file mode 100644 index 0000000..ab978fa --- /dev/null +++ b/sites/all/modules/queue2civicrm/tests/phpunit/DonationQueueTest.php @@ -0,0 +1,336 @@ +<?php + +use queue2civicrm\DonationQueueConsumer; +use SmashPig\Core\Context; +use SmashPig\Core\DataStores\PendingDatabase; +use SmashPig\Tests\QueueTestConfiguration; + +/** + * @group Pipeline + * @group Queue2Civicrm + */ +class DonationQueueTest extends BaseWmfDrupalPhpUnitTestCase { + /** + * @var PendingDatabase + */ + protected $pendingDb; + + /** + * @var DonationQueueConsumer + */ + protected $queueConsumer; + + public function setUp() { + parent::setUp(); + $config = QueueTestConfiguration::instance(); + Context::initWithLogger( $config ); + $this->pendingDb = PendingDatabase::get(); + $this->pendingDb->createTable(); + $this->queueConsumer = new DonationQueueConsumer( 'test' ); + } + + /** + * Process an ordinary (one-time) donation message + */ + public function testDonation() { + $message = new TransactionMessage( + array( 'gross' => 400, 'original_gross' => 400, 'original_currency' => 'USD' ) + ); + $message2 = new TransactionMessage(); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + $this->queueConsumer->processMessage( $message2->getBody() ); + + $campaignField = wmf_civicrm_get_custom_field_name( 'campaign' ); + + $expected = array( + 'contact_type' => 'Individual', + 'sort_name' => 'laast, firrst', + 'display_name' => 'firrst laast', + 'first_name' => 'firrst', + 'last_name' => 'laast', + 'currency' => 'USD', + 'total_amount' => '400.00', + 'fee_amount' => '0.00', + 'net_amount' => '400.00', + 'trxn_id' => 'GLOBALCOLLECT ' . $message->getGatewayTxnId(), + 'contribution_source' => 'USD 400', + 'financial_type' => 'Cash', + 'contribution_status' => 'Completed', + 'payment_instrument' => 'Credit Card: Visa', + $campaignField => '', + ); + $returnFields = array_keys( $expected ); + + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + wmf_civicrm_get_custom_field_name( 'gateway_txn_id' ) => $message->getGatewayTxnId(), + 'return' => $returnFields + ) + ); + + $this->assertArraySubset( $expected, $contribution ); + + $contribution2 = civicrm_api3( + 'Contribution', + 'getsingle', + array( + wmf_civicrm_get_custom_field_name( 'gateway_txn_id' ) => $message2->getGatewayTxnId(), + 'return' => $returnFields + ) + ); + + $expected = array( + 'contact_type' => 'Individual', + 'sort_name' => 'laast, firrst', + 'display_name' => 'firrst laast', + 'first_name' => 'firrst', + 'last_name' => 'laast', + 'currency' => 'USD', + 'total_amount' => '2857.02', + 'fee_amount' => '0.00', + 'net_amount' => '2857.02', + 'trxn_id' => 'GLOBALCOLLECT ' . $message2->getGatewayTxnId(), + 'contribution_source' => 'PLN 952.34', + 'financial_type' => 'Cash', + 'contribution_status' => 'Completed', + 'payment_instrument' => 'Credit Card: Visa', + $campaignField => 'Benefactor Gift', + ); + $this->assertArraySubset( $expected, $contribution2 ); + $this->assertNotEquals( $contribution['contact_id'], $contribution2['contact_id'] ); + } + + /** + * Process an ordinary (one-time) donation message with an UTF campaign. + */ + public function testDonationWithUTFCampaignOption() { + $message = new TransactionMessage( array( 'utm_campaign' => 'EmailCampaign1' ) ); + $appealFieldID = $this->createCustomOption( 'Appeal', 'EmailCampaign1' ); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + 'id' => $contributions[0]['id'], + 'return' => 'custom_' . $appealFieldID, + ) + ); + $this->assertEquals( 'EmailCampaign1', $contribution['custom_' . $appealFieldID] ); + $this->deleteCustomOption( 'Appeal', 'EmailCampaign1' ); + } + + /** + * Process an ordinary (one-time) donation message with an UTF campaign not already existing. + */ + public function testDonationWithInvalidUTFCampaignOption() { + civicrm_initialize(); + $optionValue = uniqid(); + $message = new TransactionMessage( array( 'utm_campaign' => $optionValue ) ); + $appealField = civicrm_api3( 'custom_field', 'getsingle', array( 'name' => 'Appeal' ) ); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + 'id' => $contributions[0]['id'], + 'return' => 'custom_' . $appealField['id'], + ) + ); + $this->assertEquals( $optionValue, $contribution['custom_' . $appealField['id']] ); + $this->deleteCustomOption( 'Appeal', $optionValue ); + } + + /** + * Process an ordinary (one-time) donation message with an UTF campaign previously disabled. + */ + public function testDonationWithDisabledUTFCampaignOption() { + civicrm_initialize(); + $optionValue = uniqid(); + $message = new TransactionMessage( array( 'utm_campaign' => $optionValue ) ); + $appealFieldID = $this->createCustomOption( 'Appeal', $optionValue, FALSE ); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + 'id' => $contributions[0]['id'], + 'return' => 'custom_' . $appealFieldID, + ) + ); + $this->assertEquals( $optionValue, $contribution['custom_' . $appealFieldID] ); + $this->deleteCustomOption( 'Appeal', $optionValue ); + } + + /** + * Process an ordinary (one-time) donation message with an UTF campaign with a different label. + */ + public function testDonationWithDifferentLabelUTFCampaignOption() { + civicrm_initialize(); + $optionValue = uniqid(); + $message = new TransactionMessage( array( 'utm_campaign' => $optionValue ) ); + $appealFieldID = $this->createCustomOption( 'Appeal', $optionValue, TRUE, uniqid() ); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + 'id' => $contributions[0]['id'], + 'return' => 'custom_' . $appealFieldID, + ) + ); + $this->assertEquals( $optionValue, $contribution['custom_' . $appealFieldID] ); + $values = $this->callAPISuccess( 'OptionValue', 'get', array( 'value' => $optionValue ) ); + $this->assertEquals( 1, $values['count'] ); + $this->deleteCustomOption( 'Appeal', $optionValue ); + } + + /** + * Create a custom option for the given field. + * + * @param string $fieldName + * + * @param string $optionValue + * @param bool $is_active + * Is the option value enabled. + * + * @return mixed + * @throws \CiviCRM_API3_Exception + */ + public function createCustomOption( $fieldName, $optionValue, $is_active = 1, $label = NULL ) { + if ( !$label ) { + $label = $optionValue; + } + $appealField = civicrm_api3( 'custom_field', 'getsingle', array( 'name' => $fieldName ) ); + civicrm_api3( + 'OptionValue', + 'create', + array( + 'name' => $label, + 'value' => $optionValue, + 'option_group_id' => $appealField['option_group_id'], + 'is_active' => $is_active, + ) + ); + civicrm_api_option_group( wmf_civicrm_get_direct_mail_field_option_name(), null, TRUE ); + return $appealField['id']; + } + + /** + * Cleanup custom field option after test. + * + * @param string $fieldName + * + * @param string $optionValue + * + * @return mixed + * @throws \CiviCRM_API3_Exception + */ + public function deleteCustomOption( $fieldName, $optionValue ) { + $appealField = civicrm_api3( 'custom_field', 'getsingle', array( 'name' => $fieldName ) ); + return $appealField['id']; + } + + /** + * Process a donation message with some info from pending db + * @dataProvider getSparseMessages + * @param TransactionMessage $message + * @param array $pendingMessage + */ + public function testDonationSparseMessages( $message, $pendingMessage ) { + $pendingMessage['order_id'] = $message->get( 'order_id' ); + $this->pendingDb->storeMessage( $pendingMessage ); + $appealFieldID = $this->createCustomOption( + 'Appeal', + $pendingMessage['utm_campaign'], + false + ); + + exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); + + $this->queueConsumer->processMessage( $message->getBody() ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $contribution = civicrm_api3( + 'Contribution', + 'getsingle', + array( + 'id' => $contributions[0]['id'], + 'return' => 'custom_' . $appealFieldID, + ) + ); + $this->assertEquals( $pendingMessage['utm_campaign'], $contribution['custom_' . $appealFieldID] ); + $this->deleteCustomOption( 'Appeal', $pendingMessage['utm_campaign'] ); + $pendingEntry = $this->pendingDb->fetchMessageByGatewayOrderId( + $message->get( 'gateway' ), + $pendingMessage['order_id'] + ); + $this->assertNull( $pendingEntry, 'Should have deleted pending DB entry' ); + civicrm_api3( 'Contribution', 'delete', array( 'id' => $contributions[0]['id'] ) ); + civicrm_api3( 'Contact', 'delete', array( 'id' => $contributions[0]['contact_id'] ) ); + } + + public function getSparseMessages() { + return array( + array( + new AmazonDonationMessage(), + json_decode( + file_get_contents( __DIR__ . '/../data/pending_amazon.json' ), + true + ) + ), + array( + new AstroPayDonationMessage(), + json_decode( + file_get_contents( __DIR__ . '/../data/pending_astropay.json' ), + true + ) + ), + ); + } +} diff --git a/sites/all/modules/queue2civicrm/tests/phpunit/ProcessMessageTest.php b/sites/all/modules/queue2civicrm/tests/phpunit/ProcessMessageTest.php deleted file mode 100644 index 7c1dbd5..0000000 --- a/sites/all/modules/queue2civicrm/tests/phpunit/ProcessMessageTest.php +++ /dev/null @@ -1,577 +0,0 @@ -<?php - -use queue2civicrm\DonationQueueConsumer; -use queue2civicrm\refund\RefundQueueConsumer; -use queue2civicrm\recurring\RecurringQueueConsumer; -use SmashPig\Core\Context; -use SmashPig\Core\DataStores\PendingDatabase; -use SmashPig\Tests\SmashPigDatabaseTestConfiguration; - -/** - * @group Pipeline - * @group Queue2Civicrm - */ -class ProcessMessageTest extends BaseWmfDrupalPhpUnitTestCase { - /** - * @var PendingDatabase - */ - protected $pendingDb; - - /** - * @var DonationQueueConsumer - */ - protected $queueConsumer; - - /** - * @var RefundQueueConsumer - */ - protected $refundConsumer; - - /** - * @var RecurringQueueConsumer - */ - protected $recurringConsumer; - - public function setUp() { - parent::setUp(); - $config = SmashPigDatabaseTestConfiguration::instance(); - // FIXME: Use all-purpose SmashPig test config when ready - $config->override( array( - 'data-store' => array( - 'donations' => array( - 'class' => 'PHPQueue\Backend\PDO', - 'constructor-parameters' => array( array( - 'connection_string' => 'sqlite::memory:' - ) ) - ), - 'refund-new' => array( - 'class' => 'PHPQueue\Backend\PDO', - 'constructor-parameters' => array( array( - 'connection_string' => 'sqlite::memory:' - ) ) - ), - 'recurring-new' => array( - 'class' => 'PHPQueue\Backend\PDO', - 'constructor-parameters' => array( array( - 'connection_string' => 'sqlite::memory:' - ) ) - ), - ) - ) ); - Context::initWithLogger( $config ); - $this->pendingDb = PendingDatabase::get(); - $this->pendingDb->createTable(); - $this->queueConsumer = new DonationQueueConsumer( 'donations' ); - $this->refundConsumer = new RefundQueueConsumer( 'refund-new' ); - $this->recurringConsumer = new RecurringQueueConsumer( 'recurring-new' ); - } - - /** - * Process an ordinary (one-time) donation message - */ - public function testDonation() { - $message = new TransactionMessage(array('gross' => 400, 'original_gross' => 400, 'original_currency' => 'USD')); - $message2 = new TransactionMessage(); - - exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); - exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); - - $this->queueConsumer->processMessage( $message->getBody() ); - $this->queueConsumer->processMessage( $message2->getBody() ); - - $campaignField = wmf_civicrm_get_custom_field_name('campaign'); - - $expected = array( - 'contact_type' => 'Individual', - 'sort_name' => 'laast, firrst', - 'display_name' => 'firrst laast', - 'first_name' => 'firrst', - 'last_name' => 'laast', - 'currency' => 'USD', - 'total_amount' => '400.00', - 'fee_amount' => '0.00', - 'net_amount' => '400.00', - 'trxn_id' => 'GLOBALCOLLECT ' . $message->getGatewayTxnId(), - 'contribution_source' => 'USD 400', - 'financial_type' => 'Cash', - 'contribution_status' => 'Completed', - 'payment_instrument' => 'Credit Card: Visa', - $campaignField => '', - ); - $returnFields = array_keys( $expected ); - - $contribution = civicrm_api3('Contribution', 'getsingle', array( - wmf_civicrm_get_custom_field_name('gateway_txn_id') => $message->getGatewayTxnId(), - 'return' => $returnFields - )); - - $this->assertArraySubset( $expected, $contribution ); - - $contribution2 = civicrm_api3('Contribution', 'getsingle', array( - wmf_civicrm_get_custom_field_name('gateway_txn_id') => $message2->getGatewayTxnId(), - 'return' => $returnFields - )); - - $expected = array( - 'contact_type' => 'Individual', - 'sort_name' => 'laast, firrst', - 'display_name' => 'firrst laast', - 'first_name' => 'firrst', - 'last_name' => 'laast', - 'currency' => 'USD', - 'total_amount' => '2857.02', - 'fee_amount' => '0.00', - 'net_amount' => '2857.02', - 'trxn_id' => 'GLOBALCOLLECT ' . $message2->getGatewayTxnId(), - 'contribution_source' => 'PLN 952.34', - 'financial_type' => 'Cash', - 'contribution_status' => 'Completed', - 'payment_instrument' => 'Credit Card: Visa', - $campaignField => 'Benefactor Gift', - ); - $this->assertArraySubset( $expected, $contribution2 ); - $this->assertNotEquals( $contribution['contact_id'], $contribution2['contact_id'] ); - } - - /** - * Process an ordinary (one-time) donation message with an UTF campaign. - */ - public function testDonationWithUTFCampaignOption() { - $message = new TransactionMessage(array('utm_campaign' => 'EmailCampaign1')); - $appealFieldID = $this->createCustomOption('Appeal', 'EmailCampaign1'); - - exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); - exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); - - $this->queueConsumer->processMessage( $message->getBody() ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $message->getGateway(), $message->getGatewayTxnId() ); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'id' => $contributions[0]['id'], - 'return' => 'custom_' . $appealFieldID, - )); - $this->assertEquals('EmailCampaign1', $contribution['custom_' . $appealFieldID]); - $this->deleteCustomOption('Appeal', 'EmailCampaign1'); - } - - /** - * Process an ordinary (one-time) donation message with an UTF campaign not already existing. - */ - public function testDonationWithInvalidUTFCampaignOption() { - civicrm_initialize(); - $optionValue = uniqid(); - $message = new TransactionMessage(array('utm_campaign' => $optionValue)); - $appealField = civicrm_api3('custom_field', 'getsingle', array('name' => 'Appeal')); - - exchange_rate_cache_set('USD', $message->get('date'), 1); - exchange_rate_cache_set($message->get('currency'), $message->get('date'), 3); - - $this->queueConsumer->processMessage( $message->getBody() ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id($message->getGateway(), $message->getGatewayTxnId()); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'id' => $contributions[0]['id'], - 'return' => 'custom_' . $appealField['id'], - )); - $this->assertEquals($optionValue, $contribution['custom_' . $appealField['id']]); - $this->deleteCustomOption('Appeal', $optionValue); - } - - /** - * Process an ordinary (one-time) donation message with an UTF campaign previously disabled. - */ - public function testDonationWithDisabledUTFCampaignOption() { - civicrm_initialize(); - $optionValue = uniqid(); - $message = new TransactionMessage(array('utm_campaign' => $optionValue)); - $appealFieldID = $this->createCustomOption('Appeal', $optionValue, FALSE); - - exchange_rate_cache_set('USD', $message->get('date'), 1); - exchange_rate_cache_set($message->get('currency'), $message->get('date'), 3); - - $this->queueConsumer->processMessage( $message->getBody() ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id($message->getGateway(), $message->getGatewayTxnId()); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'id' => $contributions[0]['id'], - 'return' => 'custom_' . $appealFieldID, - )); - $this->assertEquals($optionValue, $contribution['custom_' . $appealFieldID]); - $this->deleteCustomOption('Appeal', $optionValue); - } - - /** - * Process an ordinary (one-time) donation message with an UTF campaign with a different label. - */ - public function testDonationWithDifferentLabelUTFCampaignOption() { - civicrm_initialize(); - $optionValue = uniqid(); - $message = new TransactionMessage(array('utm_campaign' => $optionValue)); - $appealFieldID = $this->createCustomOption('Appeal', $optionValue, TRUE, uniqid()); - - exchange_rate_cache_set('USD', $message->get('date'), 1); - exchange_rate_cache_set($message->get('currency'), $message->get('date'), 3); - - $this->queueConsumer->processMessage( $message->getBody() ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id($message->getGateway(), $message->getGatewayTxnId()); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'id' => $contributions[0]['id'], - 'return' => 'custom_' . $appealFieldID, - )); - $this->assertEquals($optionValue, $contribution['custom_' . $appealFieldID]); - $values = $this->callAPISuccess('OptionValue', 'get', array('value' => $optionValue)); - $this->assertEquals(1, $values['count']); - $this->deleteCustomOption('Appeal', $optionValue); - } - - /** - * Create a custom option for the given field. - * - * @param string $fieldName - * - * @param string $optionValue - * @param bool $is_active - * Is the option value enabled. - * - * @return mixed - * @throws \CiviCRM_API3_Exception - */ - public function createCustomOption($fieldName, $optionValue, $is_active = 1, $label = NULL) { - if (!$label) { - $label = $optionValue; - } - $appealField = civicrm_api3('custom_field', 'getsingle', array('name' => $fieldName)); - civicrm_api3('OptionValue', 'create', array( - 'name' => $label, - 'value' => $optionValue, - 'option_group_id' => $appealField['option_group_id'], - 'is_active' => $is_active, - )); - civicrm_api_option_group(wmf_civicrm_get_direct_mail_field_option_name(), null, TRUE); - return $appealField['id']; - } - - /** - * Cleanup custom field option after test. - * - * @param string $fieldName - * - * @param string $optionValue - * - * @return mixed - * @throws \CiviCRM_API3_Exception - */ - public function deleteCustomOption($fieldName, $optionValue) { - $appealField = civicrm_api3('custom_field', 'getsingle', array('name' => $fieldName)); - return $appealField['id']; - } - - public function testRecurring() { - civicrm_initialize(); - $subscr_id = mt_rand(); - $values = $this->processRecurringSignup($subscr_id); - - $message = new RecurringPaymentMessage( $values ); - $message2 = new RecurringPaymentMessage( $values ); - - $payment_time = strtotime( $message->get( 'payment_date' ) ); - exchange_rate_cache_set( 'USD', $payment_time, 1 ); - exchange_rate_cache_set( $message->get('mc_currency'), $payment_time, 3 ); - - $msg = $message->getBody(); - db_insert('contribution_tracking') - ->fields(array('id' => $msg['custom'])) - ->execute(); - - $this->recurringConsumer->processMessage($msg); - $this->recurringConsumer->processMessage( $message2->getBody() ); - - $recur_record = wmf_civicrm_get_recur_record( $subscr_id ); - $this->assertNotEquals( false, $recur_record ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $message->getGateway(), $message->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions ) ); - $this->assertEquals( $recur_record->id, $contributions[0]['contribution_recur_id']); - - $contributions2 = wmf_civicrm_get_contributions_from_gateway_id( $message2->getGateway(), $message2->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions2 ) ); - $this->assertEquals( $recur_record->id, $contributions2[0]['contribution_recur_id']); - - $this->assertEquals( $contributions[0]['contact_id'], $contributions2[0]['contact_id'] ); - $addresses = $this->callAPISuccess('Address', 'get', array('contact_id' => $contributions2[0]['contact_id'])); - $this->assertEquals(1, $addresses['count']); - // The address comes from the recurring_payment.json not the recurring_signup.json as it - // has been overwritten. This is perhaps not a valid scenario in production but it is - // the scenario the code works to. In production they would probably always be the same. - $this->assertEquals('1211122 132 st', $addresses['values'][$addresses['id']]['street_address']); - - $emails = $this->callAPISuccess('Email', 'get', array('contact_id' => $contributions2[0]['contact_id'])); - $this->assertEquals(1, $addresses['count']); - $this->assertEquals('test...@wikimedia.org', $emails['values'][$emails['id']]['email']); - - db_delete('contribution_tracking') - ->condition('id', $msg['custom']) - ->execute(); - } - - public function testNormalizedRecurring() { - civicrm_initialize(); - $subscr_id = mt_rand(); - $values = $this->processNormalizedRecurringSignup($subscr_id); - - $message = new NormalizedSubscriptionPaymentMessage( $values ); - - $payment_time = $message->get( 'date' ); - exchange_rate_cache_set( 'USD', $payment_time, 1 ); - exchange_rate_cache_set( $message->get( 'currency' ), $payment_time, 2 ); - - db_insert( 'contribution_tracking' ) - ->fields( array( 'id' => $message->get( 'contribution_tracking_id' ) ) ) - ->execute(); - - $this->recurringConsumer->processMessage( $message->getBody() ); - - $recur_record = wmf_civicrm_get_recur_record( $subscr_id ); - $this->assertNotEquals( false, $recur_record ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $message->getGateway(), $message->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions ) ); - $this->assertEquals( $recur_record->id, $contributions[0]['contribution_recur_id']); - - $addresses = $this->callAPISuccess('Address', 'get', array('contact_id' => $contributions[0]['contact_id'])); - $this->assertEquals(1, $addresses['count']); - - $emails = $this->callAPISuccess('Email', 'get', array('contact_id' => $contributions[0]['contact_id'])); - $this->assertEquals(1, $addresses['count']); - $this->assertEquals('test...@wikimedia.org', $emails['values'][$emails['id']]['email']); - - db_delete( 'contribution_tracking' ) - ->condition('id', $message->get( 'contribution_tracking_id' ) ) - ->execute(); - CRM_Core_DAO::executeQuery(" - DELETE FROM civicrm_contribution - WHERE id = %1", - array( 1 => array( $contributions[0]['id'], 'Positive' ) ) - ); - CRM_Core_DAO::executeQuery(" - DELETE FROM civicrm_contact - WHERE id = %1", - array( 1 => array( $contributions[0]['contact_id'], 'Positive' ) ) - ); - } - - /** - * Test that the a blank address is not written to the DB. - */ - public function testRecurringBlankEmail() { - civicrm_initialize(); - $subscr_id = mt_rand(); - $values = $this->processRecurringSignup($subscr_id); - - $message = new RecurringPaymentMessage($values); - $this->setExchangeRates($message->get('payment_date'), array('USD' => 1, $message->get('mc_currency') => 3)); - $messageBody = $message->getBody(); - - $addressFields = array('address_city', "address_country_code", "address_country", "address_state","address_street", "address_zip"); - foreach ($addressFields as $addressField) { - $messageBody[$addressField] = ''; - } - - db_insert('contribution_tracking') - ->fields(array('id' => $messageBody['custom'])) - ->execute(); - - $this->recurringConsumer->processMessage($messageBody); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id( - $message->getGateway(), - $message->getGatewayTxnId() - ); - $addresses = $this->callAPISuccess('Address', 'get', array('contact_id' => $contributions[0]['contact_id'], 'sequential' => 1)); - $this->assertEquals(1, $addresses['count']); - // The address created by the sign up (Lockwood Rd) should not have been overwritten by the blank. - $this->assertEquals('5109 Lockwood Rd', $addresses['values'][0]['street_address']); - db_delete( 'contribution_tracking' ) - ->condition('id', $messageBody['custom']) - ->execute(); - } - - /** - * @expectedException WmfException - * @expectedExceptionCode WmfException::MISSING_PREDECESSOR - */ - public function testRecurringNoPredecessor() { - $message = new RecurringPaymentMessage( array( - 'subscr_id' => mt_rand(), - ) ); - - $payment_time = strtotime( $message->get( 'payment_date' ) ); - exchange_rate_cache_set( 'USD', $payment_time, 1 ); - exchange_rate_cache_set( $message->get('mc_currency'), $payment_time, 3 ); - - $this->recurringConsumer->processMessage( $message->getBody() ); - } - - /** - * @expectedException WmfException - * @expectedExceptionCode WmfException::INVALID_RECURRING - */ - public function testRecurringNoSubscrId() { - $message = new RecurringPaymentMessage( array( - 'subscr_id' => null, - ) ); - - $payment_time = strtotime( $message->get( 'payment_date' ) ); - exchange_rate_cache_set( 'USD', $payment_time, 1 ); - exchange_rate_cache_set( $message->get('mc_currency'), $payment_time, 3 ); - - $this->recurringConsumer->processMessage( $message->getBody() ); - } - - public function testRefund() { - $donation_message = new TransactionMessage(); - $refund_message = new RefundMessage( array( - 'gateway' => $donation_message->getGateway(), - 'gateway_parent_id' => $donation_message->getGatewayTxnId(), - 'gateway_refund_id' => mt_rand(), - 'gross' => $donation_message->get( 'original_gross' ), - 'gross_currency' => $donation_message->get( 'original_currency' ), - ) ); - - exchange_rate_cache_set( 'USD', $donation_message->get('date'), 1 ); - exchange_rate_cache_set( $donation_message->get('currency'), $donation_message->get('date'), 3 ); - - $this->queueConsumer->processMessage( $donation_message->getBody() ); - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $donation_message->getGateway(), $donation_message->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions ) ); - - $this->refundConsumer->processMessage( $refund_message->getBody() ); - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $refund_message->getGateway(), $refund_message->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions ) ); - } - - /** - * @expectedException WmfException - * @expectedExceptionCode WmfException::MISSING_PREDECESSOR - */ - public function testRefundNoPredecessor() { - $refund_message = new RefundMessage(); - - $this->refundConsumer->processMessage( $refund_message->getBody() ); - } - - /** - * Test refunding a mismatched amount. - * - * Note that we were checking against an exception - but it turned out the exception - * could be thrown in this fn $this->queueConsumer->processMessage if the exchange rate does not - * exist - which is not what we are testing for. - */ - public function testRefundMismatched() { - $this->setExchangeRates(1234567, array( 'USD' => 1, 'PLN' => 0.5 ) ); - $donation_message = new TransactionMessage( array( - 'gateway' => 'test_gateway', - 'gateway_txn_id' => mt_rand(), - ) ); - $refund_message = new RefundMessage( array( - 'gateway' => 'test_gateway', - 'gateway_parent_id' => $donation_message->getGatewayTxnId(), - 'gateway_refund_id' => mt_rand(), - 'gross' => $donation_message->get( 'original_gross' ) + 1, - 'gross_currency' => $donation_message->get( 'original_currency' ), - ) ); - - $this->queueConsumer->processMessage( $donation_message->getBody() ); - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $donation_message->getGateway(), $donation_message->getGatewayTxnId() ); - $this->assertEquals( 1, count( $contributions ) ); - - $this->refundConsumer->processMessage( $refund_message->getBody() ); - $contributions = $this->callAPISuccess('Contribution', 'get', array('contact_id' => $contributions[0]['contact_id'], 'sequential' => 1)); - $this->assertEquals(2, count($contributions['values'])); - $this->assertEquals('Chargeback', CRM_Contribute_PseudoConstant::contributionStatus($contributions['values'][0]['contribution_status_id'])); - $this->assertEquals('-.5', $contributions['values'][1]['total_amount']); - } - - /** - * Process a donation message with some info from pending db - * @dataProvider getSparseMessages - * @param TransactionMessage $message - * @param array $pendingMessage - */ - public function testDonationSparseMessages( $message, $pendingMessage ) { - $pendingMessage['order_id'] = $message->get( 'order_id' ); - $this->pendingDb->storeMessage( $pendingMessage ); - $appealFieldID = $this->createCustomOption( - 'Appeal', $pendingMessage['utm_campaign'], false - ); - - exchange_rate_cache_set( 'USD', $message->get( 'date' ), 1 ); - exchange_rate_cache_set( $message->get( 'currency' ), $message->get( 'date' ), 3 ); - - $this->queueConsumer->processMessage( $message->getBody() ); - - $contributions = wmf_civicrm_get_contributions_from_gateway_id( $message->getGateway(), $message->getGatewayTxnId() ); - $contribution = civicrm_api3('Contribution', 'getsingle', array( - 'id' => $contributions[0]['id'], - 'return' => 'custom_' . $appealFieldID, - )); - $this->assertEquals( $pendingMessage['utm_campaign'], $contribution['custom_' . $appealFieldID]); - $this->deleteCustomOption('Appeal', $pendingMessage['utm_campaign']); - $pendingEntry = $this->pendingDb->fetchMessageByGatewayOrderId( - $message->get( 'gateway' ), $pendingMessage['order_id'] - ); - $this->assertNull( $pendingEntry, 'Should have deleted pending DB entry' ); - civicrm_api3( 'Contribution', 'delete', array( 'id' => $contributions[0]['id'] ) ); - civicrm_api3( 'Contact', 'delete', array( 'id' => $contributions[0]['contact_id'] ) ); - } - - public function getSparseMessages() { - return array( - array( - new AmazonDonationMessage(), - json_decode( - file_get_contents( __DIR__ . '/../data/pending_amazon.json'), true - ) - ), - array( - new AstroPayDonationMessage(), - json_decode( - file_get_contents( __DIR__ . '/../data/pending_astropay.json'), true - ) - ), - ); - } - - /** - * Process the original recurring sign up message. - * - * @param string $subscr_id - * @return array - */ - private function processRecurringSignup($subscr_id) { - $values = array('subscr_id' => $subscr_id); - $signup_message = new RecurringSignupMessage($values); - $subscr_time = strtotime($signup_message->get('subscr_date')); - exchange_rate_cache_set('USD', $subscr_time, 1); - exchange_rate_cache_set($signup_message->get('mc_currency'), $subscr_time, 3); - $this->recurringConsumer->processMessage($signup_message->getBody()); - return $values; - } - - /** - * Process the original recurring sign up message. - * - * @param string $subscr_id - * @return array - */ - private function processNormalizedRecurringSignup($subscr_id) { - $values = array('subscr_id' => $subscr_id); - $signup_message = new NormalizedRecurringSignupMessage($values); - $subscr_time = $signup_message->get('date'); - exchange_rate_cache_set('USD', $subscr_time, 1); - exchange_rate_cache_set($signup_message->get('currency'), $subscr_time, 2); - $this->recurringConsumer->processMessage($signup_message->getBody()); - return $values; - } -} diff --git a/sites/all/modules/queue2civicrm/tests/phpunit/RecurringQueueTest.php b/sites/all/modules/queue2civicrm/tests/phpunit/RecurringQueueTest.php new file mode 100644 index 0000000..67328e1 --- /dev/null +++ b/sites/all/modules/queue2civicrm/tests/phpunit/RecurringQueueTest.php @@ -0,0 +1,251 @@ +<?php +use queue2civicrm\recurring\RecurringQueueConsumer; +use SmashPig\Core\Context; +use SmashPig\Core\QueueConsumers\BaseQueueConsumer; + +/** + * @group Queue2Civicrm + */ +class RecurringQueueTest extends BaseWmfDrupalPhpUnitTestCase { + + /** + * @var RecurringQueueConsumer + */ + protected $consumer; + + public function setUp() { + parent::setUp(); + $config = TestingSmashPigDbQueueConfiguration::instance(); + Context::initWithLogger( $config ); + $queue = BaseQueueConsumer::getQueue( 'test' ); + $queue->createTable( 'test' ); + $this->consumer = new RecurringQueueConsumer( + 'test' + ); + } + + + public function testCreateDistinctContributions() { + civicrm_initialize(); + $subscr_id = mt_rand(); + $values = $this->processRecurringSignup( $subscr_id ); + + $message = new RecurringPaymentMessage( $values ); + $message2 = new RecurringPaymentMessage( $values ); + + $payment_time = strtotime( $message->get( 'payment_date' ) ); + exchange_rate_cache_set( 'USD', $payment_time, 1 ); + exchange_rate_cache_set( $message->get( 'mc_currency' ), $payment_time, 3 ); + + $msg = $message->getBody(); + db_insert( 'contribution_tracking' ) + ->fields( array( 'id' => $msg['custom'] ) ) + ->execute(); + + $this->consumer->processMessage( $msg ); + $this->consumer->processMessage( $message2->getBody() ); + + $recur_record = wmf_civicrm_get_recur_record( $subscr_id ); + $this->assertNotEquals( false, $recur_record ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions ) ); + $this->assertEquals( $recur_record->id, $contributions[0]['contribution_recur_id'] ); + + $contributions2 = wmf_civicrm_get_contributions_from_gateway_id( + $message2->getGateway(), + $message2->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions2 ) ); + $this->assertEquals( $recur_record->id, $contributions2[0]['contribution_recur_id'] ); + + $this->assertEquals( $contributions[0]['contact_id'], $contributions2[0]['contact_id'] ); + $addresses = $this->callAPISuccess( + 'Address', + 'get', + array( 'contact_id' => $contributions2[0]['contact_id'] ) + ); + $this->assertEquals( 1, $addresses['count'] ); + // The address comes from the recurring_payment.json not the recurring_signup.json as it + // has been overwritten. This is perhaps not a valid scenario in production but it is + // the scenario the code works to. In production they would probably always be the same. + $this->assertEquals( '1211122 132 st', $addresses['values'][$addresses['id']]['street_address'] ); + + $emails = $this->callAPISuccess( 'Email', 'get', array( 'contact_id' => $contributions2[0]['contact_id'] ) ); + $this->assertEquals( 1, $addresses['count'] ); + $this->assertEquals( 'test...@wikimedia.org', $emails['values'][$emails['id']]['email'] ); + + db_delete( 'contribution_tracking' ) + ->condition( 'id', $msg['custom'] ) + ->execute(); + } + + public function testNormalizedMessages() { + civicrm_initialize(); + $subscr_id = mt_rand(); + $values = $this->processNormalizedRecurringSignup( $subscr_id ); + + $message = new NormalizedSubscriptionPaymentMessage( $values ); + + $payment_time = $message->get( 'date' ); + exchange_rate_cache_set( 'USD', $payment_time, 1 ); + exchange_rate_cache_set( $message->get( 'currency' ), $payment_time, 2 ); + + db_insert( 'contribution_tracking' ) + ->fields( array( 'id' => $message->get( 'contribution_tracking_id' ) ) ) + ->execute(); + + $this->consumer->processMessage( $message->getBody() ); + + $recur_record = wmf_civicrm_get_recur_record( $subscr_id ); + $this->assertNotEquals( false, $recur_record ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions ) ); + $this->assertEquals( $recur_record->id, $contributions[0]['contribution_recur_id'] ); + + $addresses = $this->callAPISuccess( + 'Address', + 'get', + array( 'contact_id' => $contributions[0]['contact_id'] ) + ); + $this->assertEquals( 1, $addresses['count'] ); + + $emails = $this->callAPISuccess( 'Email', 'get', array( 'contact_id' => $contributions[0]['contact_id'] ) ); + $this->assertEquals( 1, $addresses['count'] ); + $this->assertEquals( 'test...@wikimedia.org', $emails['values'][$emails['id']]['email'] ); + + db_delete( 'contribution_tracking' ) + ->condition( 'id', $message->get( 'contribution_tracking_id' ) ) + ->execute(); + CRM_Core_DAO::executeQuery( + " + DELETE FROM civicrm_contribution + WHERE id = %1", + array( 1 => array( $contributions[0]['id'], 'Positive' ) ) + ); + CRM_Core_DAO::executeQuery( + " + DELETE FROM civicrm_contact + WHERE id = %1", + array( 1 => array( $contributions[0]['contact_id'], 'Positive' ) ) + ); + } + + /** + * Test that the a blank address is not written to the DB. + */ + public function testBlankEmail() { + civicrm_initialize(); + $subscr_id = mt_rand(); + $values = $this->processRecurringSignup( $subscr_id ); + + $message = new RecurringPaymentMessage( $values ); + $this->setExchangeRates( + $message->get( 'payment_date' ), + array( 'USD' => 1, $message->get( 'mc_currency' ) => 3 ) + ); + $messageBody = $message->getBody(); + + $addressFields = array( 'address_city', "address_country_code", "address_country", "address_state", "address_street", "address_zip" ); + foreach ( $addressFields as $addressField ) { + $messageBody[$addressField] = ''; + } + + db_insert( 'contribution_tracking' ) + ->fields( array( 'id' => $messageBody['custom'] ) ) + ->execute(); + + $this->consumer->processMessage( $messageBody ); + + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $message->getGateway(), + $message->getGatewayTxnId() + ); + $addresses = $this->callAPISuccess( + 'Address', + 'get', + array( 'contact_id' => $contributions[0]['contact_id'], 'sequential' => 1 ) + ); + $this->assertEquals( 1, $addresses['count'] ); + // The address created by the sign up (Lockwood Rd) should not have been overwritten by the blank. + $this->assertEquals( '5109 Lockwood Rd', $addresses['values'][0]['street_address'] ); + db_delete( 'contribution_tracking' ) + ->condition( 'id', $messageBody['custom'] ) + ->execute(); + } + + /** + * @expectedException WmfException + * @expectedExceptionCode WmfException::MISSING_PREDECESSOR + */ + public function testMissingPredecessor() { + $message = new RecurringPaymentMessage( + array( + 'subscr_id' => mt_rand(), + ) + ); + + $payment_time = strtotime( $message->get( 'payment_date' ) ); + exchange_rate_cache_set( 'USD', $payment_time, 1 ); + exchange_rate_cache_set( $message->get( 'mc_currency' ), $payment_time, 3 ); + + $this->consumer->processMessage( $message->getBody() ); + } + + /** + * @expectedException WmfException + * @expectedExceptionCode WmfException::INVALID_RECURRING + */ + public function testNoSubscrId() { + $message = new RecurringPaymentMessage( + array( + 'subscr_id' => null, + ) + ); + + $payment_time = strtotime( $message->get( 'payment_date' ) ); + exchange_rate_cache_set( 'USD', $payment_time, 1 ); + exchange_rate_cache_set( $message->get( 'mc_currency' ), $payment_time, 3 ); + + $this->consumer->processMessage( $message->getBody() ); + } + + /** + * Process the original recurring sign up message. + * + * @param string $subscr_id + * @return array + */ + private function processRecurringSignup( $subscr_id ) { + $values = array( 'subscr_id' => $subscr_id ); + $signup_message = new RecurringSignupMessage( $values ); + $subscr_time = strtotime( $signup_message->get( 'subscr_date' ) ); + exchange_rate_cache_set( 'USD', $subscr_time, 1 ); + exchange_rate_cache_set( $signup_message->get( 'mc_currency' ), $subscr_time, 3 ); + $this->consumer->processMessage( $signup_message->getBody() ); + return $values; + } + + /** + * Process the original recurring sign up message. + * + * @param string $subscr_id + * @return array + */ + private function processNormalizedRecurringSignup( $subscr_id ) { + $values = array( 'subscr_id' => $subscr_id ); + $signup_message = new NormalizedRecurringSignupMessage( $values ); + $subscr_time = $signup_message->get( 'date' ); + exchange_rate_cache_set( 'USD', $subscr_time, 1 ); + exchange_rate_cache_set( $signup_message->get( 'currency' ), $subscr_time, 2 ); + $this->consumer->processMessage( $signup_message->getBody() ); + return $values; + } +} diff --git a/sites/all/modules/queue2civicrm/tests/phpunit/RefundQueueTest.php b/sites/all/modules/queue2civicrm/tests/phpunit/RefundQueueTest.php new file mode 100644 index 0000000..618b94e --- /dev/null +++ b/sites/all/modules/queue2civicrm/tests/phpunit/RefundQueueTest.php @@ -0,0 +1,115 @@ +<?php +use queue2civicrm\refund\RefundQueueConsumer; +use SmashPig\Core\Context; +use SmashPig\Core\QueueConsumers\BaseQueueConsumer; + +/** + * @group Queue2Civicrm + */ +class RefundQueueTest extends BaseWmfDrupalPhpUnitTestCase { + + /** + * @var RefundQueueConsumer + */ + protected $consumer; + + public function setUp() { + parent::setUp(); + $config = TestingSmashPigDbQueueConfiguration::instance(); + Context::initWithLogger( $config ); + $queue = BaseQueueConsumer::getQueue( 'test' ); + $queue->createTable( 'test' ); + $this->consumer = new RefundQueueConsumer( + 'test' + ); + } + + public function testRefund() { + $donation_message = new TransactionMessage(); + $refund_message = new RefundMessage( + array( + 'gateway' => $donation_message->getGateway(), + 'gateway_parent_id' => $donation_message->getGatewayTxnId(), + 'gateway_refund_id' => mt_rand(), + 'gross' => $donation_message->get( 'original_gross' ), + 'gross_currency' => $donation_message->get( 'original_currency' ), + ) + ); + + exchange_rate_cache_set( 'USD', $donation_message->get( 'date' ), 1 ); + exchange_rate_cache_set( $donation_message->get( 'currency' ), $donation_message->get( 'date' ), 3 ); + + $message_body = $donation_message->getBody(); + wmf_civicrm_contribution_message_import( $message_body ); + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $donation_message->getGateway(), + $donation_message->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions ) ); + + $this->consumer->processMessage( $refund_message->getBody() ); + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $refund_message->getGateway(), + $refund_message->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions ) ); + } + + /** + * @expectedException WmfException + * @expectedExceptionCode WmfException::MISSING_PREDECESSOR + */ + public function testRefundNoPredecessor() { + $refund_message = new RefundMessage(); + + $this->consumer->processMessage( $refund_message->getBody() ); + } + + /** + * Test refunding a mismatched amount. + * + * Note that we were checking against an exception - but it turned out the exception + * could be thrown in this fn $this->queueConsumer->processMessage if the exchange rate does not + * exist - which is not what we are testing for. + */ + public function testRefundMismatched() { + $this->setExchangeRates( 1234567, array( 'USD' => 1, 'PLN' => 0.5 ) ); + $donation_message = new TransactionMessage( + array( + 'gateway' => 'test_gateway', + 'gateway_txn_id' => mt_rand(), + ) + ); + $refund_message = new RefundMessage( + array( + 'gateway' => 'test_gateway', + 'gateway_parent_id' => $donation_message->getGatewayTxnId(), + 'gateway_refund_id' => mt_rand(), + 'gross' => $donation_message->get( 'original_gross' ) + 1, + 'gross_currency' => $donation_message->get( 'original_currency' ), + ) + ); + + $message_body = $donation_message->getBody(); + wmf_civicrm_contribution_message_import( $message_body ); + $contributions = wmf_civicrm_get_contributions_from_gateway_id( + $donation_message->getGateway(), + $donation_message->getGatewayTxnId() + ); + $this->assertEquals( 1, count( $contributions ) ); + + $this->consumer->processMessage( $refund_message->getBody() ); + $contributions = $this->callAPISuccess( + 'Contribution', + 'get', + array( 'contact_id' => $contributions[0]['contact_id'], 'sequential' => 1 ) + ); + $this->assertEquals( 2, count( $contributions['values'] ) ); + $this->assertEquals( + 'Chargeback', + CRM_Contribute_PseudoConstant::contributionStatus( $contributions['values'][0]['contribution_status_id'] ) + ); + $this->assertEquals( '-.5', $contributions['values'][1]['total_amount'] ); + } + +} -- To view, visit https://gerrit.wikimedia.org/r/343421 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1ddbcd72d62c0085e11def6d8a410914a40b8c27 Gerrit-PatchSet: 1 Gerrit-Project: wikimedia/fundraising/crm Gerrit-Branch: master Gerrit-Owner: Ejegg <eeggles...@wikimedia.org> Gerrit-Reviewer: Awight <awi...@wikimedia.org> Gerrit-Reviewer: Cdentinger <cdentin...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits