def test_non_post_is_405(self):
     self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
     self.make_participant('bob', claimed_time=utcnow())
     actual = self.client.GxT( '/bob/history/record-an-exchange'
                             , auth_as='alice'
                              ).code
     assert actual == 405
 def test_withdrawals_work(self):
     self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
     self.make_participant('bob', claimed_time=utcnow(), balance=20)
     self.record_an_exchange('-7', '0', 'noted', make_participants=False)
     expected = Decimal('13.00')
     SQL = "SELECT balance FROM participants WHERE username='******'"
     actual = self.db.one(SQL)
     assert actual == expected
 def test_withdrawals_work(self):
     self.make_participant("alice", claimed_time=utcnow(), is_admin=True)
     self.make_participant("bob", claimed_time=utcnow(), balance=20)
     self.record_an_exchange("-7", "0", "noted", False)
     expected = Decimal("13.00")
     SQL = "SELECT balance FROM participants WHERE username='******'"
     actual = self.db.one(SQL)
     assert actual == expected
    def test_route_should_belong_to_user_else_400(self):
        alice = self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
        self.make_participant('bob', claimed_time=utcnow())
        route = ExchangeRoute.insert(alice, 'paypal', '*****@*****.**')

        response = self.record_an_exchange({'amount': '10', 'fee': '0', 'route_id': route.id}, False)
        assert response.code == 400
        assert response.body == "Route doesn't exist"
 def test_withdrawals_work(self):
     self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
     self.make_participant('bob', claimed_time=utcnow(), balance=20)
     self.record_an_exchange('-7', '0', 'noted', False)
     expected = [{"balance": Decimal('13.00')}]
     SQL = "SELECT balance FROM participants WHERE id='bob'"
     actual = list(gittip.db.fetchall(SQL))
     assert actual == expected, actual
