source: OpenWorkouts-current/ow/tests/views/test_user.py @ bddf042

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

(#7) Allow users profiles to be accessed using a more friendly url:

https://openworkouts.org/profile/NICKNAME

IMPORTANT: This change adds a new index to the catalog, so ensure you
update any existing databases after pulling.

Enter pshell and run this code:

root._update_indexes()
for user in root.users:

root.reindex(user)

  • Property mode set to 100644
File size: 28.9 KB
Line 
1import os
2import json
3from decimal import Decimal
4from datetime import datetime, timedelta, timezone
5from shutil import copyfileobj
6from unittest.mock import Mock, patch
7
8import pytest
9
10from ZODB.blob import Blob
11
12from pyramid.testing import DummyRequest
13from pyramid.httpexceptions import HTTPFound
14from pyramid.response import Response
15
16from webob.multidict import MultiDict
17
18from ow.models.root import OpenWorkouts
19from ow.models.user import User
20from ow.models.workout import Workout
21from ow.views.renderers import OWFormRenderer
22import ow.views.user as user_views
23
24
25class TestUserViews(object):
26
27    @pytest.fixture
28    def john(self):
29        user = User(firstname='John', lastname='Doe',
30                    email='john.doe@example.net')
31        user.password = 's3cr3t'
32        return user
33
34    @pytest.fixture
35    def root(self, john):
36        root = OpenWorkouts()
37        root.add_user(john)
38        workout = Workout(
39            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
40            duration=timedelta(minutes=60),
41            distance=30
42        )
43        john.add_workout(workout)
44        return root
45
46    @pytest.fixture
47    def dummy_request(self, root):
48        request = DummyRequest()
49        request.root = root
50        return request
51
52    @pytest.fixture
53    def profile_post_request(self, root, john):
54        """
55        This is a valid POST request to update an user profile.
56        Form will validate, but nothing will be really updated/changed.
57        """
58        user = john
59        request = DummyRequest()
60        request.root = root
61        request.method = 'POST'
62        request.POST = MultiDict({
63            'submit': True,
64            'firstname': user.firstname,
65            'lastname': user.lastname,
66            'email': user.email,
67            'bio': user.bio,
68            'weight': user.weight,
69            'height': user.height,
70            'gender': user.gender,
71            'birth_date': user.birth_date,
72            'picture': user.picture,
73            })
74        return request
75
76    @pytest.fixture
77    def passwd_post_request(self, root):
78        """
79        This is a valid POST request to change the user password, but
80        the form will not validate (empty fields)
81        """
82        request = DummyRequest()
83        request.root = root
84        request.method = 'POST'
85        request.POST = MultiDict({
86            'submit': True,
87            'old_password': '',
88            'password': '',
89            'password_confirm': ''
90            })
91        return request
92
93    @pytest.fixture
94    def signup_post_request(self, root):
95        """
96        This is a valid POST request to signup a new user.
97        """
98        request = DummyRequest()
99        request.root = root
100        request.method = 'POST'
101        request.POST = MultiDict({
102            'submit': True,
103            'nickname': 'JackBlack',
104            'email': 'jack.black@example.net',
105            'firstname': 'Jack',
106            'lastname': 'Black',
107            'password': 'j4ck s3cr3t',
108            'password_confirm': 'j4ck s3cr3t'
109            })
110        return request
111
112    def test_dashboard_redirect_unauthenticated(self, root):
113        """
114        Anoymous access to the root object, send the user to the login page.
115
116        Instead of reusing the DummyRequest from the request fixture, we do
117        Mock fully the request here, because we need to use
118        authenticated_userid, which cannot be easily set in the DummyRequest
119        """
120        request = DummyRequest()
121        request.root = root
122        response = user_views.dashboard_redirect(root, request)
123        assert isinstance(response, HTTPFound)
124        assert response.location == request.resource_url(root, 'login')
125
126    def test_dashboard_redirect_authenticated(self, root, john):
127        """
128        Authenticated user accesing the root object, send the user to her
129        dashboard
130
131        Instead of reusing the DummyRequest from the request fixture, we do
132        Mock fully the request here, because we need to use
133        authenticated_userid, which cannot be easily set in the DummyRequest
134        """
135        alt_request = DummyRequest()
136        request = Mock()
137        request.root = root
138        request.authenticated_userid = str(john.uid)
139        request.resource_url = alt_request.resource_url
140        response = user_views.dashboard_redirect(root, request)
141        assert isinstance(response, HTTPFound)
142        assert response.location == request.resource_url(john)
143        # if authenticated_userid is the id of an user that does not exist
144        # anymore, we send the user to the logout page
145        request.authenticated_userid = 'faked-uid'
146        response = user_views.dashboard_redirect(root, request)
147        assert isinstance(response, HTTPFound)
148        assert response.location == request.resource_url(root, 'logout')
149
150    def test_dashboard(self, dummy_request, john):
151        """
152        Renders the user dashboard
153        """
154        request = dummy_request
155        response = user_views.dashboard(john, request)
156        assert len(response) == 6
157        assert 'month_name' in response.keys()
158        assert response['current_year'] == datetime.now().year
159        assert response['current_day_name'] == datetime.now().strftime('%a')
160        # this user has a single workout, in 2015
161        assert response['viewing_year'] == 2015
162        assert response['viewing_month'] == 6
163        assert response['workouts'] == [w for w in john.workouts()]
164
165    def test_dashboard_year(self, dummy_request, john):
166        """
167        Renders the user dashboard for a chosen year.
168        """
169        request = dummy_request
170        # first test the year for which we know there is a workout
171        request.GET['year'] = 2015
172        response = user_views.dashboard(john, request)
173        assert len(response) == 6
174        assert 'month_name' in response.keys()
175        assert response['current_year'] == datetime.now().year
176        assert response['current_day_name'] == datetime.now().strftime('%a')
177        # this user has a single workout, in 2015
178        assert response['viewing_year'] == 2015
179        assert response['viewing_month'] == 6
180        assert response['workouts'] == [w for w in john.workouts()]
181        # now, a year we know there is no workout info
182        request.GET['year'] = 2000
183        response = user_views.dashboard(john, request)
184        assert len(response) == 6
185        assert 'month_name' in response.keys()
186        assert response['current_year'] == datetime.now().year
187        assert response['current_day_name'] == datetime.now().strftime('%a')
188        # this user has a single workout, in 2015
189        assert response['viewing_year'] == 2000
190        # we have no data for that year and we didn't ask for a certain month,
191        # so the passing value for that is None
192        assert response['viewing_month'] is None
193        assert response['workouts'] == []
194
195    def test_dashboard_year_month(self, dummy_request, john):
196        """
197        Renders the user dashboard for a chosen year and month.
198        """
199        request = dummy_request
200        # first test the year/month for which we know there is a workout
201        request.GET['year'] = 2015
202        request.GET['month'] = 6
203        response = user_views.dashboard(john, request)
204        assert len(response) == 6
205        assert 'month_name' in response.keys()
206        assert response['current_year'] == datetime.now().year
207        assert response['current_day_name'] == datetime.now().strftime('%a')
208        # this user has a single workout, in 2015
209        assert response['viewing_year'] == 2015
210        assert response['viewing_month'] == 6
211        assert response['workouts'] == [w for w in john.workouts()]
212        # now, change month to one without values
213        request.GET['month'] = 2
214        response = user_views.dashboard(john, request)
215        assert len(response) == 6
216        assert 'month_name' in response.keys()
217        assert response['current_year'] == datetime.now().year
218        assert response['current_day_name'] == datetime.now().strftime('%a')
219        # this user has a single workout, in 2015
220        assert response['viewing_year'] == 2015
221        assert response['viewing_month'] == 2
222        assert response['workouts'] == []
223        # now the month with data, but in a different year
224        request.GET['year'] = 2010
225        request.GET['month'] = 6
226        response = user_views.dashboard(john, request)
227        assert len(response) == 6
228        assert 'month_name' in response.keys()
229        assert response['current_year'] == datetime.now().year
230        assert response['current_day_name'] == datetime.now().strftime('%a')
231        # this user has a single workout, in 2015
232        assert response['viewing_year'] == 2010
233        assert response['viewing_month'] == 6
234        assert response['workouts'] == []
235
236    def test_dashboard_month(self, dummy_request, john):
237        """
238        Passing a month without a year when rendering the dashboard. The last
239        year for which workout data is available is assumed
240        """
241        request = dummy_request
242        # Set a month without workout data
243        request.GET['month'] = 5
244        response = user_views.dashboard(john, request)
245        assert len(response) == 6
246        assert 'month_name' in response.keys()
247        assert response['current_year'] == datetime.now().year
248        assert response['current_day_name'] == datetime.now().strftime('%a')
249        # this user has a single workout, in 2015
250        assert response['viewing_year'] == 2015
251        assert response['viewing_month'] == 5
252        assert response['workouts'] == []
253        # now a month with data
254        request.GET['month'] = 6
255        response = user_views.dashboard(john, request)
256        assert len(response) == 6
257        assert 'month_name' in response.keys()
258        assert response['current_year'] == datetime.now().year
259        assert response['current_day_name'] == datetime.now().strftime('%a')
260        # this user has a single workout, in 2015
261        assert response['viewing_year'] == 2015
262        assert response['viewing_month'] == 6
263        assert response['workouts'] == [w for w in john.workouts()]
264
265    def test_profile(self, dummy_request, john):
266        """
267        Renders the user profile page
268        """
269        request = dummy_request
270        # profile page for the current day (no workouts avalable)
271        response = user_views.profile(john, request)
272        assert len(response.keys()) == 5
273        current_month = datetime.now(timezone.utc).strftime('%Y-%m')
274        assert response['user'] == john
275        assert response['current_month'] == current_month
276        assert response['current_week'] is None
277        assert response['workouts'] == []
278        assert response['totals'] == {
279            'distance': Decimal(0),
280            'time': timedelta(0),
281            'elevation': Decimal(0)
282        }
283        # profile page for a previous date, that has workouts
284        request.GET['year'] = 2015
285        request.GET['month'] = 6
286        response = user_views.profile(john, request)
287        assert len(response.keys()) == 5
288        assert response['user'] == john
289        assert response['current_month'] == '2015-06'
290        assert response['current_week'] is None
291        workouts = john.workouts(2015, 6)
292        assert response['workouts'] == workouts
293        assert response['totals'] == {
294            'distance': workouts[0].distance,
295            'time': workouts[0].duration,
296            'elevation': Decimal(0)
297        }
298        # same, passing a week, first on a week without workouts
299        request.GET['year'] = 2015
300        request.GET['month'] = 6
301        request.GET['week'] = 25
302        response = user_views.profile(john, request)
303        assert len(response.keys()) == 5
304        assert response['user'] == john
305        assert response['current_month'] == '2015-06'
306        assert response['current_week'] == 25
307        assert response['workouts'] == []
308        assert response['totals'] == {
309            'distance': Decimal(0),
310            'time': timedelta(0),
311            'elevation': Decimal(0)
312        }
313        # now in a week with workouts
314        request.GET['year'] = 2015
315        request.GET['month'] = 6
316        request.GET['week'] = 26
317        response = user_views.profile(john, request)
318        assert len(response.keys()) == 5
319        assert response['user'] == john
320        assert response['current_month'] == '2015-06'
321        assert response['current_week'] == 26
322        workouts = john.workouts(2015, 6)
323        assert response['workouts'] == workouts
324        assert response['totals'] == {
325            'distance': workouts[0].distance,
326            'time': workouts[0].duration,
327            'elevation': Decimal(0)
328        }
329
330    def test_login_get(self, dummy_request):
331        """
332        GET request to access the login page
333        """
334        request = dummy_request
335        response = user_views.login(request.root, request)
336        assert response['message'] == ''
337        assert response['email'] == ''
338        assert response['password'] == ''
339        assert response['redirect_url'] == request.resource_url(request.root)
340
341    def test_login_get_return_to(self, dummy_request, john):
342        """
343        GET request to access the login page, if there is a page set to where
344        the user should be sent to, the response "redirect_url" key will have
345        such url
346        """
347        request = dummy_request
348        workout = john.workouts()[0]
349        workout_url = request.resource_url(workout)
350        request.params['return_to'] = workout_url
351        response = user_views.login(request.root, request)
352        assert response['redirect_url'] == workout_url
353
354    def test_login_post_wrong_email(self, dummy_request):
355        request = dummy_request
356        request.method = 'POST'
357        request.POST['submit'] = True
358        request.POST['email'] = 'jack@example.net'
359        response = user_views.login(request.root, request)
360        assert response['message'] == u'Wrong email address'
361
362    def test_login_post_wrong_password(self, dummy_request):
363        request = dummy_request
364        request.method = 'POST'
365        request.POST['submit'] = True
366        request.POST['email'] = 'john.doe@example.net'
367        request.POST['password'] = 'badpassword'
368        response = user_views.login(request.root, request)
369        assert response['message'] == u'Wrong password'
370
371    @patch('ow.views.user.remember')
372    def test_login_post_ok(self, rem, dummy_request, john):
373        request = dummy_request
374        request.method = 'POST'
375        request.POST['submit'] = True
376        request.POST['email'] = 'john.doe@example.net'
377        request.POST['password'] = 's3cr3t'
378        response = user_views.login(request.root, request)
379        assert isinstance(response, HTTPFound)
380        assert rem.called
381        assert response.location == request.resource_url(john)
382
383    @patch('ow.views.user.forget')
384    def test_logout(self, forg, dummy_request):
385        request = dummy_request
386        response = user_views.logout(request.root, request)
387        assert isinstance(response, HTTPFound)
388        assert forg.called
389        assert response.location == request.resource_url(request.root)
390
391    extensions = ('png', 'jpg', 'jpeg', 'gif')
392
393    @pytest.mark.parametrize('extension', extensions)
394    def test_profile_picture(self, extension, dummy_request, john):
395        """
396        GET request to get the profile picture of an user.
397        """
398        request = dummy_request
399        # Get the user
400        user = john
401        # Get the path to the image, then open it and copy it to a new Blob
402        # object
403        path = 'fixtures/image.' + extension
404        image_path = os.path.join(
405            os.path.dirname(os.path.dirname(__file__)), path)
406        blob = Blob()
407        with open(image_path, 'rb') as infile, blob.open('w') as out:
408            infile.seek(0)
409            copyfileobj(infile, out)
410
411        # Set the blob with the picture
412        user.picture = blob
413
414        # Call the profile_picture view
415        response = user_views.profile_picture(user, request)
416        assert isinstance(response, Response)
417        assert response.status_int == 200
418        assert response.content_type == 'image'
419
420    def test_edit_profile_get(self, dummy_request, john):
421        """
422        GET request to the edit profile page, returns the form ready to
423        be rendered
424        """
425        request = dummy_request
426        user = john
427        response = user_views.edit_profile(user, request)
428        assert isinstance(response['form'], OWFormRenderer)
429        # no errors in the form (first load)
430        assert response['form'].errorlist() == ''
431        # the form carries along the proper data keys, taken from the
432        # loaded user profile
433        data = ['firstname', 'lastname', 'email', 'nickname', 'bio',
434                'birth_date', 'height', 'weight', 'gender', 'timezone']
435        assert list(response['form'].data.keys()) == data
436        # and check the email to see data is properly loaded
437        assert response['form'].data['email'] == 'john.doe@example.net'
438
439    def test_edit_profile_post_ok(self, profile_post_request, john):
440        request = profile_post_request
441        user = john
442        # Update the bio field
443        bio = 'Some text about this user'
444        request.POST['bio'] = bio
445        response = user_views.edit_profile(user, request)
446        assert isinstance(response, HTTPFound)
447        assert response.location == request.resource_url(user, 'profile')
448        assert user.bio == bio
449
450    def test_edit_profile_post_missing_required(
451            self, profile_post_request, john):
452        request = profile_post_request
453        request.POST['email'] = ''
454        user = john
455        response = user_views.edit_profile(user, request)
456        assert isinstance(response['form'], OWFormRenderer)
457        # error on the missing email field
458        error = u'Please enter an email address'
459        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
460        assert response['form'].errorlist() == html_error
461        assert response['form'].errors_for('email') == [error]
462
463    def test_edit_profile_post_ok_picture_empty_bytes(
464            self, profile_post_request, john):
465        """
466        POST request with an empty picture, the content of
467        request['POST'].picture is a empty bytes string (b'') which triggers
468        a bug in formencode, we put a fix in place, test that
469        (more in ow.user.views.edit_profile)
470        """
471        # for the purposes of this test, we can mock the picture
472        picture = Mock()
473        john.picture = picture
474        request = profile_post_request
475        user = john
476        # Mimic what happens when a picture is not provided by the user
477        request.POST['picture'] = b''
478        response = user_views.edit_profile(user, request)
479        assert isinstance(response, HTTPFound)
480        assert response.location == request.resource_url(user, 'profile')
481        assert user.picture == picture
482
483    def test_edit_profile_post_ok_missing_picture(
484            self, profile_post_request, john):
485        """
486        POST request without picture
487        """
488        # for the purposes of this test, we can mock the picture
489        picture = Mock()
490        john.picture = picture
491        request = profile_post_request
492        user = john
493        # No pic is provided in the request POST values
494        del request.POST['picture']
495        response = user_views.edit_profile(user, request)
496        assert isinstance(response, HTTPFound)
497        assert response.location == request.resource_url(user, 'profile')
498        assert user.picture == picture
499
500    def test_edit_profile_post_ok_nickname(self, profile_post_request, john):
501        """
502        User with a nickname set saves profile without changing the profile,
503        we have to be sure there are no "nickname already in use" errors
504        """
505        request = profile_post_request
506        user = john
507        user.nickname = 'mr_jones'
508        # add the nickname, the default post request has not a nickname set
509        request.POST['nickname'] = 'mr_jones'
510        response = user_views.edit_profile(user, request)
511        assert isinstance(response, HTTPFound)
512        assert response.location == request.resource_url(user, 'profile')
513
514    def test_change_password_get(self, dummy_request, john):
515        request = dummy_request
516        user = john
517        response = user_views.change_password(user, request)
518        assert isinstance(response['form'], OWFormRenderer)
519        # no errors in the form (first load)
520        assert response['form'].errorlist() == ''
521
522    def test_change_password_post_ok(self, passwd_post_request, john):
523        request = passwd_post_request
524        user = john
525        request.POST['old_password'] = 's3cr3t'
526        request.POST['password'] = 'h1dd3n s3cr3t'
527        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
528        response = user_views.change_password(user, request)
529        assert isinstance(response, HTTPFound)
530        assert response.location == request.resource_url(user, 'profile')
531        # password was changed
532        assert not user.check_password('s3cr3t')
533        assert user.check_password('h1dd3n s3cr3t')
534
535    def test_change_password_post_no_values(self, passwd_post_request, john):
536        request = passwd_post_request
537        user = john
538        response = user_views.change_password(user, request)
539        assert isinstance(response['form'], OWFormRenderer)
540        error = u'Please enter a value'
541        html_error = u'<ul class="error">'
542        html_error += ('<li>' + error + '</li>') * 3  # 3 fields
543        html_error += '</ul>'
544        errorlist = response['form'].errorlist().replace('\n', '')
545        assert errorlist == html_error
546        assert response['form'].errors_for('old_password') == [error]
547        assert response['form'].errors_for('password') == [error]
548        assert response['form'].errors_for('password_confirm') == [error]
549        # password was not changed
550        assert user.check_password('s3cr3t')
551
552    def test_change_password_post_bad_old_password(
553            self, passwd_post_request, john):
554        request = passwd_post_request
555        user = john
556        request.POST['old_password'] = 'FAIL PASSWORD'
557        request.POST['password'] = 'h1dd3n s3cr3t'
558        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
559        response = user_views.change_password(user, request)
560        assert isinstance(response['form'], OWFormRenderer)
561        error = u'The given password does not match the existing one '
562        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
563        assert response['form'].errorlist() == html_error
564        assert response['form'].errors_for('old_password') == [error]
565        # password was not changed
566        assert user.check_password('s3cr3t')
567        assert not user.check_password('h1dd3n s3cr3t')
568
569    def test_change_password_post_password_mismatch(
570            self, passwd_post_request, john):
571        request = passwd_post_request
572        user = john
573        request.POST['old_password'] = 's3cr3t'
574        request.POST['password'] = 'h1dd3n s3cr3ts'
575        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
576        response = user_views.change_password(user, request)
577        assert isinstance(response['form'], OWFormRenderer)
578        error = u'Fields do not match'
579        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
580        assert response['form'].errorlist() == html_error
581        assert response['form'].errors_for('password_confirm') == [error]
582        # password was not changed
583        assert user.check_password('s3cr3t')
584        assert not user.check_password('h1dd3n s3cr3t')
585
586    def test_signup_get(self, dummy_request):
587        request = dummy_request
588        response = user_views.signup(request.root, request)
589        assert isinstance(response['form'], OWFormRenderer)
590        # no errors in the form (first load)
591        assert response['form'].errorlist() == ''
592
593    def test_signup_post_ok(self, signup_post_request):
594        request = signup_post_request
595        assert 'jack.black@example.net' not in request.root.emails
596        assert 'JackBlack' not in request.root.all_nicknames
597        response = user_views.signup(request.root, request)
598        assert isinstance(response, HTTPFound)
599        assert response.location == request.resource_url(request.root)
600        assert 'jack.black@example.net' in request.root.emails
601        assert 'JackBlack' in request.root.all_nicknames
602
603    def test_signup_missing_required(self, signup_post_request):
604        request = signup_post_request
605        request.POST['email'] = ''
606        assert 'jack.black@example.net' not in request.root.emails
607        assert 'JackBlack' not in request.root.all_nicknames
608        response = user_views.signup(request.root, request)
609        assert isinstance(response['form'], OWFormRenderer)
610        error = u'Please enter an email address'
611        html_error = '<ul class="error">'
612        html_error += '<li>' + error + '</li>'
613        html_error += '</ul>'
614        errorlist = response['form'].errorlist().replace('\n', '')
615        assert errorlist == html_error
616        assert response['form'].errors_for('email') == [error]
617        assert 'jack.black@example.net' not in request.root.emails
618        assert 'JackBlack' not in request.root.all_nicknames
619
620    def test_signup_existing_nickname(self, signup_post_request, john):
621        request = signup_post_request
622        # assign john a nickname first
623        john.nickname = 'john'
624        # now set it for the POST request
625        request.POST['nickname'] = 'john'
626        # check jack is not there yet
627        assert 'jack.black@example.net' not in request.root.emails
628        assert 'JackBlack' not in request.root.all_nicknames
629        # now signup as jack, but trying to set the nickname 'john'
630        response = user_views.signup(request.root, request)
631        assert isinstance(response['form'], OWFormRenderer)
632        error = u'Another user is already using the nickname john'
633        html_error = '<ul class="error">'
634        html_error += '<li>' + error + '</li>'
635        html_error += '</ul>'
636        errorlist = response['form'].errorlist().replace('\n', '')
637        assert errorlist == html_error
638        assert response['form'].errors_for('nickname') == [error]
639        # all the errors, and jack is not there
640        assert 'jack.black@example.net' not in request.root.emails
641        assert 'JackBlack' not in request.root.all_nicknames
642
643    def test_signup_existing_email(self, signup_post_request):
644        request = signup_post_request
645        request.POST['email'] = 'john.doe@example.net'
646        assert 'jack.black@example.net' not in request.root.emails
647        assert 'JackBlack' not in request.root.all_nicknames
648        response = user_views.signup(request.root, request)
649        assert isinstance(response['form'], OWFormRenderer)
650        error = u'Another user is already registered with the email '
651        error += u'john.doe@example.net'
652        html_error = '<ul class="error">'
653        html_error += '<li>' + error + '</li>'
654        html_error += '</ul>'
655        errorlist = response['form'].errorlist().replace('\n', '')
656        assert errorlist == html_error
657        assert response['form'].errors_for('email') == [error]
658        assert 'jack.black@example.net' not in request.root.emails
659        assert 'JackBlack' not in request.root.all_nicknames
660
661    def test_week_stats_no_stats(self, dummy_request, john):
662        response = user_views.week_stats(john, dummy_request)
663        assert isinstance(response, Response)
664        assert response.content_type == 'application/json'
665        # the body is a valid json-encoded stream
666        obj = json.loads(response.body)
667        assert obj == [
668            {'distance': 0, 'elevation': 0, 'name': 'Mon',
669             'time': '00', 'workouts': 0},
670            {'distance': 0, 'elevation': 0, 'name': 'Tue',
671             'time': '00', 'workouts': 0},
672            {'distance': 0, 'elevation': 0, 'name': 'Wed',
673             'time': '00', 'workouts': 0},
674            {'distance': 0, 'elevation': 0, 'name': 'Thu',
675             'time': '00', 'workouts': 0},
676            {'distance': 0, 'elevation': 0, 'name': 'Fri',
677             'time': '00', 'workouts': 0},
678            {'distance': 0, 'elevation': 0, 'name': 'Sat',
679             'time': '00', 'workouts': 0},
680            {'distance': 0, 'elevation': 0, 'name': 'Sun',
681             'time': '00', 'workouts': 0}
682        ]
683
684    def test_week_stats(self, dummy_request, john):
685        workout = Workout(
686            start=datetime.now(timezone.utc),
687            duration=timedelta(minutes=60),
688            distance=30,
689            elevation=540
690        )
691        john.add_workout(workout)
692        response = user_views.week_stats(john, dummy_request)
693        assert isinstance(response, Response)
694        assert response.content_type == 'application/json'
695        # the body is a valid json-encoded stream
696        obj = json.loads(response.body)
697        assert len(obj) == 7
698        for day in obj:
699            if datetime.now(timezone.utc).strftime('%a') == day['name']:
700                day['distance'] == 30
701                day['elevation'] == 540
702                day['time'] == '01'
703                day['workouts'] == 1
704            else:
705                day['distance'] == 0
706                day['elevation'] == 0
707                day['time'] == '00'
708                day['workouts'] == 0
Note: See TracBrowser for help on using the repository browser.