<s> from __future__ import absolute_import , division , print_function , unicode_literals <EOL> from decimal import Decimal as D <EOL> import braintree <EOL> from braintree . test . nonces import Nonces <EOL> import mock <EOL> import pytest <EOL> from aspen . utils import typecheck <EOL> from gratipay . billing . exchanges import ( <EOL> _prep_hit , <EOL> cancel_card_hold , <EOL> capture_card_hold , <EOL> create_card_hold , <EOL> record_exchange , <EOL> record_exchange_result <EOL> ) <EOL> from gratipay . exceptions import NegativeBalance , NotWhitelisted <EOL> from gratipay . models . exchange_route import ExchangeRoute <EOL> from gratipay . models . participant import Participant <EOL> from gratipay . testing import Foobar , Harness <EOL> from gratipay . testing . billing import BillingHarness <EOL> class TestCardHolds ( BillingHarness ) : <EOL> def test_create_card_hold_success ( self ) : <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert isinstance ( hold , braintree . Transaction ) <EOL> assert hold . status == '<STR_LIT>' <EOL> assert hold . amount == D ( '<STR_LIT>' ) <EOL> assert error == '<STR_LIT>' <EOL> assert self . obama . balance == obama . balance == <NUM_LIT:0> <EOL> def test_create_card_hold_for_suspicious_raises_NotWhitelisted ( self ) : <EOL> bob = self . make_participant ( '<STR_LIT>' , is_suspicious = True , <EOL> balanced_customer_href = '<STR_LIT>' ) <EOL> with self . assertRaises ( NotWhitelisted ) : <EOL> create_card_hold ( self . db , bob , D ( '<STR_LIT>' ) ) <EOL> @ mock . patch ( '<STR_LIT>' ) <EOL> def test_create_card_hold_failure ( self , btsale ) : <EOL> btsale . side_effect = Foobar <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> assert hold is None <EOL> assert error == "<STR_LIT>" <EOL> exchange = self . db . one ( "<STR_LIT>" ) <EOL> assert exchange <EOL> assert exchange . amount == D ( '<STR_LIT>' ) <EOL> assert exchange . fee == D ( '<STR_LIT>' ) <EOL> assert exchange . status == '<STR_LIT>' <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert self . obama . get_credit_card_error ( ) == '<STR_LIT>' <EOL> assert self . obama . balance == obama . balance == <NUM_LIT:0> <EOL> def test_create_card_hold_bad_card ( self ) : <EOL> bob = self . make_participant ( '<STR_LIT>' , is_suspicious = False ) <EOL> customer_id = bob . get_braintree_account ( ) . id <EOL> result = braintree . PaymentMethod . create ( { <EOL> "<STR_LIT>" : customer_id , <EOL> "<STR_LIT>" : Nonces . Transactable <EOL> } ) <EOL> assert result . is_success <EOL> ExchangeRoute . insert ( bob , '<STR_LIT>' , result . payment_method . token ) <EOL> hold , error = create_card_hold ( self . db , bob , D ( '<STR_LIT>' ) ) <EOL> assert hold is None <EOL> assert error . startswith ( '<STR_LIT>' ) <EOL> def test_create_card_hold_multiple_cards ( self ) : <EOL> bob = self . make_participant ( '<STR_LIT>' , is_suspicious = False ) <EOL> customer_id = bob . get_braintree_account ( ) . id <EOL> for i in range ( <NUM_LIT:2> ) : <EOL> result = braintree . PaymentMethod . create ( { <EOL> "<STR_LIT>" : customer_id , <EOL> "<STR_LIT>" : Nonces . Transactable <EOL> } ) <EOL> assert result . is_success <EOL> ExchangeRoute . insert ( bob , '<STR_LIT>' , result . payment_method . token ) <EOL> hold , error = create_card_hold ( self . db , bob , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> cancel_card_hold ( hold ) <EOL> def test_create_card_hold_no_card ( self ) : <EOL> bob = self . make_participant ( '<STR_LIT>' , is_suspicious = False ) <EOL> hold , error = create_card_hold ( self . db , bob , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> def test_create_card_hold_invalidated_card ( self ) : <EOL> bob = self . make_participant ( '<STR_LIT>' , is_suspicious = False ) <EOL> ExchangeRoute . insert ( bob , '<STR_LIT>' , '<STR_LIT:foo>' , error = '<STR_LIT>' ) <EOL> hold , error = create_card_hold ( self . db , bob , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> def test_capture_card_hold_full_amount ( self ) : <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> assert hold . status == '<STR_LIT>' <EOL> capture_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) , hold ) <EOL> hold = braintree . Transaction . find ( hold . id ) <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert self . obama . balance == obama . balance == D ( '<STR_LIT>' ) <EOL> assert self . obama . get_credit_card_error ( ) == '<STR_LIT>' <EOL> assert hold . status == '<STR_LIT>' <EOL> cancel_card_hold ( hold ) <EOL> def test_capture_card_hold_partial_amount ( self ) : <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> capture_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) , hold ) <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert self . obama . balance == obama . balance == D ( '<STR_LIT>' ) <EOL> assert self . obama . get_credit_card_error ( ) == '<STR_LIT>' <EOL> cancel_card_hold ( hold ) <EOL> def test_capture_card_hold_too_high_amount ( self ) : <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> with self . assertRaises ( Exception ) : <EOL> capture_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) , hold ) <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert self . obama . balance == obama . balance == <NUM_LIT:0> <EOL> cancel_card_hold ( hold ) <EOL> def test_capture_card_hold_amount_under_minimum ( self ) : <EOL> hold , error = create_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) ) <EOL> assert error == '<STR_LIT>' <EOL> capture_card_hold ( self . db , self . obama , D ( '<STR_LIT>' ) , hold ) <EOL> obama = Participant . from_id ( self . obama . id ) <EOL> assert self . obama . balance == obama . balance == D ( '<STR_LIT>' ) <EOL> assert self . obama . get_credit_card_error ( ) == '<STR_LIT>' <EOL> class TestFees ( Harness ) : <EOL> def prep ( self , amount ) : <EOL> """<STR_LIT>""" <EOL> typecheck ( amount , unicode ) <EOL> out = list ( _prep_hit ( D ( amount ) ) ) <EOL> out = [ out [ <NUM_LIT:0> ] ] + out [ <NUM_LIT:2> : ] <EOL> return tuple ( out ) <EOL> def test_prep_hit_basically_works ( self ) : <EOL> actual = _prep_hit ( D ( '<STR_LIT>' ) ) <EOL> expected = ( <NUM_LIT> , <EOL> u'<STR_LIT>' , <EOL> D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_full_in_rounded_case ( self ) : <EOL> actual = _prep_hit ( D ( '<STR_LIT>' ) ) <EOL> expected = ( <NUM_LIT:1000> , <EOL> u'<STR_LIT>' , <EOL> D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_ten_dollars ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_forty_cents ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_fifty_cents ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_sixty_cents ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_eighty_cents ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_nine_fifteen ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_nine_forty ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_nine_forty_one ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT:1000> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> def test_prep_hit_at_nine_forty_two ( self ) : <EOL> actual = self . prep ( u'<STR_LIT>' ) <EOL> expected = ( <NUM_LIT> , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) ) <EOL> assert actual == expected <EOL> class TestRecordExchange ( Harness ) : <EOL> def test_record_exchange_records_exchange ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , last_bill_result = '<STR_LIT>' ) <EOL> record_exchange ( self . db <EOL> , ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> , amount = D ( "<STR_LIT>" ) <EOL> , fee = D ( "<STR_LIT>" ) <EOL> , participant = alice <EOL> , status = '<STR_LIT>' <EOL> ) <EOL> actual = self . db . one ( """<STR_LIT>""" , back_as = dict ) <EOL> expected = { "<STR_LIT>" : D ( '<STR_LIT>' ) <EOL> , "<STR_LIT>" : D ( '<STR_LIT>' ) <EOL> , "<STR_LIT>" : "<STR_LIT>" <EOL> , "<STR_LIT:status>" : '<STR_LIT>' <EOL> , "<STR_LIT>" : ExchangeRoute . from_network ( alice , '<STR_LIT>' ) . id <EOL> } <EOL> assert actual == expected <EOL> def test_record_exchange_requires_valid_route ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , last_bill_result = '<STR_LIT>' ) <EOL> bob = self . make_participant ( '<STR_LIT>' , last_bill_result = '<STR_LIT>' ) <EOL> with self . assertRaises ( AssertionError ) : <EOL> record_exchange ( self . db <EOL> , ExchangeRoute . from_network ( bob , '<STR_LIT>' ) <EOL> , amount = D ( "<STR_LIT>" ) <EOL> , fee = D ( "<STR_LIT>" ) <EOL> , participant = alice <EOL> , status = '<STR_LIT>' <EOL> ) <EOL> def test_record_exchange_stores_error_in_note ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , last_bill_result = '<STR_LIT>' ) <EOL> record_exchange ( self . db <EOL> , ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> , amount = D ( "<STR_LIT>" ) <EOL> , fee = D ( "<STR_LIT>" ) <EOL> , participant = alice <EOL> , status = '<STR_LIT>' <EOL> , error = '<STR_LIT>' <EOL> ) <EOL> exchange = self . db . one ( "<STR_LIT>" ) <EOL> assert exchange . note == '<STR_LIT>' <EOL> def test_record_exchange_doesnt_update_balance_for_positive_amounts ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , last_bill_result = '<STR_LIT>' ) <EOL> record_exchange ( self . db <EOL> , ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> , amount = D ( "<STR_LIT>" ) <EOL> , fee = D ( "<STR_LIT>" ) <EOL> , participant = alice <EOL> , status = '<STR_LIT>' <EOL> ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> def test_record_exchange_updates_balance_for_negative_amounts ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , balance = <NUM_LIT:50> , last_paypal_result = '<STR_LIT>' ) <EOL> record_exchange ( self . db <EOL> , ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> , amount = D ( '<STR_LIT>' ) <EOL> , fee = D ( '<STR_LIT>' ) <EOL> , participant = alice <EOL> , status = '<STR_LIT>' <EOL> ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> def test_record_exchange_fails_if_negative_balance ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , last_paypal_result = '<STR_LIT>' ) <EOL> ba = ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> with pytest . raises ( NegativeBalance ) : <EOL> record_exchange ( self . db , ba , D ( "<STR_LIT>" ) , D ( "<STR_LIT>" ) , alice , '<STR_LIT>' ) <EOL> def test_record_exchange_result_restores_balance_on_error ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , balance = <NUM_LIT:30> , last_paypal_result = '<STR_LIT>' ) <EOL> ba = ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> e_id = record_exchange ( self . db , ba , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) , alice , '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> record_exchange_result ( self . db , e_id , '<STR_LIT>' , '<STR_LIT>' , alice ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> def test_record_exchange_result_restores_balance_on_error_with_invalidated_route ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , balance = <NUM_LIT> , last_paypal_result = '<STR_LIT>' ) <EOL> pp = ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> e_id = record_exchange ( self . db , pp , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) , alice , '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> pp . update_error ( '<STR_LIT>' ) <EOL> record_exchange_result ( self . db , e_id , '<STR_LIT>' , '<STR_LIT>' , alice ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> assert pp . error == alice . get_paypal_error ( ) == '<STR_LIT>' <EOL> def test_record_exchange_result_doesnt_restore_balance_on_success ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , balance = <NUM_LIT:50> , last_paypal_result = '<STR_LIT>' ) <EOL> ba = ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> e_id = record_exchange ( self . db , ba , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) , alice , '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> record_exchange_result ( self . db , e_id , '<STR_LIT>' , None , alice ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> def test_record_exchange_result_updates_balance_for_positive_amounts ( self ) : <EOL> alice = self . make_participant ( '<STR_LIT>' , balance = <NUM_LIT:4> , last_bill_result = '<STR_LIT>' ) <EOL> cc = ExchangeRoute . from_network ( alice , '<STR_LIT>' ) <EOL> e_id = record_exchange ( self . db , cc , D ( '<STR_LIT>' ) , D ( '<STR_LIT>' ) , alice , '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) <EOL> record_exchange_result ( self . db , e_id , '<STR_LIT>' , None , alice ) <EOL> alice = Participant . from_username ( '<STR_LIT>' ) <EOL> assert alice . balance == D ( '<STR_LIT>' ) </s>
