コード例 #1
0
    def test_stats_description_accurate_outside_of_payday(self, mock_datetime):
        """Test stats page outside of the payday running"""
        a_monday = datetime(2012, 8, 6, 11, 00, 01)
        mock_datetime.utcnow.return_value = a_monday

        payday = Payday(self.postgres)
        payday.start()

        body = self.get_stats_page()
        assert "is ready for <b>this Thursday</b>" in body, body
        payday.end()
コード例 #2
0
    def test_stats_description_accurate_outside_of_payday(self, mock_datetime):
        """Test stats page outside of the payday running"""
        a_monday = datetime(2012, 8, 6, 11, 00, 01)
        mock_datetime.utcnow.return_value = a_monday

        payday = Payday(self.postgres)
        payday.start()

        body = self.get_stats_page()
        assert "is ready for <b>this Thursday</b>" in body, body
        payday.end()
コード例 #3
0
ファイル: test_stats.py プロジェクト: break123/www.gittip.com
def test_stats_description_accurate_outside_of_payday(mock_datetime):
    """Test stats page outside of the payday running"""
    with testing.load() as context:
        a_monday = datetime(2012, 8, 6, 12, 00, 01)
        mock_datetime.utcnow.return_value = a_monday

        pd = Payday(context.db)
        pd.start()

        body = get_stats_page()
        assert "is ready for <b>this Thursday</b>" in body, body
        pd.end()
コード例 #4
0
ファイル: test_stats.py プロジェクト: turicas/www.gittip.com
    def test_stats_description_accurate_outside_of_payday(self, mock_datetime):
        """Test stats page outside of the payday running"""
        self.clear_paydays()
        a_monday = datetime(2012, 8, 6, 12, 00, 01)
        mock_datetime.utcnow.return_value = a_monday

        db = wireup.db()
        wireup.billing()
        pd = Payday(db)
        pd.start()

        body = self.get_stats_page()
        self.assertTrue("is ready for <b>this Friday</b>" in body)
        pd.end()
コード例 #5
0
    def test_stats_description_accurate_outside_of_payday(self, utcnow):
        """Test stats page outside of the payday running"""

        a_monday = DateTime(2012, 8, 6, 11, 00, 01)
        utcnow.return_value = a_monday

        self.client.hydrate_website()

        payday = Payday(self.db)
        payday.start()

        body = self.get_stats_page()
        assert "is ready for <b>this Thursday</b>" in body, body
        payday.end()
コード例 #6
0
    def test_stats_description_accurate_outside_of_payday(self, utcnow):
        """Test stats page outside of the payday running"""

        a_monday = DateTime(2012, 8, 6, 11, 00, 01)
        utcnow.return_value = a_monday

        self.client.hydrate_website()

        payday = Payday(self.db)
        payday.start()

        body = self.get_stats_page()
        assert "is ready for <b>this Thursday</b>" in body, body
        payday.end()
コード例 #7
0
    def test_stats_description_accurate_during_payday_run(self, mock_datetime):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """
        a_thursday = datetime(2012, 8, 9, 11, 00, 01)
        mock_datetime.utcnow.return_value = a_thursday

        wireup.billing()
        payday = Payday(self.postgres)
        payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
コード例 #8
0
ファイル: test_stats.py プロジェクト: tomwaits/www.gittip.com
    def test_stats_description_accurate_outside_of_payday(self):
        """Test stats page outside of the payday running"""

        # Hydrating a website requires a functioning datetime module.
        self.client.hydrate_website()

        a_monday = datetime.datetime(2012, 8, 6, 11, 00, 01)
        with patch.object(datetime, 'datetime') as mock_datetime:
            mock_datetime.utcnow.return_value = a_monday

            payday = Payday(self.db)
            payday.start()

            body = self.get_stats_page()
            assert "is ready for <b>this Thursday</b>" in body, body
            payday.end()
コード例 #9
0
ファイル: test_stats.py プロジェクト: D-Land/www.gittip.com
    def test_stats_description_accurate_outside_of_payday(self):
        """Test stats page outside of the payday running"""

        # Hydrating a website requires a functioning datetime module.
        self.client.hydrate_website()

        a_monday = datetime.datetime(2012, 8, 6, 11, 00, 01)
        with patch.object(datetime, 'datetime') as mock_datetime:
            mock_datetime.utcnow.return_value = a_monday

            payday = Payday(self.db)
            payday.start()

            body = self.get_stats_page()
            assert "is ready for <b>this Thursday</b>" in body, body
            payday.end()
コード例 #10
0
    def test_stats_description_accurate_during_payday_run(self, mock_datetime):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """
        a_thursday = datetime(2012, 8, 9, 11, 00, 01)
        mock_datetime.utcnow.return_value = a_thursday

        wireup.billing()
        payday = Payday(self.postgres)
        payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
