diff --git a/fittrackee/migrations/versions/23_5e3a3a31c432_update_username_length.py b/fittrackee/migrations/versions/23_5e3a3a31c432_update_username_length.py new file mode 100644 index 00000000..52d81104 --- /dev/null +++ b/fittrackee/migrations/versions/23_5e3a3a31c432_update_username_length.py @@ -0,0 +1,30 @@ +"""update username length + +Revision ID: 5e3a3a31c432 +Revises: e30007d681cb +Create Date: 2022-02-23 11:05:24.223304 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5e3a3a31c432' +down_revision = 'e30007d681cb' +branch_labels = None +depends_on = None + + +def upgrade(): + op.alter_column( + 'users', 'username', existing_type=sa.String(length=20), + type_=sa.String(length=255), existing_nullable=False + ) + + +def downgrade(): + op.alter_column( + 'users', 'username', existing_type=sa.String(length=255), + type_=sa.String(length=20), existing_nullable=False + ) diff --git a/fittrackee/tests/users/test_auth_api.py b/fittrackee/tests/users/test_auth_api.py index 79c67e50..bfddb843 100644 --- a/fittrackee/tests/users/test_auth_api.py +++ b/fittrackee/tests/users/test_auth_api.py @@ -93,7 +93,7 @@ class TestUserRegistration(ApiTestCaseMixin): '/api/auth/register', data=json.dumps( dict( - username='t', + username='', email='test@test.com', password='12345678', password_conf='12345678', @@ -102,17 +102,25 @@ class TestUserRegistration(ApiTestCaseMixin): content_type='application/json', ) - self.assert_400(response, "username: 3 to 12 characters required\n") + self.assert_400( + response, + ( + 'username: 3 to 30 characters required\n' + 'username: only alphanumeric characters and ' + 'the underscore character "_" allowed\n' + ), + ) def test_it_returns_error_if_username_is_too_long( self, app: Flask ) -> None: client = app.test_client() + response = client.post( '/api/auth/register', data=json.dumps( dict( - username='testestestestestest', + username='a' * 31, email='test@test.com', password='12345678', password_conf='12345678', @@ -121,7 +129,38 @@ class TestUserRegistration(ApiTestCaseMixin): content_type='application/json', ) - self.assert_400(response, "username: 3 to 12 characters required\n") + self.assert_400(response, "username: 3 to 30 characters required\n") + + @pytest.mark.parametrize( + 'input_description,input_username', + [ + ('account_handle', '@sam@example.com'), + ('with special characters', 'sam*'), + ], + ) + def test_it_returns_error_if_username_is_invalid( + self, app: Flask, input_description: str, input_username: str + ) -> None: + client = app.test_client() + + response = client.post( + '/api/auth/register', + data=json.dumps( + dict( + username=input_username, + email='test@test.com', + password='12345678', + password_conf='12345678', + ) + ), + content_type='application/json', + ) + + self.assert_400( + response, + 'username: only alphanumeric characters and ' + 'the underscore character "_" allowed\n', + ) def test_it_returns_error_if_email_is_invalid(self, app: Flask) -> None: client = app.test_client() @@ -261,24 +300,6 @@ class TestUserRegistration(ApiTestCaseMixin): self.assert_400(response) - def test_it_returns_error_if_username_is_invalid(self, app: Flask) -> None: - client = app.test_client() - - response = client.post( - '/api/auth/register', - data=json.dumps( - dict( - username=1, - email='test@test.com', - password='12345678', - password_conf='12345678', - ) - ), - content_type='application/json', - ) - - self.assert_500(response) - class TestUserLogin(ApiTestCaseMixin): @pytest.mark.parametrize( diff --git a/fittrackee/tests/users/test_users_utils.py b/fittrackee/tests/users/test_users_utils.py index d5c9dd24..30428508 100644 --- a/fittrackee/tests/users/test_users_utils.py +++ b/fittrackee/tests/users/test_users_utils.py @@ -120,7 +120,7 @@ class TestIsUsernameValid: ('input_username_length',), [ (2,), - (13,), + (31,), ], ) def test_it_returns_error_message_when_username_length_is_invalid( @@ -128,9 +128,9 @@ class TestIsUsernameValid: ) -> None: assert ( check_username( - username=random_string(input_username_length), + username=random_string(31), ) - == 'username: 3 to 12 characters required\n' + == 'username: 3 to 30 characters required\n' ) @pytest.mark.parametrize( @@ -154,9 +154,9 @@ class TestIsUsernameValid: assert check_username(username=random_string()) == '' def test_it_returns_multiple_errors(self) -> None: - username = random_string(1) + '.' + username = random_string(31) + '.' assert check_username(username=username) == ( - 'username: 3 to 12 characters required\n' + 'username: 3 to 30 characters required\n' 'username: only alphanumeric characters and the underscore ' 'character "_" allowed\n' ) @@ -201,14 +201,14 @@ class TestRegisterControls: ) def test_it_returns_multiple_errors_when_inputs_are_invalid(self) -> None: - invalid_username = random_string(2) + invalid_username = random_string(31) assert register_controls( username=invalid_username, email=invalid_username, password=random_string(8), password_conf=random_string(8), ) == ( - 'username: 3 to 12 characters required\n' + 'username: 3 to 30 characters required\n' 'email: valid email must be provided\n' 'password: password and password confirmation do not match\n' ) diff --git a/fittrackee/users/auth.py b/fittrackee/users/auth.py index 5855cf12..389c6e28 100644 --- a/fittrackee/users/auth.py +++ b/fittrackee/users/auth.py @@ -74,7 +74,7 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]: "status": "error" } - : Union[Tuple[Dict, int], HttpResponse]: - invalid payload - sorry, that user already exists - Errors: - - username: 3 to 12 characters required + - username: 3 to 30 characters required + - username: only alphanumeric characters and the underscore + character "_" allowed - email: valid email must be provided - password: password and password confirmation don't match - password: 8 characters required diff --git a/fittrackee/users/models.py b/fittrackee/users/models.py index e1599dab..fded7c51 100644 --- a/fittrackee/users/models.py +++ b/fittrackee/users/models.py @@ -19,7 +19,7 @@ BaseModel: DeclarativeMeta = db.Model class User(BaseModel): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True, autoincrement=True) - username = db.Column(db.String(20), unique=True, nullable=False) + username = db.Column(db.String(255), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password = db.Column(db.String(255), nullable=False) created_at = db.Column(db.DateTime, nullable=False) @@ -32,7 +32,7 @@ class User(BaseModel): picture = db.Column(db.String(255), nullable=True) timezone = db.Column(db.String(50), nullable=True) # does the week start Monday? - weekm = db.Column(db.Boolean(50), default=False, nullable=False) + weekm = db.Column(db.Boolean, default=False, nullable=False) workouts = db.relationship( 'Workout', lazy=True, diff --git a/fittrackee/users/utils/controls.py b/fittrackee/users/utils/controls.py index cd3a9c90..1fffec9b 100644 --- a/fittrackee/users/utils/controls.py +++ b/fittrackee/users/utils/controls.py @@ -40,8 +40,8 @@ def check_username(username: str) -> str: Return if username is valid """ ret = '' - if not 2 < len(username) < 13: - ret += 'username: 3 to 12 characters required\n' + if not (2 < len(username) < 31): + ret += 'username: 3 to 30 characters required\n' if not re.match(r'^[a-zA-Z0-9_]+$', username): ret += ( 'username: only alphanumeric characters and the ' diff --git a/fittrackee_client/src/components/User/UserAuthForm.vue b/fittrackee_client/src/components/User/UserAuthForm.vue index a4f82936..f569f0a0 100644 --- a/fittrackee_client/src/components/User/UserAuthForm.vue +++ b/fittrackee_client/src/components/User/UserAuthForm.vue @@ -23,7 +23,7 @@ required pattern="[a-zA-Z0-9_]+" minlength="3" - maxlength="12" + maxlength="30" @invalid="invalidateForm" v-model="formData.username" :placeholder="$t('user.USERNAME')"