def test_stt_doesnt_allow_just_any_ole_amount(self): alice = self.make_participant('alice') bob = self.make_participant('bob') with self.assertRaises(BadAmount) as cm: alice.set_tip_to(bob, EUR('0.001')) expected = "'€0.00' is not a valid weekly donation amount (min=€0.01, max=€100.00)" actual = cm.exception.render_in_english() assert actual == expected with self.assertRaises(BadAmount) as cm: alice.set_tip_to(bob, USD('1000.00')) expected = "'$1,000.00' is not a valid weekly donation amount (min=$0.01, max=$100.00)" actual = cm.exception.render_in_english() assert actual == expected with self.assertRaises(BadAmount) as cm: alice.set_tip_to(bob, USD('0.01'), 'yearly') expected = "'$0.01' is not a valid yearly donation amount (min=$0.52, max=$5,200.00)" actual = cm.exception.render_in_english() assert actual == expected with self.assertRaises(BadAmount) as cm: alice.set_tip_to(bob, EUR('10000'), 'yearly') expected = "'€10,000.00' is not a valid yearly donation amount (min=€0.52, max=€5,200.00)" actual = cm.exception.render_in_english() assert actual == expected
def test_payout_amount_under_minimum(self, gba): usd_user = self.make_participant('usd_user', main_currency='USD') route = ExchangeRoute.insert(usd_user, 'mango-ba', 'fake ID') self.make_exchange('mango-cc', USD(8), 0, usd_user) gba.return_value = self.bank_account_outside_sepa with self.assertRaises(FeeExceedsAmount): payout(self.db, route, USD('0.10'))
def test_stripe_direct_debit_receipt(self): self.donor.set_tip_to(self.recipient, USD('0.99')) route = self.upsert_route(self.donor, 'stripe-sdd', address='src_1E42IaFk4eGpfLOCUau5nIdg') payin = self.make_payin_and_transfer(route, self.recipient, USD('20.02'))[0] r = self.client.GET(self.donor.path('receipts/direct/%s' % payin.id), auth_as=self.donor) assert r.code == 200, r.text
def set_up_team_with_two_currencies(self): team = self.team = self.make_participant( 'team', kind='group', accepted_currencies='EUR,USD' ) self.alice = self.make_participant('alice', main_currency='EUR', accepted_currencies='EUR,USD') team.set_take_for(self.alice, EUR('1.00'), team) self.bob = self.make_participant('bob', main_currency='USD', accepted_currencies='EUR,USD') team.set_take_for(self.bob, EUR('1.00'), team) self.donor1_eur = self.make_participant('donor1_eur', balance=EUR(100)) self.donor2_usd = self.make_participant('donor2_usd', balance=USD(100)) self.donor3_eur = self.make_participant('donor3_eur', balance=EUR(100)) self.donor4_usd = self.make_participant('donor4_usd', balance=USD(100))
def test_stt_converts_monthly_and_yearly_amounts_correctly(self): alice = self.make_participant('alice') bob = self.make_participant('bob', accepted_currencies='EUR,USD') t = alice.set_tip_to(bob, EUR('0.05'), 'monthly') assert t['amount'] == EUR('0.01') t = alice.set_tip_to(bob, USD('433.34'), 'monthly') assert t['amount'] == USD('100.00') t = alice.set_tip_to(bob, USD('0.52'), 'yearly') assert t['amount'] == USD('0.01') t = alice.set_tip_to(bob, EUR('5200.00'), 'yearly') assert t['amount'] == EUR('100.00')
def test_donation_form_v2(self): creator = self.make_participant('creator', accepted_currencies=None) r = self.client.GET('/creator/donate?currency=KRW') assert r.code == 200 assert ">Pledge<" in r.text assert ' name="currency" value="KRW"' in r.text, r.text self.add_payment_account(creator, 'stripe') r = self.client.GET('/creator/donate?currency=JPY&amount=2000&period=monthly') assert r.code == 200 assert ">Donate<" in r.text assert ' value="2,000"' assert ' name="period" value="monthly" checked' donor = self.make_participant('donor') r = self.client.PxST( '/creator/tip', { 'currency': 'USD', 'selected_amount': '1.00', 'renewal_mode': '2', 'visibility': '3', }, auth_as=donor, ) assert r.code == 302 assert r.headers[b'Location'] == (b'/donor/giving/pay/?beneficiary=%i' % creator.id) tip = donor.get_tip_to(creator) assert tip.amount == USD('1.00') assert tip.renewal_mode == 2 assert tip.visibility == 3
def test_get_end_of_period_balances(self): make_history(self) today = get_start_of_current_utc_day() period_end = today.replace(month=1, day=1) balances = get_end_of_period_balances(self.db, self.alice, period_end, today) assert list(balances) == [EUR('10.00'), USD('0.00')]
def test_take_paid_in_advance_in_unaccepted_currency(self): team = self.make_participant('team', kind='group', accepted_currencies=None) alice = self.make_participant('alice', main_currency='EUR', accepted_currencies='EUR,USD') team.set_take_for(alice, EUR('1.00'), team) bob = self.make_participant('bob', main_currency='USD', accepted_currencies='EUR,USD') team.set_take_for(bob, USD('1.00'), team) stripe_account_alice = self.add_payment_account(alice, 'stripe', default_currency='EUR') self.add_payment_account(bob, 'stripe', country='US', default_currency='USD') carl = self.make_participant('carl') carl.set_tip_to(team, JPY('1250')) carl_card = ExchangeRoute.insert( carl, 'stripe-card', 'x', 'chargeable', remote_user_id='x' ) payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250')) assert pt.destination == stripe_account_alice.pk Payday.start().run() transfers = self.db.all("SELECT * FROM transfers ORDER BY id") assert len(transfers) == 1 assert transfers[0].virtual is True assert transfers[0].tipper == carl.id assert transfers[0].tippee == alice.id assert transfers[0].amount == JPY('125')
def test_int_to_Money(self): expected = USD('1.02') actual = int_to_Money(102, 'USD') assert expected == actual expected = JPY('1') actual = int_to_Money(1, 'JPY') assert expected == actual
def test_sums(self): # Empty sum actual = self.db.one( "SELECT sum(x) FROM unnest(NULL::currency_amount[]) x") assert actual is None actual = self.db.one( "SELECT sum(x) FROM unnest(ARRAY[NULL]::currency_amount[]) x") assert actual is None # Empty fuzzy sum actual = self.db.one( "SELECT sum(x, 'EUR') FROM unnest(NULL::currency_amount[]) x") assert actual is None actual = self.db.one( "SELECT sum(x, 'EUR') FROM unnest(ARRAY[NULL]::currency_amount[]) x" ) assert actual is None # Single-currency sum amounts = [JPY('133'), JPY('977')] expected = sum(amounts) actual = self.db.one("SELECT sum(x, 'JPY') FROM unnest(%s) x", (amounts + [None], )) assert expected == actual # Fuzzy sum amounts = [EUR('0.50'), USD('1.20')] expected = MoneyBasket(*amounts).fuzzy_sum('EUR') actual = self.db.one("SELECT sum(x, 'EUR') FROM unnest(%s) x", (amounts + [None], )) assert expected == actual, (expected.__dict__, actual.__dict__)
def test_payin_pages_when_currencies_dont_match(self): self.add_payment_account(self.creator_1, 'stripe') self.add_payment_account(self.creator_2, 'paypal') self.add_payment_account(self.creator_3, 'stripe') self.add_payment_account(self.creator_3, 'paypal') self.donor.set_tip_to(self.creator_1, EUR('11.00')) self.donor.set_tip_to(self.creator_2, JPY('1100')) self.donor.set_tip_to(self.creator_3, USD('11.00')) paypal_path = '/donor/giving/pay/paypal/?beneficiary=%i,%i' % ( self.creator_2.id, self.creator_3.id ) stripe_path = '/donor/giving/pay/stripe/?beneficiary=%i,%i&method=card' % ( self.creator_1.id, self.creator_3.id ) r = self.client.GET('/donor/giving/pay/', auth_as=self.donor) assert r.code == 200, r.text assert str(Markup.escape(paypal_path)) not in r.text assert str(Markup.escape(stripe_path)) not in r.text r = self.client.GxT(paypal_path, auth_as=self.donor) assert r.code == 400, r.text r = self.client.GxT(stripe_path, auth_as=self.donor) assert r.code == 400, r.text
def test_convert(self): original = EUR('1.00') expected = USD('1.20') actual = self.db.one("SELECT convert(%s, %s)", (original, expected.currency)) assert expected == actual actual = original.convert(expected.currency) assert expected == actual
def test_underfunded_team_with_two_balanced_currencies(self): self.set_up_team_with_two_currencies() self.donor1_eur.set_tip_to(self.team, EUR('0.25')) self.donor2_usd.set_tip_to(self.team, USD('0.30')) self.donor3_eur.set_tip_to(self.team, EUR('0.25')) self.donor4_usd.set_tip_to(self.team, USD('0.30')) Payday.start().shuffle() expected = { 'alice': MoneyBasket(EUR('0.50')), 'bob': MoneyBasket(USD('0.60')), 'donor1_eur': MoneyBasket(EUR('99.75')), 'donor2_usd': MoneyBasket(USD('99.70')), 'donor3_eur': MoneyBasket(EUR('99.75')), 'donor4_usd': MoneyBasket(USD('99.70')), } actual = self.get_balances() assert expected == actual
def test_transfer_takes_with_two_currencies_on_both_sides(self): self.set_up_team_with_two_currencies() self.team.set_take_for(self.alice, USD('0.01'), self.alice) self.team.set_take_for(self.bob, EUR('0.01'), self.bob) self.donor1_eur.set_tip_to(self.team, EUR('0.01')) self.donor2_usd.set_tip_to(self.team, USD('0.01')) Payday.start().shuffle() expected = { 'alice': MoneyBasket(EUR('0.01')), 'bob': MoneyBasket(USD('0.01')), 'donor1_eur': MoneyBasket(EUR('99.99')), 'donor2_usd': MoneyBasket(USD('99.99')), 'donor3_eur': MoneyBasket(EUR('100')), 'donor4_usd': MoneyBasket(USD('100')), } actual = self.get_balances() assert expected == actual
def test_donation_form_v2_does_not_overwrite_visibility(self): creator = self.make_participant('creator', accepted_currencies=None) self.add_payment_account(creator, 'stripe') donor = self.make_participant('donor') donor.set_tip_to(creator, USD('10.00'), renewal_mode=1, visibility=3) r = self.client.PxST( '/creator/tip', { 'currency': 'USD', 'selected_amount': '1.00', 'renewal_mode': '2', }, auth_as=donor, ) assert r.code == 302 assert r.headers[b'Location'].startswith(b'/donor/giving/') tip = donor.get_tip_to(creator) assert tip.amount == USD('1.00') assert tip.renewal_mode == 2 assert tip.visibility == 3
def test_schedule_renewals_handles_currency_switch(self): alice = self.make_participant('alice', email='*****@*****.**') bob = self.make_participant('bob', email='*****@*****.**', accepted_currencies=None) alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2) alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa') self.make_payin_and_transfer(alice_card, bob, EUR('37.00')) schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL") next_payday = compute_next_payday_date() expected_transfers = [ { 'tippee_id': bob.id, 'tippee_username': '******', 'amount': EUR('37.00').for_json(), } ] assert len(schedule) == 1 assert schedule[0].amount == EUR('37.00') assert schedule[0].transfers == expected_transfers expected_renewal_date = next_payday + timedelta(weeks=37) assert schedule[0].execution_date == expected_renewal_date assert schedule[0].automatic is True # Change the currency alice.set_tip_to(bob, USD('1.20'), renewal_mode=2) schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL") assert len(schedule) == 1 expected_transfers = [dict(expected_transfers[0], amount=USD('44.40').for_json())] assert schedule[0].amount == USD('44.40') assert schedule[0].transfers == expected_transfers assert schedule[0].execution_date == expected_renewal_date assert schedule[0].automatic is True # Customize the renewal sp_id = schedule[0].id r = self.client.GET("/alice/giving/schedule", auth_as=alice) assert r.code == 200 r = self.client.GET( "/alice/giving/schedule?id=%i&action=modify" % sp_id, auth_as=alice ) assert r.code == 200 new_date = expected_renewal_date - timedelta(days=14) r = self.client.PxST( "/alice/giving/schedule?id=%i&action=modify" % sp_id, { 'amount': '42.00', 'currency': 'USD', 'new_date': new_date.isoformat(), }, auth_as=alice ) assert r.code == 302 sp = self.db.one("SELECT * FROM scheduled_payins WHERE id = %s", (sp_id,)) assert sp.amount == USD('42.00') assert sp.execution_date == new_date assert sp.customized is True schedule = alice.schedule_renewals() assert len(schedule) == 1 assert schedule[0].amount == USD('42.00') assert schedule[0].execution_date == new_date assert schedule[0].customized is True # Change the currency again alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2) schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL") assert len(schedule) == 1 expected_transfers = [dict(expected_transfers[0], amount=EUR('35.00').for_json())] assert schedule[0].amount == EUR('35.00') assert schedule[0].transfers == expected_transfers assert schedule[0].execution_date == new_date assert schedule[0].automatic is True assert schedule[0].customized is True
def test_sorting(self): amounts = [JPY('130'), EUR('99.58'), Money('79', 'KRW'), USD('35.52')] expected = sorted(amounts, key=lambda m: -m.convert('EUR').amount) actual = self.db.all("SELECT x FROM unnest(%s) x ORDER BY x DESC", (amounts, )) assert expected == actual
def test_takes_paid_in_advance_to_now_inactive_members(self): team = self.make_participant('team', kind='group', accepted_currencies=None) alice = self.make_participant('alice', main_currency='EUR', accepted_currencies=None) team.set_take_for(alice, EUR('1.00'), team) bob = self.make_participant('bob', main_currency='USD', accepted_currencies=None) team.set_take_for(bob, USD('1.00'), team) stripe_account_alice = self.add_payment_account( alice, 'stripe', default_currency='EUR' ) stripe_account_bob = self.add_payment_account( bob, 'stripe', country='US', default_currency='USD' ) carl = self.make_participant('carl') carl.set_tip_to(team, JPY('250')) carl_card = ExchangeRoute.insert( carl, 'stripe-card', 'x', 'chargeable', remote_user_id='x' ) payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250')) assert pt.destination == stripe_account_alice.pk payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250')) assert pt.destination == stripe_account_bob.pk team.set_take_for(alice, EUR('0.00'), team) team.set_take_for(bob, None, team) takes = dict(self.db.all(""" SELECT DISTINCT ON (member) member, paid_in_advance FROM takes ORDER BY member, mtime DESC """)) assert takes == { alice.id: EUR('10.00'), bob.id: USD('12.00'), } Payday.start().run() transfers = self.db.all("SELECT * FROM transfers ORDER BY id") assert len(transfers) == 2 assert transfers[0].virtual is True assert transfers[0].tipper == carl.id assert transfers[0].tippee == alice.id assert transfers[0].amount == JPY('125') assert transfers[1].virtual is True assert transfers[1].tipper == carl.id assert transfers[1].tippee == bob.id assert transfers[1].amount == JPY('125') takes = dict(self.db.all(""" SELECT DISTINCT ON (member) member, paid_in_advance FROM takes ORDER BY member, mtime DESC """)) assert takes == { alice.id: EUR('9.00'), bob.id: USD('10.80'), } notifications = self.db.all("SELECT * FROM notifications") assert len(notifications) == 0 leftovers = dict(self.db.all("SELECT username, leftover FROM participants")) assert leftovers == { 'team': MoneyBasket(JPY('250.00')), 'alice': None, 'bob': None, 'carl': None, }
def test_can_change_take_currency(self): team, alice, bob = self.make_team_of_two() self.take_last_week(team, alice, EUR('30.00')) team.set_take_for(alice, USD('20.00'), alice) self.take_last_week(team, bob, USD('42.00')) team.set_take_for(bob, EUR('70.00'), bob)
def test_swap_currencies(self, TR_save): TR_save.side_effect = fake_transfer self.make_exchange('mango-cc', EUR('10.00'), 0, self.janet) self.make_exchange('mango-cc', USD('7.00'), 0, self.homer) start_balances = { 'janet': EUR('10.00'), 'homer': USD('7.00'), 'david': MoneyBasket(), } balances = self.get_balances() assert balances == start_balances # Test failure when there isn't enough money in the 1st wallet with self.assertRaises(AssertionError): swap_currencies(self.db, self.janet, self.homer, EUR('100.00'), USD('120.00')) balances = self.get_balances() assert balances == start_balances # Test failure when there isn't enough money in the 2nd wallet with self.assertRaises(AssertionError): swap_currencies(self.db, self.janet, self.homer, EUR('10.00'), USD('12.00')) balances = self.get_balances() assert balances == start_balances # Test failure of the 1st `prepare_transfer()` call with patch('liberapay.billing.transactions.lock_bundles') as lock_bundles: lock_bundles.side_effect = NegativeBalance with self.assertRaises(NegativeBalance): swap_currencies(self.db, self.janet, self.homer, EUR('3.00'), USD('3.00')) balances = self.get_balances() assert balances == start_balances # Test failure of the 2nd `prepare_transfer()` call cash_bundle = self.db.one("SELECT * FROM cash_bundles WHERE owner = %s", (self.homer.id,)) self.db.run("UPDATE cash_bundles SET amount = %s WHERE id = %s", (USD('0.01'), cash_bundle.id)) with self.assertRaises(NegativeBalance): swap_currencies(self.db, self.janet, self.homer, EUR('5.00'), USD('6.99')) self.db.run("UPDATE cash_bundles SET amount = %s WHERE id = %s", (cash_bundle.amount, cash_bundle.id)) balances = self.get_balances() assert balances == start_balances # Test failure of the 1st `initiate_transfer()` call self.transfer_mock.side_effect = Foobar with self.assertRaises(TransferError): swap_currencies(self.db, self.janet, self.homer, EUR('4.25'), USD('5.55')) balances = self.get_balances() assert balances == start_balances # Test failure of the 2nd `initiate_transfer()` call def fail_on_second(tr): if getattr(fail_on_second, 'called', False): raise Foobar fail_on_second.called = True fake_transfer(tr) self.transfer_mock.side_effect = fail_on_second self.db.run("ALTER SEQUENCE transfers_id_seq RESTART WITH 1") with patch('mangopay.resources.Transfer.get') as T_get: T_get.return_value = Transfer(Id=-1, AuthorId=self.janet_id, Tag='1') with self.assertRaises(TransferError): swap_currencies(self.db, self.janet, self.homer, EUR('0.01'), USD('0.01')) balances = self.get_balances() assert balances == start_balances # Test success self.transfer_mock.side_effect = fake_transfer swap_currencies(self.db, self.janet, self.homer, EUR('5.00'), USD('6.00')) balances = self.get_balances() assert balances == { 'janet': MoneyBasket(EUR('5.00'), USD('6.00')), 'homer': MoneyBasket(EUR('5.00'), USD('1.00')), 'david': MoneyBasket(), }
def test_get_end_of_year_balances(self): make_history(self) balances = get_end_of_year_balances(self.db, self.alice, self.past_year, datetime.now().year) assert list(balances) == [EUR('10.00'), USD('0.00')]