コード例 #11
0
ファイル: test_stats.py プロジェクト: break123/www.gittip.com
def test_stats_description_accurate_during_payday_run(mock_datetime):
    """Test that stats page takes running payday into account.

    This test was originally written to expose the fix required for
    https://github.com/whit537/www.gittip.com/issues/92.
    """
    with testing.load() as context:
        a_thursday = datetime(2012, 8, 9, 12, 00, 01)
        mock_datetime.utcnow.return_value = a_thursday

        wireup.billing()
        pd = Payday(context.db)
        pd.start()

        body = get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        pd.end()
コード例 #12
0
ファイル: test_stats.py プロジェクト: turicas/www.gittip.com
    def test_stats_description_accurate_during_payday_run(self, mock_datetime):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/whit537/www.gittip.com/issues/92.
        """
        self.clear_paydays()
        a_friday = datetime(2012, 8, 10, 12, 00, 01)
        mock_datetime.utcnow.return_value = a_friday

        db = wireup.db()
        wireup.billing()
        pd = Payday(db)
        pd.start()

        body = self.get_stats_page()
        self.assertTrue("is changing hands <b>right now!</b>" in body)
        pd.end()
コード例 #13
0
    def test_stats_description_accurate_during_payday_run(self, utcnow):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """
        a_thursday = DateTime(2012, 8, 9, 11, 00, 01)
        utcnow.return_value = a_thursday

        self.client.hydrate_website()

        env = wireup.env()
        wireup.billing(env)
        payday = Payday(self.db)
        payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
コード例 #14
0
    def test_stats_description_accurate_during_payday_run(self, utcnow):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """
        a_thursday = DateTime(2012, 8, 9, 11, 00, 01)
        utcnow.return_value = a_thursday

        self.client.hydrate_website()

        env = wireup.env()
        wireup.billing(env)
        payday = Payday(self.db)
        payday.start()

        body = self.get_stats_page()
        assert "is changing hands <b>right now!</b>" in body, body
        payday.end()
コード例 #15
0
ファイル: test_stats.py プロジェクト: tomwaits/www.gittip.com
    def test_stats_description_accurate_during_payday_run(self):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """

        # Hydrating a website requires a functioning datetime module.
        self.client.hydrate_website()

        a_thursday = datetime.datetime(2012, 8, 9, 11, 00, 01)
        with patch.object(datetime, 'datetime') as mock_datetime:
            mock_datetime.utcnow.return_value = a_thursday

            wireup.billing()
            payday = Payday(self.db)
            payday.start()

            body = self.get_stats_page()
            assert "is changing hands <b>right now!</b>" in body, body
            payday.end()
コード例 #16
0
ファイル: test_stats.py プロジェクト: D-Land/www.gittip.com
    def test_stats_description_accurate_during_payday_run(self):
        """Test that stats page takes running payday into account.

        This test was originally written to expose the fix required for
        https://github.com/gittip/www.gittip.com/issues/92.
        """

        # Hydrating a website requires a functioning datetime module.
        self.client.hydrate_website()

        a_thursday = datetime.datetime(2012, 8, 9, 11, 00, 01)
        with patch.object(datetime, 'datetime') as mock_datetime:
            mock_datetime.utcnow.return_value = a_thursday

            wireup.billing()
            payday = Payday(self.db)
            payday.start()

            body = self.get_stats_page()
            assert "is changing hands <b>right now!</b>" in body, body
            payday.end()
