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

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

Imported sources from the old python2-only repository:

  • Modified the code so it is python 3.6 compatible
  • Fixed deprecation warnings, pyramid 1.10.x supported now
  • Fixed deprecation warnings about some libraries, like pyramid-simpleform
  • Added pytest-pycodestyle and pytest-flakes for automatic checks on the source code files when running tests.
  • Added default pytest.ini setup to enforce some default parameters when running tests.
  • Cleaned up the code a bit, catched up with tests coverage.
  • Property mode set to 100644
File size: 16.3 KB
Line 
1import os
2from datetime import datetime, timedelta, timezone
3from shutil import copyfileobj
4from unittest.mock import Mock, patch
5
6import pytest
7
8from ZODB.blob import Blob
9
10from pyramid.testing import DummyRequest
11from pyramid.httpexceptions import HTTPFound
12from pyramid.response import Response
13
14from webob.multidict import MultiDict
15
16from ow.models.root import OpenWorkouts
17from ow.models.user import User
18from ow.models.workout import Workout
19from ow.views.renderers import OWFormRenderer
20import ow.views.user as user_views
21
22
23class TestUserViews(object):
24
25    @pytest.fixture
26    def root(self):
27        root = OpenWorkouts()
28        root['john'] = User(firstname='John', lastname='Doe',
29                            email='john.doe@example.net')
30        root['john'].password = 's3cr3t'
31        workout = Workout(
32            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
33            duration=timedelta(minutes=60),
34            distance=30
35        )
36        root['john'].add_workout(workout)
37        return root
38
39    @pytest.fixture
40    def dummy_request(self, root):
41        request = DummyRequest()
42        request.root = root
43        return request
44
45    @pytest.fixture
46    def profile_post_request(self, root):
47        """
48        This is a valid POST request to update an user profile.
49        Form will validate, but nothing will be really updated/changed.
50        """
51        user = root['john']
52        request = DummyRequest()
53        request.root = root
54        request.method = 'POST'
55        request.POST = MultiDict({
56            'submit': True,
57            'firstname': user.firstname,
58            'lastname': user.lastname,
59            'email': user.email,
60            'bio': user.bio,
61            'weight': user.weight,
62            'height': user.height,
63            'gender': user.gender,
64            'birth_date': user.birth_date,
65            'picture': user.picture,
66            })
67        return request
68
69    @pytest.fixture
70    def passwd_post_request(self, root):
71        """
72        This is a valid POST request to change the user password, but
73        the form will not validate (empty fields)
74        """
75        request = DummyRequest()
76        request.root = root
77        request.method = 'POST'
78        request.POST = MultiDict({
79            'submit': True,
80            'old_password': '',
81            'password': '',
82            'password_confirm': ''
83            })
84        return request
85
86    @pytest.fixture
87    def signup_post_request(self, root):
88        """
89        This is a valid POST request to signup a new user.
90        """
91        request = DummyRequest()
92        request.root = root
93        request.method = 'POST'
94        request.POST = MultiDict({
95            'submit': True,
96            'username': 'JackBlack',
97            'email': 'jack.black@example.net',
98            'firstname': 'Jack',
99            'lastname': 'Black',
100            'password': 'j4ck s3cr3t',
101            'password_confirm': 'j4ck s3cr3t'
102            })
103        return request
104
105    def test_dashboard_redirect_unauthenticated(self, root):
106        """
107        Anoymous access to the root object, send the user to the login page.
108
109        Instead of reusing the DummyRequest from the request fixture, we do
110        Mock fully the request here, because we need to use
111        authenticated_userid, which cannot be easily set in the DummyRequest
112        """
113        request = DummyRequest()
114        request.root = root
115        response = user_views.dashboard_redirect(root, request)
116        assert isinstance(response, HTTPFound)
117        assert response.location == request.resource_url(root, 'login')
118
119    def test_dashboard_redirect_authenticated(self, root):
120        """
121        Authenticated user accesing the root object, send the user to her
122        dashboard
123
124        Instead of reusing the DummyRequest from the request fixture, we do
125        Mock fully the request here, because we need to use
126        authenticated_userid, which cannot be easily set in the DummyRequest
127        """
128        request = Mock()
129        request.root = root
130        request.authenticated_userid = 'john'
131        request.resource_url.return_value = '/dashboard'
132        response = user_views.dashboard_redirect(root, request)
133        assert isinstance(response, HTTPFound)
134        assert response.location == '/dashboard'
135
136    def test_dashboard(self, dummy_request):
137        """
138        Renders the user dashboard
139        """
140        request = dummy_request
141        user = request.root['john']
142        response = user_views.dashboard(user, request)
143        assert response == {}
144
145    def test_profile(self, dummy_request):
146        """
147        Renders the user profile page
148        """
149        request = dummy_request
150        user = request.root['john']
151        response = user_views.profile(user, request)
152        assert response == {}
153
154    def test_login_get(self, dummy_request):
155        """
156        GET request to access the login page
157        """
158        request = dummy_request
159        response = user_views.login(request.root, request)
160        assert response['message'] == ''
161        assert response['username'] == ''
162        assert response['password'] == ''
163        assert response['redirect_url'] == request.resource_url(request.root)
164
165    def test_login_get_return_to(self, dummy_request):
166        """
167        GET request to access the login page, if there is a page set to where
168        the user should be sent to, the response "redirect_url" key will have
169        such url
170        """
171        request = dummy_request
172        workout = request.root['john'].workouts()[0]
173        workout_url = request.resource_url(workout)
174        request.params['return_to'] = workout_url
175        response = user_views.login(request.root, request)
176        assert response['redirect_url'] == workout_url
177
178    def test_login_post_bad_username(self, dummy_request):
179        request = dummy_request
180        request.method = 'POST'
181        request.POST['submit'] = True
182        request.POST['username'] = 'jack'
183        response = user_views.login(request.root, request)
184        assert response['message'] == u'Bad username'
185
186    def test_login_post_bad_password(self, dummy_request):
187        request = dummy_request
188        request.method = 'POST'
189        request.POST['submit'] = True
190        request.POST['username'] = 'john'
191        request.POST['password'] = 'badpassword'
192        response = user_views.login(request.root, request)
193        assert response['message'] == u'Bad password'
194
195    @patch('ow.views.user.remember')
196    def test_login_post_ok(self, rem, dummy_request):
197        request = dummy_request
198        request.method = 'POST'
199        request.POST['submit'] = True
200        request.POST['username'] = 'john'
201        request.POST['password'] = 's3cr3t'
202        response = user_views.login(request.root, request)
203        assert isinstance(response, HTTPFound)
204        assert rem.called
205        assert response.location == request.resource_url(request.root)
206
207    @patch('ow.views.user.forget')
208    def test_logout(self, forg, dummy_request):
209        request = dummy_request
210        response = user_views.logout(request.root, request)
211        assert isinstance(response, HTTPFound)
212        assert forg.called
213        assert response.location == request.resource_url(request.root)
214
215    extensions = ('png', 'jpg', 'jpeg', 'gif')
216
217    @pytest.mark.parametrize('extension', extensions)
218    def test_profile_picture(self, extension, dummy_request):
219        """
220        GET request to get the profile picture of an user.
221        """
222        request = dummy_request
223        # Get the user
224        user = request.root['john']
225        # Get the path to the image, then open it and copy it to a new Blob
226        # object
227        path = 'fixtures/image.' + extension
228        image_path = os.path.join(
229            os.path.dirname(os.path.dirname(__file__)), path)
230        blob = Blob()
231        with open(image_path, 'rb') as infile, blob.open('w') as out:
232            infile.seek(0)
233            copyfileobj(infile, out)
234
235        # Set the blob with the picture
236        user.picture = blob
237
238        # Call the profile_picture view
239        response = user_views.profile_picture(user, request)
240        assert isinstance(response, Response)
241        assert response.status_int == 200
242        assert response.content_type == 'image'
243
244    def test_edit_profile_get(self, dummy_request):
245        """
246        GET request to the edit profile page, returns the form ready to
247        be rendered
248        """
249        request = dummy_request
250        user = request.root['john']
251        response = user_views.edit_profile(user, request)
252        assert isinstance(response['form'], OWFormRenderer)
253        # no errors in the form (first load)
254        assert response['form'].errorlist() == ''
255        # the form carries along the proper data keys, taken from the
256        # loaded user profile
257        data = ['firstname', 'lastname', 'email', 'bio', 'birth_date',
258                'height', 'weight', 'gender']
259        assert list(response['form'].data.keys()) == data
260        # and check the email to see data is properly loaded
261        assert response['form'].data['email'] == 'john.doe@example.net'
262
263    def test_edit_profile_post_ok(self, profile_post_request):
264        request = profile_post_request
265        user = request.root['john']
266        # Update the bio field
267        bio = 'Some text about this user'
268        request.POST['bio'] = bio
269        response = user_views.edit_profile(user, request)
270        assert isinstance(response, HTTPFound)
271        assert response.location == request.resource_url(user, 'profile')
272        assert user.bio == bio
273
274    def test_edit_profile_post_missing_required(self, profile_post_request):
275        request = profile_post_request
276        request.POST['email'] = ''
277        user = request.root['john']
278        response = user_views.edit_profile(user, request)
279        assert isinstance(response['form'], OWFormRenderer)
280        # error on the missing email field
281        error = u'Please enter an email address'
282        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
283        assert response['form'].errorlist() == html_error
284        assert response['form'].errors_for('email') == [error]
285
286    def test_change_password_get(self, dummy_request):
287        request = dummy_request
288        user = request.root['john']
289        response = user_views.change_password(user, request)
290        assert isinstance(response['form'], OWFormRenderer)
291        # no errors in the form (first load)
292        assert response['form'].errorlist() == ''
293
294    def test_change_password_post_ok(self, passwd_post_request):
295        request = passwd_post_request
296        user = request.root['john']
297        request.POST['old_password'] = 's3cr3t'
298        request.POST['password'] = 'h1dd3n s3cr3t'
299        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
300        response = user_views.change_password(user, request)
301        assert isinstance(response, HTTPFound)
302        assert response.location == request.resource_url(user, 'profile')
303        # password was changed
304        assert not user.check_password('s3cr3t')
305        assert user.check_password('h1dd3n s3cr3t')
306
307    def test_change_password_post_no_values(self, passwd_post_request):
308        request = passwd_post_request
309        user = request.root['john']
310        response = user_views.change_password(user, request)
311        assert isinstance(response['form'], OWFormRenderer)
312        error = u'Please enter a value'
313        html_error = u'<ul class="error">'
314        html_error += ('<li>' + error + '</li>') * 3  # 3 fields
315        html_error += '</ul>'
316        errorlist = response['form'].errorlist().replace('\n', '')
317        assert errorlist == html_error
318        assert response['form'].errors_for('old_password') == [error]
319        assert response['form'].errors_for('password') == [error]
320        assert response['form'].errors_for('password_confirm') == [error]
321        # password was not changed
322        assert user.check_password('s3cr3t')
323
324    def test_change_password_post_bad_old_password(self, passwd_post_request):
325        request = passwd_post_request
326        user = request.root['john']
327        request.POST['old_password'] = 'FAIL PASSWORD'
328        request.POST['password'] = 'h1dd3n s3cr3t'
329        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
330        response = user_views.change_password(user, request)
331        assert isinstance(response['form'], OWFormRenderer)
332        error = u'The given password does not match the existing one '
333        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
334        assert response['form'].errorlist() == html_error
335        assert response['form'].errors_for('old_password') == [error]
336        # password was not changed
337        assert user.check_password('s3cr3t')
338        assert not user.check_password('h1dd3n s3cr3t')
339
340    def test_change_password_post_password_mismatch(self, passwd_post_request):
341        request = passwd_post_request
342        user = request.root['john']
343        request.POST['old_password'] = 's3cr3t'
344        request.POST['password'] = 'h1dd3n s3cr3ts'
345        request.POST['password_confirm'] = 'h1dd3n s3cr3t'
346        response = user_views.change_password(user, request)
347        assert isinstance(response['form'], OWFormRenderer)
348        error = u'Fields do not match'
349        html_error = u'<ul class="error"><li>' + error + '</li></ul>'
350        assert response['form'].errorlist() == html_error
351        assert response['form'].errors_for('password_confirm') == [error]
352        # password was not changed
353        assert user.check_password('s3cr3t')
354        assert not user.check_password('h1dd3n s3cr3t')
355
356    def test_signup_get(self, dummy_request):
357        request = dummy_request
358        response = user_views.signup(request.root, request)
359        assert isinstance(response['form'], OWFormRenderer)
360        # no errors in the form (first load)
361        assert response['form'].errorlist() == ''
362
363    def test_signup_post_ok(self, signup_post_request):
364        request = signup_post_request
365        assert 'JackBlack' not in request.root.all_usernames()
366        response = user_views.signup(request.root, request)
367        assert isinstance(response, HTTPFound)
368        assert response.location == request.resource_url(request.root)
369        assert 'JackBlack' in request.root.all_usernames()
370
371    def test_signup_missing_required(self, signup_post_request):
372        request = signup_post_request
373        request.POST['email'] = ''
374        assert 'JackBlack' not in request.root.all_usernames()
375        response = user_views.signup(request.root, request)
376        assert isinstance(response['form'], OWFormRenderer)
377        error = u'Please enter an email address'
378        html_error = '<ul class="error">'
379        html_error += '<li>' + error + '</li>'
380        html_error += '</ul>'
381        errorlist = response['form'].errorlist().replace('\n', '')
382        assert errorlist == html_error
383        assert response['form'].errors_for('email') == [error]
384        assert 'JackBlack' not in request.root.all_usernames()
385
386    def test_signup_existing_username(self, signup_post_request):
387        request = signup_post_request
388        request.POST['username'] = 'john'
389        assert 'JackBlack' not in request.root.all_usernames()
390        response = user_views.signup(request.root, request)
391        assert isinstance(response['form'], OWFormRenderer)
392        error = u'Another user is already registered with the username john'
393        html_error = '<ul class="error">'
394        html_error += '<li>' + error + '</li>'
395        html_error += '</ul>'
396        errorlist = response['form'].errorlist().replace('\n', '')
397        assert errorlist == html_error
398        assert response['form'].errors_for('username') == [error]
399        assert 'JackBlack' not in request.root.all_usernames()
400
401    def test_signup_existing_email(self, signup_post_request):
402        request = signup_post_request
403        request.POST['email'] = 'john.doe@example.net'
404        assert 'JackBlack' not in request.root.all_usernames()
405        response = user_views.signup(request.root, request)
406        assert isinstance(response['form'], OWFormRenderer)
407        error = u'Another user is already registered with the email '
408        error += u'john.doe@example.net'
409        html_error = '<ul class="error">'
410        html_error += '<li>' + error + '</li>'
411        html_error += '</ul>'
412        errorlist = response['form'].errorlist().replace('\n', '')
413        assert errorlist == html_error
414        assert response['form'].errors_for('email') == [error]
415        assert 'JackBlack' not in request.root.all_usernames()
Note: See TracBrowser for help on using the repository browser.