2017-12-16 21:00:46 +01:00
|
|
|
import json
|
2020-05-17 16:42:44 +02:00
|
|
|
from datetime import datetime, timedelta
|
2018-01-01 21:54:03 +01:00
|
|
|
from io import BytesIO
|
2022-07-03 13:29:50 +02:00
|
|
|
from typing import Optional
|
2022-03-13 08:56:23 +01:00
|
|
|
from unittest.mock import MagicMock, Mock, patch
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2021-11-03 10:23:28 +01:00
|
|
|
import pytest
|
2021-01-20 16:47:00 +01:00
|
|
|
from flask import Flask
|
|
|
|
from freezegun import freeze_time
|
|
|
|
|
2021-12-19 11:51:28 +01:00
|
|
|
from fittrackee.users.models import User, UserSportPreference
|
2022-02-16 18:07:05 +01:00
|
|
|
from fittrackee.users.utils.token import get_user_token
|
2022-04-02 17:16:10 +02:00
|
|
|
from fittrackee.workouts.models import Sport
|
2017-12-16 21:00:46 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
from ..mixins import ApiTestCaseMixin
|
2022-04-02 17:16:10 +02:00
|
|
|
from ..utils import jsonify_dict
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
USER_AGENT = (
|
|
|
|
'Mozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0'
|
|
|
|
)
|
|
|
|
|
2017-12-16 21:00:46 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
class TestUserRegistration(ApiTestCaseMixin):
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_payload_is_empty(self, app: Flask) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict()),
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_username_is_missing(self, app: Flask) -> None:
|
2021-11-03 10:23:28 +01:00
|
|
|
client = app.test_client()
|
2022-03-19 20:34:36 +01:00
|
|
|
|
2021-11-03 10:23:28 +01:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2021-11-03 10:23:28 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2021-11-03 10:23:28 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2022-03-19 20:34:36 +01:00
|
|
|
'input_username_length',
|
|
|
|
[1, 31],
|
2021-11-03 10:23:28 +01:00
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_username_length_is_invalid(
|
|
|
|
self, app: Flask, input_username_length: int
|
2021-01-02 19:28:03 +01:00
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(length=input_username_length),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response, 'username: 3 to 30 characters required\n')
|
2022-03-13 08:38:45 +01:00
|
|
|
|
|
|
|
@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()
|
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-13 08:38:45 +01:00
|
|
|
username=input_username,
|
2022-03-19 20:34:36 +01:00
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_email(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-03-13 08:38:45 +01:00
|
|
|
self.assert_400(
|
|
|
|
response,
|
|
|
|
'username: only alphanumeric characters and '
|
|
|
|
'the underscore character "_" allowed\n',
|
|
|
|
)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'text_transformation',
|
|
|
|
['upper', 'lower'],
|
|
|
|
)
|
|
|
|
def test_it_returns_error_if_user_already_exists_with_same_username(
|
|
|
|
self, app: Flask, user_1: User, text_transformation: str
|
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=(
|
|
|
|
user_1.username.upper()
|
|
|
|
if text_transformation == 'upper'
|
|
|
|
else user_1.username.lower()
|
|
|
|
),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
self.assert_400(response, 'sorry, that username is already taken')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_password_is_missing(self, app: Flask) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_password_is_too_short(
|
|
|
|
self, app: Flask
|
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(length=7),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response, 'password: 8 characters required\n')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_email_is_missing(self, app: Flask) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_email_is_invalid(self, app: Flask) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_string(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response, 'email: valid email must be provided\n')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
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
|
|
|
|
|
2022-07-03 13:29:50 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'input_language,expected_language',
|
|
|
|
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
|
|
|
|
)
|
|
|
|
def test_it_creates_user_with_inactive_account(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
input_language: Optional[str],
|
|
|
|
expected_language: str,
|
|
|
|
) -> None:
|
2022-03-19 22:02:06 +01:00
|
|
|
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(),
|
2022-07-03 13:29:50 +02:00
|
|
|
language=input_language,
|
2022-03-19 22:02:06 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
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
|
2022-07-03 13:29:50 +02:00
|
|
|
assert new_user.language == expected_language
|
2022-03-19 22:02:06 +01:00
|
|
|
|
2022-07-03 13:29:50 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'input_language,expected_language',
|
|
|
|
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
|
|
|
|
)
|
|
|
|
def test_it_calls_account_confirmation_email_when_payload_is_valid(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
account_confirmation_email_mock: Mock,
|
|
|
|
input_language: Optional[str],
|
|
|
|
expected_language: str,
|
2022-03-19 22:02:06 +01:00
|
|
|
) -> 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',
|
2022-07-03 13:29:50 +02:00
|
|
|
language=input_language,
|
2022-03-19 22:02:06 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
account_confirmation_email_mock.send.assert_called_once_with(
|
|
|
|
{
|
2022-07-03 13:29:50 +02:00
|
|
|
'language': expected_language,
|
2022-03-19 22:02:06 +01:00
|
|
|
'email': email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-19 22:02:06 +01:00
|
|
|
'account_confirmation_url': (
|
|
|
|
'http://0.0.0.0:5000/account-confirmation'
|
|
|
|
f'?token={expected_token}'
|
|
|
|
),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_does_not_call_account_confirmation_email_when_email_sending_is_disabled( # noqa
|
|
|
|
self,
|
|
|
|
app_wo_email_activation: Flask,
|
|
|
|
account_confirmation_email_mock: Mock,
|
|
|
|
) -> None:
|
|
|
|
client = app_wo_email_activation.test_client()
|
|
|
|
email = self.random_email()
|
|
|
|
username = self.random_string()
|
|
|
|
|
|
|
|
response = 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},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
account_confirmation_email_mock.send.assert_not_called()
|
|
|
|
|
2021-11-03 10:23:28 +01:00
|
|
|
@pytest.mark.parametrize(
|
2022-03-19 20:34:36 +01:00
|
|
|
'text_transformation',
|
|
|
|
['upper', 'lower'],
|
2021-11-03 10:23:28 +01:00
|
|
|
)
|
2022-03-19 22:02:06 +01:00
|
|
|
def test_it_does_not_return_error_if_a_user_already_exists_with_same_email(
|
2022-03-19 20:34:36 +01:00
|
|
|
self, app: Flask, user_1: User, text_transformation: str
|
2021-11-03 10:23:28 +01:00
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
response = client.post(
|
2022-03-19 20:34:36 +01:00
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
username=self.random_string(),
|
|
|
|
email=(
|
|
|
|
user_1.email.upper()
|
|
|
|
if text_transformation == 'upper'
|
|
|
|
else user_1.email.lower()
|
|
|
|
),
|
|
|
|
password=self.random_string(),
|
|
|
|
)
|
|
|
|
),
|
2021-11-03 10:23:28 +01:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
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
|
2021-11-03 10:23:28 +01:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
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:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
client.post(
|
2022-03-19 20:34:36 +01:00
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
username=self.random_string(),
|
2022-03-19 22:02:06 +01:00
|
|
|
email=user_1.email,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
|
|
|
)
|
|
|
|
),
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
account_confirmation_email_mock.send.assert_not_called()
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
|
|
|
|
class TestUserLogin(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_payload_is_empty(self, app: Flask) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/login',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict()),
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_user_does_not_exists(
|
|
|
|
self, app: Flask
|
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/login',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(email=self.random_email(), password=self.random_string())
|
|
|
|
),
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
2021-01-01 16:39:25 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_401(response, 'invalid credentials')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
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')
|
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_password_is_invalid(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/login',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(email=user_1.email, password=self.random_email())
|
|
|
|
),
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_401(response, 'invalid credentials')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'text_transformation',
|
|
|
|
['upper', 'lower'],
|
|
|
|
)
|
|
|
|
def test_user_can_login_regardless_username_case(
|
|
|
|
self, app: Flask, user_1: User, text_transformation: str
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/login',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=(
|
|
|
|
user_1.email.upper()
|
|
|
|
if text_transformation == 'upper'
|
|
|
|
else user_1.email.lower()
|
|
|
|
),
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
assert response.status_code == 200
|
|
|
|
assert response.content_type == 'application/json'
|
2020-05-10 15:55:56 +02:00
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
2022-03-19 20:34:36 +01:00
|
|
|
assert data['message'] == 'successfully logged in'
|
|
|
|
assert data['auth_token']
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
|
|
|
|
class TestUserProfile(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_auth_token_is_missing(
|
|
|
|
self, app: Flask
|
2021-01-02 19:28:03 +01:00
|
|
|
) -> None:
|
2022-03-19 20:34:36 +01:00
|
|
|
client = app.test_client()
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
response = client.get('/api/auth/profile')
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_401(response, 'provide a valid auth token')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_auth_token_is_invalid(
|
|
|
|
self, app: Flask
|
|
|
|
) -> None:
|
2020-05-10 15:55:56 +02:00
|
|
|
client = app.test_client()
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.get(
|
2022-03-19 20:34:36 +01:00
|
|
|
'/api/auth/profile', headers=dict(Authorization='Bearer invalid')
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2022-03-13 08:36:49 +01:00
|
|
|
|
2022-05-27 15:51:40 +02:00
|
|
|
self.assert_invalid_token(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-04-02 17:16:10 +02:00
|
|
|
def test_it_returns_user(self, app: Flask, user_1: User) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.get(
|
|
|
|
'/api/auth/profile',
|
2021-02-20 23:20:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
assert response.status_code == 200
|
2020-05-10 15:55:56 +02:00
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
2022-04-02 17:16:10 +02:00
|
|
|
assert data['data'] == jsonify_dict(user_1.serialize(user_1))
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', True),
|
|
|
|
('profile:write', False),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self, app: Flask, user_1: User, client_scope: str, can_access: bool
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.get(
|
|
|
|
'/api/auth/profile',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2021-02-20 23:20:20 +01:00
|
|
|
class TestUserProfileUpdate(ApiTestCaseMixin):
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_payload_is_empty(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit',
|
|
|
|
content_type='application/json',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict()),
|
2021-02-20 23:20:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-13 08:43:20 +01:00
|
|
|
def test_it_returns_error_if_fields_are_missing(
|
2021-01-02 19:28:03 +01:00
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit',
|
|
|
|
content_type='application/json',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict(first_name=self.random_string())),
|
2021-02-20 23:20:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:43:20 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_updates_user_profile(self, app: Flask, user_1: User) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
first_name = self.random_string()
|
|
|
|
last_name = self.random_string()
|
|
|
|
location = self.random_string()
|
|
|
|
bio = self.random_string()
|
|
|
|
birth_date = '1980-01-01'
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit',
|
|
|
|
content_type='application/json',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
first_name=first_name,
|
|
|
|
last_name=last_name,
|
|
|
|
location=location,
|
|
|
|
bio=bio,
|
|
|
|
birth_date=birth_date,
|
|
|
|
)
|
|
|
|
),
|
2021-02-20 23:20:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user profile updated'
|
2022-04-02 17:16:10 +02:00
|
|
|
assert data['data'] == jsonify_dict(user_1.serialize(user_1))
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2022-03-13 08:43:20 +01:00
|
|
|
|
|
|
|
class TestUserAccountUpdate(ApiTestCaseMixin):
|
2022-03-13 08:56:23 +01:00
|
|
|
@staticmethod
|
|
|
|
def assert_no_emails_sent(
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
email_updated_to_current_address_mock.send.assert_not_called()
|
|
|
|
email_updated_to_new_address_mock.send.assert_not_called()
|
|
|
|
password_change_email_mock.send.assert_not_called()
|
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_payload_is_empty(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:43:20 +01:00
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
2020-05-10 15:55:56 +02:00
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict()),
|
2021-02-20 23:20:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:52:09 +01:00
|
|
|
self.assert_400(response)
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
def test_it_returns_error_if_current_password_is_missing(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-13 08:43:20 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
2022-03-13 08:52:09 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:52:09 +01:00
|
|
|
)
|
|
|
|
),
|
2022-03-13 08:43:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
self.assert_400(response, error_message='current password is missing')
|
|
|
|
|
2022-03-13 08:52:09 +01:00
|
|
|
def test_it_returns_error_if_email_is_missing(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:52:09 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response, 'email is missing')
|
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
def test_it_returns_error_if_current_password_is_invalid(
|
2022-03-13 08:43:20 +01:00
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
2022-03-13 08:48:37 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-13 08:52:09 +01:00
|
|
|
email=user_1.email,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:48:37 +01:00
|
|
|
)
|
|
|
|
),
|
2022-03-13 08:43:20 +01:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
self.assert_401(response, error_message='invalid credentials')
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_does_not_send_emails_when_error_occurs(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:56:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_no_emails_sent(
|
|
|
|
email_updated_to_current_address_mock,
|
|
|
|
email_updated_to_new_address_mock,
|
|
|
|
password_change_email_mock,
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:52:09 +01:00
|
|
|
def test_it_does_not_returns_error_if_no_new_password_provided(
|
2022-03-13 08:56:23 +01:00
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:52:09 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user account updated'
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_does_not_send_emails_if_no_change(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:52:09 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
client.patch(
|
2022-03-13 08:52:09 +01:00
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
self.assert_no_emails_sent(
|
|
|
|
email_updated_to_current_address_mock,
|
|
|
|
email_updated_to_new_address_mock,
|
|
|
|
password_change_email_mock,
|
|
|
|
)
|
2022-03-13 08:52:09 +01:00
|
|
|
|
|
|
|
def test_it_returns_error_if_new_email_is_invalid(
|
2022-03-13 08:56:23 +01:00
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:52:09 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
email=self.random_string(),
|
2022-03-13 08:52:09 +01:00
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response, 'email: valid email must be provided\n')
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_only_updates_email_to_confirm_if_new_email_provided(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:52:09 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
current_email = user_1.email
|
|
|
|
new_email = 'new.email@example.com'
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert current_email == user_1.email
|
|
|
|
assert new_email == user_1.email_to_confirm
|
|
|
|
assert user_1.confirmation_token is not None
|
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_updates_email_when_email_sending_is_disabled(
|
|
|
|
self,
|
|
|
|
app_wo_email_activation: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app_wo_email_activation, user_1.email
|
|
|
|
)
|
|
|
|
new_email = 'new.email@example.com'
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert user_1.email == new_email
|
|
|
|
assert user_1.email_to_confirm is None
|
|
|
|
assert user_1.confirmation_token is None
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_calls_email_updated_to_current_email_send_when_new_email_provided( # noqa
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:52:09 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2022-03-13 08:56:23 +01:00
|
|
|
new_email = 'new.email@example.com'
|
2022-03-13 08:52:09 +01:00
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
client.patch(
|
2022-03-13 08:52:09 +01:00
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
2022-03-13 08:56:23 +01:00
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
2022-03-13 08:52:09 +01:00
|
|
|
)
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
email_updated_to_current_address_mock.send.assert_called_once_with(
|
|
|
|
{
|
|
|
|
'language': 'en',
|
|
|
|
'email': user_1.email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': user_1.username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-13 08:56:23 +01:00
|
|
|
'new_email_address': new_email,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_it_calls_email_updated_to_new_email_send_when_new_email_provided(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
new_email = 'new.email@example.com'
|
2022-03-19 20:34:36 +01:00
|
|
|
expected_token = self.random_string()
|
2022-03-13 08:56:23 +01:00
|
|
|
|
|
|
|
with patch('secrets.token_urlsafe', return_value=expected_token):
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
email_updated_to_new_address_mock.send.assert_called_once_with(
|
|
|
|
{
|
|
|
|
'language': 'en',
|
|
|
|
'email': user_1.email_to_confirm,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': user_1.username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-13 08:56:23 +01:00
|
|
|
'email_confirmation_url': (
|
|
|
|
f'http://0.0.0.0:5000/email-update?token={expected_token}'
|
|
|
|
),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_it_does_not_calls_password_change_email_send_when_new_email_provided( # noqa
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
new_email = 'new.email@example.com'
|
2022-03-19 20:34:36 +01:00
|
|
|
expected_token = self.random_string()
|
2022-03-13 08:56:23 +01:00
|
|
|
|
|
|
|
with patch('secrets.token_urlsafe', return_value=expected_token):
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
password_change_email_mock.send.assert_not_called()
|
2022-03-13 08:52:09 +01:00
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
def test_it_returns_error_if_controls_fail_on_new_password(
|
2022-03-13 08:56:23 +01:00
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:48:37 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-13 08:52:09 +01:00
|
|
|
email=user_1.email,
|
2022-03-13 08:48:37 +01:00
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(length=3),
|
2022-03-13 08:48:37 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response, 'password: 8 characters required')
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_updates_auth_user_password_when_new_password_provided(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
2022-03-13 08:48:37 +01:00
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
current_hashed_password = user_1.password
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-13 08:52:09 +01:00
|
|
|
email=user_1.email,
|
2022-03-13 08:48:37 +01:00
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:48:37 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
2022-03-13 08:56:23 +01:00
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user account updated'
|
|
|
|
assert current_hashed_password != user_1.password
|
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_new_password_is_hashed(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
2022-03-13 08:48:37 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
2022-03-13 08:43:20 +01:00
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password = self.random_string()
|
2022-03-13 08:48:37 +01:00
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-13 08:52:09 +01:00
|
|
|
email=user_1.email,
|
2022-03-13 08:48:37 +01:00
|
|
|
password='12345678',
|
|
|
|
new_password=new_password,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
2022-03-13 08:56:23 +01:00
|
|
|
|
2022-03-13 08:48:37 +01:00
|
|
|
assert response.status_code == 200
|
|
|
|
assert new_password != user_1.password
|
2022-03-13 08:43:20 +01:00
|
|
|
|
2022-03-13 08:56:23 +01:00
|
|
|
def test_it_calls_password_change_email_when_new_password_provided(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:56:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
password_change_email_mock.send.assert_called_once_with(
|
|
|
|
{
|
|
|
|
'language': 'en',
|
|
|
|
'email': user_1.email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': user_1.username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-13 08:56:23 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_it_does_not_call_email_updated_emails_send_when_new_password_provided( # noqa
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=user_1.email,
|
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:56:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
email_updated_to_current_address_mock.send.assert_not_called()
|
|
|
|
email_updated_to_new_address_mock.send.assert_not_called()
|
|
|
|
|
|
|
|
def test_it_updates_email_to_confirm_and_password_when_new_email_and_password_provided( # noqa
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
current_email = user_1.email
|
|
|
|
current_hashed_password = user_1.password
|
|
|
|
new_email = 'new.email@example.com'
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email=new_email,
|
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:56:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user account updated'
|
|
|
|
assert user_1.email == current_email
|
|
|
|
assert user_1.email_to_confirm == new_email
|
|
|
|
assert user_1.password != current_hashed_password
|
|
|
|
|
|
|
|
def test_it_calls_all_email_send_when_new_email_and_password_provided(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email='new.email@example.com',
|
|
|
|
password='12345678',
|
2022-03-19 20:34:36 +01:00
|
|
|
new_password=self.random_string(),
|
2022-03-13 08:56:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
email_updated_to_current_address_mock.send.assert_called_once()
|
|
|
|
email_updated_to_new_address_mock.send.assert_called_once()
|
|
|
|
password_change_email_mock.send.assert_called_once()
|
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_does_not_calls_all_email_send_when_email_sending_is_disabled(
|
|
|
|
self,
|
|
|
|
app_wo_email_activation: Flask,
|
|
|
|
user_1: User,
|
|
|
|
email_updated_to_current_address_mock: MagicMock,
|
|
|
|
email_updated_to_new_address_mock: MagicMock,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app_wo_email_activation, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
email='new.email@example.com',
|
|
|
|
password='12345678',
|
|
|
|
new_password=self.random_string(),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_no_emails_sent(
|
|
|
|
email_updated_to_current_address_mock,
|
|
|
|
email_updated_to_new_address_mock,
|
|
|
|
password_change_email_mock,
|
|
|
|
)
|
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.patch(
|
|
|
|
'/api/auth/profile/edit/account',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2021-10-17 21:01:14 +02:00
|
|
|
class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_if_payload_is_empty(
|
2021-10-17 21:01:14 +02:00
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-10-17 21:01:14 +02:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/preferences',
|
|
|
|
content_type='application/json',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict()),
|
2021-10-17 21:01:14 +02:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
self.assert_400(response)
|
2021-10-17 21:01:14 +02:00
|
|
|
|
|
|
|
def test_it_returns_error_if_fields_are_missing(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-10-17 21:01:14 +02:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/preferences',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict(weekm=True)),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2021-10-17 21:01:14 +02:00
|
|
|
|
2022-07-03 13:29:50 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'input_language,expected_language',
|
|
|
|
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
|
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_updates_user_preferences(
|
2022-07-03 13:29:50 +02:00
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
input_language: Optional[str],
|
|
|
|
expected_language: str,
|
2021-10-17 21:01:14 +02:00
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-10-17 21:01:14 +02:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/preferences',
|
|
|
|
content_type='application/json',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
timezone='America/New_York',
|
|
|
|
weekm=True,
|
2022-07-03 13:29:50 +02:00
|
|
|
language=input_language,
|
2022-03-19 20:34:36 +01:00
|
|
|
imperial_units=True,
|
|
|
|
)
|
|
|
|
),
|
2021-10-17 21:01:14 +02:00
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user preferences updated'
|
2022-07-03 13:29:50 +02:00
|
|
|
assert data['data']['language'] == expected_language
|
2022-04-02 17:16:10 +02:00
|
|
|
assert data['data'] == jsonify_dict(user_1.serialize(user_1))
|
2021-10-17 21:01:14 +02:00
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/preferences',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2021-10-17 21:01:14 +02:00
|
|
|
|
2021-11-12 12:22:07 +01:00
|
|
|
class TestUserSportPreferencesUpdate(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_payload_is_empty(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict()),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
def test_it_returns_error_if_sport_id_is_missing(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict(is_active=True)),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
def test_it_returns_error_if_sport_not_found(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict(sport_id=1, is_active=True)),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_404_with_entity(response, 'sport')
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
def test_it_returns_error_if_payload_contains_only_sport_id(
|
|
|
|
self, app: Flask, user_1: User, sport_1_cycling: Sport
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(dict(sport_id=1)),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
def test_it_returns_error_if_color_is_invalid(
|
|
|
|
self, app: Flask, user_1: User, sport_1_cycling: Sport
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
sport_id=sport_1_cycling.id,
|
2022-03-19 20:34:36 +01:00
|
|
|
color=self.random_string(),
|
2021-11-12 12:22:07 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response, 'invalid hexadecimal color')
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'input_color',
|
|
|
|
['#000000', '#FFF'],
|
|
|
|
)
|
|
|
|
def test_it_updates_sport_color_for_auth_user(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
sport_2_running: Sport,
|
|
|
|
input_color: str,
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
sport_id=sport_2_running.id,
|
|
|
|
color=input_color,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user sport preferences updated'
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert data['data']['user_id'] == user_1.id
|
|
|
|
assert data['data']['sport_id'] == sport_2_running.id
|
|
|
|
assert data['data']['color'] == input_color
|
|
|
|
assert data['data']['is_active'] is True
|
|
|
|
assert data['data']['stopped_speed_threshold'] == 0.1
|
|
|
|
|
|
|
|
def test_it_disables_sport_for_auth_user(
|
|
|
|
self, app: Flask, user_1: User, sport_1_cycling: Sport
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
sport_id=sport_1_cycling.id,
|
|
|
|
is_active=False,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user sport preferences updated'
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert data['data']['user_id'] == user_1.id
|
|
|
|
assert data['data']['sport_id'] == sport_1_cycling.id
|
|
|
|
assert data['data']['color'] is None
|
|
|
|
assert data['data']['is_active'] is False
|
|
|
|
assert data['data']['stopped_speed_threshold'] == 1
|
|
|
|
|
|
|
|
def test_it_updates_stopped_speed_threshold_for_auth_user(
|
|
|
|
self, app: Flask, user_1: User, sport_1_cycling: Sport
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-11-12 12:22:07 +01:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
sport_id=sport_1_cycling.id,
|
|
|
|
stopped_speed_threshold=0.5,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user sport preferences updated'
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert data['data']['user_id'] == user_1.id
|
|
|
|
assert data['data']['sport_id'] == sport_1_cycling.id
|
|
|
|
assert data['data']['color'] is None
|
|
|
|
assert data['data']['is_active']
|
|
|
|
assert data['data']['stopped_speed_threshold'] == 0.5
|
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/profile/edit/sports',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2021-11-12 12:22:07 +01:00
|
|
|
|
2021-12-19 11:51:28 +01:00
|
|
|
class TestUserSportPreferencesReset(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_sport_does_not_exist(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-12-19 11:51:28 +01:00
|
|
|
|
|
|
|
response = client.delete(
|
|
|
|
'/api/auth/profile/reset/sports/1',
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_404_with_entity(response, 'sport')
|
2021-12-19 11:51:28 +01:00
|
|
|
|
|
|
|
def test_it_resets_sport_preferences(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
sport_1_cycling: Sport,
|
|
|
|
user_sport_1_preference: UserSportPreference,
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-12-19 11:51:28 +01:00
|
|
|
|
|
|
|
response = client.delete(
|
|
|
|
f'/api/auth/profile/reset/sports/{sport_1_cycling.id}',
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 204
|
|
|
|
assert (
|
|
|
|
UserSportPreference.query.filter_by(
|
|
|
|
user_id=user_1.id,
|
|
|
|
sport_id=sport_1_cycling.id,
|
|
|
|
).first()
|
|
|
|
is None
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_it_does_not_raise_error_if_sport_preferences_do_not_exist(
|
|
|
|
self, app: Flask, user_1: User, sport_1_cycling: Sport
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-12-19 11:51:28 +01:00
|
|
|
|
|
|
|
response = client.delete(
|
|
|
|
f'/api/auth/profile/reset/sports/{sport_1_cycling.id}',
|
|
|
|
headers=dict(Authorization=f'Bearer {auth_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 204
|
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
sport_1_cycling: Sport,
|
|
|
|
user_sport_1_preference: UserSportPreference,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.delete(
|
|
|
|
f'/api/auth/profile/reset/sports/{sport_1_cycling.id}',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2021-12-19 11:51:28 +01:00
|
|
|
|
2021-02-20 23:20:20 +01:00
|
|
|
class TestUserPicture(ApiTestCaseMixin):
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_file_is_missing(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
2021-02-20 23:20:20 +01:00
|
|
|
Authorization=f'Bearer {auth_token}',
|
2020-05-10 15:55:56 +02:00
|
|
|
),
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response, 'no file part', 'fail')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_file_is_invalid(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-12 17:56:06 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
data=dict(file=(BytesIO(b'avatar'), 'avatar.bmp')),
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
2021-02-20 23:20:20 +01:00
|
|
|
Authorization=f'Bearer {auth_token}',
|
2020-05-10 15:55:56 +02:00
|
|
|
),
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response, 'file extension not allowed', 'fail')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2021-02-20 21:37:31 +01:00
|
|
|
def test_it_returns_error_if_image_size_exceeds_file_limit(
|
|
|
|
self,
|
|
|
|
app_with_max_file_size: Flask,
|
|
|
|
user_1: User,
|
|
|
|
sport_1_cycling: Sport,
|
|
|
|
gpx_file: str,
|
|
|
|
) -> None:
|
2021-02-20 23:20:20 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
2022-03-12 17:56:06 +01:00
|
|
|
app_with_max_file_size, user_1.email
|
2021-02-20 21:37:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
data=dict(
|
|
|
|
file=(BytesIO(b'test_file_for_avatar' * 50), 'avatar.jpg')
|
|
|
|
),
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
2021-02-20 23:20:20 +01:00
|
|
|
Authorization=f'Bearer {auth_token}',
|
2021-02-20 21:37:31 +01:00
|
|
|
),
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
data = self.assert_413(
|
|
|
|
response,
|
|
|
|
'Error during picture upload, file size (1.2KB) exceeds 1.0KB.',
|
2021-02-20 21:37:31 +01:00
|
|
|
)
|
|
|
|
assert 'data' not in data
|
|
|
|
|
|
|
|
def test_it_returns_error_if_image_size_exceeds_archive_limit(
|
|
|
|
self,
|
|
|
|
app_with_max_zip_file_size: Flask,
|
|
|
|
user_1: User,
|
|
|
|
sport_1_cycling: Sport,
|
|
|
|
gpx_file: str,
|
|
|
|
) -> None:
|
2021-02-20 23:20:20 +01:00
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
2022-03-12 17:56:06 +01:00
|
|
|
app_with_max_zip_file_size, user_1.email
|
2021-02-20 21:37:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
data=dict(
|
|
|
|
file=(BytesIO(b'test_file_for_avatar' * 50), 'avatar.jpg')
|
|
|
|
),
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
2021-02-20 23:20:20 +01:00
|
|
|
Authorization=f'Bearer {auth_token}',
|
2021-02-20 21:37:31 +01:00
|
|
|
),
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
data = self.assert_413(
|
|
|
|
response,
|
|
|
|
'Error during picture upload, file size (1.2KB) exceeds 1.0KB.',
|
2021-02-20 21:37:31 +01:00
|
|
|
)
|
|
|
|
assert 'data' not in data
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_updates_user_picture(self, app: Flask, user_1: User) -> None:
|
|
|
|
client, auth_token = self.get_test_client_and_auth_token(
|
|
|
|
app, user_1.email
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
data=dict(file=(BytesIO(b'avatar'), 'avatar.png')),
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
|
|
|
Authorization=f'Bearer {auth_token}',
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user picture updated'
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert 'avatar.png' in user_1.picture
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
data=dict(file=(BytesIO(b'avatar2'), 'avatar2.png')),
|
|
|
|
headers=dict(
|
|
|
|
content_type='multipart/form-data',
|
|
|
|
Authorization=f'Bearer {auth_token}',
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'user picture updated'
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert 'avatar.png' not in user_1.picture
|
|
|
|
assert 'avatar2.png' in user_1.picture
|
|
|
|
|
2022-06-15 19:16:14 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
'client_scope, can_access',
|
|
|
|
[
|
|
|
|
('application:write', False),
|
|
|
|
('profile:read', False),
|
|
|
|
('profile:write', True),
|
|
|
|
('users:read', False),
|
|
|
|
('users:write', False),
|
|
|
|
('workouts:read', False),
|
|
|
|
('workouts:write', False),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_expected_scopes_are_defined(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
client_scope: str,
|
|
|
|
can_access: bool,
|
|
|
|
) -> None:
|
|
|
|
(
|
|
|
|
client,
|
|
|
|
oauth_client,
|
|
|
|
access_token,
|
|
|
|
_,
|
2022-06-19 20:04:42 +02:00
|
|
|
) = self.create_oauth2_client_and_issue_token(
|
2022-06-15 19:16:14 +02:00
|
|
|
app, user_1, scope=client_scope
|
|
|
|
)
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/picture',
|
|
|
|
content_type='application/json',
|
|
|
|
headers=dict(Authorization=f'Bearer {access_token}'),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_response_scope(response, can_access)
|
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
class TestRegistrationConfiguration(ApiTestCaseMixin):
|
2020-05-10 15:55:56 +02:00
|
|
|
def test_it_returns_error_if_it_exceeds_max_users(
|
2021-02-20 23:20:20 +01:00
|
|
|
self,
|
|
|
|
app_with_3_users_max: Flask,
|
|
|
|
user_1_admin: User,
|
|
|
|
user_2: User,
|
|
|
|
user_3: User,
|
2021-01-02 19:28:03 +01:00
|
|
|
) -> None:
|
2021-02-20 23:20:20 +01:00
|
|
|
client = app_with_3_users_max.test_client()
|
2020-05-10 15:55:56 +02:00
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_403(response, 'error, registration is disabled')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
|
|
|
def test_it_disables_registration_on_user_registration(
|
2021-01-02 19:28:03 +01:00
|
|
|
self,
|
2021-02-20 23:20:20 +01:00
|
|
|
app_with_3_users_max: Flask,
|
2021-01-02 19:28:03 +01:00
|
|
|
user_1_admin: User,
|
|
|
|
user_2: User,
|
|
|
|
) -> None:
|
2021-02-20 23:20:20 +01:00
|
|
|
client = app_with_3_users_max.test_client()
|
2020-05-10 15:55:56 +02:00
|
|
|
client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2021-02-20 23:20:20 +01:00
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_403(response, 'error, registration is disabled')
|
2020-05-10 15:55:56 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_does_not_disable_registration_if_users_count_below_limit(
|
2020-09-16 11:09:32 +02:00
|
|
|
self,
|
2021-02-20 23:20:20 +01:00
|
|
|
app_with_3_users_max: Flask,
|
|
|
|
user_1: User,
|
2021-01-02 19:28:03 +01:00
|
|
|
) -> None:
|
2021-02-20 23:20:20 +01:00
|
|
|
client = app_with_3_users_max.test_client()
|
2020-05-10 15:55:56 +02:00
|
|
|
client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
|
2020-05-10 15:55:56 +02:00
|
|
|
response = client.post(
|
|
|
|
'/api/auth/register',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
username=self.random_string(),
|
|
|
|
email=self.random_email(),
|
|
|
|
password=self.random_string(),
|
2020-05-10 15:55:56 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
2022-03-19 20:34:36 +01:00
|
|
|
|
2022-03-19 22:02:06 +01:00
|
|
|
assert response.status_code == 200
|
2020-05-10 17:08:18 +02:00
|
|
|
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
class TestPasswordResetRequest(ApiTestCaseMixin):
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_returns_error_on_empty_payload(self, app: Flask) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/reset-request',
|
|
|
|
data=json.dumps(dict()),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response)
|
|
|
|
|
|
|
|
def test_it_returns_error_on_invalid_payload(self, app: Flask) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/reset-request',
|
|
|
|
data=json.dumps(dict(username=self.random_string())),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response)
|
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_returns_error_when_email_sending_is_disabled(
|
|
|
|
self, app_wo_email_activation: Flask
|
|
|
|
) -> None:
|
|
|
|
client = app_wo_email_activation.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/reset-request',
|
|
|
|
data=json.dumps(dict(email='test@test.com')),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_404_with_message(
|
|
|
|
response, 'the requested URL was not found on the server'
|
|
|
|
)
|
|
|
|
|
2020-05-17 16:42:44 +02:00
|
|
|
def test_it_requests_password_reset_when_user_exists(
|
2022-03-19 20:34:36 +01:00
|
|
|
self, app: Flask, user_1: User, user_reset_password_email: Mock
|
2021-01-02 19:28:03 +01:00
|
|
|
) -> None:
|
2020-05-10 17:08:18 +02:00
|
|
|
client = app.test_client()
|
2022-03-19 20:34:36 +01:00
|
|
|
|
2020-05-10 17:08:18 +02:00
|
|
|
response = client.post(
|
2020-05-17 16:42:44 +02:00
|
|
|
'/api/auth/password/reset-request',
|
2020-05-10 17:08:18 +02:00
|
|
|
data=json.dumps(dict(email='test@test.com')),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
2021-11-01 09:44:10 +01:00
|
|
|
assert data['message'] == 'password reset request processed'
|
2020-05-10 17:08:18 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_calls_reset_password_email_when_user_exists(
|
|
|
|
self, app: Flask, user_1: User, reset_password_email: Mock
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
token = self.random_string()
|
|
|
|
|
|
|
|
with patch('jwt.encode', return_value=token):
|
|
|
|
client.post(
|
|
|
|
'/api/auth/password/reset-request',
|
|
|
|
data=json.dumps(dict(email='test@test.com')),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
reset_password_email.send.assert_called_once_with(
|
|
|
|
{
|
|
|
|
'language': 'en',
|
|
|
|
'email': user_1.email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'expiration_delay': '3 seconds',
|
|
|
|
'username': user_1.username,
|
|
|
|
'password_reset_url': (
|
|
|
|
f'http://0.0.0.0:5000/password-reset?token={token}'
|
|
|
|
),
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-19 20:34:36 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_does_not_return_error_when_user_does_not_exist(
|
|
|
|
self, app: Flask
|
|
|
|
) -> None:
|
2020-05-10 17:08:18 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
2020-05-17 16:42:44 +02:00
|
|
|
'/api/auth/password/reset-request',
|
2020-05-10 17:08:18 +02:00
|
|
|
data=json.dumps(dict(email='test@test.com')),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
2021-11-01 09:44:10 +01:00
|
|
|
assert data['message'] == 'password reset request processed'
|
2020-05-10 17:08:18 +02:00
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
def test_it_does_not_call_reset_password_email_when_user_does_not_exist(
|
|
|
|
self, app: Flask, reset_password_email: Mock
|
|
|
|
) -> None:
|
2020-05-10 17:08:18 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
client.post(
|
2020-05-17 16:42:44 +02:00
|
|
|
'/api/auth/password/reset-request',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict(email='test@test.com')),
|
2020-05-10 17:08:18 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-19 20:34:36 +01:00
|
|
|
reset_password_email.assert_not_called()
|
2020-05-17 16:42:44 +02:00
|
|
|
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
class TestPasswordUpdate(ApiTestCaseMixin):
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_payload_is_empty(self, app: Flask) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
2022-03-13 08:39:50 +01:00
|
|
|
data=json.dumps(dict()),
|
2020-05-17 16:42:44 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_token_is_missing(self, app: Flask) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
2020-09-16 11:09:32 +02:00
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
2020-09-16 11:09:32 +02:00
|
|
|
)
|
2020-05-17 16:42:44 +02:00
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_password_is_missing(self, app: Flask) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
2020-09-16 11:09:32 +02:00
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2022-03-19 20:34:36 +01:00
|
|
|
token=self.random_string(),
|
2020-09-16 11:09:32 +02:00
|
|
|
)
|
|
|
|
),
|
2020-05-17 16:42:44 +02:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response)
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_token_is_invalid(self, app: Flask) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
token = get_user_token(1)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2020-12-25 19:35:15 +01:00
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
2020-05-17 16:42:44 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_401(response, 'invalid token, please request a new token')
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_token_is_expired(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
now = datetime.utcnow()
|
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
with freeze_time(now + timedelta(seconds=4)):
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2020-12-25 19:35:15 +01:00
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
2020-05-17 16:42:44 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_401(
|
|
|
|
response, 'invalid token, please request a new token'
|
2020-05-17 16:42:44 +02:00
|
|
|
)
|
|
|
|
|
2021-01-02 19:28:03 +01:00
|
|
|
def test_it_returns_error_if_password_is_invalid(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2020-12-25 19:35:15 +01:00
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(length=7),
|
2020-05-17 16:42:44 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
2022-03-13 08:36:49 +01:00
|
|
|
self.assert_400(response, 'password: 8 characters required\n')
|
2020-05-17 16:42:44 +02:00
|
|
|
|
2022-03-13 09:01:23 +01:00
|
|
|
def test_it_does_not_send_email_after_error(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(length=7),
|
2022-03-13 09:01:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
password_change_email_mock.assert_not_called()
|
|
|
|
|
|
|
|
def test_it_updates_password(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
2020-05-17 16:42:44 +02:00
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
2020-12-25 19:35:15 +01:00
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
2020-05-17 16:42:44 +02:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
2021-11-01 09:44:10 +01:00
|
|
|
assert data['message'] == 'password updated'
|
2022-03-13 08:59:37 +01:00
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_sends_email_after_successful_update(
|
2022-03-13 09:01:23 +01:00
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
token=token,
|
2022-03-19 20:34:36 +01:00
|
|
|
password=self.random_string(),
|
2022-03-13 09:01:23 +01:00
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
password_change_email_mock.send.assert_called_once_with(
|
|
|
|
{
|
|
|
|
'language': 'en',
|
|
|
|
'email': user_1.email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': user_1.username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-13 09:01:23 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2022-04-23 18:04:20 +02:00
|
|
|
def test_it_does_not_send_email_when_email_sending_is_disabled(
|
|
|
|
self,
|
|
|
|
app_wo_email_activation: Flask,
|
|
|
|
user_1: User,
|
|
|
|
password_change_email_mock: MagicMock,
|
|
|
|
) -> None:
|
|
|
|
token = get_user_token(user_1.id, password_reset=True)
|
|
|
|
client = app_wo_email_activation.test_client()
|
|
|
|
|
|
|
|
client.post(
|
|
|
|
'/api/auth/password/update',
|
|
|
|
data=json.dumps(
|
|
|
|
dict(
|
|
|
|
token=token,
|
|
|
|
password=self.random_string(),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
password_change_email_mock.send.assert_not_called()
|
|
|
|
|
2022-03-13 08:59:37 +01:00
|
|
|
|
|
|
|
class TestEmailUpdateWitUnauthenticatedUser(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_token_is_missing(self, app: Flask) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/email/update',
|
|
|
|
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/email/update',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict(token=self.random_string())),
|
2022-03-13 08:59:37 +01:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response)
|
|
|
|
|
|
|
|
def test_it_does_not_update_email_if_token_mismatches(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
2022-03-19 20:34:36 +01:00
|
|
|
user_1.confirmation_token = self.random_string()
|
2022-03-13 08:59:37 +01:00
|
|
|
new_email = 'new.email@example.com'
|
|
|
|
user_1.email_to_confirm = new_email
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/email/update',
|
2022-03-19 20:34:36 +01:00
|
|
|
data=json.dumps(dict(token=self.random_string())),
|
2022-03-13 08:59:37 +01:00
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response)
|
|
|
|
|
|
|
|
def test_it_updates_email(self, app: Flask, user_1: User) -> None:
|
2022-03-19 20:34:36 +01:00
|
|
|
token = self.random_string()
|
2022-03-13 08:59:37 +01:00
|
|
|
user_1.confirmation_token = token
|
|
|
|
new_email = 'new.email@example.com'
|
|
|
|
user_1.email_to_confirm = new_email
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/email/update',
|
|
|
|
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'] == 'email updated'
|
|
|
|
assert user_1.email == new_email
|
|
|
|
assert user_1.email_to_confirm is None
|
|
|
|
assert user_1.confirmation_token is None
|
2022-03-19 22:02:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2022-03-20 12:15:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestResendAccountConfirmationEmail(ApiTestCaseMixin):
|
|
|
|
def test_it_returns_error_if_email_is_missing(self, app: Flask) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict()),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_400(response)
|
|
|
|
|
|
|
|
def test_it_does_not_return_error_if_account_does_not_exist(
|
|
|
|
self, app: Flask
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=self.random_email())),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'confirmation email resent'
|
|
|
|
|
|
|
|
def test_it_does_not_return_error_if_account_already_active(
|
|
|
|
self, app: Flask, user_1: User
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=user_1.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'confirmation email resent'
|
|
|
|
|
|
|
|
def test_it_does_not_call_account_confirmation_email_if_user_is_active(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
user_1: User,
|
|
|
|
account_confirmation_email_mock: Mock,
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=user_1.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
account_confirmation_email_mock.send.assert_not_called()
|
|
|
|
|
|
|
|
def test_it_returns_success_if_user_is_inactive(
|
|
|
|
self, app: Flask, inactive_user: User
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=inactive_user.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
data = json.loads(response.data.decode())
|
|
|
|
assert data['status'] == 'success'
|
|
|
|
assert data['message'] == 'confirmation email resent'
|
|
|
|
|
|
|
|
def test_it_updates_token_if_user_is_inactive(
|
|
|
|
self, app: Flask, inactive_user: User
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
previous_token = inactive_user.confirmation_token
|
|
|
|
|
|
|
|
client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=inactive_user.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
assert inactive_user.confirmation_token != previous_token
|
|
|
|
|
|
|
|
def test_it_calls_account_confirmation_email_if_user_is_inactive(
|
|
|
|
self,
|
|
|
|
app: Flask,
|
|
|
|
inactive_user: User,
|
|
|
|
account_confirmation_email_mock: Mock,
|
|
|
|
) -> None:
|
|
|
|
client = app.test_client()
|
|
|
|
expected_token = self.random_string()
|
2022-07-03 13:29:50 +02:00
|
|
|
inactive_user.language = 'fr'
|
2022-03-20 12:15:23 +01:00
|
|
|
|
|
|
|
with patch('secrets.token_urlsafe', return_value=expected_token):
|
|
|
|
client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=inactive_user.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
environ_base={'HTTP_USER_AGENT': USER_AGENT},
|
|
|
|
)
|
|
|
|
|
|
|
|
account_confirmation_email_mock.send.assert_called_once_with(
|
|
|
|
{
|
2022-07-03 13:29:50 +02:00
|
|
|
'language': inactive_user.language,
|
2022-03-20 12:15:23 +01:00
|
|
|
'email': inactive_user.email,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'username': inactive_user.username,
|
|
|
|
'fittrackee_url': 'http://0.0.0.0:5000',
|
2022-04-03 11:42:03 +02:00
|
|
|
'operating_system': 'Linux',
|
|
|
|
'browser_name': 'Firefox',
|
2022-03-20 12:15:23 +01:00
|
|
|
'account_confirmation_url': (
|
|
|
|
'http://0.0.0.0:5000/account-confirmation'
|
|
|
|
f'?token={expected_token}'
|
|
|
|
),
|
|
|
|
},
|
|
|
|
)
|
2022-04-23 18:04:20 +02:00
|
|
|
|
|
|
|
def test_it_returns_error_if_email_sending_is_disabled(
|
|
|
|
self, app_wo_email_activation: Flask, inactive_user: User
|
|
|
|
) -> None:
|
|
|
|
client = app_wo_email_activation.test_client()
|
|
|
|
|
|
|
|
response = client.post(
|
|
|
|
'/api/auth/account/resend-confirmation',
|
|
|
|
data=json.dumps(dict(email=inactive_user.email)),
|
|
|
|
content_type='application/json',
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_404_with_message(
|
|
|
|
response, 'the requested URL was not found on the server'
|
|
|
|
)
|