test_services.py 10.9 KB
Newer Older
1

2
from datetime import timedelta, date
3
4
from django.test import TestCase
from django.utils import timezone
5
from unittest import mock
6

7
8
from freezegun import freeze_time

9
10
from members import services
from members.models import Member, Membership, Profile, EmailChange
11
from members.services import gen_stats_year, gen_stats_member_type
12
from utils.snippets import datetime_to_lectureyear
13
14


15
16
class StatisticsTest(TestCase):

Thom Wiggers's avatar
Thom Wiggers committed
17
18
    @classmethod
    def setUpTestData(cls):
19
        # Add 10 members with default membership
20
21
        members = [Member(id=i, username=i) for i in range(10)]
        Member.objects.bulk_create(members)
22
        memberships = [Membership(user_id=i, type=Membership.MEMBER)
Thom Wiggers's avatar
Thom Wiggers committed
23
24
                       for i in range(10)]
        Membership.objects.bulk_create(memberships)
25
26
        profiles = [Profile(user_id=i) for i in range(10)]
        Profile.objects.bulk_create(profiles)
27
28

    def sum_members(self, members, type=None):
Thom Wiggers's avatar
Thom Wiggers committed
29
        if type is None:
30
            return sum(sum(list(i.values())[1:]) for i in members)
Thom Wiggers's avatar
Thom Wiggers committed
31
32
        else:
            return sum(map(lambda x: x[type], members))
33
34

    def sum_member_types(self, members):
Thom Wiggers's avatar
Thom Wiggers committed
35
        return sum(members.values())
36
37

    def test_gen_stats_year_no_members(self):
38
        member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
39
40
41
42
43
44
45
        result = gen_stats_year(member_types)
        self.assertEqual(0, self.sum_members(result))

    def test_gen_stats_active(self):
        """
        Testing if active and non-active objects are counted correctly
        """
46
        member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
47
        current_year = datetime_to_lectureyear(date.today())
48
49
50

        # Set start date to current year - 1:
        for m in Member.objects.all():
51
52
            m.profile.starting_year = current_year - 1
            m.profile.save()
53
54
        result = gen_stats_year(member_types)
        self.assertEqual(10, self.sum_members(result))
55
        self.assertEqual(10, self.sum_members(result, Membership.MEMBER))
56
57
58
59

        result = gen_stats_member_type(member_types)
        self.assertEqual(10, self.sum_member_types(result))

60
        # Change one membership to benefactor should decrease amount of members
61
        m = Membership.objects.all()[0]
62
        m.type = Membership.BENEFACTOR
63
64
65
66
        m.save()

        result = gen_stats_year(member_types)
        self.assertEqual(10, self.sum_members(result))
67
68
        self.assertEqual(9, self.sum_members(result, Membership.MEMBER))
        self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
69
70
71

        result = gen_stats_member_type(member_types)
        self.assertEqual(10, self.sum_member_types(result))
72
73
        self.assertEqual(9, result[Membership.MEMBER])
        self.assertEqual(1, result[Membership.BENEFACTOR])
74
75
76

        # Same for honorary members
        m = Membership.objects.all()[1]
77
        m.type = Membership.HONORARY
78
79
80
81
        m.save()

        result = gen_stats_year(member_types)
        self.assertEqual(10, self.sum_members(result))
82
83
84
        self.assertEqual(8, self.sum_members(result, Membership.MEMBER))
        self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
        self.assertEqual(1, self.sum_members(result, Membership.HONORARY))
85
86
87

        result = gen_stats_member_type(member_types)
        self.assertEqual(10, self.sum_member_types(result))
88
89
90
        self.assertEqual(8, result[Membership.MEMBER])
        self.assertEqual(1, result[Membership.BENEFACTOR])
        self.assertEqual(1, result[Membership.HONORARY])
91
92
93
94
95
96
97
98

        # Terminate one membership by setting end date to current_year -1,
        # should decrease total amount and total members
        m = Membership.objects.all()[2]
        m.until = timezone.now() - timedelta(days=365)
        m.save()
        result = gen_stats_year(member_types)
        self.assertEqual(9, self.sum_members(result))
99
100
101
        self.assertEqual(7, self.sum_members(result, Membership.MEMBER))
        self.assertEqual(1, self.sum_members(result, Membership.BENEFACTOR))
        self.assertEqual(1, self.sum_members(result, Membership.HONORARY))
102
103
104

        result = gen_stats_member_type(member_types)
        self.assertEqual(9, self.sum_member_types(result))
105
106
107
        self.assertEqual(7, result[Membership.MEMBER])
        self.assertEqual(1, result[Membership.BENEFACTOR])
        self.assertEqual(1, result[Membership.HONORARY])
108
109

    def test_gen_stats_different_years(self):
110
        member_types = [t[0] for t in Membership.MEMBERSHIP_TYPES]
111
        current_year = datetime_to_lectureyear(date.today())
112

113
114
115
        # postgres does not define random access directly on unsorted querysets
        members = [member for member in Member.objects.all()]

116
        # one first year student
117
        m = members[0]
118
119
        m.profile.starting_year = current_year
        m.profile.save()
120
121

        # one second year student
122
        m = members[1]
123
124
        m.profile.starting_year = current_year - 1
        m.profile.save()
125
126
127
128

        # no third year students

        # one fourth year student
