API - init user account activation
This commit is contained in:
@ -123,7 +123,7 @@ class TestUserRegistration(ApiTestCaseMixin):
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_400(response, 'sorry, that user already exists')
|
||||
self.assert_400(response, 'sorry, that username is already taken')
|
||||
|
||||
def test_it_returns_error_if_password_is_missing(self, app: Flask) -> None:
|
||||
client = app.test_client()
|
||||
@ -193,11 +193,111 @@ class TestUserRegistration(ApiTestCaseMixin):
|
||||
|
||||
self.assert_400(response, 'email: valid email must be provided\n')
|
||||
|
||||
def test_it_does_not_send_email_after_error(
|
||||
self, app: Flask, account_confirmation_email_mock: Mock
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=self.random_string(),
|
||||
email=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
account_confirmation_email_mock.send.assert_not_called()
|
||||
|
||||
def test_it_returns_success_if_payload_is_valid(self, app: Flask) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=self.random_string(),
|
||||
email=self.random_email(),
|
||||
password=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.content_type == 'application/json'
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['status'] == 'success'
|
||||
assert 'auth_token' not in data
|
||||
|
||||
def test_it_creates_user_with_inactive_account(self, app: Flask) -> None:
|
||||
client = app.test_client()
|
||||
username = self.random_string()
|
||||
email = self.random_email()
|
||||
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=username,
|
||||
email=email,
|
||||
password=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
new_user = User.query.filter_by(username=username).first()
|
||||
assert new_user.email == email
|
||||
assert new_user.password is not None
|
||||
assert new_user.is_active is False
|
||||
|
||||
def test_it_calls_account_confirmation_email_if_payload_is_valid(
|
||||
self, app: Flask, account_confirmation_email_mock: Mock
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
email = self.random_email()
|
||||
username = self.random_string()
|
||||
expected_token = self.random_string()
|
||||
|
||||
with patch('secrets.token_urlsafe', return_value=expected_token):
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=username,
|
||||
email=email,
|
||||
password='12345678',
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
||||
)
|
||||
|
||||
account_confirmation_email_mock.send.assert_called_once_with(
|
||||
{
|
||||
'language': 'en',
|
||||
'email': email,
|
||||
},
|
||||
{
|
||||
'username': username,
|
||||
'fittrackee_url': 'http://0.0.0.0:5000',
|
||||
'operating_system': 'linux',
|
||||
'browser_name': 'firefox',
|
||||
'account_confirmation_url': (
|
||||
'http://0.0.0.0:5000/account-confirmation'
|
||||
f'?token={expected_token}'
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'text_transformation',
|
||||
['upper', 'lower'],
|
||||
)
|
||||
def test_it_returns_error_if_user_already_exists_with_same_email(
|
||||
def test_it_does_not_return_error_if_a_user_already_exists_with_same_email(
|
||||
self, app: Flask, user_1: User, text_transformation: str
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
@ -217,29 +317,30 @@ class TestUserRegistration(ApiTestCaseMixin):
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_400(response, 'sorry, that user already exists')
|
||||
assert response.status_code == 200
|
||||
assert response.content_type == 'application/json'
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['status'] == 'success'
|
||||
assert 'auth_token' not in data
|
||||
|
||||
def test_user_can_register(self, app: Flask) -> None:
|
||||
def test_it_does_not_call_account_confirmation_email_if_user_already_exists( # noqa
|
||||
self, app: Flask, user_1: User, account_confirmation_email_mock: Mock
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username=self.random_string(),
|
||||
email=self.random_email(),
|
||||
email=user_1.email,
|
||||
password=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert response.content_type == 'application/json'
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['status'] == 'success'
|
||||
assert data['message'] == 'successfully registered'
|
||||
assert data['auth_token']
|
||||
account_confirmation_email_mock.send.assert_not_called()
|
||||
|
||||
|
||||
class TestUserLogin(ApiTestCaseMixin):
|
||||
@ -269,6 +370,21 @@ class TestUserLogin(ApiTestCaseMixin):
|
||||
|
||||
self.assert_401(response, 'invalid credentials')
|
||||
|
||||
def test_it_returns_error_if_user_account_is_inactive(
|
||||
self, app: Flask, inactive_user: User
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(
|
||||
dict(email=inactive_user.email, password='12345678')
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_401(response, 'invalid credentials')
|
||||
|
||||
def test_it_returns_error_if_password_is_invalid(
|
||||
self, app: Flask, user_1: User
|
||||
) -> None:
|
||||
@ -1628,7 +1744,7 @@ class TestRegistrationConfiguration(ApiTestCaseMixin):
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
class TestPasswordResetRequest(ApiTestCaseMixin):
|
||||
@ -1974,3 +2090,47 @@ class TestEmailUpdateWitUnauthenticatedUser(ApiTestCaseMixin):
|
||||
assert user_1.email == new_email
|
||||
assert user_1.email_to_confirm is None
|
||||
assert user_1.confirmation_token is None
|
||||
|
||||
|
||||
class TestConfirmationAccount(ApiTestCaseMixin):
|
||||
def test_it_returns_error_if_token_is_missing(self, app: Flask) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/account/confirm',
|
||||
data=json.dumps(dict()),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_400(response)
|
||||
|
||||
def test_it_returns_error_if_token_is_invalid(self, app: Flask) -> None:
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/account/confirm',
|
||||
data=json.dumps(dict(token=self.random_string())),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
self.assert_400(response)
|
||||
|
||||
def test_it_activates_user_account(
|
||||
self, app: Flask, inactive_user: User
|
||||
) -> None:
|
||||
token = self.random_string()
|
||||
inactive_user.confirmation_token = token
|
||||
client = app.test_client()
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/account/confirm',
|
||||
data=json.dumps(dict(token=token)),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['status'] == 'success'
|
||||
assert data['message'] == 'account confirmation successful'
|
||||
assert inactive_user.is_active is True
|
||||
assert inactive_user.confirmation_token is None
|
||||
|
@ -28,6 +28,41 @@ class TestGetUser(ApiTestCaseMixin):
|
||||
|
||||
self.assert_403(response)
|
||||
|
||||
def test_it_gets_inactive_user(
|
||||
self, app: Flask, user_1_admin: User, inactive_user: User
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1_admin.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
f'/api/users/{inactive_user.username}',
|
||||
content_type='application/json',
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
assert response.status_code == 200
|
||||
assert data['status'] == 'success'
|
||||
assert len(data['data']['users']) == 1
|
||||
user = data['data']['users'][0]
|
||||
assert user['username'] == inactive_user.username
|
||||
assert user['email'] == inactive_user.email
|
||||
assert user['created_at']
|
||||
assert not user['admin']
|
||||
assert not user['is_active']
|
||||
assert user['first_name'] is None
|
||||
assert user['last_name'] is None
|
||||
assert user['birth_date'] is None
|
||||
assert user['bio'] is None
|
||||
assert user['location'] is None
|
||||
assert user['nb_sports'] == 0
|
||||
assert user['nb_workouts'] == 0
|
||||
assert user['records'] == []
|
||||
assert user['sports_list'] == []
|
||||
assert user['total_distance'] == 0
|
||||
assert user['total_duration'] == '0:00:00'
|
||||
|
||||
def test_it_gets_single_user_without_workouts(
|
||||
self, app: Flask, user_1_admin: User, user_2: User
|
||||
) -> None:
|
||||
@ -46,10 +81,11 @@ class TestGetUser(ApiTestCaseMixin):
|
||||
assert data['status'] == 'success'
|
||||
assert len(data['data']['users']) == 1
|
||||
user = data['data']['users'][0]
|
||||
assert user['username'] == 'toto'
|
||||
assert user['email'] == 'toto@toto.com'
|
||||
assert user['username'] == user_2.username
|
||||
assert user['email'] == user_2.email
|
||||
assert user['created_at']
|
||||
assert not user['admin']
|
||||
assert user['is_active']
|
||||
assert user['first_name'] is None
|
||||
assert user['last_name'] is None
|
||||
assert user['birth_date'] is None
|
||||
@ -87,10 +123,11 @@ class TestGetUser(ApiTestCaseMixin):
|
||||
assert data['status'] == 'success'
|
||||
assert len(data['data']['users']) == 1
|
||||
user = data['data']['users'][0]
|
||||
assert user['username'] == 'test'
|
||||
assert user['email'] == 'test@test.com'
|
||||
assert user['username'] == user_1.username
|
||||
assert user['email'] == user_1.email
|
||||
assert user['created_at']
|
||||
assert not user['admin']
|
||||
assert user['is_active']
|
||||
assert user['first_name'] is None
|
||||
assert user['last_name'] is None
|
||||
assert user['birth_date'] is None
|
||||
@ -134,8 +171,8 @@ class TestGetUsers(ApiTestCaseMixin):
|
||||
|
||||
self.assert_403(response)
|
||||
|
||||
def test_it_get_users_list(
|
||||
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
|
||||
def test_it_get_users_list_regardless_their_account_status(
|
||||
self, app: Flask, user_1_admin: User, inactive_user: User, user_3: User
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_1_admin.email
|
||||
@ -154,11 +191,14 @@ class TestGetUsers(ApiTestCaseMixin):
|
||||
assert 'created_at' in data['data']['users'][1]
|
||||
assert 'created_at' in data['data']['users'][2]
|
||||
assert 'admin' in data['data']['users'][0]['username']
|
||||
assert 'toto' in data['data']['users'][1]['username']
|
||||
assert 'inactive' in data['data']['users'][1]['username']
|
||||
assert 'sam' in data['data']['users'][2]['username']
|
||||
assert 'admin@example.com' in data['data']['users'][0]['email']
|
||||
assert 'toto@toto.com' in data['data']['users'][1]['email']
|
||||
assert 'inactive@example.com' in data['data']['users'][1]['email']
|
||||
assert 'sam@test.com' in data['data']['users'][2]['email']
|
||||
assert data['data']['users'][0]['is_active']
|
||||
assert not data['data']['users'][1]['is_active']
|
||||
assert data['data']['users'][2]['is_active']
|
||||
assert data['data']['users'][0]['imperial_units'] is False
|
||||
assert data['data']['users'][0]['timezone'] is None
|
||||
assert data['data']['users'][0]['weekm'] is False
|
||||
@ -223,6 +263,9 @@ class TestGetUsers(ApiTestCaseMixin):
|
||||
assert 'admin@example.com' in data['data']['users'][0]['email']
|
||||
assert 'toto@toto.com' in data['data']['users'][1]['email']
|
||||
assert 'sam@test.com' in data['data']['users'][2]['email']
|
||||
assert data['data']['users'][0]['is_active']
|
||||
assert data['data']['users'][1]['is_active']
|
||||
assert data['data']['users'][2]['is_active']
|
||||
assert data['data']['users'][0]['imperial_units'] is False
|
||||
assert data['data']['users'][0]['timezone'] is None
|
||||
assert data['data']['users'][0]['weekm'] is False
|
||||
@ -1344,7 +1387,7 @@ class TestDeleteUser(ApiTestCaseMixin):
|
||||
'you can not delete your account, no other user has admin rights',
|
||||
)
|
||||
|
||||
def test_it_enables_registration_on_user_delete(
|
||||
def test_it_enables_registration_after_user_delete(
|
||||
self,
|
||||
app_with_3_users_max: Flask,
|
||||
user_1_admin: User,
|
||||
@ -1363,15 +1406,15 @@ class TestDeleteUser(ApiTestCaseMixin):
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username='justatest',
|
||||
email='test@test.com',
|
||||
password='12345678',
|
||||
password_conf='12345678',
|
||||
username=self.random_string(),
|
||||
email=self.random_email(),
|
||||
password=self.random_string(),
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_it_does_not_enable_registration_on_user_delete(
|
||||
self,
|
||||
|
@ -14,6 +14,7 @@ class TestUserModel:
|
||||
assert 'created_at' in serialized_user
|
||||
assert serialized_user['admin'] is False
|
||||
assert serialized_user['first_name'] is None
|
||||
assert serialized_user['is_active']
|
||||
assert serialized_user['last_name'] is None
|
||||
assert serialized_user['bio'] is None
|
||||
assert serialized_user['location'] is None
|
||||
@ -99,6 +100,15 @@ class TestUserModel:
|
||||
)
|
||||
assert serialized_user['records'][0]['workout_date']
|
||||
|
||||
def test_it_returns_is_active_to_false_fot_inactive_user(
|
||||
self,
|
||||
app: Flask,
|
||||
inactive_user: User,
|
||||
) -> None:
|
||||
serialized_user = inactive_user.serialize(inactive_user)
|
||||
|
||||
assert serialized_user['is_active'] is False
|
||||
|
||||
|
||||
class TestUserSportModel:
|
||||
def test_user_model(
|
||||
|
Reference in New Issue
Block a user