Changeset 1d92bf2 in OpenWorkouts-current for ow/tests


Ignore:
Timestamp:
Dec 16, 2018, 1:07:04 AM (5 years ago)
Author:
borja <borja@…>
Branches:
current, feature/docs, master
Children:
6560b8f
Parents:
929097a
Message:

(#37) Allow login using email address instead of username:

  • Use user uids as keys in the root folder for referencing user objects (instead of username)
  • Use uids for referencing users all over the place (auth, permissions, traversal urls, etc)
  • Replaced the username concept with nickname. This nickname will be used as a shortcut to access "public profile" pages for users
  • Reworked lots of basic methods in the OpenWorkouts root object (s/username/nickname, marked as properties some methods like users, emails, etc)
  • Added new add_user() and delete_user() helpers to the OpenWorkouts root object
  • Fixed bug in the dashboard redirect view, causing an endless loop if an authenticated user does not exist anymore when loading a page.
  • Lots of tests fixes, adaptations and catch up.
Location:
ow/tests
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • ow/tests/models/test_root.py

    r929097a r1d92bf2  
    11import json
     2from unittest.mock import Mock
    23from datetime import datetime, timedelta, timezone
    34
    45import pytest
     6from repoze.catalog.catalog import Catalog
    57
    68from ow.models.workout import Workout
     
    1214
    1315    @pytest.fixture
    14     def root(self):
     16    def john(self):
     17        user = User(firstname='John', lastname='Doe',
     18                    email='john.doe@example.net')
     19        user.password = 's3cr3t'
     20        return user
     21
     22    @pytest.fixture
     23    def root(self, john):
    1524        root = OpenWorkouts()
    16         root['john'] = User(firstname='John', lastname='Doe',
    17                             email='john.doe@example.net')
    18         root['john'].password = 's3cr3t'
     25        root.add_user(john)
    1926        workout = Workout(
    2027            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    2229            distance=30, sport='cycling'
    2330        )
    24         root['john'].add_workout(workout)
     31        john.add_workout(workout)
    2532        return root
    2633
    27     def test_all_usernames(self, root):
    28         # the method returns an OOBtree object
    29         assert [u for u in root.all_usernames()] == ['john']
     34    def test__init__(self, root):
     35        # a new OpenWorkouts instance has a catalog created automatically
     36        assert isinstance(root.catalog, Catalog)
     37        assert len(root.catalog) == 2
     38        assert 'email' in root.catalog
     39        assert 'sport' in root.catalog
    3040
    31     def test_lowercase_usernames(self, root):
    32         root.add_user(uid='Jack', firstname='Jack', lastname='Dumb',
    33                       email='jack.dumb@example.net')
    34         assert root.lowercase_usernames() == ['jack', 'john']
     41    def test_add_user_ok(self, root):
     42        assert len(root.users) == 1
     43        user = User(firstname='New', lastname='For Testing',
     44                    email='new.for.testing@example.net')
     45        root.add_user(user)
     46        assert len(root.users) == 2
     47        assert user in root.users
     48
     49    def test_add_user_invalid(self, root):
     50        assert len(root.users) == 1
     51        with pytest.raises(AttributeError):
     52            root.add_user('faked-user-object')
     53
     54    def test_del_user_ok(self, root, john):
     55        assert len(root.users) == 1
     56        root.del_user(john)
     57        assert len(root.users) == 0
     58
     59    def test_del_user_failure(self, root):
     60        assert len(root.users) == 1
     61        with pytest.raises(AttributeError):
     62            root.add_user('faked-user-object')
     63
     64    def test_get_user_by_uid(self, root, john):
     65        # first, get an user that does exist
     66        user = root.get_user_by_uid(str(john.uid))
     67        assert user == john
     68        # now, without converting first to str, works too
     69        user = root.get_user_by_uid(john.uid)
     70        assert user == john
     71        # now, something that is not there
     72        new_user = User(firstname='someone', lastname='else',
     73                        email='someone.else@example.net')
     74        user = root.get_user_by_uid(new_user.uid)
     75        assert user is None
     76        # now, something that is not an uid
     77        user = root.get_user_by_uid('faked-user-uid')
     78        assert user is None
     79
     80    def test_get_user_by_email(self, root, john):
     81        # first, get an user that does exist
     82        user = root.get_user_by_email(str(john.email))
     83        assert user == john
     84        # now, something that is not there
     85        new_user = User(firstname='someone', lastname='else',
     86                        email='someone.else@example.net')
     87        user = root.get_user_by_email(new_user.email)
     88        assert user is None
     89        # now, something that is not an email
     90        user = root.get_user_by_email('faked-user-email')
     91        assert user is None
     92        # passing in None
     93        user = root.get_user_by_email(None)
     94        assert user is None
     95        # passing in something that is not None or a string will break
     96        # the query code
     97        with pytest.raises(TypeError):
     98            user = root.get_user_by_email(False)
     99        with pytest.raises(TypeError):
     100            user = root.get_user_by_email(Mock())
     101
     102    def test_users(self, root, john):
     103        assert root.users == [john]
     104
     105    def test_all_nicknames(self, root, john):
     106        # the existing user has not a nickname, and empty nicknames are not
     107        # added to the list of nicknames
     108        assert root.all_nicknames == []
     109        # now set one
     110        john.nickname = 'MrJohn'
     111        assert root.all_nicknames == ['MrJohn']
     112
     113    def test_lowercase_nicknames(self, root, john):
     114        # the existing user has not a nickname
     115        assert root.lowercase_nicknames == []
     116        # now set one
     117        john.nickname = 'MrJohn'
     118        assert root.lowercase_nicknames == ['mrjohn']
    35119
    36120    def test_emails(self, root):
    37         assert root.emails() == ['john.doe@example.net']
     121        assert root.emails == ['john.doe@example.net']
    38122
    39123    def test_lowercase_emails(self, root):
    40         root.add_user(uid='Jack', firstname='Jack', lastname='Dumb',
    41                       email='Jack.Dumb@example.net')
    42         assert root.lowercase_emails() == ['jack.dumb@example.net',
    43                                            'john.doe@example.net']
     124        user = User(firstname='Jack', lastname='Dumb',
     125                    email='Jack.Dumb@example.net')
     126        root.add_user(user)
     127        assert root.lowercase_emails == ['john.doe@example.net',
     128                                         'jack.dumb@example.net']
    44129
    45     def test_users(self, root):
    46         # the method returns an OOBtree object
    47         assert [u for u in root.users()] == [root['john']]
    48 
    49     def test_get_user(self, root):
    50         assert root.get_user('john') == root['john']
    51         assert root.get_user('jack') is None
    52         with pytest.raises(TypeError):
    53             root.get_user()
    54 
    55     def test_add_user(self, root):
    56         assert len(root.users()) == 1
    57         root.add_user(uid='jack', firstname='Jack', lastname='Dumb',
    58                       email='jack.dumb@example.net')
    59         assert len(root.users()) == 2
    60         assert 'jack' in root.all_usernames()
    61 
    62     def test_sports(self, root):
     130    def test_sports(self, root, john):
    63131        assert root.sports == ['cycling']
    64132        workout = Workout(
     
    66134            duration=timedelta(minutes=60),
    67135            distance=10, sport='running')
    68         root['john'].add_workout(workout)
     136        john.add_workout(workout)
    69137        assert root.sports == ['cycling', 'running']
    70138
    71     def test_sports_json(self, root):
     139    def test_sports_json(self, root, john):
    72140        assert root.sports_json == json.dumps(["cycling"])
    73141        workout = Workout(
     
    75143            duration=timedelta(minutes=60),
    76144            distance=10, sport='running')
    77         root['john'].add_workout(workout)
     145        john.add_workout(workout)
    78146        assert root.sports_json == json.dumps(["cycling", "running"])
  • ow/tests/models/test_workout.py

    r929097a r1d92bf2  
    5656        assert expected == joe['1'].end
    5757        assert 250 == joe['1'].distance * 1000
    58 
    59     def test_add_user(self, root):
    60         root.add_user('fred', firstname=u'Fred', lastname=u'Flintstone')
    61         assert u'Fred Flintstone' == root['fred'].fullname
    6258
    6359    def test_workout_id(self, root):
  • ow/tests/schemas/test_user.py

    r929097a r1d92bf2  
    44
    55from ow.models.user import User
    6 from ow.schemas.user import PasswordMatch, UniqueUsername, UniqueEmail
     6from ow.schemas.user import PasswordMatch, UniqueNickname, UniqueEmail
    77
    88
     
    2727
    2828
    29 class TestUniqueUsername(object):
     29class TestUniqueNickname(object):
    3030
    3131    @pytest.fixture
     
    3535
    3636    def test_validate_python_exists(self, state):
    37         validator = UniqueUsername()
     37        validator = UniqueNickname()
    3838        with pytest.raises(Invalid):
    3939            validator._validate_python('test', state)
     
    4242
    4343    def test_validate_python_not_exists(self, state):
    44         validator = UniqueUsername()
     44        validator = UniqueNickname()
    4545        res = validator._validate_python('not-existing', state)
    4646        assert res is None
  • ow/tests/test_catalog.py

    r929097a r1d92bf2  
    5656        changes = update_indexes(catalog, indexes)
    5757        assert changes['added'] == ['newindex']
    58         assert changes['removed'] == ['sport']
     58        assert changes['removed'] == ['email', 'sport']
    5959
    6060    def test_update_indexes_empty(self, root):
     
    6363        changes = update_indexes(catalog, indexes)
    6464        assert changes['added'] == []
    65         assert changes['removed'] == ['sport']
     65        assert changes['removed'] == ['email', 'sport']
    6666
    6767    def test_install_catalog(self):
    6868        root = OpenWorkouts()
     69        assert isinstance(getattr(root, 'catalog', None), Catalog)
     70        del root.catalog
    6971        assert getattr(root, 'catalog', None) is None
    7072        install_catalog(root)
     
    7880    def test_get_catalog_not_existing_catalog(self):
    7981        root = OpenWorkouts()
     82        assert isinstance(getattr(root, 'catalog', None), Catalog)
     83        del root.catalog
    8084        assert getattr(root, 'catalog', None) is None
    8185        catalog = get_catalog(root)
  • ow/tests/test_security.py

    r929097a r1d92bf2  
    2222        request.root = root
    2323        # User does exist
    24         assert groupfinder('john', request) == ['john']
     24        assert groupfinder('john', request) == [str(root['john'].uid)]
    2525        # User does not exist
    2626        assert groupfinder('jack', request) == []
  • ow/tests/views/test_root.py

    r929097a r1d92bf2  
    2020
    2121    @pytest.fixture
    22     def root(self):
     22    def john(self):
     23        user = User(firstname='John', lastname='Doe',
     24                    email='john.doe@example.net')
     25        user.password = 's3cr3t'
     26        return user
     27
     28    @pytest.fixture
     29    def root(self, john):
    2330        root = OpenWorkouts()
    24         root['john'] = User(firstname='John', lastname='Doe',
    25                             email='john.doe@example.net')
    26         root['john'].password = 's3cr3t'
     31        root.add_user(john)
    2732        workout = Workout(
    2833            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    3035            distance=30, sport='cycling'
    3136        )
    32         root['john'].add_workout(workout)
     37        john.add_workout(workout)
    3338        return root
    3439
     
    5055        return request
    5156
    52     def test_user_list(self, get_request):
     57    def test_user_list(self, get_request, john):
    5358        request = get_request
    5459        response = user_list(request.root, request)
    55         assert list(response['users']) == [request.root['john']]
     60        assert list(response['users']) == [john]
    5661
    5762    def test_add_user_get(self, get_request):
     
    6671        response = add_user(request.root, request)
    6772        assert 'form' in response
    68         # All required fields (4) are marked in the form errors
     73        # All required fields (3) are marked in the form errors
    6974        # You can see which fields are required in the schema
    7075        # ow.schemas.user.UserAddSchema
    71         assert len(response['form'].form.errors) == 4
     76        errors = response['form'].form.errors
     77        assert len(errors) == 3
     78        assert 'email' in errors
     79        assert 'firstname' in errors
     80        assert 'lastname' in errors
    7281
    7382    def test_add_user_post_valid(self, post_request):
    7483        request = post_request
    75         request.POST['uid'] = 'addeduser'
     84        request.POST['nickname'] = 'addeduser'
    7685        request.POST['email'] = 'addeduser@example.net'
    7786        request.POST['firstname'] = 'added'
     
    8089        assert isinstance(response, HTTPFound)
    8190        assert response.location.endswith('/userlist')
    82         assert len(request.root.all_usernames()) == 2
    83         assert 'addeduser' in request.root.all_usernames()
     91        # 1 nick name, as the default user has no nickname
     92        assert len(request.root.all_nicknames) == 1
     93        assert 'addeduser' in request.root.all_nicknames
  • ow/tests/views/test_user.py

    r929097a r1d92bf2  
    2424
    2525    @pytest.fixture
    26     def root(self):
     26    def john(self):
     27        user = User(firstname='John', lastname='Doe',
     28                    email='john.doe@example.net')
     29        user.password = 's3cr3t'
     30        return user
     31
     32    @pytest.fixture
     33    def root(self, john):
    2734        root = OpenWorkouts()
    28         root['john'] = User(firstname='John', lastname='Doe',
    29                             email='john.doe@example.net')
    30         root['john'].password = 's3cr3t'
     35        root.add_user(john)
    3136        workout = Workout(
    3237            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    3439            distance=30
    3540        )
    36         root['john'].add_workout(workout)
     41        john.add_workout(workout)
    3742        return root
    3843
     
    4449
    4550    @pytest.fixture
    46     def profile_post_request(self, root):
     51    def profile_post_request(self, root, john):
    4752        """
    4853        This is a valid POST request to update an user profile.
    4954        Form will validate, but nothing will be really updated/changed.
    5055        """
    51         user = root['john']
     56        user = john
    5257        request = DummyRequest()
    5358        request.root = root
     
    9499        request.POST = MultiDict({
    95100            'submit': True,
    96             'username': 'JackBlack',
     101            'nickname': 'JackBlack',
    97102            'email': 'jack.black@example.net',
    98103            'firstname': 'Jack',
     
    134139        assert response.location == '/dashboard'
    135140
    136     def test_dashboard(self, dummy_request):
     141    def test_dashboard(self, dummy_request, john):
    137142        """
    138143        Renders the user dashboard
    139144        """
    140145        request = dummy_request
    141         user = request.root['john']
    142         response = user_views.dashboard(user, request)
     146        response = user_views.dashboard(john, request)
    143147        assert response == {}
    144148
    145     def test_profile(self, dummy_request):
     149    def test_profile(self, dummy_request, john):
    146150        """
    147151        Renders the user profile page
    148152        """
    149153        request = dummy_request
    150         user = request.root['john']
    151         response = user_views.profile(user, request)
     154        response = user_views.profile(john, request)
    152155        assert response == {}
    153156
     
    159162        response = user_views.login(request.root, request)
    160163        assert response['message'] == ''
    161         assert response['username'] == ''
     164        assert response['email'] == ''
    162165        assert response['password'] == ''
    163166        assert response['redirect_url'] == request.resource_url(request.root)
    164167
    165     def test_login_get_return_to(self, dummy_request):
     168    def test_login_get_return_to(self, dummy_request, john):
    166169        """
    167170        GET request to access the login page, if there is a page set to where
     
    170173        """
    171174        request = dummy_request
    172         workout = request.root['john'].workouts()[0]
     175        workout = john.workouts()[0]
    173176        workout_url = request.resource_url(workout)
    174177        request.params['return_to'] = workout_url
     
    176179        assert response['redirect_url'] == workout_url
    177180
    178     def test_login_post_bad_username(self, dummy_request):
     181    def test_login_post_wrong_email(self, dummy_request):
    179182        request = dummy_request
    180183        request.method = 'POST'
    181184        request.POST['submit'] = True
    182         request.POST['username'] = 'jack'
     185        request.POST['email'] = 'jack@example.net'
    183186        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        assert response['message'] == u'Wrong email address'
     188
     189    def test_login_post_wrong_password(self, dummy_request):
    187190        request = dummy_request
    188191        request.method = 'POST'
    189192        request.POST['submit'] = True
    190         request.POST['username'] = 'john'
     193        request.POST['email'] = 'john.doe@example.net'
    191194        request.POST['password'] = 'badpassword'
    192195        response = user_views.login(request.root, request)
    193         assert response['message'] == u'Bad password'
     196        assert response['message'] == u'Wrong password'
    194197
    195198    @patch('ow.views.user.remember')
     
    198201        request.method = 'POST'
    199202        request.POST['submit'] = True
    200         request.POST['username'] = 'john'
     203        request.POST['email'] = 'john.doe@example.net'
    201204        request.POST['password'] = 's3cr3t'
    202205        response = user_views.login(request.root, request)
     
    216219
    217220    @pytest.mark.parametrize('extension', extensions)
    218     def test_profile_picture(self, extension, dummy_request):
     221    def test_profile_picture(self, extension, dummy_request, john):
    219222        """
    220223        GET request to get the profile picture of an user.
     
    222225        request = dummy_request
    223226        # Get the user
    224         user = request.root['john']
     227        user = john
    225228        # Get the path to the image, then open it and copy it to a new Blob
    226229        # object
     
    242245        assert response.content_type == 'image'
    243246
    244     def test_edit_profile_get(self, dummy_request):
     247    def test_edit_profile_get(self, dummy_request, john):
    245248        """
    246249        GET request to the edit profile page, returns the form ready to
     
    248251        """
    249252        request = dummy_request
    250         user = request.root['john']
     253        user = john
    251254        response = user_views.edit_profile(user, request)
    252255        assert isinstance(response['form'], OWFormRenderer)
     
    261264        assert response['form'].data['email'] == 'john.doe@example.net'
    262265
    263     def test_edit_profile_post_ok(self, profile_post_request):
     266    def test_edit_profile_post_ok(self, profile_post_request, john):
    264267        request = profile_post_request
    265         user = request.root['john']
     268        user = john
    266269        # Update the bio field
    267270        bio = 'Some text about this user'
     
    272275        assert user.bio == bio
    273276
    274     def test_edit_profile_post_missing_required(self, profile_post_request):
     277    def test_edit_profile_post_missing_required(
     278            self, profile_post_request, john):
    275279        request = profile_post_request
    276280        request.POST['email'] = ''
    277         user = request.root['john']
     281        user = john
    278282        response = user_views.edit_profile(user, request)
    279283        assert isinstance(response['form'], OWFormRenderer)
     
    284288        assert response['form'].errors_for('email') == [error]
    285289
    286     def test_change_password_get(self, dummy_request):
    287         request = dummy_request
    288         user = request.root['john']
     290    def test_change_password_get(self, dummy_request, john):
     291        request = dummy_request
     292        user = john
    289293        response = user_views.change_password(user, request)
    290294        assert isinstance(response['form'], OWFormRenderer)
     
    292296        assert response['form'].errorlist() == ''
    293297
    294     def test_change_password_post_ok(self, passwd_post_request):
     298    def test_change_password_post_ok(self, passwd_post_request, john):
    295299        request = passwd_post_request
    296         user = request.root['john']
     300        user = john
    297301        request.POST['old_password'] = 's3cr3t'
    298302        request.POST['password'] = 'h1dd3n s3cr3t'
     
    305309        assert user.check_password('h1dd3n s3cr3t')
    306310
    307     def test_change_password_post_no_values(self, passwd_post_request):
     311    def test_change_password_post_no_values(self, passwd_post_request, john):
    308312        request = passwd_post_request
    309         user = request.root['john']
     313        user = john
    310314        response = user_views.change_password(user, request)
    311315        assert isinstance(response['form'], OWFormRenderer)
     
    322326        assert user.check_password('s3cr3t')
    323327
    324     def test_change_password_post_bad_old_password(self, passwd_post_request):
     328    def test_change_password_post_bad_old_password(
     329            self, passwd_post_request, john):
    325330        request = passwd_post_request
    326         user = request.root['john']
     331        user = john
    327332        request.POST['old_password'] = 'FAIL PASSWORD'
    328333        request.POST['password'] = 'h1dd3n s3cr3t'
     
    338343        assert not user.check_password('h1dd3n s3cr3t')
    339344
    340     def test_change_password_post_password_mismatch(self, passwd_post_request):
     345    def test_change_password_post_password_mismatch(
     346            self, passwd_post_request, john):
    341347        request = passwd_post_request
    342         user = request.root['john']
     348        user = john
    343349        request.POST['old_password'] = 's3cr3t'
    344350        request.POST['password'] = 'h1dd3n s3cr3ts'
     
    363369    def test_signup_post_ok(self, signup_post_request):
    364370        request = signup_post_request
    365         assert 'JackBlack' not in request.root.all_usernames()
     371        assert 'jack.black@example.net' not in request.root.emails
     372        assert 'JackBlack' not in request.root.all_nicknames
    366373        response = user_views.signup(request.root, request)
    367374        assert isinstance(response, HTTPFound)
    368375        assert response.location == request.resource_url(request.root)
    369         assert 'JackBlack' in request.root.all_usernames()
     376        assert 'jack.black@example.net' in request.root.emails
     377        assert 'JackBlack' in request.root.all_nicknames
    370378
    371379    def test_signup_missing_required(self, signup_post_request):
    372380        request = signup_post_request
    373381        request.POST['email'] = ''
    374         assert 'JackBlack' not in request.root.all_usernames()
     382        assert 'jack.black@example.net' not in request.root.emails
     383        assert 'JackBlack' not in request.root.all_nicknames
    375384        response = user_views.signup(request.root, request)
    376385        assert isinstance(response['form'], OWFormRenderer)
     
    382391        assert errorlist == html_error
    383392        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):
     393        assert 'jack.black@example.net' not in request.root.emails
     394        assert 'JackBlack' not in request.root.all_nicknames
     395
     396    def test_signup_existing_nickname(self, signup_post_request, john):
    387397        request = signup_post_request
    388         request.POST['username'] = 'john'
    389         assert 'JackBlack' not in request.root.all_usernames()
     398        # assign john a nickname first
     399        john.nickname = 'john'
     400        # now set it for the POST request
     401        request.POST['nickname'] = 'john'
     402        # check jack is not there yet
     403        assert 'jack.black@example.net' not in request.root.emails
     404        assert 'JackBlack' not in request.root.all_nicknames
     405        # now signup as jack, but trying to set the nickname 'john'
    390406        response = user_views.signup(request.root, request)
    391407        assert isinstance(response['form'], OWFormRenderer)
    392         error = u'Another user is already registered with the username john'
     408        error = u'Another user is already using the nickname john'
    393409        html_error = '<ul class="error">'
    394410        html_error += '<li>' + error + '</li>'
     
    396412        errorlist = response['form'].errorlist().replace('\n', '')
    397413        assert errorlist == html_error
    398         assert response['form'].errors_for('username') == [error]
    399         assert 'JackBlack' not in request.root.all_usernames()
     414        assert response['form'].errors_for('nickname') == [error]
     415        # all the errors, and jack is not there
     416        assert 'jack.black@example.net' not in request.root.emails
     417        assert 'JackBlack' not in request.root.all_nicknames
    400418
    401419    def test_signup_existing_email(self, signup_post_request):
    402420        request = signup_post_request
    403421        request.POST['email'] = 'john.doe@example.net'
    404         assert 'JackBlack' not in request.root.all_usernames()
     422        assert 'jack.black@example.net' not in request.root.emails
     423        assert 'JackBlack' not in request.root.all_nicknames
    405424        response = user_views.signup(request.root, request)
    406425        assert isinstance(response['form'], OWFormRenderer)
     
    413432        assert errorlist == html_error
    414433        assert response['form'].errors_for('email') == [error]
    415         assert 'JackBlack' not in request.root.all_usernames()
     434        assert 'jack.black@example.net' not in request.root.emails
     435        assert 'JackBlack' not in request.root.all_nicknames
Note: See TracChangeset for help on using the changeset viewer.