source: OpenWorkouts-current/ow/tests/models/test_user.py @ 24596da

currentfeature/docs
Last change on this file since 24596da was 24596da, checked in by Borja Lopez <borja@…>, 5 years ago

Added missing tests, raised overall coverage

  • Property mode set to 100644
File size: 20.6 KB
Line 
1from decimal import Decimal
2from datetime import datetime, timedelta, timezone
3
4import pytest
5from pyramid.security import Allow, Everyone, Deny, ALL_PERMISSIONS
6
7from ow.models.root import OpenWorkouts
8from ow.models.workout import Workout
9from ow.models.user import User
10
11
12class TestUser(object):
13    @pytest.fixture
14    def root(self):
15        root = OpenWorkouts()
16        root['john'] = User(firstname='John', lastname='Doe',
17                            email='john.doe@example.net')
18        root['john'].password = 's3cr3t'
19        return root
20
21    @pytest.fixture
22    def workouts(self):
23        workouts = [Workout(sport='running'),
24                    Workout(sport='cycling'),
25                    Workout(sport='swimming')]
26        return workouts
27
28    def test_user_attrs(self, root):
29        assert root['john'].firstname == 'John'
30        assert root['john'].lastname == 'Doe'
31        assert root['john'].email == 'john.doe@example.net'
32
33    def test__acl__(self, root):
34        uid = str(root['john'].uid)
35        permissions = [
36            (Allow, uid, 'view'),
37            (Allow, uid, 'edit'),
38            (Deny, Everyone, ALL_PERMISSIONS),
39        ]
40        assert root['john'].__acl__() == permissions
41
42    def test__str__(self, root):
43        email = root['john'].email
44        uid = str(root['john'].uid)
45        assert root['john'].__str__() == u'User: ' + uid + ' (' + email + ')'
46
47    def test__repr__(self, root):
48        email = root['john'].email
49        uid = str(root['john'].uid)
50        expected = u'<ow.models.user.User: ' + uid + ' (' + email + ')>'
51        assert root['john'].__repr__() == expected
52
53    def test_fullname(self, root):
54        assert root['john'].fullname == 'John Doe'
55
56    def test_password_is_encrypted(self, root):
57        assert root['john'].password != 's3cr3t'
58
59    def test_check_wrong_password(self, root):
60        assert not root['john'].check_password('badpass')
61
62    def test_check_good_password(self, root):
63        assert root['john'].check_password('s3cr3t')
64
65    def test_add_workout(self, root, workouts):
66        # First add all workouts at once
67        for workout in workouts:
68            root['john'].add_workout(workout)
69        # Then check they are there, in the correct position/number
70        for workout in workouts:
71            index = workouts.index(workout) + 1
72            assert root['john'][str(index)] == workout
73
74    def test_workouts(self, root, workouts):
75        # First add all workouts at once
76        for workout in workouts:
77            root['john'].add_workout(workout)
78        # workouts() will return the workouts sorted from newest to oldest
79        workouts.reverse()
80        assert list(root['john'].workouts()) == workouts
81        assert list(root['john'].workout_ids()) == ['1', '2', '3']
82        assert root['john'].num_workouts == len(workouts)
83
84    def test_activity_dates_tree(self, root):
85        # first an empty test
86        assert root['john'].activity_dates_tree == {}
87        # now add a cycling workout in a given date (25/11/2018)
88        workout = Workout(
89            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
90            duration=timedelta(minutes=(60*4)),
91            distance=115,
92            sport='cycling')
93        root['john'].add_workout(workout)
94        assert root['john'].activity_dates_tree == {
95            2018: {11: {'cycling': 1}}
96        }
97        # add a running workout on the same date
98        workout = Workout(
99            start=datetime(2018, 11, 25, 16, 30, tzinfo=timezone.utc),
100            duration=timedelta(minutes=60),
101            distance=12,
102            sport='running')
103        root['john'].add_workout(workout)
104        assert root['john'].activity_dates_tree == {
105            2018: {11: {'cycling': 1, 'running': 1}}
106        }
107        # add a swimming workout on a different date, same year
108        workout = Workout(
109            start=datetime(2018, 8, 15, 11, 30, tzinfo=timezone.utc),
110            duration=timedelta(minutes=30),
111            distance=2,
112            sport='swimming')
113        root['john'].add_workout(workout)
114        assert root['john'].activity_dates_tree == {
115            2018: {8: {'swimming': 1},
116                   11: {'cycling': 1, 'running': 1}}
117        }
118        # now add some more cycling in a different year
119        # add a swimming workout on a different date, same year
120        workout = Workout(
121            start=datetime(2017, 4, 15, 15, 00, tzinfo=timezone.utc),
122            duration=timedelta(minutes=(60*3)),
123            distance=78,
124            sport='cycling')
125        root['john'].add_workout(workout)
126        assert root['john'].activity_dates_tree == {
127            2017: {4: {'cycling': 1}},
128            2018: {8: {'swimming': 1},
129                   11: {'cycling': 1, 'running': 1}}
130        }
131
132    def test_stats(self, root):
133        expected_no_stats = {
134            'workouts': 0,
135            'time': timedelta(seconds=0),
136            'distance': Decimal(0),
137            'elevation': Decimal(0),
138            'sports': {}
139        }
140        # no stats
141        assert root['john'].stats() == expected_no_stats
142        # add a cycling workout
143        workout = Workout(
144            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
145            duration=timedelta(minutes=(60*4)),
146            distance=115,
147            sport='cycling')
148        root['john'].add_workout(workout)
149        # asking for a different year, future
150        assert root['john'].stats(2019) == expected_no_stats
151        # asking for a different year, past
152        assert root['john'].stats(2016) == expected_no_stats
153        # asking fot the year the workout is in
154        assert root['john'].stats(2018) == {
155            'workouts': 1,
156            'time': timedelta(minutes=(60*4)),
157            'distance': Decimal(115),
158            'elevation': Decimal(0),
159            'sports': {
160                'cycling': {
161                    'workouts': 1,
162                    'time': timedelta(minutes=(60*4)),
163                    'distance': Decimal(115),
164                    'elevation': Decimal(0),
165                }
166            }
167        }
168        # add a second cycling workout
169        workout = Workout(
170            start=datetime(2018, 11, 26, 10, 00, tzinfo=timezone.utc),
171            duration=timedelta(minutes=(60*3)),
172            distance=100,
173            sport='cycling')
174        root['john'].add_workout(workout)
175        assert root['john'].stats(2018) == {
176            'workouts': 2,
177            'time': timedelta(minutes=(60*7)),
178            'distance': Decimal(215),
179            'elevation': Decimal(0),
180            'sports': {
181                'cycling': {
182                    'workouts': 2,
183                    'time': timedelta(minutes=(60*7)),
184                    'distance': Decimal(215),
185                    'elevation': Decimal(0),
186                }
187            }
188        }
189        # add a running workout
190        workout = Workout(
191            start=datetime(2018, 11, 26, 16, 00, tzinfo=timezone.utc),
192            duration=timedelta(minutes=(60)),
193            distance=10,
194            sport='running')
195        root['john'].add_workout(workout)
196        assert root['john'].stats(2018) == {
197            'workouts': 3,
198            'time': timedelta(minutes=(60*8)),
199            'distance': Decimal(225),
200            'elevation': Decimal(0),
201            'sports': {
202                'cycling': {
203                    'workouts': 2,
204                    'time': timedelta(minutes=(60*7)),
205                    'distance': Decimal(215),
206                    'elevation': Decimal(0),
207                },
208                'running': {
209                    'workouts': 1,
210                    'time': timedelta(minutes=(60)),
211                    'distance': Decimal(10),
212                    'elevation': Decimal(0),
213                }
214            }
215        }
216        # ensure the stats for future/past years did not change after
217        # adding those workouts
218        assert root['john'].stats(2019) == expected_no_stats
219        assert root['john'].stats(2016) == expected_no_stats
220
221    def test_get_week_stats(self, root):
222        expected_no_stats_per_day = {
223            'workouts': 0,
224            'time': timedelta(0),
225            'distance': Decimal(0),
226            'elevation': Decimal(0),
227            'sports': {}
228        }
229
230        expected_no_stats = {}
231        for i in range(19, 26):
232            day = datetime(2018, 11, i, 10, 00, tzinfo=timezone.utc)
233            expected_no_stats[day] = expected_no_stats_per_day
234
235        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
236        assert root['john'].get_week_stats(day) == expected_no_stats
237
238        # add a cycling workout
239        workout = Workout(
240            start=datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc),
241            duration=timedelta(minutes=(60*4)),
242            distance=115,
243            sport='cycling')
244        root['john'].add_workout(workout)
245
246        # check a week in the future
247        day = datetime(2019, 11, 25, 10, 00, tzinfo=timezone.utc)
248        week_stats = root['john'].get_week_stats(day)
249        for day in week_stats:
250            assert week_stats[day] == expected_no_stats_per_day
251
252        # check a week in the past
253        day = datetime(2017, 11, 25, 10, 00, tzinfo=timezone.utc)
254        week_stats = root['john'].get_week_stats(day)
255        for day in week_stats:
256            assert week_stats[day] == expected_no_stats_per_day
257
258        # Check the week where the workout is
259        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
260        week_stats = root['john'].get_week_stats(day)
261        for day in week_stats:
262            if day.day == 25:
263                # this is the day where we have a workout
264                assert week_stats[day] == {
265                    'workouts': 1,
266                    'time': timedelta(minutes=(60*4)),
267                    'distance': Decimal(115),
268                    'elevation': Decimal(0),
269                    'sports': {
270                        'cycling': {
271                            'workouts': 1,
272                            'time': timedelta(minutes=(60*4)),
273                            'distance': Decimal(115),
274                            'elevation': Decimal(0)
275                        }
276                    }
277                }
278            else:
279                # day without workout
280                assert week_stats[day] == expected_no_stats_per_day
281
282        # add a second cycling workout
283        workout = Workout(
284            start=datetime(2018, 11, 23, 10, 00, tzinfo=timezone.utc),
285            duration=timedelta(minutes=(60*3)),
286            distance=100,
287            sport='cycling')
288        root['john'].add_workout(workout)
289        day = datetime(2018, 11, 25, 10, 00, tzinfo=timezone.utc)
290        week_stats = root['john'].get_week_stats(day)
291        for day in week_stats:
292            if day.day == 25:
293                # this is the day where we have a workout
294                assert week_stats[day] == {
295                    'workouts': 1,
296                    'time': timedelta(minutes=(60*4)),
297                    'distance': Decimal(115),
298                    'elevation': Decimal(0),
299                    'sports': {
300                        'cycling': {
301                            'workouts': 1,
302                            'time': timedelta(minutes=(60*4)),
303                            'distance': Decimal(115),
304                            'elevation': Decimal(0)
305                        }
306                    }
307                }
308            elif day.day == 23:
309                # this is the day where we have a workout
310                assert week_stats[day] == {
311                    'workouts': 1,
312                    'time': timedelta(minutes=(60*3)),
313                    'distance': Decimal(100),
314                    'elevation': Decimal(0),
315                    'sports': {
316                        'cycling': {
317                            'workouts': 1,
318                            'time': timedelta(minutes=(60*3)),
319                            'distance': Decimal(100),
320                            'elevation': Decimal(0)
321                        }
322                    }
323                }
324            else:
325                # day without workout
326                assert week_stats[day] == expected_no_stats_per_day
327
328    def test_week_stats(self, root):
329        expected_no_stats_per_day = {
330            'workouts': 0,
331            'time': timedelta(0),
332            'distance': Decimal(0),
333            'elevation': Decimal(0),
334            'sports': {}
335        }
336
337        # no workouts for the current week (this tests is for coverage
338        # purposes mostly, as the main logic is tested in test_get_week_stats)
339        day = datetime.now(timezone.utc)
340        week_stats = root['john'].get_week_stats(day)
341        for day in week_stats:
342            assert week_stats[day] == expected_no_stats_per_day
343
344    def test_week_totals(self, root):
345        # no data, empty totals
346        assert root['john'].week_totals == {
347            'distance': Decimal(0),
348            'time': timedelta(0)
349        }
350
351    def test_yearly_stats(self, root):
352        expected_no_stats_per_month = {
353            'workouts': 0,
354            'time': timedelta(0),
355            'distance': Decimal(0),
356            'elevation': Decimal(0),
357            'sports': {}
358        }
359
360        yearly_stats = root['john'].yearly_stats
361        for month, stats in yearly_stats.items():
362            assert stats == expected_no_stats_per_month
363
364        # add a cycling workout
365        start_date = datetime.now(timezone.utc)
366        workout = Workout(
367            start=start_date,
368            duration=timedelta(minutes=(60*4)),
369            uphill=1200,
370            distance=115,
371            sport='cycling')
372        root['john'].add_workout(workout)
373
374        yearly_stats = root['john'].yearly_stats
375        for month, stats in yearly_stats.items():
376            if month == (start_date.year, start_date.month):
377                assert stats == {
378                    'workouts': 1,
379                    'time': timedelta(minutes=(60*4)),
380                    'distance': Decimal(115),
381                    'elevation': Decimal(1200),
382                    'sports': {
383                        'cycling': {'distance': Decimal(115),
384                                    'elevation': Decimal(1200),
385                                    'time': timedelta(minutes=(60*4)),
386                                    'workouts': 1}
387                    }
388                }
389            else:
390                assert stats == expected_no_stats_per_month
391
392        # add a second cycling workout
393        workout = Workout(
394            start=start_date,
395            duration=timedelta(minutes=(30)),
396            uphill=500,
397            distance=20,
398            sport='cycling')
399        root['john'].add_workout(workout)
400
401        yearly_stats = root['john'].yearly_stats
402        for month, stats in yearly_stats.items():
403            if month == (start_date.year, start_date.month):
404                assert stats == {
405                    'workouts': 2,
406                    'time': timedelta(minutes=((60*4)+30)),
407                    'distance': Decimal(115+20),
408                    'elevation': Decimal(1200+500),
409                    'sports': {
410                        'cycling': {'distance': Decimal(115+20),
411                                    'elevation': Decimal(1200+500),
412                                    'time': timedelta(minutes=((60*4)+30)),
413                                    'workouts': 2}
414                    }
415                }
416            else:
417                assert stats == expected_no_stats_per_month
418
419        # add a running workout
420        workout = Workout(
421            start=start_date,
422            duration=timedelta(minutes=(60)),
423            uphill=200,
424            distance=5,
425            sport='running')
426        root['john'].add_workout(workout)
427
428        yearly_stats = root['john'].yearly_stats
429        for month, stats in yearly_stats.items():
430            if month == (start_date.year, start_date.month):
431                assert stats == {
432                    'workouts': 3,
433                    'time': timedelta(minutes=((60*4)+30+60)),
434                    'distance': Decimal(115+20+5),
435                    'elevation': Decimal(1200+500+200),
436                    'sports': {
437                        'cycling': {'distance': Decimal(115+20),
438                                    'elevation': Decimal(1200+500),
439                                    'time': timedelta(minutes=((60*4)+30)),
440                                    'workouts': 2},
441                        'running': {'distance': Decimal(5),
442                                    'elevation': Decimal(200),
443                                    'time': timedelta(minutes=(60)),
444                                    'workouts': 1}
445                    }
446                }
447            else:
448                assert stats == expected_no_stats_per_month
449
450    def test_weekly_year_stats(self, root):
451        expected_no_stats_per_week = {
452            'workouts': 0,
453            'time': timedelta(0),
454            'distance': Decimal(0),
455            'elevation': Decimal(0),
456            'sports': {}
457        }
458
459        weekly_year_stats = root['john'].weekly_year_stats
460        for week, stats in weekly_year_stats.items():
461            assert stats == expected_no_stats_per_week
462
463        # add a cycling workout
464        start_date = datetime.now(timezone.utc)
465        workout = Workout(
466            start=start_date,
467            duration=timedelta(minutes=(60*4)),
468            uphill=1200,
469            distance=115,
470            sport='cycling')
471        root['john'].add_workout(workout)
472
473        weekly_year_stats = root['john'].weekly_year_stats
474        workout_week = (start_date.year, start_date.month,
475                        start_date.isocalendar()[1])
476        for week, stats in weekly_year_stats.items():
477            if week[:3] == workout_week:
478                assert stats == {
479                    'workouts': 1,
480                    'time': timedelta(minutes=(60*4)),
481                    'distance': Decimal(115),
482                    'elevation': Decimal(1200),
483                    'sports': {
484                        'cycling': {'distance': Decimal(115),
485                                    'elevation': Decimal(1200),
486                                    'time': timedelta(minutes=(60*4)),
487                                    'workouts': 1}
488                    }
489                }
490            else:
491                assert stats == expected_no_stats_per_week
492
493        # add a second cycling workout
494        workout = Workout(
495            start=start_date,
496            duration=timedelta(minutes=(30)),
497            uphill=500,
498            distance=20,
499            sport='cycling')
500        root['john'].add_workout(workout)
501
502        weekly_year_stats = root['john'].weekly_year_stats
503        workout_week = (start_date.year, start_date.month,
504                        start_date.isocalendar()[1])
505        for week, stats in weekly_year_stats.items():
506            if week[:3] == workout_week:
507                assert stats == {
508                    'workouts': 2,
509                    'time': timedelta(minutes=((60*4)+30)),
510                    'distance': Decimal(115+20),
511                    'elevation': Decimal(1200+500),
512                    'sports': {
513                        'cycling': {'distance': Decimal(115+20),
514                                    'elevation': Decimal(1200+500),
515                                    'time': timedelta(minutes=((60*4)+30)),
516                                    'workouts': 2}
517                    }
518                }
519            else:
520                assert stats == expected_no_stats_per_week
521
522        # add a running workout
523        workout = Workout(
524            start=start_date,
525            duration=timedelta(minutes=(60)),
526            uphill=200,
527            distance=5,
528            sport='running')
529        root['john'].add_workout(workout)
530
531        weekly_year_stats = root['john'].weekly_year_stats
532        workout_week = (start_date.year, start_date.month,
533                        start_date.isocalendar()[1])
534        for week, stats in weekly_year_stats.items():
535            if week[:3] == workout_week:
536                assert stats == {
537                    'workouts': 3,
538                    'time': timedelta(minutes=((60*4)+30+60)),
539                    'distance': Decimal(115+20+5),
540                    'elevation': Decimal(1200+500+200),
541                    'sports': {
542                        'cycling': {'distance': Decimal(115+20),
543                                    'elevation': Decimal(1200+500),
544                                    'time': timedelta(minutes=((60*4)+30)),
545                                    'workouts': 2},
546                        'running': {'distance': Decimal(5),
547                                    'elevation': Decimal(200),
548                                    'time': timedelta(minutes=(60)),
549                                    'workouts': 1}
550                    }
551                }
552            else:
553                assert stats == expected_no_stats_per_week
Note: See TracBrowser for help on using the repository browser.