129
        m = members[2]
130
131
        m.profile.starting_year = current_year - 3
        m.profile.save()
132
133
134
135

        # no fifth year students

        # one >5 year student
136
        m = members[3]
137
138
        m.profile.starting_year = current_year - 5
        m.profile.save()
139
140
141
142

        # 4 active members
        result = gen_stats_year(member_types)
        self.assertEqual(4, self.sum_members(result))
143
        self.assertEqual(4, self.sum_members(result, Membership.MEMBER))
144
145

        # one first year student
146
        self.assertEqual(1, result[0][Membership.MEMBER])
147
148

        # one second year student
149
        self.assertEqual(1, result[1][Membership.MEMBER])
150
151

        # no third year students
152
        self.assertEqual(0, result[2][Membership.MEMBER])
153
154

        # one fourth year student
155
        self.assertEqual(1, result[3][Membership.MEMBER])
156
157

        # no fifth year students
158
        self.assertEqual(0, result[4][Membership.MEMBER])
159
160

        # one >5 year student
161
        self.assertEqual(1, result[5][Membership.MEMBER])
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219


class EmailChangeTest(TestCase):
    fixtures = ['members.json']

    @classmethod
    def setUpTestData(cls):
        # Add 10 members with default membership
        cls.member = Member.objects.get(pk=2)

    def setUp(self):
        self.member.refresh_from_db()

    def test_verify_email_change(self):
        change_request = EmailChange(
            member=self.member,
            email='new@example.org'
        )

        with mock.patch('members.services.process_email_change') as proc:
            services.verify_email_change(change_request)
            self.assertTrue(change_request.verified)
            proc.assert_called_once_with(change_request)

    def test_confirm_email_change(self):
        change_request = EmailChange(
            member=self.member,
            email='new@example.org'
        )

        with mock.patch('members.services.process_email_change') as proc:
            services.confirm_email_change(change_request)
            self.assertTrue(change_request.confirmed)
            proc.assert_called_once_with(change_request)

    @mock.patch('members.emails.send_email_change_completion_message')
    def test_process_email_change(self, send_message_mock):
        change_request = EmailChange(
            member=self.member,
            email='new@example.org'
        )

        original_email = self.member.email

        with self.subTest('Uncompleted request'):
            services.process_email_change(change_request)

            self.assertEqual(self.member.email, original_email)
            send_message_mock.assert_not_called()

        with self.subTest('Completed request'):
            change_request.verified = True
            change_request.confirmed = True

            services.process_email_change(change_request)

            self.assertEqual(self.member.email, change_request.email)
            send_message_mock.assert_called_once_with(change_request)
220
221


222
223
@freeze_time('2018-10-2')
class DataMinimisationTest(TestCase):
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

    @classmethod
    def setUpTestData(cls):
        cls.member = Member.objects.create(
            username='test1',
            first_name='Test1',
            last_name='Example',
            email='test1@example.org'
        )
        Profile.objects.create(
            user=cls.member,
            language='nl',
            student_number='s1234567'
        )
        cls.membership = Membership.objects.create(
            user=cls.member,
            type=Membership.MEMBER,
            since=timezone.now().replace(year=2017, month=9, day=1),
            until=timezone.now().replace(year=2018, month=9, day=1)
        )

    def test_removes_after_31_days(self):
        processed = services.execute_data_minimisation(True)
        self.assertEqual(len(processed), 1)
        self.assertEqual(processed[0], self.member)

        self.membership.until = timezone.now().replace(
            year=2018, month=11, day=1)
        self.membership.save()
        processed = services.execute_data_minimisation(True)
        self.assertEqual(len(processed), 0)

    def test_dry_run(self):
        with self.subTest('With dry_run=True'):
            services.execute_data_minimisation(True)
            self.member.refresh_from_db()
            self.assertEqual(self.member.profile.student_number, 's1234567')
        with self.subTest('With dry_run=False'):
            services.execute_data_minimisation(False)
            self.member.refresh_from_db()
            self.assertIsNone(self.member.profile.student_number)

    def test_does_not_affect_current_members(self):
        with self.subTest('Membership ends in future'):
            self.membership.until = timezone.now().replace(
                year=2019, month=9, day=1)
            self.membership.save()
            processed = services.execute_data_minimisation(True)
            self.assertEqual(len(processed), 0)
        with self.subTest('Never ending membership'):
            self.membership.until = None
            self.membership.save()
            processed = services.execute_data_minimisation(True)
            self.assertEqual(len(processed), 0)
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
            self.membership.until = timezone.now().replace(
                year=2018, month=9, day=1)
            self.membership.save()
        with self.subTest('Newer year membership after expired one'):
            m = Membership.objects.create(
                user=self.member,
                type=Membership.MEMBER,
                since=timezone.now().replace(year=2018, month=9, day=10),
                until=timezone.now().replace(year=2019, month=8, day=31),
            )
            processed = services.execute_data_minimisation(True)
            self.assertEqual(len(processed), 0)
            m.delete()
        with self.subTest('Newer study membership after expired one'):
            m = Membership.objects.create(
                user=self.member,
                type=Membership.MEMBER,
                since=timezone.now().replace(year=2018, month=9, day=10),
                until=None
            )
            processed = services.execute_data_minimisation(True)
            self.assertEqual(len(processed), 0)
            m.delete()