Example #1
0
    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')
Example #3
0
    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'
Example #7
0
    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')
Example #11
0
    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
Example #12
0
    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})
Example #14
0
    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')
Example #16
0
 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')
Example #17
0
 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')
Example #23
0
 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
Example #27
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
Example #31
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
Example #32
0
 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
Example #33
0
    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()
Example #34
0
 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
Example #35
0
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)
Example #36
0
    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)
Example #37
0
    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
Example #38
0
    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')
Example #39
0
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
Example #40
0
    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
Example #41
0
 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
Example #42
0
 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
Example #43
0
    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
Example #44
0
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)
Example #45
0
 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
Example #46
0
 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
Example #47
0
    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'
Example #48
0
    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')
Example #52
0
    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()
Example #53
0
 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
Example #56
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')
Example #57
0
 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 == ''