Repository: gratipay/gratipay.com

Issue:
fill out the test suite for the mixins, prune old tests
Reticketed from #3994.



Discussion:
Coverage is good but not 100% for all four:

| mixin | coverage |
| :-- | --: |
| `identity` | [100](https://travis-ci.org/gratipay/gratipay.com/#L1366) |
| `available` | [100](https://travis-ci.org/gratipay/gratipay.com/#L1369) |
| `membership` | [81](https://travis-ci.org/gratipay/gratipay.com/#L1370) |
| `takes` | [95](https://travis-ci.org/gratipay/gratipay.com/#L1371) |


Resolving patch:
diff --git a/gratipay/models/team/mixins/membership.py b/gratipay/models/team/mixins/membership.py
--- a/gratipay/models/team/mixins/membership.py
+++ b/gratipay/models/team/mixins/membership.py
@@ -27,17 +27,6 @@ def remove_member(self, participant, recorder):
         self.set_take_for(participant, ZERO, recorder)
 
 
-    def remove_all_members(self, cursor=None):
-        (cursor or self.db).run("""
-            INSERT INTO takes (ctime, member, team, amount, recorder) (
-                SELECT ctime, member, %(username)s, 0.00, %(username)s
-                  FROM current_takes
-                 WHERE team=%(username)s
-                   AND amount > 0
-            );
-        """, dict(username=self.username))
-
-
     @property
     def nmembers(self):
         """The number of members. Read-only and computed (not in the db); equal to
@@ -64,11 +53,8 @@ def get_memberships(self, current_participant=None):
             if current_participant:
                 member['removal_allowed'] = current_participant.username == self.owner
                 if member['username'] == current_participant.username:
-                    member['is_current_user'] = True
-                    if take['ctime'] is not None:
-                        # current user, but not the team itself
-                        member['editing_allowed']= True
+                    member['editing_allowed']= True
 
-            member['last_week'] = self.get_take_last_week_for(member['participant_id'])
+            member['last_week'] = self.get_take_last_week_for(take['participant'])
             members.append(member)
         return members
diff --git a/gratipay/models/team/mixins/takes.py b/gratipay/models/team/mixins/takes.py
--- a/gratipay/models/team/mixins/takes.py
+++ b/gratipay/models/team/mixins/takes.py
@@ -31,25 +31,6 @@ class TakesMixin(object):
     ndistributing_to = 0
 
 
-    def get_take_last_week_for(self, participant_id):
-        """Get the participant's nominal take last week.
-        """
-        return self.db.one("""
-
-            SELECT amount
-              FROM takes
-             WHERE team_id=%s AND participant_id=%s
-               AND mtime < (
-                       SELECT ts_start
-                         FROM paydays
-                        WHERE ts_end > ts_start
-                     ORDER BY ts_start DESC LIMIT 1
-                   )
-          ORDER BY mtime DESC LIMIT 1
-
-        """, (self.id, participant_id), default=ZERO)
-
-
     def set_take_for(self, participant, take, recorder, cursor=None):
         """Set the amount a participant wants to take from this team during payday.
 
@@ -72,10 +53,10 @@ def set_take_for(self, participant, take, recorder, cursor=None):
         def vet(p):
             if p.is_suspicious:
                 raise NotAllowed("user must not be flagged as suspicious")
-            elif not p.has_verified_identity:
-                raise NotAllowed("user must have a verified identity")
             elif not p.email_address:
                 raise NotAllowed("user must have added at least one email address")
+            elif not p.has_verified_identity:
+                raise NotAllowed("user must have a verified identity")
             elif not p.is_claimed:
                 raise NotAllowed("user must have claimed the account")
 
@@ -148,6 +129,30 @@ def get_take_for(self, participant, cursor=None):
         """, (self.id, participant.id), default=ZERO)
 
 
+    def get_take_last_week_for(self, participant, cursor=None):
+        """
+        :param Participant participant: the participant to get the take for
+        :param GratipayDB cursor: a database cursor; if ``None``, a new cursor
+            will be used
+        :return: a :py:class:`~decimal.Decimal`: the ``participant``'s take
+            from this team at the beginning of the last completed payday, or 0.
+        """
+        return (cursor or self.db).one("""
+
+            SELECT amount
+              FROM takes
+             WHERE team_id=%s AND participant_id=%s
+               AND mtime < (
+                       SELECT ts_start
+                         FROM paydays
+                        WHERE ts_end > ts_start
+                     ORDER BY ts_start DESC LIMIT 1
+                   )
+          ORDER BY mtime DESC LIMIT 1
+
+        """, (self.id, participant.id), default=ZERO)
+
+
     def update_taking(self, old_takes, new_takes, cursor=None, member=None):
         """Update `taking` amounts based on the difference between `old_takes`
         and `new_takes`.


Test patch:
diff --git a/gratipay/testing/billing.py b/gratipay/testing/billing.py
--- a/gratipay/testing/billing.py
+++ b/gratipay/testing/billing.py
@@ -3,8 +3,10 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
 import braintree
+import mock
 from braintree.test.nonces import Nonces
 
+from gratipay.billing.payday import Payday
 from gratipay.billing.exchanges import cancel_card_hold
 from gratipay.models.exchange_route import ExchangeRoute
 
@@ -12,7 +14,18 @@
 from .vcr import use_cassette
 
 
-class BillingHarness(Harness):
+class PaydayMixin(object):
+
+    @mock.patch.object(Payday, 'fetch_card_holds')
+    def run_payday(self, fch):
+        fch.return_value = {}
+        Payday.start().run()
+
+    def start_payday(self):
+        Payday.start()
+
+
+class BillingHarness(Harness, PaydayMixin):
     """This is a harness for billing-related tests.
     """
 
diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py
--- a/tests/py/test_billing_exchanges.py
+++ b/tests/py/test_billing_exchanges.py
@@ -15,7 +15,6 @@
     record_exchange_result,
     get_ready_payout_routes_by_network
 )
-from gratipay.billing.payday import Payday
 from gratipay.exceptions import NegativeBalance, NotWhitelisted
 from gratipay.models.exchange_route import ExchangeRoute
 from gratipay.testing import Foobar, Harness, D,P
@@ -149,11 +148,6 @@ def test_capch_amount_under_minimum(self):
 
     # grprbn - get_ready_payout_routes_by_network
 
-    @mock.patch.object(Payday, 'fetch_card_holds')
-    def run_payday(self, fch):
-        fch.return_value = {}
-        Payday.start().run()
-
     def test_grprbn_that_its_empty_to_start_with(self):
         assert get_ready_payout_routes_by_network(self.db, 'paypal') == []
 
diff --git a/tests/py/test_team_membership.py b/tests/py/test_team_membership.py
--- a/tests/py/test_team_membership.py
+++ b/tests/py/test_team_membership.py
@@ -1,6 +1,6 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 
-from test_team_takes import TeamTakesHarness
+from test_team_takes import TeamTakesHarness, PENNY
 from gratipay.models.team import mixins
 
 
@@ -32,6 +32,32 @@ def test_gm_returns_more_memberships_when_there_are_more_members(self):
         self.enterprise.add_member(self.bruiser, self.picard)
         assert len(self.enterprise.get_memberships()) == 2
 
+    def test_gm_sets_removal_allowed_to_true_when_removal_allowed(self):
+        self.enterprise.add_member(self.bruiser, self.picard)
+        memberships = self.enterprise.get_memberships(self.picard)
+        assert memberships[0]['removal_allowed']
+
+    def test_gm_sets_removal_allowed_to_false_when_removal_not_allowed(self):
+        self.enterprise.add_member(self.bruiser, self.picard)
+        memberships = self.enterprise.get_memberships(self.bruiser)
+        assert not memberships[0]['removal_allowed']
+
+    def test_gm_sets_editing_allowed_to_true_when_editing_allowed(self):
+        self.enterprise.add_member(self.bruiser, self.picard)
+        memberships = self.enterprise.get_memberships(self.bruiser)
+        assert memberships[0]['editing_allowed']
+
+    def test_gm_sets_editing_allowed_to_false_when_editing_not_allowed(self):
+        self.enterprise.add_member(self.bruiser, self.picard)
+        memberships = self.enterprise.get_memberships(self.picard)
+        assert not memberships[0]['editing_allowed']
+
+    def test_gm_sets_last_week(self):
+        self.enterprise.add_member(self.bruiser, self.picard)
+        self.run_payday()
+        memberships = self.enterprise.get_memberships(self.picard)
+        assert memberships[0]['last_week'] == PENNY
+
 
     # am - add_member
 
diff --git a/tests/py/test_team_takes.py b/tests/py/test_team_takes.py
--- a/tests/py/test_team_takes.py
+++ b/tests/py/test_team_takes.py
@@ -3,9 +3,10 @@
 from pytest import raises
 from gratipay.models.team.mixins.takes import NotAllowed, PENNY, ZERO
 from gratipay.testing import Harness, D,P,T
+from gratipay.testing.billing import PaydayMixin
 
 
-class TeamTakesHarness(Harness):
+class TeamTakesHarness(Harness, PaydayMixin):
     # Factored out to share with membership tests ...
 
     def setUp(self):
@@ -122,6 +123,75 @@ def test_stf_doesnt_let_anyone_set_a_take_who_is_not_already_on_the_team_even_to
         assert actual == 'can only set take if already a member of the team'
 
 
+    def test_stf_vets_participant_for_suspiciousness(self):
+        mallory = self.make_participant('mallory', is_suspicious=True)
+        actual = self.err(mallory, 0, self.picard)
+        assert actual == 'user must not be flagged as suspicious'
+
+    def test_stf_vets_participant_for_email(self):
+        mallory = self.make_participant('mallory')
+        actual = self.err(mallory, 0, self.picard)
+        assert actual == 'user must have added at least one email address'
+
+    def test_stf_vets_participant_for_verified_identity(self):
+        mallory = self.make_participant('mallory', email_address='m@example.com')
+        actual = self.err(mallory, 0, self.picard)
+        assert actual == 'user must have a verified identity'
+
+    def test_stf_vets_participant_for_claimed(self):
+        mallory = self.make_participant('mallory', email_address='m@example.com', verified_in='TT')
+        actual = self.err(mallory, 0, self.picard)
+        assert actual == 'user must have claimed the account'
+
+
+    def test_stf_vets_recorder_for_suspiciousness(self):
+        mallory = self.make_participant('mallory', is_suspicious=True)
+        actual = self.err(self.crusher, 0, mallory)
+        assert actual == 'user must not be flagged as suspicious'
+
+    def test_stf_vets_recorder_for_email(self):
+        mallory = self.make_participant('mallory')
+        actual = self.err(self.crusher, 0, mallory)
+        assert actual == 'user must have added at least one email address'
+
+    def test_stf_vets_recorder_for_verified_identity(self):
+        mallory = self.make_participant('mallory', email_address='m@example.com')
+        actual = self.err(self.crusher, 0, mallory)
+        assert actual == 'user must have a verified identity'
+
+    def test_stf_vets_recorder_for_claimed(self):
+        mallory = self.make_participant('mallory', email_address='m@example.com', verified_in='TT')
+        actual = self.err(self.crusher, 0, mallory)
+        assert actual == 'user must have claimed the account'
+
+
+    # gtlwf - get_take_last_week_for
+
+    def test_gtlwf_gets_take_last_week_for_someone(self):
+        self.enterprise.set_take_for(self.crusher, PENNY*1, self.picard)
+        self.enterprise.set_take_for(self.crusher, PENNY*24, self.crusher)
+        self.run_payday()
+        self.enterprise.set_take_for(self.crusher, PENNY*48, self.crusher)
+        assert self.enterprise.get_take_for(self.crusher) == PENNY*48  # sanity check
+        assert self.enterprise.get_take_last_week_for(self.crusher) == PENNY*24
+
+    def test_gtlwf_returns_zero_when_they_werent_taking(self):
+        self.run_payday()
+        self.enterprise.set_take_for(self.crusher, PENNY*1, self.picard)
+        assert self.enterprise.get_take_for(self.crusher) == PENNY*1  # sanity check
+        assert self.enterprise.get_take_last_week_for(self.crusher) == ZERO
+
+    def test_gtlwf_ignores_a_currently_running_payday(self):
+        self.enterprise.set_take_for(self.crusher, PENNY*1, self.picard)
+        self.enterprise.set_take_for(self.crusher, PENNY*24, self.crusher)
+        self.run_payday()
+        self.enterprise.set_take_for(self.crusher, PENNY*48, self.crusher)
+        self.start_payday()
+        self.enterprise.set_take_for(self.crusher, PENNY*96, self.crusher)
+        assert self.enterprise.get_take_for(self.crusher) == PENNY*96  # sanity check
+        assert self.enterprise.get_take_last_week_for(self.crusher) == PENNY*24
+
+
     # ut - update_taking
 
     def test_ut_updates_taking(self):