def test_cpi_clears_personal_information(self, mailer): alice = self.make_participant( 'alice' , anonymous_giving=True , avatar_url='img-url' , email_address='*****@*****.**' , claimed_time='now' , session_token='deadbeef' , session_expires='2000-01-01' , giving=20 , taking=40 ) alice.upsert_statement('en', 'not forgetting to be awesome!') alice.add_email('*****@*****.**') with self.db.get_cursor() as cursor: alice.clear_personal_information(cursor) new_alice = Participant.from_username('alice') assert alice.get_statement(['en']) == (None, None) assert alice.anonymous_giving == new_alice.anonymous_giving == False assert alice.number == new_alice.number == 'singular' assert alice.avatar_url == new_alice.avatar_url == None assert alice.email_address == new_alice.email_address == None assert alice.claimed_time == new_alice.claimed_time == None assert alice.giving == new_alice.giving == 0 assert alice.taking == new_alice.taking == 0 assert alice.session_token == new_alice.session_token == None assert alice.session_expires.year == new_alice.session_expires.year == date.today().year assert not alice.get_emails() team = self.make_participant('team', number='plural') with self.db.get_cursor() as cursor: team.clear_personal_information(cursor) team2 = Participant.from_username('team') assert team.number == team2.number == 'singular'
def test_transfer_tips_whole_graph(self): alice = self.make_participant('alice', claimed_time='now', balance=0, last_bill_result='') alice.set_tip_to(self.homer, D('50')) self.homer.set_tip_to(self.janet, D('20')) self.janet.set_tip_to(self.david, D('5')) self.david.set_tip_to(self.homer, D('20')) # Should be unfunded payday = Payday.start() with self.db.get_cursor() as cursor: payday.prepare(cursor, payday.ts_start) cursor.run("""UPDATE payday_participants SET card_hold_ok = true WHERE id = %s """, (alice.id,)) payday.transfer_tips(cursor) cursor.run("""UPDATE payday_participants SET new_balance = 0 WHERE id = %s """, (alice.id,)) payday.update_balances(cursor) alice = Participant.from_id(alice.id) assert Participant.from_id(alice.id).balance == D('0') assert Participant.from_id(self.homer.id).balance == D('30') assert Participant.from_id(self.janet.id).balance == D('15') assert Participant.from_id(self.david.id).balance == D('5')
def update_receiving(self, cursor=None): r = (cursor or self.db).one(""" WITH our_receiving AS ( SELECT amount FROM current_payment_instructions JOIN participants p ON p.username = participant WHERE team = %(slug)s AND p.is_suspicious IS NOT true AND amount > 0 AND is_funded ) UPDATE teams t SET receiving = COALESCE((SELECT sum(amount) FROM our_receiving), 0) , nreceiving_from = COALESCE((SELECT count(*) FROM our_receiving), 0) , distributing = COALESCE((SELECT sum(amount) FROM our_receiving), 0) , ndistributing_to = 1 WHERE t.slug = %(slug)s RETURNING receiving, nreceiving_from, distributing, ndistributing_to """, dict(slug=self.slug)) # This next step is easy for now since we don't have payroll. from gratipay.models.participant import Participant Participant.from_username(self.owner).update_taking(cursor or self.db) self.set_attributes( receiving=r.receiving , nreceiving_from=r.nreceiving_from , distributing=r.distributing , ndistributing_to=r.ndistributing_to )
def test_dequeueing_an_email_without_address_just_skips_it(self): larry = self.make_participant('larry') larry.queue_email("verification") assert self.db.one("SELECT spt_name FROM email_queue") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 0 assert self.db.one("SELECT spt_name FROM email_queue") is None
def test_update_receiving_amounts_updates_receiving_amounts(self): A = self.make_participant('A') B = self.make_participant('B', claimed_time='now', last_bill_result='') B.set_tip_to(A, D('10.00'), update_tippee=False) assert Participant.from_username('A').receiving == 0 Payday.start().update_receiving_amounts() assert Participant.from_username('A').receiving == 10
def test_admin_can_change_status(self): response = self.hit('pending-payout') assert response.code == 302 assert Participant.from_username('alice').status_of_1_0_balance == 'pending-payout' response = self.hit('unresolved') assert response.code == 302 assert Participant.from_username('alice').status_of_1_0_balance == 'unresolved'
def test_uic_uses_supplied_cursor(self): alice = self.make_participant('alice') with self.db.get_cursor() as cursor: alice.update_is_closed(True, cursor) assert alice.is_closed assert not Participant.from_username('alice').is_closed assert Participant.from_username('alice').is_closed
def test_opt_in_notification_includes_unsubscribe(self): carl_twitter = self.make_elsewhere('twitter', 1, 'carl') roy = self.make_participant('roy', claimed_time='now', email_address='*****@*****.**', notify_on_opt_in=1) roy.set_tip_to(carl_twitter.participant.username, '100') AccountElsewhere.from_user_name('twitter', 'carl').opt_in('carl') Participant.dequeue_emails() assert "To stop receiving" in self.get_last_email()['text']
def test_user_cant_change_status_except_for_applying(self): self.db.run("UPDATE participants SET status_of_1_0_payout='pending-application' " "WHERE username='******'") response = self.hit('pending-payout', auth_as='alice') assert response.code == 403 assert Participant.from_username('alice').status_of_1_0_payout == 'pending-application' response = self.hit('pending-review', auth_as='alice', expecting_error=False) assert Participant.from_username('alice').status_of_1_0_payout == 'pending-review'
def test_payday_moves_money(self, fch): self.janet.set_tip_to(self.homer, '6.00') # under $10! fch.return_value = {} Payday.start().run() janet = Participant.from_username('janet') homer = Participant.from_username('homer') assert homer.balance == D('6.00') assert janet.balance == D('3.41')
def test_iter_payday_events(self): now = datetime.now() Payday().start().run() Enterprise = self.make_team(is_approved=True) self.obama.set_payment_instruction(Enterprise, '10.00') # >= MINIMUM_CHARGE! for i in range(2): with patch.object(Payday, 'fetch_card_holds') as fch: fch.return_value = {} Payday.start().run() self.db.run(""" UPDATE paydays SET ts_start = ts_start - interval '1 week' , ts_end = ts_end - interval '1 week'; UPDATE payments SET timestamp = "timestamp" - interval '1 week'; UPDATE transfers SET timestamp = "timestamp" - interval '1 week'; """) obama = Participant.from_username('obama') picard = Participant.from_username('picard') assert obama.balance == D('0.00') assert picard.balance == D('20.00') Payday().start() # to demonstrate that we ignore any open payday? # Make all events in the same year. delta = '%s days' % (364 - (now - datetime(now.year, 1, 1)).days) self.db.run(""" UPDATE paydays SET ts_start = ts_start + interval %(delta)s , ts_end = ts_end + interval %(delta)s; UPDATE payments SET timestamp = "timestamp" + interval %(delta)s; UPDATE transfers SET timestamp = "timestamp" + interval %(delta)s; """, dict(delta=delta)) events = list(iter_payday_events(self.db, picard, now.year)) assert len(events) == 7 assert events[0]['kind'] == 'totals' assert events[0]['given'] == 0 assert events[0]['received'] == 20 assert events[1]['kind'] == 'day-open' assert events[1]['payday_number'] == 2 assert events[2]['balance'] == 20 assert events[-1]['kind'] == 'day-close' assert events[-1]['balance'] == 0 events = list(iter_payday_events(self.db, obama)) assert events[0]['given'] == 20 assert len(events) == 11
def test_can_post_to_close_page(self): alice = self.make_participant('alice', claimed_time='now', balance=7) bob = self.make_participant('bob', claimed_time='now') alice.set_tip_to(bob, D('10.00')) data = {'disbursement_strategy': 'downstream'} response = self.client.PxST('/alice/settings/close', auth_as='alice', data=data) assert response.code == 302 assert response.headers['Location'] == '/alice/' assert Participant.from_username('alice').balance == 0 assert Participant.from_username('bob').balance == 7
def sign_in(self, username): """Given a username, sign in the user. """ if self.url == "about:blank": # We need a page loaded in order to set an authentication cookie. self.visit("/") # This is duplicated from User.sign_in to work with Splinter's cookie API. token = user.uuid.uuid4().hex expires = user.utcnow() + user.SESSION_TIMEOUT Participant.from_username(username).update_session(token, expires) self.cookies.add({user.SESSION: token})
def test_iter_payday_events(self): Payday.start().run() team = self.make_participant('team', number='plural', claimed_time='now') alice = self.make_participant('alice', claimed_time='now') self.make_exchange('balanced-cc', 10000, 0, team) self.make_exchange('balanced-cc', 10000, 0, alice) self.make_exchange('balanced-cc', -5000, 0, alice) self.db.run(""" UPDATE transfers SET timestamp = "timestamp" - interval '1 month' """) bob = self.make_participant('bob', claimed_time='now') carl = self.make_participant('carl', claimed_time='now') team.add_member(bob) team.set_take_for(bob, Decimal('1.00'), team) alice.set_tip_to(bob, Decimal('5.00')) assert bob.balance == 0 for i in range(2): with patch.object(Payday, 'fetch_card_holds') as fch: fch.return_value = {} Payday.start().run() self.db.run(""" UPDATE paydays SET ts_start = ts_start - interval '1 week' , ts_end = ts_end - interval '1 week'; UPDATE transfers SET timestamp = "timestamp" - interval '1 week'; """) bob = Participant.from_id(bob.id) assert bob.balance == 12 Payday().start() events = list(iter_payday_events(self.db, bob)) assert len(events) == 9 assert events[0]['kind'] == 'totals' assert events[0]['given'] == 0 assert events[0]['received'] == 12 assert events[1]['kind'] == 'day-open' assert events[1]['payday_number'] == 2 assert events[2]['balance'] == 12 assert events[-1]['kind'] == 'day-close' assert events[-1]['balance'] == 0 alice = Participant.from_id(alice.id) assert alice.balance == 4990 events = list(iter_payday_events(self.db, alice)) assert events[0]['given'] == 10 assert len(events) == 11 carl = Participant.from_id(carl.id) assert carl.balance == 0 events = list(iter_payday_events(self.db, carl)) assert len(events) == 0
def test_payday_moves_money(self, fch): A = self.make_team(is_approved=True) self.obama.set_subscription_to(A, '6.00') # under $10! fch.return_value = {} Payday.start().run() obama = Participant.from_username('obama') hannibal = Participant.from_username('hannibal') assert hannibal.balance == D('6.00') assert obama.balance == D('3.41')
def test_dbafg_gives_all_to_claimed(self): alice = self.make_participant('alice', claimed_time='now', balance=D('10.00')) bob = self.make_participant('bob', claimed_time='now') carl = self.make_participant('carl') alice.set_tip_to(bob, D('3.00')) alice.set_tip_to(carl, D('2.00')) with self.db.get_cursor() as cursor: alice.distribute_balance_as_final_gift(cursor) assert Participant.from_username('bob').balance == D('10.00') assert Participant.from_username('carl').balance == D('0.00') assert Participant.from_username('alice').balance == D('0.00')
def test_dbafg_favors_highest_tippee_in_rounding_errors(self): alice = self.make_participant('alice', claimed_time='now', balance=D('10.00')) bob = self.make_participant('bob', claimed_time='now') carl = self.make_participant('carl', claimed_time='now') alice.set_tip_to(bob, D('3.00')) alice.set_tip_to(carl, D('6.00')) with self.db.get_cursor() as cursor: alice.distribute_balance_as_final_gift(cursor) assert Participant.from_username('bob').balance == D('3.33') assert Participant.from_username('carl').balance == D('6.67') assert Participant.from_username('alice').balance == D('0.00')
def test_transfer_takes_doesnt_make_negative_transfers(self, fch): hold = balanced.CardHold(amount=1500, meta={'participant_id': self.janet.id}) hold.capture = lambda *a, **kw: None hold.save = lambda *a, **kw: None fch.return_value = {self.janet.id: hold} self.janet.update_number('plural') self.janet.set_tip_to(self.homer, 10) self.janet.add_member(self.david) Payday.start().payin() assert Participant.from_id(self.david.id).balance == 0 assert Participant.from_id(self.homer.id).balance == 10 assert Participant.from_id(self.janet.id).balance == 0
def test_can_dequeue_an_email(self): larry = self.make_participant('larry', email_address='*****@*****.**') larry.queue_email("verification") assert self.db.one("SELECT spt_name FROM email_queue") == "verification" Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0]['email'] == '*****@*****.**' expected = "connect larry" assert expected in last_email['text'] assert self.db.one("SELECT spt_name FROM email_queue") is None
def test_payin_cancels_uncaptured_holds(self, log): self.janet.set_tip_to(self.homer, 42) alice = self.make_participant('alice', claimed_time='now', is_suspicious=False) self.make_exchange('balanced-cc', 50, 0, alice) alice.set_tip_to(self.janet, 50) Payday.start().payin() assert log.call_args_list[-3][0] == ("Captured 0 card holds.",) assert log.call_args_list[-2][0] == ("Canceled 1 card holds.",) assert Participant.from_id(alice.id).balance == 0 assert Participant.from_id(self.janet.id).balance == 8 assert Participant.from_id(self.homer.id).balance == 42
def test_payday_does_not_move_money_below_min_charge(self, fch): Enterprise = self.make_team(is_approved=True) self.obama.set_payment_instruction(Enterprise, '6.00') # not enough to reach MINIMUM_CHARGE fch.return_value = {} Payday.start().run() obama = Participant.from_username('obama') picard = Participant.from_username('picard') assert picard.balance == D('0.00') assert obama.balance == D('0.00') assert obama.get_due('TheEnterprise') == D('6.00')
def test_payday_moves_money(self): A = self.make_team(is_approved=True) self.obama.set_payment_instruction(A, '6.00') # under $10! with mock.patch.object(Payday, 'fetch_card_holds') as fch: fch.return_value = {} Payday.start().run() obama = Participant.from_username('obama') hannibal = Participant.from_username('hannibal') assert hannibal.balance == D('6.00') assert obama.balance == D('3.41')
def test_dbafg_needs_claimed_tips(self): alice = self.make_participant('alice', claimed_time='now', balance=D('10.00')) bob = self.make_participant('bob') carl = self.make_participant('carl') alice.set_tip_to(bob, D('3.00')) alice.set_tip_to(carl, D('2.00')) with self.db.get_cursor() as cursor: with pytest.raises(alice.NoOneToGiveFinalGiftTo): alice.distribute_balance_as_final_gift(cursor) assert Participant.from_username('bob').balance == D('0.00') assert Participant.from_username('carl').balance == D('0.00') assert Participant.from_username('alice').balance == D('10.00')
def test_can_update_status_via_trigger_on_participant_balance(self): self.db.run("UPDATE participants " "SET balance=10, status_of_1_0_payout='pending-application' " "WHERE username='******'") alice = Participant.from_username('alice') assert alice.balance == 10 assert alice.status_of_1_0_payout == 'pending-application'; self.db.run("UPDATE participants SET balance=0 WHERE username='******'") alice = Participant.from_username('alice') assert alice.balance == 0 assert alice.status_of_1_0_payout == 'completed';
def test_hvi_changes_are_scoped_to_a_participant(self): self.crusher.store_identity_info(self.US, 'nothing-enforced', {}) bruiser = self.make_participant('bruiser', email_address='*****@*****.**') bruiser.store_identity_info(self.US, 'nothing-enforced', {}) self.crusher.set_identity_verification(self.US, True) assert self.crusher.has_verified_identity assert Participant.from_username('crusher').has_verified_identity assert not bruiser.has_verified_identity assert not Participant.from_username('bruiser').has_verified_identity
def test_payin_doesnt_process_tips_when_goal_is_negative(self): alice = self.make_participant('alice', claimed_time='now', balance=20) bob = self.make_participant('bob', claimed_time='now') alice.set_tip_to(bob, 13) self.db.run("UPDATE participants SET goal = -1 WHERE username='******'") payday = Payday.start() with self.db.get_cursor() as cursor: payday.prepare(cursor, payday.ts_start) payday.transfer_tips(cursor) payday.update_balances(cursor) assert Participant.from_id(alice.id).balance == 20 assert Participant.from_id(bob.id).balance == 0
def test_dbafg_with_zero_balance_is_a_noop(self): alice = self.make_participant('alice', claimed_time='now', balance=D('0.00')) bob = self.make_participant('bob', claimed_time='now') carl = self.make_participant('carl', claimed_time='now') alice.set_tip_to(bob, D('3.00')) alice.set_tip_to(carl, D('6.00')) with self.db.get_cursor() as cursor: alice.distribute_balance_as_final_gift(cursor) assert self.db.one("SELECT count(*) FROM tips") == 2 assert Participant.from_username('bob').balance == D('0.00') assert Participant.from_username('carl').balance == D('0.00') assert Participant.from_username('alice').balance == D('0.00')
def test_payday_moves_money_above_min_charge(self): Enterprise = self.make_team(is_approved=True) self.obama.set_payment_instruction(Enterprise, MINIMUM_CHARGE) # must be >= MINIMUM_CHARGE with mock.patch.object(Payday, 'fetch_card_holds') as fch: fch.return_value = {} Payday.start().run() obama = Participant.from_username('obama') picard = Participant.from_username('picard') assert picard.balance == D(MINIMUM_CHARGE) assert obama.balance == D('0.00') assert obama.get_due('TheEnterprise') == D('0.00')
def test_take_over_sends_notifications_to_patrons(self): dan_twitter = self.make_elsewhere('twitter', 1, 'dan') self.alice.set_tip_to(self.dan, '100') # Alice shouldn't receive an email. self.bob.set_tip_to(dan_twitter.participant.username, '100') # Bob should receive an email. self.dan.take_over(dan_twitter, have_confirmation=True) Participant.dequeue_emails() assert self.mailer.call_count == 1 last_email = self.get_last_email() assert last_email['to'][0]['email'] == '*****@*****.**' expected = "to dan" assert expected in last_email['text']
def test_transfer_tips(self): alice = self.make_participant('alice', claimed_time='now', balance=1, last_bill_result='') alice.set_tip_to(self.janet, D('0.51')) alice.set_tip_to(self.homer, D('0.50')) payday = Payday.start() with self.db.get_cursor() as cursor: payday.prepare(cursor, payday.ts_start) payday.transfer_tips(cursor) payday.update_balances(cursor) alice = Participant.from_id(alice.id) assert Participant.from_id(alice.id).balance == D('0.49') assert Participant.from_id(self.janet.id).balance == D('0.51') assert Participant.from_id(self.homer.id).balance == 0
def cast(path_part, state): """This is an Aspen typecaster. Given a slug and a state dict, raise Response or return Team. """ redirect = state['website'].redirect request = state['request'] user = state['user'] slug = path_part qs = request.line.uri.querystring try: team = Team.from_slug(slug) except: raise Response(400, 'bad slug') if team is None: # Try to redirect to a Participant. from gratipay.models.participant import Participant # avoid circular import participant = Participant.from_username(slug) if participant is not None: qs = '?' + request.qs.raw if request.qs.raw else '' redirect('/~' + request.path.raw[1:] + qs) raise Response(404) canonicalize(redirect, request.line.uri.path.raw, '/', team.slug, slug, qs) if team.is_closed and not user.ADMIN: raise Response(410) return team
def test_ach_credit_withhold(self): self.make_exchange('balanced-cc', 27, 0, self.homer) withhold = D('1.00') error = ach_credit(self.db, self.homer, withhold) assert error == '' homer = Participant.from_id(self.homer.id) assert self.homer.balance == homer.balance == 1
def update_error(self, new_error): id = self.id old_error = self.error if old_error == 'invalidated': return self.db.run(""" UPDATE exchange_routes SET error = %(new_error)s WHERE id = %(id)s """, locals()) self.set_attributes(error=new_error) # Update cached amounts if requested and necessary if self.network != 'braintree-cc': return if self.participant.is_suspicious or bool(new_error) == bool(old_error): return # XXX *White* hot hack! # ===================== # During payday, participant is a record from a select of # payday_participants (or whatever), *not* an actual Participant # object. We need the real deal so we can use a method on it ... from gratipay.models.participant import Participant participant = Participant.from_username(self.participant.username) participant.update_giving_and_teams()
def test_ach_credit_failure(self, tfh): tfh.side_effect = Foobar self.make_exchange('balanced-cc', 20, 0, self.homer) error = ach_credit(self.db, self.homer, D('1.00')) homer = Participant.from_id(self.homer.id) assert self.homer.get_bank_account_error() == error == "Foobar()" assert self.homer.balance == homer.balance == 20
def sync_with_balanced(db): """We can get out of sync with Balanced if record_exchange_result was interrupted or wasn't called. This is where we fix that. """ check_db(db) exchanges = db.all(""" SELECT * FROM exchanges WHERE status = 'pre' """) meta_exchange_id = balanced.Transaction.f.meta.exchange_id for e in exchanges: p = Participant.from_username(e.participant) cls = balanced.Debit if e.amount > 0 else balanced.Credit transactions = cls.query.filter(meta_exchange_id == e.id).all() assert len(transactions) < 2 if transactions: t = transactions[0] error = t.failure_reason status = t.status assert (not error) ^ (status == 'failed') record_exchange_result(db, e.id, status, error, p) else: # The exchange didn't happen, remove it db.run("DELETE FROM exchanges WHERE id=%s", (e.id, )) # and restore the participant's balance if it was a credit if e.amount < 0: db.run( """ UPDATE participants SET balance=(balance + %s) WHERE id=%s """, (-e.amount + e.fee, p.id)) check_db(db)
def test_jsonp_works(self): alice = self.make_participant('alice', last_bill_result='') Enterprise = self.make_team(is_approved=True) alice.set_payment_instruction(Enterprise, '3.00') picard = Participant.from_username('picard') raw = self.client.GxT('/~picard/public.json?callback=foo', auth_as='picard').body assert raw == '''\ foo({ "avatar": null, "cryptocoins": {}, "elsewhere": { "github": { "id": %(elsewhere_id)s, "user_id": "%(user_id)s", "user_name": "picard" } }, "giving": "0.00", "id": %(user_id)s, "ngiving_to": 0, "ntaking_from": 1, "on": "gratipay", "taking": "3.00", "username": "******" })''' % dict(user_id=picard.id, elsewhere_id=picard.get_accounts_elsewhere()['github'].id)
def test_take_over_during_payin(self): alice = self.make_participant('alice', claimed_time='now', balance=50) enterprise = self.make_team('The Enterprise', is_approved=True) picard = Participant.from_username(enterprise.owner) self.make_participant('bob', claimed_time='now', elsewhere='twitter') alice.set_payment_instruction(enterprise, 18) payday = Payday.start() with self.db.get_cursor() as cursor: payday.prepare(cursor) # bruce takes over picard bruce = self.make_participant('bruce', claimed_time='now') bruce.take_over(('github', str(picard.id)), have_confirmation=True) payday.process_payment_instructions(cursor) # billy takes over bruce bruce.delete_elsewhere('twitter', str(picard.id)) billy = self.make_participant('billy', claimed_time='now') billy.take_over(('github', str(bruce.id)), have_confirmation=True) payday.update_balances(cursor) payday.take_over_balances() # billy ends up with the money assert P('bob').balance == 0 assert P('bruce').balance == 0 assert P('billy').balance == 18
def test_payday_doesnt_move_money_to_a_suspicious_account(self, fch): self.db.run(""" UPDATE participants SET is_suspicious = true WHERE username = '******' """) team = self.make_team(owner=self.homer, is_approved=True) self.obama.set_payment_instruction(team, '6.00') # under $10! fch.return_value = {} Payday.start().run() obama = Participant.from_username('obama') homer = Participant.from_username('homer') assert obama.balance == D('0.00') assert homer.balance == D('0.00')
def get_team(state): """Given a Request, raise Response or return Team. """ redirect = state['website'].redirect request = state['request'] user = state['user'] slug = request.line.uri.path['team'] qs = request.line.uri.querystring from gratipay.models.team import Team # avoid circular import team = Team.from_slug(slug) if team is None: # Try to redirect to a Participant. from gratipay.models.participant import Participant # avoid circular import participant = Participant.from_username(slug) if participant is not None: qs = '?' + request.qs.raw if request.qs.raw else '' redirect('/~' + request.path.raw[1:] + qs) raise Response(404) canonicalize(redirect, request.line.uri.path.raw, '/', team.slug, slug, qs) if team.is_closed and not user.ADMIN: raise Response(410) return team
def _prepare_email_message_for_ses(self, rec): """Prepare an email message for delivery via Amazon SES. :param Record rec: a database record from the ``email_messages`` table :returns: ``dict`` if we can find an email address to send to :raises: ``NoEmailAddress`` if we can't find an email address to send to We look for an email address to send to in two places: #. the context stored in ``rec.context``, and then #. ``participant.email_address``. """ to = Participant.from_id(rec.participant) spt = self._email_templates[rec.spt_name] context = pickle.loads(rec.context) context['participant'] = to context['username'] = to.username context['button_style'] = ( "color: #fff; text-decoration:none; display:inline-block; " "padding: 0 15px; background: #396; white-space: nowrap; " "font: normal 14px/40px Arial, sans-serif; border-radius: 3px") context.setdefault('include_unsubscribe', True) email = context.setdefault('email', to.email_address) if not email: raise NoEmailAddress() langs = i18n.parse_accept_lang(to.email_lang or 'en') locale = i18n.match_lang(langs) i18n.add_helpers_to_context(self.tell_sentry, context, locale) context['escape'] = lambda s: s context_html = dict(context) i18n.add_helpers_to_context(self.tell_sentry, context_html, locale) context_html['escape'] = htmlescape base_spt = self._email_templates['base'] def render(t, context): b = base_spt[t].render(context).strip() return b.replace('$body', spt[t].render(context).strip()) message = {} message['Source'] = 'Gratipay Support <*****@*****.**>' message['Destination'] = {} message['Destination']['ToAddresses'] = [ "%s <%s>" % (to.username, email) ] # "Name <*****@*****.**>" message['Message'] = {} message['Message']['Subject'] = {} message['Message']['Subject']['Data'] = spt['subject'].render( context).strip() message['Message']['Body'] = { 'Text': { 'Data': render('text/plain', context) }, 'Html': { 'Data': render('text/html', context_html) } } return message
def test_verify_email_after_update(self): self.verify_and_change_email('*****@*****.**', '*****@*****.**') nonce = self.alice.get_email('*****@*****.**').nonce self.verify_email('*****@*****.**', nonce) expected = '*****@*****.**' actual = Participant.from_username('alice').email_address assert expected == actual
def test_verify_email(self): self.hit_email_spt('add-email', '*****@*****.**') nonce = self.alice.get_email('*****@*****.**').nonce self.verify_email('*****@*****.**', nonce) expected = '*****@*****.**' actual = Participant.from_username('alice').email_address assert expected == actual
def test_uic_updates_is_closed_False(self): alice = self.make_participant('alice') alice.update_is_closed(True) alice.update_is_closed(False) assert not alice.is_closed assert not Participant.from_username('alice').is_closed
def fake_participant(db, number="singular", is_admin=False): """Create a fake User. """ username = faker.first_name() + fake_text_id(3) _fake_thing( db , "participants" , id=fake_int_id() , username=username , username_lower=username.lower() , statement=fake_sentence() , ctime=faker.date_time_this_year() , is_admin=is_admin , balance=fake_balance() , anonymous_giving=(random.randrange(5) == 0) , anonymous_receiving=(random.randrange(5) == 0) , goal=fake_balance() , balanced_customer_href=faker.uri() , last_ach_result='' , is_suspicious=False , last_bill_result='' # Needed to not be suspicious , claimed_time=faker.date_time_this_year() , number=number ) #Call participant constructor to perform other DB initialization return Participant.from_username(username)
def test_taking_is_zero_for_team(self): team = self.make_team() alice = self.make_participant('alice', claimed_time='now') team.add_member(alice) team = Participant.from_id(team.id) assert team.taking == 0 assert team.receiving == 100
def test_associate_and_delete_bank_account_valid(self): bank_account = balanced.BankAccount(name='Alice G. Krebs', routing_number='321174851', account_number='9900000001', account_type='checking').save() customer = self.david.get_balanced_account() customer.merchant_status = 'underwritten' with mock.patch.object(Participant, 'get_balanced_account') as gba: gba.return_value = customer self.hit('david', 'associate', 'balanced-ba', bank_account.href) bank_accounts = customer.bank_accounts.all() assert len(bank_accounts) == 1 assert bank_accounts[0].href == bank_account.href assert self.david.get_bank_account_error() == '' self.hit('david', 'delete', 'balanced-ba', bank_account.href) david = Participant.from_username('david') route = ExchangeRoute.from_address(david, 'balanced-ba', bank_account.href) assert route.error == david.get_bank_account_error() == 'invalidated' assert david.balanced_customer_href # Check that update_error doesn't update an invalidated route route.update_error('some error') assert route.error == david.get_bank_account_error() == 'invalidated'
def test_changes_to_others_take_can_increase_members_take(self): team = self.make_team() alice = self.make_participant('alice', claimed_time='now') self.take_last_week(team, alice, '30.00') team.set_take_for(alice, D('42.00'), alice) bob = self.make_participant('bob', claimed_time='now') self.take_last_week(team, bob, '60.00') team.set_take_for(bob, D('80.00'), bob) alice = Participant.from_username('alice') assert alice.receiving == alice.taking == 20 team.set_take_for(bob, D('30.00'), bob) alice = Participant.from_username('alice') assert alice.receiving == alice.taking == 42
def test_record_exchange_result_restores_balance_on_error(self): alice = self.make_participant('alice', balance=30) e_id = record_exchange(self.db, 'ach', D('-27.06'), D('0.81'), alice, 'pre') assert alice.balance == D('02.13') record_exchange_result( self.db, e_id, 'failed', 'SOME ERROR', alice) alice = Participant.from_username('alice') assert alice.balance == D('30.00')
def test_record_exchange_result_doesnt_restore_balance_on_success(self): alice = self.make_participant('alice', balance=50) e_id = record_exchange(self.db, 'ach', D('-43.98'), D('1.60'), alice, 'pre') assert alice.balance == D('4.42') record_exchange_result( self.db, e_id, 'succeeded', None, alice) alice = Participant.from_username('alice') assert alice.balance == D('4.42')
def test_record_exchange_result_updates_balance_for_positive_amounts(self): alice = self.make_participant('alice', balance=4) e_id = record_exchange(self.db, 'bill', D('31.59'), D('0.01'), alice, 'pre') assert alice.balance == D('4.00') record_exchange_result( self.db, e_id, 'succeeded', None, alice) alice = Participant.from_username('alice') assert alice.balance == D('35.59')
def test_cpi_clears_personal_information(self, mailer): alice = self.make_participant('alice', anonymous_giving=True, avatar_url='img-url', email_address='*****@*****.**', claimed_time='now', session_token='deadbeef', session_expires='2000-01-01', giving=20, taking=40) alice.upsert_statement('en', 'not forgetting to be awesome!') alice.add_email('*****@*****.**') with self.db.get_cursor() as cursor: alice.clear_personal_information(cursor) new_alice = Participant.from_username('alice') assert alice.get_statement(['en']) == (None, None) assert alice.anonymous_giving == new_alice.anonymous_giving == False assert alice.avatar_url == new_alice.avatar_url == None assert alice.email_address == new_alice.email_address == None assert alice.claimed_time == new_alice.claimed_time == None assert alice.giving == new_alice.giving == 0 assert alice.taking == new_alice.taking == 0 assert alice.session_token == new_alice.session_token == None assert alice.session_expires.year == new_alice.session_expires.year == date.today( ).year assert not alice.get_emails()
def test_stt_resets_is_free_rider_to_null(self): alice = self.make_participant('alice', claimed_time='now', last_bill_result='') gratipay = self.make_participant('Gratipay', number='plural') alice.set_tip_to(gratipay, '0.00') assert alice.is_free_rider is None assert Participant.from_username('alice').is_free_rider is None
def test_create_card_hold_no_card(self): customer_href = self.make_balanced_customer() bob = self.make_participant('bob', balanced_customer_href=customer_href, is_suspicious=False) hold, error = create_card_hold(self.db, bob, D('10.00')) bob2 = Participant.from_id(bob.id) assert error == 'NoResultFound()' assert bob.last_bill_result == bob2.last_bill_result == None
def test_create_card_hold_success(self): hold, error = create_card_hold(self.db, self.obama, D('1.00')) obama = Participant.from_id(self.obama.id) assert isinstance(hold, braintree.Transaction) assert hold.status == 'authorized' assert hold.amount == D('10.00') assert error == '' assert self.obama.balance == obama.balance == 0
def test_receiving_includes_taking_when_updated_from_set_tip_to(self): alice = self.make_participant('alice', claimed_time='now', last_bill_result='') bob = self.make_participant('bob', taking=Decimal('42.00')) alice.set_tip_to(bob, '3.00') assert Participant.from_username( 'bob').receiving == bob.receiving == Decimal('45.00')
def test_can_go_plural(self): alice = self.make_participant('alice', claimed_time='now', last_bill_result='') bob = self.make_participant('bob') alice.set_tip_to(bob, '100.00') bob.update_number('plural') assert Participant.from_username('bob').number == 'plural'
def test_capture_card_hold_amount_under_minimum(self): hold, error = create_card_hold(self.db, self.obama, D('20.00')) assert error == '' # sanity check capture_card_hold(self.db, self.obama, D('0.01'), hold) obama = Participant.from_id(self.obama.id) assert self.obama.balance == obama.balance == D('9.41') assert self.obama.get_credit_card_error() == ''
def test_going_singular_clears_takes(self): team = self.make_participant('team', number='plural') alice = self.make_participant('alice', claimed_time='now', last_bill_result='') team.set_take_for(alice, Decimal('10'), alice) team.update_number('singular') assert Participant.from_username('alice').get_old_teams() == []
def test_capture_card_hold_amount_under_minimum(self): hold, error = create_card_hold(self.db, self.janet, D('20.00')) assert error == '' # sanity check capture_card_hold(self.db, self.janet, D('0.01'), hold) janet = Participant.from_id(self.janet.id) assert self.janet.balance == janet.balance == D('9.41') assert self.janet.last_bill_result == janet.last_bill_result == ''