コード例 #17
0
class TestBillingPayday(Harness):
    BALANCED_ACCOUNT_URI = '/v1/marketplaces/M123/accounts/A123'

    def setUp(self):
        super(TestBillingPayday, self).setUp()
        self.payday = Payday(self.postgres)

    def test_assert_one_payday(self):
        with assert_raises(AssertionError):
            self.payday.assert_one_payday(None)
        with assert_raises(AssertionError):
            self.payday.assert_one_payday([1, 2])

    @mock.patch('gittip.participant.Participant.get_tips_and_total')
    def test_charge_and_or_transfer_no_tips(self, get_tips_and_total):
        alice = self.make_participant('alice', balance='1.00',
                                      balanced_account_uri=self.BALANCED_ACCOUNT_URI,
                                      is_suspicious=False)
        amount = Decimal('1.00')

        ts_start = self.payday.start()

        tips, total = [], amount

        initial_payday = PaydayModel.query.first().attrs_dict()
        self.payday.charge_and_or_transfer(ts_start, alice.attrs_dict(),
                                           tips, total)
        resulting_payday = PaydayModel.query.first().attrs_dict()

        assert_equals(initial_payday['ntippers'], resulting_payday['ntippers'])
        assert_equals(initial_payday['ntips'], resulting_payday['ntips'])
        assert_equals(initial_payday['nparticipants'] + 1,
                      resulting_payday['nparticipants'])

    @mock.patch('gittip.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.tip')
    def test_charge_and_or_transfer(self, tip, get_tips_and_total):
        alice = self.make_participant('alice', balance='1.00',
                                      balanced_account_uri=self.BALANCED_ACCOUNT_URI,
                                      is_suspicious=False)
        ts_start = self.payday.start()
        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now,
                      'claimed_time': now}

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        total = amount

        ts_start = datetime.utcnow()

        return_values = [1, 1, 0, -1]
        return_values.reverse()

        def tip_return_values(*_):
            return return_values.pop()

        tip.side_effect = tip_return_values

        initial_payday = PaydayModel.query.first().attrs_dict()
        self.payday.charge_and_or_transfer(ts_start, alice.attrs_dict(), tips,
                                           total)
        resulting_payday = PaydayModel.query.first().attrs_dict()

        assert_equals(initial_payday['ntippers'] + 1,
                      resulting_payday['ntippers'])
        assert_equals(initial_payday['ntips'] + 2,
                      resulting_payday['ntips'])
        assert_equals(initial_payday['nparticipants'] + 1,
                      resulting_payday['nparticipants'])

    @mock.patch('gittip.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.charge')
    def test_charge_and_or_transfer_short(self, charge, get_tips_and_total):
        alice = self.make_participant('alice', balance='1.00',
                                      balanced_account_uri=self.BALANCED_ACCOUNT_URI,
                                      is_suspicious=False)
        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now,
                      'claimed_time': now}

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        get_tips_and_total.return_value = tips, amount

        ts_start = datetime.utcnow()

        # In real-life we wouldn't be able to catch an error as the charge
        # method will swallow any errors and return false. We don't handle this
        # return value within charge_and_or_transfer but instead continue on
        # trying to use the remaining credit in the user's account to payout as
        # many tips as possible.
        #
        # Here we're hacking the system and throwing the exception so execution
        # stops since we're only testing this part of the method. That smells
        # like we need to refactor.

        charge.side_effect = Exception()
        with self.assertRaises(Exception):
            billing.charge_and_or_transfer(ts_start, alice.attrs_dict())
        self.assertTrue(charge.called_with(alice.username,
                                           self.BALANCED_ACCOUNT_URI,
                                           amount))

    @mock.patch('gittip.billing.payday.Payday.transfer')
    @mock.patch('gittip.billing.payday.log')
    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.username
              , 'claimed_time': utcnow()
               }
        ts_start = utcnow()

        result = self.payday.tip(participant, tip, ts_start)
        assert_equals(result, 1)
        result = transfer.called_with(participant['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(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)

    @mock.patch('gittip.billing.payday.log')
    def test_start_zero_out_and_get_participants(self, log):
        self.make_participant('alice', balance=0, claimed_time=None,
                              pending=None,
                              balanced_account_uri=self.BALANCED_ACCOUNT_URI)
        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)

        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())

    @mock.patch('gittip.billing.payday.log')
    def test_end(self, log):
        self.payday.start()
        self.payday.end()
        self.assertTrue(log.called_with('Finished payday.'))

        # finishing the payday will set the ts_end date on this payday record
        # to now, so this will not return any result
        result = PaydayModel.query.filter(PaydayModel.ts_end > '1970-01-01')\
                                  .count()
        assert_equals(result, 1)

    @mock.patch('gittip.billing.payday.log')
    @mock.patch('gittip.billing.payday.Payday.start')
    @mock.patch('gittip.billing.payday.Payday.payin')
    @mock.patch('gittip.billing.payday.Payday.end')
    def test_payday(self, end, payin, init, log):
        ts_start = utcnow()
        init.return_value = (ts_start,)
        greeting = 'Greetings, program! It\'s PAYDAY!!!!'

        self.payday.run()

        self.assertTrue(log.called_with(greeting))
        self.assertTrue(init.call_count)
        self.assertTrue(payin.called_with(init.return_value))
        self.assertTrue(end.call_count)
