jenkins-bot has submitted this change and it was merged. Change subject: Encapsulated amount validation for better messages ......................................................................
Encapsulated amount validation for better messages Use the minimum amount message where appropriate for consistency with client side and better donor experience. Had to touch a lot of config files - maybe there's SOME use for the getCoreDataTransformers outside of testing? Bug: T105618 Change-Id: If9d60491cdc992a38a0ea459f5ece3f38ce08184 --- M README.txt M adyen_gateway/AmountInMinorUnits.php M adyen_gateway/config/transformers.yaml M amazon_gateway/config/transformers.yaml M astropay_gateway/config/transformers.yaml M extension.json A gateway_common/Amount.php M gateway_common/AmountInCents.php M gateway_common/DataValidator.php M gateway_common/DonationData.php M gateway_common/FiscalNumber.php M gateway_common/ValidationHelper.php M gateway_common/gateway.adapter.php M gateway_forms/Mustache.php M globalcollect_gateway/config/transformers.yaml M paypal_gateway/legacy/config/transformers.yaml M tests/Adapter/Amazon/AmazonTest.php A tests/AmountTest.php M tests/DataValidatorTest.php M worldpay_gateway/config/transformers.yaml 20 files changed, 427 insertions(+), 166 deletions(-) Approvals: XenoRyet: Looks good to me, approved jenkins-bot: Verified diff --git a/README.txt b/README.txt index adf8784..60dd3f4 100644 --- a/README.txt +++ b/README.txt @@ -89,6 +89,9 @@ Email address donors should contact with any donation-related problems $wgDonationInterfaceProblemsEmail = '[email protected]' +Email address donors should contact with donations too big to process online +$wgDonationInterfaceMajorGiftsEmail = '[email protected]'; + The full URL for Javascript-disabled credit card form redirect $wgDonationInterfaceNoScriptRedirect = null diff --git a/adyen_gateway/AmountInMinorUnits.php b/adyen_gateway/AmountInMinorUnits.php index cc64a03..fff6ffc 100644 --- a/adyen_gateway/AmountInMinorUnits.php +++ b/adyen_gateway/AmountInMinorUnits.php @@ -17,9 +17,9 @@ } $amount = $normalized['amount']; - if ( DataValidator::is_exponent3_currency( $normalized['currency_code'] ) ) { + if ( Amount::is_exponent3_currency( $normalized['currency_code'] ) ) { $stagedData['amount'] = $amount * 1000; - } elseif ( DataValidator::is_fractional_currency( $normalized['currency_code'] ) ) { + } elseif ( Amount::is_fractional_currency( $normalized['currency_code'] ) ) { $stagedData['amount'] = $amount * 100; } else { $amount = floor( $amount ); @@ -29,13 +29,13 @@ } public function unstage( GatewayType $adapter, $stagedData, &$unstagedData ) { - if ( DataValidator::is_exponent3_currency( $stagedData['currency_code'] ) ) { + if ( Amount::is_exponent3_currency( $stagedData['currency_code'] ) ) { $unstagedData['amount'] = $stagedData['amount'] / 1000; - } elseif ( DataValidator::is_fractional_currency( $stagedData['currency_code'] ) ) { + } elseif ( Amount::is_fractional_currency( $stagedData['currency_code'] ) ) { $unstagedData['amount'] = $stagedData['amount'] / 100; } else { $unstagedData['amount'] = $stagedData['amount']; } } -} \ No newline at end of file +} diff --git a/adyen_gateway/config/transformers.yaml b/adyen_gateway/config/transformers.yaml index d6fb781..1c7f45f 100644 --- a/adyen_gateway/config/transformers.yaml +++ b/adyen_gateway/config/transformers.yaml @@ -1,4 +1,5 @@ # Core +- Amount - DonorEmail - StreetAddress # Adyen-specific diff --git a/amazon_gateway/config/transformers.yaml b/amazon_gateway/config/transformers.yaml index 89e70d8..260f807 100644 --- a/amazon_gateway/config/transformers.yaml +++ b/amazon_gateway/config/transformers.yaml @@ -1,4 +1,5 @@ # Skip AmountInCents, we want to pass the real amount x1. +- Amount - DonorEmail - DonorFullName - StreetAddress diff --git a/astropay_gateway/config/transformers.yaml b/astropay_gateway/config/transformers.yaml index caaa153..bae2a9c 100644 --- a/astropay_gateway/config/transformers.yaml +++ b/astropay_gateway/config/transformers.yaml @@ -1,7 +1,9 @@ # Skip AmountInCents. +- Amount - AstroPayFinancialNumbers - AstroPayMethodCodec - FiscalNumber +- DonorEmail - DummyFiscalNumber # see class comment - DonorFullName - StreetAddress diff --git a/extension.json b/extension.json index a18b381..df397a1 100644 --- a/extension.json +++ b/extension.json @@ -64,6 +64,7 @@ "WorldpayGatewayAlias": "worldpay_gateway/worldpay_gateway.alias.php" }, "AutoloadClasses": { + "Amount": "gateway_common/Amount.php", "AmountInCents": "gateway_common/AmountInCents.php", "ArrayHelper": "gateway_common/ArrayHelper.php", "FiscalNumber": "gateway_common/FiscalNumber.php", @@ -445,6 +446,7 @@ "DonationInterfaceFaqURL": "https://wikimediafoundation.org/wiki/Special:LandingCheck?basic=true&landing_page=FAQ&language=$language&country=$country", "DonationInterfaceTaxURL": "https://wikimediafoundation.org/wiki/Special:LandingCheck?basic=true&landing_page=Tax_Deductibility&language=$language&country=$country", "DonationInterfaceProblemsEmail": "[email protected]", + "DonationInterfaceMajorGiftsEmail": "[email protected]", "DonationInterfaceDefaultEmail": "[email protected]", "DonationInterfaceDebugLog": false, "DonationInterfaceDisplayDebug": false, diff --git a/gateway_common/Amount.php b/gateway_common/Amount.php new file mode 100644 index 0000000..8f3e802 --- /dev/null +++ b/gateway_common/Amount.php @@ -0,0 +1,176 @@ +<?php + +class Amount implements ValidationHelper { + + public function validate( GatewayType $adapter, $normalized, &$errors ) { + if ( + !isset( $normalized['amount'] ) || + !isset( $normalized['currency_code'] ) + ) { + // Not enough info to validate + return; + } + if ( isset( $errors['currency_code'] ) ) { + // Already displaying an error + return; + } + $value = $normalized['amount']; + + if ( self::isZeroIsh( $value ) ) { + $errors['amount'] = DataValidator::getErrorMessage( + 'amount', + 'not_empty', + $normalized['language'] + ); + return; + } + $currency = $normalized['currency_code']; + $min = self::convert( $adapter->getGlobal( 'PriceFloor' ), $currency ); + $max = self::convert( $adapter->getGlobal( 'PriceCeiling' ), $currency ); + if ( + !is_numeric( $value ) || + $value < 0 + ) { + $errors['amount'] = WmfFramework::formatMessage( + 'donate_interface-error-msg-invalid-amount' + ); + } else if ( $value > $max ) { + // FIXME: should format the currency values in this message + $errors['amount'] = WmfFramework::formatMessage( + 'donate_interface-bigamount-error', + $max, + $currency, + $adapter->getGlobal( 'MajorGiftsEmail' ) + ); + } else if ( $value < $min ) { + $locale = $normalized['language'] . '_' . $normalized['country']; + $formattedMin = self::format( $min, $currency, $locale ); + $errors['amount'] = WmfFramework::formatMessage( + 'donate_interface-smallamount-error', + $formattedMin + ); + } + } + + /** + * Checks if the $value is missing or equivalent to zero. + * + * @param string $value The value to check for zero-ness + * @return boolean True if the $value is missing or zero, otherwise false + */ + protected static function isZeroIsh( $value ) { + if ( + $value === null || + trim( $value ) === '' || + ( is_numeric( $value ) && abs( $value ) < 0.01 ) + ) { + return true; + } + + return false; + } + + /** + * Convert an amount in USD to a particular currency + * + * This is grossly rudimentary and likely wildly inaccurate. + * This mimics the hard-coded values used by the WMF to convert currencies + * for validation on the front-end on the first step landing pages of their + * donation process - the idea being that we can get a close approximation + * of converted currencies to ensure that contributors are not going above + * or below the price ceiling/floor, even if they are using a non-US currency. + * + * In reality, this probably ought to use some sort of webservice to get real-time + * conversion rates. + * + * @param float $amount + * @param string $currency + * @return float + * @throws UnexpectedValueException + */ + public static function convert( $amount, $currency ) { + $rates = CurrencyRates::getCurrencyRates(); + $code = strtoupper( $currency ); + if ( array_key_exists( $code, $rates ) ) { + return $amount * $rates[$code]; + } + throw new UnexpectedValueException( + 'Bad programmer! Bad currency made it too far through the portcullis' + ); + } + + /** + * Some currencies, like JPY, don't exist in fractional amounts. + * This rounds an amount to the appropriate number of decimal places. + * Use the results of this for internal use, and use @see Amount::format + * for values displayed to donors. + * + * @param float $amount + * @param string $currencyCode + * @return string rounded amount + */ + public static function round( $amount, $currencyCode ) { + if ( self::is_fractional_currency( $currencyCode ) ){ + return number_format( $amount, 2, '.', '' ); + } else { + return ( string ) floor( $amount ); + } + } + + /** + * If an amount is ever expressed for the fractional currencies defined in + * this function, they should not have an associated fractional amount + * (so: full integers only). + * + * @param string $currency_code The three-digit currency code. + * @return boolean + */ + public static function is_fractional_currency( $currency_code ){ + // these currencies cannot have cents. + $non_fractional_currencies = array( + 'CLP', 'DJF', 'IDR', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'VND', 'XAF', 'XOF', 'XPF' + ); + + if ( in_array( strtoupper( $currency_code ), $non_fractional_currencies ) ) { + return false; + } + return true; + } + + /** + * Checks if ISO 4217 defines the currency's minor units as being expressed using + * exponent 3 (three decimal places). + * @param string $currency_code The three-character currency code. + * @return boolean + */ + public static function is_exponent3_currency( $currency_code ){ + + $exponent3_currencies = array( 'BHD', 'CLF', 'IQD', 'KWD', 'LYD', 'MGA', 'MRO', 'OMR', 'TND' ); + + if ( in_array( strtoupper( $currency_code ), $exponent3_currencies ) ) { + return true; + } + return false; + } + + /** + * Format an amount and currency for display to users. + * + * @param float $amount + * @param string $currencyCode + * @param string $locale e.g. en_US + * @return string + */ + public static function format( $amount, $currencyCode, $locale ) { + $amount = self::round( $amount, $currencyCode ); + if ( class_exists( 'NumberFormatter' ) ) { + $formatter = new NumberFormatter( $locale, NumberFormatter::CURRENCY ); + return $formatter->formatCurrency( + $amount, + $currencyCode + ); + } else { + return "$amount $currencyCode"; + } + } +} diff --git a/gateway_common/AmountInCents.php b/gateway_common/AmountInCents.php index 8222dd6..545d7b3 100644 --- a/gateway_common/AmountInCents.php +++ b/gateway_common/AmountInCents.php @@ -11,16 +11,17 @@ */ class AmountInCents implements StagingHelper, UnstagingHelper { public function stage( GatewayType $adapter, $normalized, &$stagedData ) { - if ( empty( $normalized['amount'] ) || empty( $normalized['currency_code'] ) ) { + if ( + empty( $normalized['amount'] ) || + empty( $normalized['currency_code'] ) || + !is_numeric( $normalized['amount'] ) + ) { //can't do anything with amounts at all. Just go home. unset( $stagedData['amount'] ); return; } - $amount = $normalized['amount']; - if ( !DataValidator::is_fractional_currency( $normalized['currency_code'] ) ) { - $amount = floor( $amount ); - } + $amount = Amount::round( $normalized['amount'], $normalized['currency_code'] ); $stagedData['amount'] = $amount * 100; } diff --git a/gateway_common/DataValidator.php b/gateway_common/DataValidator.php index d069ca4..caf6882 100644 --- a/gateway_common/DataValidator.php +++ b/gateway_common/DataValidator.php @@ -25,10 +25,6 @@ */ public static function getErrorToken( $field ){ switch ( $field ) { - case 'amountGiven' : - case 'amountOther' : - $error_token = 'amount'; - break; case 'email' : case 'amount' : case 'currency_code' : @@ -138,14 +134,7 @@ //getErrorToken is actually for something entirely different: //Figuring out where on the form the error should land. $token = self::getErrorToken( $field ); - switch ( $token ) { - case 'amount': - $error_key_calc = 'donate_interface-error-msg-invalid-amount'; - break; - default: - $error_key_calc = 'donate_interface-error-msg-' . $token . '-calc'; - break; - } + $error_key_calc = 'donate_interface-error-msg-' . $token . '-calc'; if ( $type === 'calculated' ){ // try for the special "calculated" error message. @@ -204,7 +193,6 @@ // Define all default validations. $validations = array( 'not_empty' => array( - 'amount' => 'validate_non_zero', 'country', 'currency_code', 'gateway', @@ -212,9 +200,6 @@ 'valid_type' => array( '_cache_' => 'validate_boolean', 'account_number' => 'validate_numeric', - 'amount' => 'validate_numeric', - 'amountGiven' => 'validate_numeric', - 'amountOther' => 'validate_numeric', 'anonymous' => 'validate_boolean', 'contribution_tracking_id' => 'validate_numeric', 'currency_code' => 'validate_alphanumeric', @@ -236,10 +221,6 @@ 'fname' => 'validate_name', 'lname' => 'validate_name', 'name' => 'validate_name', - - // Depends on currency_code and gateway. - 'amount' => 'validate_amount', - ), ); @@ -295,13 +276,6 @@ $result = null; // Handle special cases. switch ( $validation_function ) { - case 'validate_amount': - if ( self::checkValidationPassed( array( 'currency_code', 'gateway' ), $results ) ){ - $priceFloor = $gateway->getGlobal( 'PriceFloor' ); - $priceCeiling = $gateway->getGlobal( 'PriceCeiling' ); - $result = call_user_func( $callable, $data[$field], $data['currency_code'], $priceFloor, $priceCeiling ); - } //otherwise, just don't do the validation. The other stuff will be complaining already. - break; case 'validate_currency_code': $result = call_user_func( $callable, $data[$field], $gateway->getCurrencies( $data ) ); break; @@ -357,31 +331,6 @@ protected static function validate_email( $value ) { return WmfFramework::validateEmail( $value ) && !DataValidator::cc_number_exists_in_str( $value ); - } - - /** - * validate_amount - * - * Determines if the $value passed in is a valid amount. - * @param string $value The piece of data that is supposed to be an amount. - * @param string $currency_code The amount was given in this currency. - * @param float $priceFloor Minimum valid amount (USD). - * @param float $priceCeiling Maximum valid amount (USD). - * - * @return boolean True if $value is a valid amount, otherwise false. - */ - protected static function validate_amount( $value, $currency_code, $priceFloor, $priceCeiling ) { - if ( !$value || !$currency_code || !is_numeric( $value ) ) { - return false; - } - - if ( !preg_match( '/^\d+(\.(\d+)?)?$/', $value ) || - ( ( float ) self::convert_to_usd( $currency_code, $value ) < ( float ) $priceFloor || - ( float ) self::convert_to_usd( $currency_code, $value ) > ( float ) $priceCeiling ) ) { - return false; - } - - return true; } protected static function validate_currency_code( $value, $acceptedCurrencies ) { @@ -567,22 +516,6 @@ } /** - * Checks to make sure that the $value is present in the $data array, and not equivalent to zero. - * @param string $value The value to check for non-zeroness. - * @param array $data The whole data set. - * @return boolean True if the $value is not missing or zero, otherwise false. - */ - public static function validate_non_zero( $value ) { - if ( $value === null || $value === '' - || preg_match( '/^0+(\.0+)?$/', $value ) - ) { - return false; - } - - return true; - } - - /** * Analyzes a string to see if any credit card numbers are hiding out in it * * @param $str @@ -671,36 +604,6 @@ } return( ( $sum % 10 ) == 0 ); } - - /** - * Convert an amount for a particular currency to an amount in USD - * - * This is grosley rudimentary and likely wildly inaccurate. - * This mimicks the hard-coded values used by the WMF to convert currencies - * for validatoin on the front-end on the first step landing pages of their - * donation process - the idea being that we can get a close approximation - * of converted currencies to ensure that contributors are not going above - * or below the price ceiling/floor, even if they are using a non-US currency. - * - * In reality, this probably ought to use some sort of webservice to get real-time - * conversion rates. - * - * @param string $currency_code - * @param float $amount - * @return float - * @throws UnexpectedValueException - */ - public static function convert_to_usd( $currency_code, $amount ) { - $rates = CurrencyRates::getCurrencyRates(); - $code = strtoupper( $currency_code ); - if ( array_key_exists( $code, $rates ) ) { - $usd_amount = $amount / $rates[$code]; - } else { - throw new UnexpectedValueException( 'Bad programmer! Bad currency made it too far through the portcullis' ); - } - return $usd_amount; - } - /** * Calculates and returns the card type for a given credit card number. @@ -832,39 +735,6 @@ } else { return false; } - } - - /** - * More of a validation helper function. If an amount is ever expressed for - * the fractional currencies defined in this function, - * they should not have an associated fractional amount (so: full integers only). - * @param string $currency_code The three-digit currency code. - * @return boolean - */ - public static function is_fractional_currency( $currency_code ){ - // these currencies cannot have cents. - $non_fractional_currencies = array( 'CLP', 'DJF', 'IDR', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'VND', 'XAF', 'XOF', 'XPF' ); - - if ( in_array( strtoupper( $currency_code ), $non_fractional_currencies ) ) { - return false; - } - return true; - } - - /** - * Checks if ISO 4217 defines the currency's minor units as being expressed using - * exponent 3 (three decimal places). - * @param string $currency_code The three-character currency code. - * @return boolean - */ - public static function is_exponent3_currency( $currency_code ){ - - $exponent3_currencies = array( 'BHD', 'CLF', 'IQD', 'KWD', 'LYD', 'MGA', 'MRO', 'OMR', 'TND' ); - - if ( in_array( strtoupper( $currency_code ), $exponent3_currencies ) ) { - return true; - } - return false; } /** diff --git a/gateway_common/DonationData.php b/gateway_common/DonationData.php index dba7860..bcde039 100644 --- a/gateway_common/DonationData.php +++ b/gateway_common/DonationData.php @@ -544,11 +544,10 @@ return; } - if ( DataValidator::is_fractional_currency( $this->getVal( 'currency_code' ) ) ) { - $this->setVal( 'amount', number_format( $this->getVal( 'amount' ), 2, '.', '' ) ); - } else { - $this->setVal( 'amount', floor( $this->getVal( 'amount' ) ) ); - } + $this->setVal( + 'amount', + Amount::round( $this->getVal( 'amount' ), $this->getVal( 'currency_code' ) ) + ); } /** @@ -985,7 +984,11 @@ $transformers = $this->gateway->getDataTransformers(); foreach ( $transformers as $transformer ) { if ( $transformer instanceof ValidationHelper ) { - $transformer->validate( $this->normalized, $this->validationErrors ); + $transformer->validate( + $this->gateway, + $this->normalized, + $this->validationErrors + ); } } } diff --git a/gateway_common/FiscalNumber.php b/gateway_common/FiscalNumber.php index 7b8b626..c2d66db 100644 --- a/gateway_common/FiscalNumber.php +++ b/gateway_common/FiscalNumber.php @@ -56,10 +56,11 @@ /** * Rudimentary tests of fiscal number format for different countries * + * @param GatewayType $unused * @param array $normalized Normalized donation data * @param array $errors Results of validation to this point */ - public function validate( $normalized, &$errors ) { + public function validate( GatewayType $unused, $normalized, &$errors ) { if ( !isset( $normalized['country'] ) || !isset( $normalized[self::$key] ) diff --git a/gateway_common/ValidationHelper.php b/gateway_common/ValidationHelper.php index 8926315..e2c0c50 100644 --- a/gateway_common/ValidationHelper.php +++ b/gateway_common/ValidationHelper.php @@ -4,6 +4,11 @@ /** * Run validation on whatever normalized data we're responsible for, * and set errors per field, or under the "general" key. + * + * @param GatewayType $adapter + * @param array $normalized Donation data in normalized form. + * @param array $errors Reference to error array + * @return void */ - function validate( $normalized, &$errors ); + function validate( GatewayType $adapter, $normalized, &$errors ); } diff --git a/gateway_common/gateway.adapter.php b/gateway_common/gateway.adapter.php index 7847f14..0e3d68d 100644 --- a/gateway_common/gateway.adapter.php +++ b/gateway_common/gateway.adapter.php @@ -453,6 +453,7 @@ // Always stage email address first, to set default if missing new DonorEmail(), new DonorFullName(), + new Amount(), new AmountInCents(), new StreetAddress(), ); diff --git a/gateway_forms/Mustache.php b/gateway_forms/Mustache.php index 9216a11..d6665ea 100644 --- a/gateway_forms/Mustache.php +++ b/gateway_forms/Mustache.php @@ -215,16 +215,12 @@ 'selected' => ( $currency === $data['currency_code'] ), ); } - if ( class_exists( 'NumberFormatter' ) ) { - $locale = $data['language'] . '_' . $data['country']; - $formatter = new NumberFormatter( $locale, NumberFormatter::CURRENCY ); - $data['display_amount'] = $formatter->formatCurrency( - $data['amount'], - $data['currency_code'] - ); - } else { - $data['display_amount'] = "{$data['amount']} {$data['currency_code']}"; - } + + $data['display_amount'] = Amount::format( + $data['amount'], + $data['currency_code'], + $data['language'] . '_' . $data['country'] + ); } /** diff --git a/globalcollect_gateway/config/transformers.yaml b/globalcollect_gateway/config/transformers.yaml index cea8f45..082adfe 100644 --- a/globalcollect_gateway/config/transformers.yaml +++ b/globalcollect_gateway/config/transformers.yaml @@ -1,4 +1,5 @@ # Core +- Amount - DonorEmail - DonorFullName - AmountInCents diff --git a/paypal_gateway/legacy/config/transformers.yaml b/paypal_gateway/legacy/config/transformers.yaml index 0e676ed..3f63205 100644 --- a/paypal_gateway/legacy/config/transformers.yaml +++ b/paypal_gateway/legacy/config/transformers.yaml @@ -1,4 +1,5 @@ # Core +- Amount - DonorEmail # PayPal diff --git a/tests/Adapter/Amazon/AmazonTest.php b/tests/Adapter/Amazon/AmazonTest.php index 1bcd5c7..aadbe31 100644 --- a/tests/Adapter/Amazon/AmazonTest.php +++ b/tests/Adapter/Amazon/AmazonTest.php @@ -87,13 +87,9 @@ 'donate_interface-fallback-currency-notice', 'USD' )->inLanguage( $language )->text(); - if ( class_exists( 'NumberFormatter' ) ) { - $locale = $init['language'] . '_' . $init['country']; - $formatter = new NumberFormatter( $locale, NumberFormatter::CURRENCY ); - $expectedDisplayAmount = $formatter->formatCurrency( $expectedAmount, 'USD' ); - } else { - $expectedDisplayAmount = "$expectedAmount USD"; - } + + $locale = $init['language'] . '_' . $init['country']; + $expectedDisplayAmount = Amount::format( $expectedAmount, 'USD', $locale ); $that = $this; //needed for PHP pre-5.4 $convertTest = function( $amountString ) use ( $expectedDisplayAmount, $that ) { diff --git a/tests/AmountTest.php b/tests/AmountTest.php new file mode 100644 index 0000000..1f365ab --- /dev/null +++ b/tests/AmountTest.php @@ -0,0 +1,199 @@ +<?php +/** + * Wikimedia Foundation + * + * LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/** + * @group Fundraising + * @group DonationInterface + * @group Validation + */ +class AmountTest extends DonationInterfaceTestCase { + + /** + * @var GatewayType + */ + protected $adapter; + /** + * @var Amount + */ + protected $validator; + /** + * @var array + */ + protected $errors; + /** + * @var array + */ + protected $normalized; + + public function setUp() { + parent::setUp(); + $this->setMwGlobals( array( + 'wgDonationInterfacePriceFloor' => 1.50, + 'wgDonationInterfacePriceCeiling' => 100, + 'wgLanguageCode' => 'en', + ) ); + + $this->setUpRequest( array( + 'country' => 'US', + 'uselang' => 'en', + ) ); + + $this->normalized = array( + 'language' => 'en', + 'country' => 'US', + 'currency_code' => 'USD', + ); + + $this->errors = array(); + $this->adapter = new TestingGenericAdapter(); + $this->validator = new Amount(); + } + + protected function validate() { + $this->validator->validate( + $this->adapter, $this->normalized, $this->errors + ); + } + + public function testValidUsd() { + $this->normalized['amount'] = '10.00'; + $this->validate(); + $this->assertEmpty( $this->errors, 'Error shown for valid amount' ); + } + + public function testZeroAmount() { + $this->normalized['amount'] = '0.00'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for zero amount' ); + $expected = DataValidator::getErrorMessage( + 'amount', 'not_empty', 'en' + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for zero amount' + ); + } + + public function testWhitespaceAmount() { + $this->normalized['amount'] = ' '; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for whitespace amount' ); + $expected = DataValidator::getErrorMessage( + 'amount', 'not_empty', 'en' + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for whitespace amount' + ); + } + + public function testNonNumericAmount() { + $this->normalized['amount'] = 'XYZ123'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for non-numeric amount' ); + $this->assertEquals( + WmfFramework::formatMessage( 'donate_interface-error-msg-invalid-amount' ), + $this->errors['amount'], + 'Wrong error message for non-numeric amount' + ); + } + + public function testNegativeAmount() { + $this->normalized['amount'] = '-100.00'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for negative amount' ); + $this->assertEquals( + WmfFramework::formatMessage( 'donate_interface-error-msg-invalid-amount' ), + $this->errors['amount'], + 'Wrong error message for negative amount' + ); + } + + public function testTooMuchUsd() { + $this->normalized['amount'] = '101.00'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for excessive amount (USD)' ); + $expected = WmfFramework::formatMessage( + 'donate_interface-bigamount-error', + 100, + 'USD', + $this->adapter->getGlobal( 'MajorGiftsEmail' ) + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for excessive amount (USD)' + ); + } + + public function testTooLittleUsd() { + $this->normalized['amount'] = '1.49'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for diminutive amount (USD)' ); + + $formattedMin = Amount::format( 1.50, 'USD', 'en_US' ); + $expected = WmfFramework::formatMessage( + 'donate_interface-smallamount-error', + $formattedMin + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for diminutive amount (USD)' + ); + } + + // Conversion tests depend on Barbadian monetary policy + // BBD is convenient as it's pegged to $0.50 + public function testTooMuchBbd() { + $this->normalized['currency_code'] = 'BBD'; + $this->normalized['amount'] = '201.00'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for excessive amount (BBD)' ); + $expected = WmfFramework::formatMessage( + 'donate_interface-bigamount-error', + 200, + 'BBD', + $this->adapter->getGlobal( 'MajorGiftsEmail' ) + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for excessive amount (BBD)' + ); + } + + public function testTooLittleBbd() { + $this->normalized['currency_code'] = 'BBD'; + $this->normalized['amount'] = '2.95'; + $this->validate(); + $this->assertNotEmpty( $this->errors, 'No error for diminutive amount (BBD)' ); + + $formattedMin = Amount::format( 3.00, 'BBD', 'en_US' ); + $expected = WmfFramework::formatMessage( + 'donate_interface-smallamount-error', + $formattedMin + ); + $this->assertEquals( + $expected, + $this->errors['amount'], + 'Wrong error message for diminutive amount (BBD)' + ); + } +} diff --git a/tests/DataValidatorTest.php b/tests/DataValidatorTest.php index 2c6c3f7..a158ae6 100644 --- a/tests/DataValidatorTest.php +++ b/tests/DataValidatorTest.php @@ -141,6 +141,7 @@ $validator = new FiscalNumber(); $errors = array(); $validator->validate( + new TestingGenericAdapter(), array( 'country' => $country, 'fiscal_number' => $value, 'language' => 'en' ), $errors ); diff --git a/worldpay_gateway/config/transformers.yaml b/worldpay_gateway/config/transformers.yaml index 7bd55ed..cf439ca 100644 --- a/worldpay_gateway/config/transformers.yaml +++ b/worldpay_gateway/config/transformers.yaml @@ -1,4 +1,5 @@ # Core +- Amount - DonorEmail - DonorFullName - AmountInCents -- To view, visit https://gerrit.wikimedia.org/r/286261 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: If9d60491cdc992a38a0ea459f5ece3f38ce08184 Gerrit-PatchSet: 13 Gerrit-Project: mediawiki/extensions/DonationInterface Gerrit-Branch: master Gerrit-Owner: Ejegg <[email protected]> Gerrit-Reviewer: AndyRussG <[email protected]> Gerrit-Reviewer: Awight <[email protected]> Gerrit-Reviewer: Cdentinger <[email protected]> Gerrit-Reviewer: Ejegg <[email protected]> Gerrit-Reviewer: Ssmith <[email protected]> Gerrit-Reviewer: XenoRyet <[email protected]> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
