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

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

Show weekly/monthly versions of the "last 12 months" workout stats chart

in the user profile page.

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