コード例 #18
0
class TestBillingPayday(TestPaydayBase):
    BALANCED_ACCOUNT_URI = '/v1/marketplaces/M123/accounts/A123'

    def setUp(self):
        super(TestBillingPayday, self).setUp()
        self.payday = Payday(self.db)

    def test_move_pending_to_balance_for_teams_does_so(self):
        self.make_participant('A', number='plural', balance=2, pending=3)
        self.payday.move_pending_to_balance_for_teams()
        actual = self.db.one("SELECT balance FROM participants WHERE username='******'")
        assert actual == 5

    def test_move_pending_to_balance_for_teams_ignores_new_teams(self):
        # See https://github.com/gittip/www.gittip.com/issues/1684
        self.make_participant('A', number='plural', balance=0, pending=None)
        self.payday.move_pending_to_balance_for_teams()
        actual = self.db.one("SELECT balance FROM participants WHERE username='******'")
        assert actual == 0

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    def test_charge_and_or_transfer_no_tips(self, get_tips_and_total):
        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')

        ts_start = self.payday.start()

        tips, total = [], amount

        initial_payday = self.fetch_payday()
        self.payday.charge_and_or_transfer(ts_start, self.alice, tips, total)
        resulting_payday = self.fetch_payday()

        assert initial_payday['ntippers'] == resulting_payday['ntippers']
        assert initial_payday['ntips'] == resulting_payday['ntips']
        assert initial_payday['nparticipants'] + 1 == resulting_payday['nparticipants']

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.tip')
    def test_charge_and_or_transfer(self, tip, get_tips_and_total):
        self.db.run("""

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

        """, (self.BALANCED_ACCOUNT_URI,))

        ts_start = self.payday.start()
        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now,
                      'claimed_time': now}

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        total = amount

        ts_start = datetime.utcnow()

        return_values = [1, 1, 0, -1]
        return_values.reverse()

        def tip_return_values(*_):
            return return_values.pop()

        tip.side_effect = tip_return_values

        initial_payday = self.fetch_payday()
        self.payday.charge_and_or_transfer(ts_start, self.alice, tips, total)
        resulting_payday = self.fetch_payday()

        assert initial_payday['ntippers'] + 1 == resulting_payday['ntippers']
        assert initial_payday['ntips'] + 2 == resulting_payday['ntips']
        assert initial_payday['nparticipants'] + 1 == resulting_payday['nparticipants']

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.charge')
    def test_charge_and_or_transfer_short(self, charge, get_tips_and_total):
        self.db.run("""

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

        """, (self.BALANCED_ACCOUNT_URI,))

        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now,
                      'claimed_time': now}

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        get_tips_and_total.return_value = tips, amount

        ts_start = datetime.utcnow()

        # In real-life we wouldn't be able to catch an error as the charge
        # method will swallow any errors and return false. We don't handle this
        # return value within charge_and_or_transfer but instead continue on
        # trying to use the remaining credit in the user's account to payout as
        # many tips as possible.
        #
        # Here we're hacking the system and throwing the exception so execution
        # stops since we're only testing this part of the method. That smells
        # like we need to refactor.

        charge.side_effect = Exception()
        with self.assertRaises(Exception):
            billing.charge_and_or_transfer(ts_start, self.alice)
        assert charge.called_with(self.alice.username,
                                  self.BALANCED_ACCOUNT_URI,
                                  amount)

    @mock.patch('gittip.billing.payday.Payday.transfer')
    @mock.patch('gittip.billing.payday.log')
    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 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

    @mock.patch('gittip.billing.payday.log')
    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 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()

    @mock.patch('gittip.billing.payday.log')
    def test_end(self, log):
        self.payday.start()
        self.payday.end()
        assert log.called_with('Finished payday.')

        # finishing the payday will set the ts_end date on this payday record
        # to now, so this will not return any result
        result = self.db.one("SELECT count(*) FROM paydays "
                             "WHERE ts_end > '1970-01-01'")
        assert result == 1

    @mock.patch('gittip.billing.payday.log')
    @mock.patch('gittip.billing.payday.Payday.start')
    @mock.patch('gittip.billing.payday.Payday.payin')
    @mock.patch('gittip.billing.payday.Payday.end')
    def test_payday(self, end, payin, init, log):
        ts_start = utcnow()
        init.return_value = (ts_start,)
        greeting = 'Greetings, program! It\'s PAYDAY!!!!'

        self.payday.run()

        assert log.called_with(greeting)
        assert init.call_count
        assert payin.called_with(init.return_value)
        assert end.call_count