def setup_tips(*recs):
    """Setup some participants and tips. recs is a list of:

        ("tipper", "tippee", '2.00', False)
                                       ^
                                       |-- good cc?

    good_cc can be True, False, or None

    """
    tips = []

    _participants = {}

    for rec in recs:
        defaults = good_cc, payin_suspended, claimed = (True, False, True)

        if len(rec) == 3:
            tipper, tippee, amount = rec
        elif len(rec) == 4:
            tipper, tippee, amount, good_cc = rec
            payin_suspended, claimed = (False, True)
        elif len(rec) == 5:
            tipper, tippee, amount, good_cc, payin_suspended = rec
            claimed = True
        elif len(rec) == 6:
            tipper, tippee, amount, good_cc, payin_suspended, claimed = rec

        assert good_cc in (True, False, None), good_cc
        assert payin_suspended in (True, False), payin_suspended
        assert claimed in (True, False), claimed

        _participants[tipper] = (good_cc, payin_suspended, claimed)
        if tippee not in _participants:
            _participants[tippee] = defaults
        now = utcnow()
        tips.append({ "ctime": now
                    , "mtime": now
                    , "tipper": tipper
                    , "tippee": tippee
                    , "amount": Decimal(amount)
                     })

    then = utcnow() - datetime.timedelta(seconds=3600)

    participants = []
    for participant_id, (good_cc, payin_suspended, claimed) in _participants.items():
        rec = {"id": participant_id}
        if good_cc is not None:
            rec["last_bill_result"] = "" if good_cc else "Failure!"
            rec["balanced_account_uri"] = "/v1/blah/blah/" + participant_id
        rec["payin_suspended"] = payin_suspended
        if claimed:
            rec["claimed_time"] = then
        participants.append(rec)

    return ["participants"] + participants + ["tips"] + tips
    def test_tip(self, log, transfer):
        self.db.run("""

            UPDATE participants
               SET balance=1
                 , balanced_customer_href=%s
                 , is_suspicious=False
             WHERE username='******'

        """, (self.BALANCED_CUSTOMER_HREF,))
        amount = D('1.00')
        invalid_amount = D('0.00')
        tip = { 'amount': amount
              , 'tippee': self.alice.username
              , 'claimed_time': utcnow()
               }
        ts_start = utcnow()

        result = self.payday.tip(self.alice, tip, ts_start)
        assert result == 1
        result = transfer.called_with(self.alice.username, tip['tippee'],
                                      tip['amount'])
        assert result

        assert log.called_with('SUCCESS: $1 from mjallday to alice.')

        # XXX: Should these tests be broken down to a separate class with the
        # common setup factored in to a setUp method.

        # XXX: We should have constants to compare the values to
        # invalid amount
        tip['amount'] = invalid_amount
        result = self.payday.tip(self.alice, tip, ts_start)
        assert result == 0

        tip['amount'] = amount

        # XXX: We should have constants to compare the values to
        # not claimed
        tip['claimed_time'] = None
        result = self.payday.tip(self.alice, tip, ts_start)
        assert result == 0

        # XXX: We should have constants to compare the values to
        # claimed after payday
        tip['claimed_time'] = utcnow()
        result = self.payday.tip(self.alice, tip, ts_start)
        assert result == 0

        ts_start = utcnow()

        # XXX: We should have constants to compare the values to
        # transfer failed
        transfer.return_value = False
        result = self.payday.tip(self.alice, tip, ts_start)
        assert result == -1
    def test_tip(self, log, transfer):
        self.db.run("""

            UPDATE participants
               SET balance=1
                 , balanced_account_uri=%s
                 , is_suspicious=False
             WHERE username='******'

        """, (self.BALANCED_ACCOUNT_URI,))
        amount = Decimal('1.00')
        invalid_amount = Decimal('0.00')
        tip = { 'amount': amount
              , 'tippee': self.alice.username
              , 'claimed_time': utcnow()
               }
        ts_start = utcnow()

        result = self.payday.tip(self.alice, tip, ts_start)
        assert_equals(result, 1)
        result = transfer.called_with(self.alice.username, tip['tippee'],
                                      tip['amount'])
        self.assertTrue(result)

        self.assertTrue(log.called_with('SUCCESS: $1 from mjallday to alice.'))

        # XXX: Should these tests be broken down to a separate class with the
        # common setup factored in to a setUp method.

        # XXX: We should have constants to compare the values to
        # invalid amount
        tip['amount'] = invalid_amount
        result = self.payday.tip(self.alice, tip, ts_start)
        assert_equals(result, 0)

        tip['amount'] = amount

        # XXX: We should have constants to compare the values to
        # not claimed
        tip['claimed_time'] = None
        result = self.payday.tip(self.alice, tip, ts_start)
        assert_equals(result, 0)

        # XXX: We should have constants to compare the values to
        # claimed after payday
        tip['claimed_time'] = utcnow()
        result = self.payday.tip(self.alice, tip, ts_start)
        assert_equals(result, 0)

        ts_start = utcnow()

        # XXX: We should have constants to compare the values to
        # transfer failed
        transfer.return_value = False
        result = self.payday.tip(self.alice, tip, ts_start)
        assert_equals(result, -1)
    def test_post_non_team_member_adds_member_returns_403(self):
        client, csrf_token = self.make_client_and_csrf()

        self.make_team_and_participant()
        self.make_participant("bob", claimed_time=utcnow())

        response = client.post('/team/members/alice.json'
            , {
                'take': '0.01'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        actual = response.code
        assert actual == 200, actual

        response = client.post('/team/members/bob.json'
            , {
                'take': '0.01'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        actual = response.code
        assert actual == 403, actual
    def test_joining_and_leaving_community(self):
        self.make_participant("alice", claimed_time=utcnow())

        response = self.client.GET('/for/communities.json', auth_as='alice')
        assert len(json.loads(response.body)['communities']) == 0

        response = self.client.POST( '/for/communities.json'
                                   , {'name': 'Test', 'is_member': 'true'}
                                   , auth_as='alice'
                                    )

        communities = json.loads(response.body)['communities']
        assert len(communities) == 1
        assert communities[0]['name'] == 'Test'
        assert communities[0]['nmembers'] == 1

        response = self.client.POST( '/for/communities.json'
                                   , {'name': 'Test', 'is_member': 'false'}
                                   , auth_as='alice'
                                    )

        response = self.client.GET('/for/communities.json', auth_as='alice')

        assert len(json.loads(response.body)['communities']) == 0

        # Check that the empty community was deleted
        community = Community.from_slug('test')
        assert not community
Beispiel #11
0
 def sign_in(self, cookies):
     """Start a new session for the user.
     """
     token = uuid.uuid4().hex
     expires = utcnow() + SESSION_TIMEOUT
     self.participant.update_session(token, expires)
     set_cookie(cookies, SESSION, token, expires)
    def test_api_returns_amount_and_totals(self):
        "Test that we get correct amounts and totals back on POSTs to subscription.json"

        # First, create some test data
        # We need accounts
        now = utcnow()
        self.make_team("A", is_approved=True)
        self.make_team("B", is_approved=True)
        self.make_participant("alice", claimed_time=now, last_bill_result='')

        # Then, add a $1.50 and $3.00 subscription
        response1 = self.client.POST( "/A/subscription.json"
                                    , {'amount': "1.50"}
                                    , auth_as='alice'
                                     )

        response2 = self.client.POST( "/B/subscription.json"
                                    , {'amount': "3.00"}
                                    , auth_as='alice'
                                     )

        # Confirm we get back the right amounts.
        first_data = json.loads(response1.body)
        second_data = json.loads(response2.body)
        assert first_data['amount'] == "1.50"
        assert first_data['total_giving'] == "1.50"
        assert second_data['amount'] == "3.00"
        assert second_data['total_giving'] == "4.50"
    def test_post_can_leave_community(self):
        client, csrf_token = self.make_client_and_csrf()
        community = 'Test'

        self.make_participant("alice", claimed_time=utcnow())

        response = client.post('/for/communities.json'
            , { 'name': community
              , 'is_member': 'true'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        response = client.post('/for/communities.json'
            , { 'name': community
              , 'is_member': 'false'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        response = client.get('/for/communities.json', 'alice')

        actual = len(json.loads(response.body)['communities'])
        assert actual == 0, actual
 def setUp(self):
     super(Harness, self).setUp()
     now = utcnow()
     for idx, username in enumerate(["alice", "bob", "carl"], start=1):
         self.make_participant(username, claimed_time=now)
         twitter_account = TwitterAccount(idx, {"screen_name": username})
         Participant(username).take_over(twitter_account)
    def test_start_zero_out_and_get_participants(self, log):
        self.make_participant(
            "bob", balance=10, claimed_time=None, pending=1, balanced_customer_href=self.BALANCED_CUSTOMER_HREF
        )
        self.make_participant(
            "carl", balance=10, claimed_time=utcnow(), pending=1, balanced_customer_href=self.BALANCED_CUSTOMER_HREF
        )
        self.db.run(
            """

            UPDATE participants
               SET balance=0
                 , claimed_time=null
                 , pending=null
                 , balanced_customer_href=%s
             WHERE username='******'

        """,
            (self.BALANCED_CUSTOMER_HREF,),
        )

        ts_start = self.payday.start()

        self.payday.zero_out_pending(ts_start)
        participants = self.payday.get_participants(ts_start)

        expected_logging_call_args = [
            ("Starting a new payday."),
            ("Payday started at {}.".format(ts_start)),
            ("Zeroed out the pending column."),
            ("Fetched participants."),
        ]
        expected_logging_call_args.reverse()
        for args, _ in log.call_args_list:
            assert args[0] == expected_logging_call_args.pop()

        log.reset_mock()

        # run a second time, we should see it pick up the existing payday
        second_ts_start = self.payday.start()
        self.payday.zero_out_pending(second_ts_start)
        second_participants = self.payday.get_participants(second_ts_start)

        assert ts_start == second_ts_start
        participants = list(participants)
        second_participants = list(second_participants)

        # carl is the only valid participant as he has a claimed time
        assert len(participants) == 1
        assert participants == second_participants

        expected_logging_call_args = [
            ("Picking up with an existing payday."),
            ("Payday started at {}.".format(second_ts_start)),
            ("Zeroed out the pending column."),
            ("Fetched participants."),
        ]
        expected_logging_call_args.reverse()
        for args, _ in log.call_args_list:
            assert args[0] == expected_logging_call_args.pop()
    def test_post_user_is_not_member_or_team_returns_403(self):
        client, csrf_token = self.make_client_and_csrf()

        self.make_team_and_participant()
        self.make_participant("bob", claimed_time=utcnow(), number='plural')

        response = client.post('/team/members/alice.json'
            , {
                'take': '0.01'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        actual = response.code
        assert actual == 200

        response = client.post('/team/members/bob.json'
            , {
                'take': '0.01'
              , 'csrf_token': csrf_token
            }
            , user='******'
        )

        actual = response.code
        assert actual == 200

        response = client.post('/team/members/alice.json'
            , { 'csrf_token': csrf_token }
            , user='******'
        )

        actual = response.code
        assert actual == 403
    def also_prune_variant(self, also_prune, tippees=1):

        now = utcnow()
        self.make_participant("test_tippee1", claimed_time=now)
        self.make_participant("test_tippee2", claimed_time=now)
        self.make_participant("test_tipper", claimed_time=now)

        data = [
            {'username': '******', 'platform': 'gittip', 'amount': '1.00'},
            {'username': '******', 'platform': 'gittip', 'amount': '2.00'}
        ]

        response = self.client.POST( '/test_tipper/tips.json'
                                   , body=json.dumps(data)
                                   , content_type='application/json'
                                   , auth_as='test_tipper'
                                    )

        assert response.code == 200
        assert len(json.loads(response.body)) == 2

        response = self.client.POST( '/test_tipper/tips.json?also_prune=' + also_prune
                                   , body=json.dumps([{ 'username': '******'
                                                      , 'platform': 'gittip'
                                                      , 'amount': '1.00'
                                                       }])
                                   , content_type='application/json'
                                   , auth_as='test_tipper'
                                    )

        assert response.code == 200

        response = self.client.GET('/test_tipper/tips.json', auth_as='test_tipper')
        assert response.code == 200
        assert len(json.loads(response.body)) == tippees
Beispiel #18
0
    def test_get_amount_and_total_back_from_api(self):
        "Test that we get correct amounts and totals back on POSTs to tip.json"

        # First, create some test data
        # We need accounts
        now = utcnow()
        self.make_participant("test_tippee1", claimed_time=now)
        self.make_participant("test_tippee2", claimed_time=now)
        self.make_participant("test_tipper")

        # Then, add a $1.50 and $3.00 tip
        response1 = self.client.POST( "/test_tippee1/tip.json"
                                    , {'amount': "1.00"}
                                    , auth_as='test_tipper'
                                     )

        response2 = self.client.POST( "/test_tippee2/tip.json"
                                    , {'amount': "3.00"}
                                    , auth_as='test_tipper'
                                     )

        # Confirm we get back the right amounts.
        first_data = json.loads(response1.body)
        second_data = json.loads(response2.body)
        assert first_data['amount'] == "1.00"
        assert first_data['total_giving'] == "1.00"
        assert second_data['amount'] == "3.00"
        assert second_data['total_giving'] == "4.00"
 def setUp(self):
     super(Harness, self).setUp()
     now = utcnow()
     for idx, username in enumerate(['alice', 'bob', 'carl'], start=1):
         self.make_participant(username, claimed_time=now)
         twitter_account = TwitterAccount(idx, {'screen_name': username})
         Participant.from_username(username).take_over(twitter_account)
Beispiel #20
0
    def make_participant(self, username, **kw):
        # At this point wireup.db() has been called, but not ...
        wireup.username_restrictions(self.client.website)

        participant = Participant.with_random_username()
        participant.change_username(username)

        if 'elsewhere' in kw or 'claimed_time' in kw:
            username = participant.username
            platform = kw.pop('elsewhere', 'github')
            self.seq += 1
            self.db.run("""
                INSERT INTO elsewhere
                            (platform, user_id, user_name, participant)
                     VALUES (%s,%s,%s,%s)
            """, (platform, self.seq, username, username))

        # Update participant
        if kw:
            if kw.get('claimed_time') == 'now':
                kw['claimed_time'] = utcnow()
            cols, vals = zip(*kw.items())
            cols = ', '.join(cols)
            placeholders = ', '.join(['%s']*len(vals))
            participant = self.db.one("""
                UPDATE participants
                   SET ({0}) = ({1})
                 WHERE username=%s
             RETURNING participants.*::participants
            """.format(cols, placeholders), vals+(participant.username,))

        return participant
    def test_tip(self, log, transfer):
        alice = self.make_participant(
            "alice", balance="1.00", balanced_account_uri=self.BALANCED_ACCOUNT_URI, is_suspicious=False
        )
        participant = alice.attrs_dict()
        amount = Decimal("1.00")
        invalid_amount = Decimal("0.00")
        tip = {"amount": amount, "tippee": alice.id, "claimed_time": utcnow()}
        ts_start = utcnow()

        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, 1)
        result = transfer.called_with(participant["id"], tip["tippee"], tip["amount"])
        self.assertTrue(result)

        self.assertTrue(log.called_with("SUCCESS: $1 from mjallday to alice."))

        # XXX: Should these tests be broken down to a separate class with the
        # common setup factored in to a setUp method.

        # XXX: We should have constants to compare the values to
        # invalid amount
        tip["amount"] = invalid_amount
        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, 0)

        tip["amount"] = amount

        # XXX: We should have constants to compare the values to
        # not claimed
        tip["claimed_time"] = None
        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, 0)

        # XXX: We should have constants to compare the values to
        # claimed after payday
        tip["claimed_time"] = utcnow()
        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, 0)

        ts_start = utcnow()

        # XXX: We should have constants to compare the values to
        # transfer failed
        transfer.return_value = False
        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, -1)
 def test_get_tip_distribution_handles_a_non_standard_amount(self):
     tip = Tip(tipper='alice', tippee='bob', amount='5.37', ctime=utcnow())
     self.session.add(tip)
     self.session.commit()
     expected = ([[-1, 1, Decimal('5.37'), 1.0, Decimal('1')]],
                 1.0, Decimal('5.37'))
     actual = Participant(u'bob').get_tip_distribution()
     assert_equals(actual, expected)
Beispiel #23
0
    def test_set_tip_to_patron(self):
        now = utcnow()
        self.make_participant("alice", claimed_time=now, goal="-1")
        self.make_participant("bob", claimed_time=now)

        response = self.client.PxST("/alice/tip.json", {"amount": "10.00"}, auth_as="bob")
        assert "user doesn't accept tips" in response.body
        assert response.code == 400
    def test_post_non_team_member_adds_member_returns_403(self):
        self.make_participant("bob", claimed_time=utcnow())

        response = self.client.POST('/team/members/alice.json', {'take': '0.01'}, auth_as='team')
        assert response.code == 200

        response = self.client.PxST('/team/members/bob.json', {'take': '0.01'}, auth_as='alice')
        assert response.code == 403
Beispiel #25
0
 def test_user_from_expired_session_is_anonymous(self):
     self.make_participant('alice')
     user = User.from_username('alice')
     user.sign_in(SimpleCookie())
     token = user.participant.session_token
     user.participant.set_session_expires(utcnow())
     user = User.from_session_token(token)
     assert user.ANON
    def test_get_response(self):
        now = utcnow()
        self.make_participant("test_tipper", claimed_time=now)

        response = self.client.GET('/test_tipper/tips.json', auth_as='test_tipper')

        assert response.code == 200
        assert len(json.loads(response.body)) == 0 # empty array
Beispiel #27
0
 def test_log_in_with_old_session(self):
     alice = self.make_participant('alice')
     alice.update_session('x', utcnow() - timedelta(days=1))
     alice.authenticated = True
     cookies = SimpleCookie()
     alice.sign_in(cookies)
     print(cookies)
     self.check_with_about_me('alice', cookies)
    def test_get_existing_user(self):
        self.make_participant("alice", claimed_time=utcnow())

        response = self.client.GET('/lookup.json?query={}'.format('alice'))
        data = json.loads(response.body)

        assert len(data) == 1
        assert data[0]['id'] != -1
Beispiel #29
0
def test_to_age_barely_works():
    now = utcnow()
    actual = to_age(now, dt_now=now)
    assert actual == "in just a moment"

    wait = timedelta(seconds=0.5)
    actual = to_age(now - wait, dt_now=now)
    assert actual == "just a moment ago"
Beispiel #30
0
 def keep_signed_in(self, cookies):
     """Extend the user's current session.
     """
     new_expires = utcnow() + SESSION_TIMEOUT
     if new_expires - self.participant.session_expires > SESSION_REFRESH:
         self.participant.set_session_expires(new_expires)
         token = self.participant.session_token
         set_cookie(cookies, SESSION, token, expires=new_expires)
Beispiel #31
0
def to_age(dt, loc, **kw):
    return format_timedelta(dt - utcnow(), locale=loc, **kw)
Beispiel #32
0
 def make_alice(self):
     return self.make_participant('alice', claimed_time=utcnow())
Beispiel #33
0
 def setUp(self):
     Harness.setUp(self)
     now = utcnow()
     for username in ['alice', 'bob', 'carl']:
         p = self.make_participant(username, claimed_time=now, elsewhere='twitter')
         setattr(self, username, p)
Beispiel #34
0
    def finish_email_verification(self, email, nonce):
        """Given an email address and a nonce as strings, return a three-tuple:

        - a ``VERIFICATION_*`` constant;
        - a list of packages if ``VERIFICATION_SUCCEEDED`` (``None``
          otherwise), and
        - a boolean indicating whether the participant's PayPal address was
          updated if applicable (``None`` if not).

        """
        _fail = VERIFICATION_FAILED, None, None
        if '' in (email.strip(), nonce.strip()):
            return _fail
        with self.db.get_cursor() as cursor:

            # Load an email record. Check for an address match, but don't check
            # the nonce at this point. We want to compare in constant time to
            # avoid timing attacks, and we'll do that below.

            record = self.get_email(email, cursor, and_lock=True)
            if record is None:

                # We don't have that email address on file. Maybe it used to be
                # on file but was explicitly removed (they followed an old link
                # after removing in the UI?), or maybe it was never on file in
                # the first place (they munged the querystring?).

                return _fail

            if record.nonce is None:

                # Nonces are nulled out only when updating to mark an email
                # address as verified; we always set a nonce when inserting.
                # Therefore, the main way to get a null nonce is to issue a
                # link, follow it, and follow it again.

                # All records with a null nonce should be verified, though not
                # all verified records will have a null nonce. That is, it's
                # possible to land here with an already-verified address, and
                # this is in fact expected when verifying package ownership via
                # an already-verified address.

                assert record.verified

                return VERIFICATION_REDUNDANT, None, None

            # *Now* verify that the nonce given matches the one expected, along
            # with the time window for verification.

            if not constant_time_compare(record.nonce, nonce):
                return _fail
            if (utcnow() - record.verification_start) > EMAIL_HASH_TIMEOUT:
                return _fail

            # And now we can load any packages associated with the nonce, and
            # save the address.

            packages = self.get_packages_claiming(cursor, nonce)
            paypal_updated = None
            try:
                if packages:
                    paypal_updated = False
                    self.finish_package_claims(cursor, nonce, *packages)
                self.save_email_address(cursor, email)
                has_no_paypal = not self.get_payout_routes(good_only=True)
                if packages and has_no_paypal:
                    self.set_paypal_address(email, cursor)
                    paypal_updated = True
            except IntegrityError:
                return VERIFICATION_STYMIED, None, None
            return VERIFICATION_SUCCEEDED, packages, paypal_updated
 def test_gais_gets_age_in_seconds(self):
     now = utcnow()
     alice = self.make_participant('alice', claimed_time=now)
     actual = alice.get_age_in_seconds()
     assert 0 < actual < 1
 def setUp(self):
     Harness.setUp(self)
     self.make_participant("team", claimed_time=utcnow(), number='plural')
     self.make_participant("alice", claimed_time=utcnow())
Beispiel #37
0
 def get_age_in_seconds(self):
     out = -1
     if self.claimed_time is not None:
         now = utcnow()
         out = (now - self.claimed_time).total_seconds()
     return out
Beispiel #38
0
def setup_tips(*recs):
    """Setup some participants and tips. recs is a list of:

        ("tipper", "tippee", '2.00', True, False, True, "github", "12345")
                                       ^     ^      ^
                                       |     |      |
                                       |     |      -- claimed?
                                       |     -- is_suspicious?
                                       |-- good cc?

    tipper must be a unicode
    tippee can be None or unicode
    amount can be None or unicode
    good_cc can be True, False, or None
    is_suspicious can be True, False, or None
    claimed can be True or False
    platform can be unicode
    user_id can be unicode

    """
    tips = []

    _participants = {}
    randid = lambda: unicode(random.randint(1, 1000000))

    for rec in recs:
        good_cc, is_suspicious, claimed, platform, user_id = \
                                        (True, False, True, "github", randid())

        if len(rec) == 3:
            tipper, tippee, amount = rec
        elif len(rec) == 4:
            tipper, tippee, amount, good_cc = rec
            is_suspicious, claimed = (False, True)
        elif len(rec) == 5:
            tipper, tippee, amount, good_cc, is_suspicious = rec
            claimed = True
        elif len(rec) == 6:
            tipper, tippee, amount, good_cc, is_suspicious, claimed = rec
        elif len(rec) == 7:
            tipper, tippee, amount, good_cc, is_suspicious, claimed, platform \
                                                                          = rec
        elif len(rec) == 8:
            tipper, tippee, amount, good_cc, is_suspicious, claimed, \
                                                        platform, user_id = rec
        else:
            raise Exception(rec)

        assert good_cc in (True, False, None), good_cc
        assert is_suspicious in (True, False, None), is_suspicious
        _participants[tipper] = \
                              (good_cc, is_suspicious, True, platform, user_id)

        if tippee is None:
            continue
        assert claimed in (True, False), claimed  # refers to tippee
        if tippee not in _participants:
            _participants[tippee] = (None, False, claimed, "github", randid())
        now = utcnow()
        tips.append({
            "ctime": now,
            "mtime": now,
            "tipper": tipper,
            "tippee": tippee,
            "amount": Decimal(amount)
        })

    then = utcnow() - datetime.timedelta(seconds=3600)

    participants = []
    elsewhere = []
    for username, crap in _participants.items():
        (good_cc, is_suspicious, claimed, platform, user_id) = crap
        username_key = "login" if platform == 'github' else "screen_name"
        elsewhere.append({
            "platform": platform,
            "user_id": user_id,
            "participant": username,
            "user_info": {
                "id": user_id,
                username_key: username
            }
        })
        rec = {"username": username}
        if good_cc is not None:
            rec["last_bill_result"] = "" if good_cc else "Failure!"
            rec["balanced_account_uri"] = "/v1/blah/blah/" + username
        rec["is_suspicious"] = is_suspicious
        if claimed:
            rec["claimed_time"] = then
        participants.append(rec)

    return ["participants"] + participants \
         + ["tips"] + tips \
         + ["elsewhere"] + elsewhere
 def make_participant(self, *a, **kw):
     kw['claimed_time'] = utcnow()
     return Harness.make_participant(self, *a, **kw)
Beispiel #40
0
    def add_email(self, email):
        """Add an email address for a participant.

        This is called when adding a new email address, and when resending the
        verification email for an unverified email address.

        :param unicode email: the email address to add

        :returns: ``None``

        :raises EmailAlreadyVerified: if the email is already verified for
            this participant
        :raises EmailTaken: if the email is verified for a different participant
        :raises TooManyEmailAddresses: if the participant already has 10 emails
        :raises Throttled: if the participant adds too many emails too quickly

        """

        # Check that this address isn't already verified
        owner = self.db.one(
            """
            SELECT p.username
              FROM emails e INNER JOIN participants p
                ON e.participant_id = p.id
             WHERE e.address = %(email)s
               AND e.verified IS true
        """, locals())
        if owner:
            if owner == self.username:
                raise EmailAlreadyVerified(email)
            else:
                raise EmailTaken(email)

        if len(self.get_emails()) > 9:
            raise TooManyEmailAddresses(email)

        nonce = str(uuid.uuid4())
        verification_start = utcnow()

        try:
            with self.db.get_cursor() as c:
                self.app.add_event(
                    c, 'participant',
                    dict(id=self.id, action='add', values=dict(email=email)))
                c.run(
                    """
                    INSERT INTO emails
                                (address, nonce, verification_start, participant_id)
                         VALUES (%s, %s, %s, %s)
                """, (email, nonce, verification_start, self.id))
        except IntegrityError:
            nonce = self.db.one(
                """
                UPDATE emails
                   SET verification_start=%s
                 WHERE participant_id=%s
                   AND address=%s
                   AND verified IS NULL
             RETURNING nonce
            """, (verification_start, self.id, email))
            if not nonce:
                return self.add_email(email)

        base_url = gratipay.base_url
        username = self.username_lower
        encoded_email = encode_for_querystring(email)
        link = "{base_url}/~{username}/emails/verify.html?email2={encoded_email}&nonce={nonce}"
        self.app.email_queue.put(self,
                                 'verification',
                                 email=email,
                                 link=link.format(**locals()),
                                 include_unsubscribe=False)
        if self.email_address:
            self.app.email_queue.put(
                self,
                'verification_notice',
                new_email=email,
                include_unsubscribe=False

                # Don't count this one against their sending quota.
                # It's going to their own verified address, anyway.
                ,
                _user_initiated=False)
Beispiel #41
0
    def make_participant(self, username, **kw):
        """Factory for :py:class:`~gratipay.models.participant.Participant`.
        """
        participant = self.db.one(
            """
            INSERT INTO participants
                        (username, username_lower)
                 VALUES (%s, %s)
              RETURNING participants.*::participants
        """, (username, username.lower()))

        if 'id' in kw:
            new_id = kw.pop('id')
            self.db.run('UPDATE participants SET id=%s WHERE id=%s',
                        (new_id, participant.id))
            participant.set_attributes(id=new_id)

        if 'elsewhere' in kw or 'claimed_time' in kw:
            platform = kw.pop('elsewhere', 'github')
            self.db.run(
                """
                INSERT INTO elsewhere
                            (platform, user_id, user_name, participant)
                     VALUES (%s,%s,%s,%s)
            """, (platform, participant.id, username, username))

        # Insert exchange routes
        if 'last_bill_result' in kw:
            route = ExchangeRoute.insert(participant, 'braintree-cc',
                                         '/cards/foo')
            route.update_error(kw.pop('last_bill_result'))
        if 'last_paypal_result' in kw:
            route = ExchangeRoute.insert(participant, 'paypal',
                                         '*****@*****.**')
            route.update_error(kw.pop('last_paypal_result'))

        # Handle email address
        if 'email_address' in kw:
            address = kw.pop('email_address')
            if address:
                self.add_and_verify_email(participant, address)
                self.db.run(
                    'DELETE FROM email_messages')  # don't confuse email tests

        # Update participant
        verified_in = kw.pop('verified_in', [])
        if kw:
            if kw.get('claimed_time') == 'now':
                kw['claimed_time'] = utcnow()
            cols, vals = zip(*kw.items())
            cols = ', '.join(cols)
            placeholders = ', '.join(['%s'] * len(vals))
            participant = self.db.one(
                """
                UPDATE participants
                   SET ({0}) = ({1})
                 WHERE username=%s
             RETURNING participants.*::participants
            """.format(cols, placeholders), vals + (username, ))

        # Verify identity
        countries = [verified_in
                     ] if type(verified_in) in (str, unicode) else verified_in
        for code in countries:
            country = self.db.one("SELECT id FROM countries WHERE code=%s",
                                  (code, ))
            participant.store_identity_info(country, 'nothing-enforced',
                                            {'name': username})
            participant.set_identity_verification(country, True)

        return participant
Beispiel #42
0
def to_age(dt):
    if isinstance(dt, datetime):
        return dt - utcnow()
    return datedelta(dt - date.today())
Beispiel #43
0
 def test_get_can_get_communities_for_user(self):
     self.make_participant("alice", claimed_time=utcnow())
     response = self.client.GET('/for/communities.json', auth_as='alice')
     assert len(json.loads(response.body)['communities']) == 0
def setup_tips(*recs):
    """Setup some participants and tips. recs is a list of:

        ("tipper", "tippee", '2.00', True, False, True)
                                       ^     ^      ^
                                       |     |      |
                                       |     |      -- claimed?
                                       |     -- is_suspicious?
                                       |-- good cc?

    good_cc can be True, False, or None
    is_suspicious can be True, False, or None
    claimed can be True or False

    """
    tips = []

    _participants = {}

    for rec in recs:
        defaults = good_cc, is_suspicious, claimed = (True, False, True)

        if len(rec) == 3:
            tipper, tippee, amount = rec
        elif len(rec) == 4:
            tipper, tippee, amount, good_cc = rec
            is_suspicious, claimed = (False, True)
        elif len(rec) == 5:
            tipper, tippee, amount, good_cc, is_suspicious = rec
            claimed = True
        elif len(rec) == 6:
            tipper, tippee, amount, good_cc, is_suspicious, claimed = rec

        assert good_cc in (True, False, None), good_cc
        assert is_suspicious in (True, False, None), is_suspicious
        assert claimed in (True, False), claimed

        _participants[tipper] = (good_cc, is_suspicious, claimed)
        if tippee not in _participants:
            _participants[tippee] = defaults
        now = utcnow()
        tips.append({
            "ctime": now,
            "mtime": now,
            "tipper": tipper,
            "tippee": tippee,
            "amount": Decimal(amount)
        })

    then = utcnow() - datetime.timedelta(seconds=3600)

    participants = []
    for participant_id, (good_cc, is_suspicious,
                         claimed) in _participants.items():
        rec = {"id": participant_id}
        if good_cc is not None:
            rec["last_bill_result"] = "" if good_cc else "Failure!"
            rec["balanced_account_uri"] = "/v1/blah/blah/" + participant_id
        rec["is_suspicious"] = is_suspicious
        if claimed:
            rec["claimed_time"] = then
        participants.append(rec)

    return ["participants"] + participants + ["tips"] + tips
Beispiel #45
0
 def test_non_admin_is_404(self):
     self.make_participant('alice', claimed_time=utcnow())
     self.make_participant('bob', claimed_time=utcnow())
     actual = self.record_an_exchange('10', '0', 'foo', False).code
     assert actual == 404, actual
Beispiel #46
0
 def make_participants(self):
     now = utcnow()
     self.make_participant('alice', claimed_time=now, is_admin=True)
     self.make_participant('bob', claimed_time=now)
Beispiel #47
0
 def test_non_post_is_405(self):
     self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
     self.make_participant('bob', claimed_time=utcnow())
     actual = \
            self.client.get('/bob/history/record-an-exchange', 'alice').code
     assert actual == 405, actual
Beispiel #48
0
 def check_connect_token(self, token):
     return (self.connect_token
             and constant_time_compare(self.connect_token, token)
             and self.connect_expires > utcnow())
Beispiel #49
0
def test_to_age_formatting_works():
    actual = to_age(utcnow(), fmt_past="Cheese, for {age}!")
    assert actual == "Cheese, for just a moment!", actual
Beispiel #50
0
 def make_connect_token(self):
     token = uuid.uuid4().hex
     expires = utcnow() + CONNECT_TOKEN_TIMEOUT
     return self.save_connect_token(token, expires)
Beispiel #51
0
def test_to_age_barely_works():
    actual = to_age(utcnow())
    assert actual == "just a moment ago", actual
    def test_start_zero_out_and_get_participants(self, log):
        self.make_participant('bob',
                              balance=10,
                              claimed_time=None,
                              pending=1,
                              balanced_account_uri=self.BALANCED_ACCOUNT_URI)
        self.make_participant('carl',
                              balance=10,
                              claimed_time=utcnow(),
                              pending=1,
                              balanced_account_uri=self.BALANCED_ACCOUNT_URI)
        self.db.run(
            """

            UPDATE participants
               SET balance=0
                 , claimed_time=null
                 , pending=null
                 , balanced_account_uri=%s
             WHERE username='******'

        """, (self.BALANCED_ACCOUNT_URI, ))

        ts_start = self.payday.start()

        self.payday.zero_out_pending(ts_start)
        participants = self.payday.get_participants(ts_start)

        expected_logging_call_args = [
            ('Starting a new payday.'),
            ('Payday started at {}.'.format(ts_start)),
            ('Zeroed out the pending column.'),
            ('Fetched participants.'),
        ]
        expected_logging_call_args.reverse()
        for args, _ in log.call_args_list:
            assert_equals(args[0], expected_logging_call_args.pop())

        log.reset_mock()

        # run a second time, we should see it pick up the existing payday
        second_ts_start = self.payday.start()
        self.payday.zero_out_pending(second_ts_start)
        second_participants = self.payday.get_participants(second_ts_start)

        self.assertEqual(ts_start, second_ts_start)
        participants = list(participants)
        second_participants = list(second_participants)

        # carl is the only valid participant as he has a claimed time
        assert_equals(len(participants), 1)
        assert_equals(participants, second_participants)

        expected_logging_call_args = [
            ('Picking up with an existing payday.'),
            ('Payday started at {}.'.format(second_ts_start)),
            ('Zeroed out the pending column.'), ('Fetched participants.')
        ]
        expected_logging_call_args.reverse()
        for args, _ in log.call_args_list:
            assert_equals(args[0], expected_logging_call_args.pop())
 def test_claiming_participant(self):
     now = utcnow()
     self.participant.set_as_claimed()
     actual = self.participant.claimed_time - now
     expected = datetime.timedelta(seconds=0.1)
     assert actual < expected
Beispiel #54
0
def to_age(dt, loc):
    return format_timedelta(dt - utcnow(), add_direction=True, locale=loc)
Beispiel #55
0
 def test_withdrawals_take_fee_out_of_balance(self):
     self.make_participant('alice', claimed_time=utcnow(), is_admin=True)
     self.make_participant('bob', claimed_time=utcnow(), balance=20)
     self.record_an_exchange('-7', '1.13', 'noted', False)
     SQL = "SELECT balance FROM participants WHERE username='******'"
     assert self.db.one(SQL) == Decimal('11.87')
 def make_team_and_participant(self):
     self.make_participant("team", claimed_time=utcnow(), number='plural')
     self.make_participant("alice", claimed_time=utcnow())