コード例 #19
0
class TestBillingPayday(TestPaydayBase):
    BALANCED_ACCOUNT_URI = '/v1/marketplaces/M123/accounts/A123'

    def setUp(self):
        super(TestBillingPayday, self).setUp()
        self.payday = Payday(self.db)

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    def test_charge_and_or_transfer_no_tips(self, get_tips_and_total):
        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')

        ts_start = self.payday.start()

        tips, total = [], amount

        initial_payday = self.fetch_payday()
        self.payday.charge_and_or_transfer(ts_start, self.alice, tips, total)
        resulting_payday = self.fetch_payday()

        assert_equals(initial_payday['ntippers'], resulting_payday['ntippers'])
        assert_equals(initial_payday['ntips'], resulting_payday['ntips'])
        assert_equals(initial_payday['nparticipants'] + 1,
                      resulting_payday['nparticipants'])

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.tip')
    def test_charge_and_or_transfer(self, tip, get_tips_and_total):
        self.db.run(
            """

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

        """, (self.BALANCED_ACCOUNT_URI, ))

        ts_start = self.payday.start()
        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {
            'amount': amount,
            'tippee': 'mjallday',
            'ctime': now,
            'claimed_time': now
        }

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        total = amount

        ts_start = datetime.utcnow()

        return_values = [1, 1, 0, -1]
        return_values.reverse()

        def tip_return_values(*_):
            return return_values.pop()

        tip.side_effect = tip_return_values

        initial_payday = self.fetch_payday()
        self.payday.charge_and_or_transfer(ts_start, self.alice, tips, total)
        resulting_payday = self.fetch_payday()

        assert_equals(initial_payday['ntippers'] + 1,
                      resulting_payday['ntippers'])
        assert_equals(initial_payday['ntips'] + 2, resulting_payday['ntips'])
        assert_equals(initial_payday['nparticipants'] + 1,
                      resulting_payday['nparticipants'])

    @mock.patch('gittip.models.participant.Participant.get_tips_and_total')
    @mock.patch('gittip.billing.payday.Payday.charge')
    def test_charge_and_or_transfer_short(self, charge, get_tips_and_total):
        self.db.run(
            """

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

        """, (self.BALANCED_ACCOUNT_URI, ))

        now = datetime.utcnow()
        amount = Decimal('1.00')
        like_a_tip = {
            'amount': amount,
            'tippee': 'mjallday',
            'ctime': now,
            'claimed_time': now
        }

        # success, success, claimed, failure
        tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip]
        get_tips_and_total.return_value = tips, amount

        ts_start = datetime.utcnow()

        # In real-life we wouldn't be able to catch an error as the charge
        # method will swallow any errors and return false. We don't handle this
        # return value within charge_and_or_transfer but instead continue on
        # trying to use the remaining credit in the user's account to payout as
        # many tips as possible.
        #
        # Here we're hacking the system and throwing the exception so execution
        # stops since we're only testing this part of the method. That smells
        # like we need to refactor.

        charge.side_effect = Exception()
        with self.assertRaises(Exception):
            billing.charge_and_or_transfer(ts_start, self.alice)
        self.assertTrue(
            charge.called_with(self.alice.username, self.BALANCED_ACCOUNT_URI,
                               amount))

    @mock.patch('gittip.billing.payday.Payday.transfer')
    @mock.patch('gittip.billing.payday.log')
    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)

    @mock.patch('gittip.billing.payday.log')
    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())

    @mock.patch('gittip.billing.payday.log')
    def test_end(self, log):
        self.payday.start()
        self.payday.end()
        self.assertTrue(log.called_with('Finished payday.'))

        # finishing the payday will set the ts_end date on this payday record
        # to now, so this will not return any result
        result = self.db.one("SELECT count(*) FROM paydays "
                             "WHERE ts_end > '1970-01-01'")
        assert_equals(result, 1)

    @mock.patch('gittip.billing.payday.log')
    @mock.patch('gittip.billing.payday.Payday.start')
    @mock.patch('gittip.billing.payday.Payday.payin')
    @mock.patch('gittip.billing.payday.Payday.end')
    def test_payday(self, end, payin, init, log):
        ts_start = utcnow()
        init.return_value = (ts_start, )
        greeting = 'Greetings, program! It\'s PAYDAY!!!!'

        self.payday.run()

        self.assertTrue(log.called_with(greeting))
        self.assertTrue(init.call_count)
        self.assertTrue(payin.called_with(init.return_value))
        self.assertTrue(end.call_count)