API - refacto + remove unused endpoint for now
This commit is contained in:
parent
dfe50b5287
commit
b5b4ac8f92
@ -5,7 +5,7 @@ from flask import Flask
|
||||
import fittrackee
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
|
||||
|
||||
class TestGetConfig(ApiTestCaseMixin):
|
||||
|
@ -7,7 +7,7 @@ from fittrackee import email_service
|
||||
from fittrackee.emails.email import EmailMessage
|
||||
from fittrackee.emails.exceptions import InvalidEmailUrlScheme
|
||||
|
||||
from ..api_test_case import CallArgsMixin
|
||||
from ..mixins import CallArgsMixin
|
||||
from .template_results.password_reset_request import expected_en_text_body
|
||||
|
||||
|
||||
|
6
fittrackee/tests/fixtures/fixtures_emails.py
vendored
6
fittrackee/tests/fixtures/fixtures_emails.py
vendored
@ -30,6 +30,12 @@ def user_password_change_email_mock() -> Iterator[MagicMock]:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reset_password_email() -> Iterator[MagicMock]:
|
||||
with patch('fittrackee.users.auth.reset_password_email') as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def user_reset_password_email() -> Iterator[MagicMock]:
|
||||
with patch('fittrackee.users.users.reset_password_email') as mock:
|
||||
|
4
fittrackee/tests/fixtures/fixtures_users.py
vendored
4
fittrackee/tests/fixtures/fixtures_users.py
vendored
@ -60,7 +60,7 @@ def user_1_paris() -> User:
|
||||
|
||||
@pytest.fixture()
|
||||
def user_2() -> User:
|
||||
user = User(username='toto', email='toto@toto.com', password='87654321')
|
||||
user = User(username='toto', email='toto@toto.com', password='12345678')
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return user
|
||||
@ -68,7 +68,7 @@ def user_2() -> User:
|
||||
|
||||
@pytest.fixture()
|
||||
def user_2_admin() -> User:
|
||||
user = User(username='toto', email='toto@toto.com', password='87654321')
|
||||
user = User(username='toto', email='toto@toto.com', password='12345678')
|
||||
user.admin = True
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
@ -6,25 +6,35 @@ from flask.testing import FlaskClient
|
||||
from werkzeug.test import TestResponse
|
||||
|
||||
from .custom_asserts import assert_errored_response
|
||||
from .utils import random_email, random_string
|
||||
|
||||
|
||||
class ApiTestCaseMixin:
|
||||
class RandomMixin:
|
||||
@staticmethod
|
||||
def random_string(
|
||||
length: Optional[int] = None,
|
||||
prefix: Optional[str] = None,
|
||||
suffix: Optional[str] = None,
|
||||
) -> str:
|
||||
return random_string(length, prefix, suffix)
|
||||
|
||||
@staticmethod
|
||||
def random_email() -> str:
|
||||
return random_email()
|
||||
|
||||
|
||||
class ApiTestCaseMixin(RandomMixin):
|
||||
@staticmethod
|
||||
def get_test_client_and_auth_token(
|
||||
app: Flask, user_email: str
|
||||
) -> Tuple[FlaskClient, str]:
|
||||
"""user_email must be user_1 or user_2 email"""
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
email=user_email,
|
||||
password=(
|
||||
'87654321'
|
||||
if user_email == 'toto@toto.com'
|
||||
else '12345678'
|
||||
),
|
||||
password='12345678',
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
File diff suppressed because it is too large
Load Diff
@ -6,11 +6,10 @@ from unittest.mock import MagicMock, patch
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User, UserSportPreference
|
||||
from fittrackee.users.utils.random import random_string
|
||||
from fittrackee.utils import get_readable_duration
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
|
||||
|
||||
class TestGetUser(ApiTestCaseMixin):
|
||||
@ -1108,7 +1107,7 @@ class TestUpdateUser(ApiTestCaseMixin):
|
||||
response = client.patch(
|
||||
f'/api/users/{user_2.username}',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict(new_email=random_string())),
|
||||
data=json.dumps(dict(new_email=self.random_string())),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
@ -1128,7 +1127,7 @@ class TestUpdateUser(ApiTestCaseMixin):
|
||||
client.patch(
|
||||
f'/api/users/{user_2.username}',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict(new_email=random_string())),
|
||||
data=json.dumps(dict(new_email=self.random_string())),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
@ -1166,7 +1165,7 @@ class TestUpdateUser(ApiTestCaseMixin):
|
||||
app, user_1_admin.email
|
||||
)
|
||||
new_email = 'new.' + user_2.email
|
||||
expected_token = random_string()
|
||||
expected_token = self.random_string()
|
||||
|
||||
with patch('secrets.token_urlsafe', return_value=expected_token):
|
||||
response = client.patch(
|
||||
|
@ -3,6 +3,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.tests.utils import random_string
|
||||
from fittrackee.users.exceptions import UserNotFoundException
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.users.utils.admin import set_admin_rights
|
||||
@ -12,7 +13,6 @@ from fittrackee.users.utils.controls import (
|
||||
is_valid_email,
|
||||
register_controls,
|
||||
)
|
||||
from fittrackee.users.utils.random import random_string
|
||||
|
||||
|
||||
class TestSetAdminRights:
|
||||
|
25
fittrackee/tests/utils.py
Normal file
25
fittrackee/tests/utils.py
Normal file
@ -0,0 +1,25 @@
|
||||
import random
|
||||
import string
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def random_string(
|
||||
length: Optional[int] = None,
|
||||
prefix: Optional[str] = None,
|
||||
suffix: Optional[str] = None,
|
||||
) -> str:
|
||||
if length is None:
|
||||
length = 10
|
||||
random_str = ''.join(
|
||||
random.choice(string.ascii_letters + string.digits)
|
||||
for _ in range(length)
|
||||
)
|
||||
return (
|
||||
f'{"" if prefix is None else prefix}'
|
||||
f'{random_str}'
|
||||
f'{"" if suffix is None else suffix}'
|
||||
)
|
||||
|
||||
|
||||
def random_email() -> str:
|
||||
return random_string(suffix='@example.com')
|
@ -5,7 +5,7 @@ from flask import Flask
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
|
||||
|
||||
class TestGetRecords(ApiTestCaseMixin):
|
||||
|
@ -6,7 +6,7 @@ from fittrackee import db
|
||||
from fittrackee.users.models import User, UserSportPreference
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
|
||||
expected_sport_1_cycling_result = {
|
||||
'id': 1,
|
||||
|
@ -5,7 +5,7 @@ from flask import Flask
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
|
||||
|
||||
class TestGetStatsByTime(ApiTestCaseMixin):
|
||||
|
@ -7,7 +7,7 @@ from flask import Flask
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
from .utils import get_random_short_id
|
||||
|
||||
|
||||
@ -73,19 +73,13 @@ class TestGetWorkouts(ApiTestCaseMixin):
|
||||
workout_cycling_user_1: Workout,
|
||||
workout_running_user_1: Workout,
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='toto@toto.com', password='87654321')),
|
||||
content_type='application/json',
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_2.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
'/api/workouts',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
data = json.loads(response.data.decode())
|
||||
|
@ -12,7 +12,7 @@ from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from fittrackee.workouts.utils.short_id import decode_short_id
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin, CallArgsMixin
|
||||
from ..mixins import ApiTestCaseMixin, CallArgsMixin
|
||||
|
||||
|
||||
def assert_workout_data_with_gpx(data: Dict) -> None:
|
||||
@ -1018,18 +1018,13 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
workout_short_id = data['data']['workouts'][0]['id']
|
||||
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='toto@toto.com', password='87654321')),
|
||||
content_type='application/json',
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_2.email
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
f'/api/workouts/{workout_short_id}/chart_data',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_403(response)
|
||||
|
@ -8,7 +8,7 @@ from flask import Flask
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
from .utils import get_random_short_id, post_an_workout
|
||||
|
||||
|
||||
@ -152,21 +152,15 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
gpx_file: str,
|
||||
) -> None:
|
||||
_, workout_short_id = post_an_workout(app, gpx_file)
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='toto@toto.com', password='87654321')),
|
||||
content_type='application/json',
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_2.email
|
||||
)
|
||||
|
||||
response = client.patch(
|
||||
f'/api/workouts/{workout_short_id}',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict(sport_id=2, title="Workout test")),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_403(response)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
@ -7,7 +6,7 @@ from fittrackee.files import get_absolute_file_path
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
from ..api_test_case import ApiTestCaseMixin
|
||||
from ..mixins import ApiTestCaseMixin
|
||||
from .utils import get_random_short_id, post_an_workout
|
||||
|
||||
|
||||
@ -39,19 +38,13 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
|
||||
gpx_file: str,
|
||||
) -> None:
|
||||
_, workout_short_id = post_an_workout(app, gpx_file)
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='toto@toto.com', password='87654321')),
|
||||
content_type='application/json',
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_2.email
|
||||
)
|
||||
|
||||
response = client.delete(
|
||||
f'/api/workouts/{workout_short_id}',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_403(response)
|
||||
@ -113,18 +106,13 @@ class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
sport_1_cycling: Sport,
|
||||
workout_cycling_user_1: Workout,
|
||||
) -> None:
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='toto@toto.com', password='87654321')),
|
||||
content_type='application/json',
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
app, user_2.email
|
||||
)
|
||||
|
||||
response = client.delete(
|
||||
f'/api/workouts/{workout_cycling_user_1.short_id}',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
headers=dict(Authorization=f'Bearer {auth_token}'),
|
||||
)
|
||||
|
||||
self.assert_403(response)
|
||||
|
@ -227,67 +227,6 @@ def login_user() -> Union[Dict, HttpResponse]:
|
||||
return handle_error_and_return_response(e, db=db)
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/logout', methods=['GET'])
|
||||
@authenticate
|
||||
def logout_user(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
"""
|
||||
user logout
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/auth/logout HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example responses**:
|
||||
|
||||
- successful logout
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "successfully logged out",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
- error on login
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 401 UNAUTHORIZED
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"message": "provide a valid auth token",
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: successfully logged out
|
||||
:statuscode 401: provide a valid auth token
|
||||
|
||||
"""
|
||||
# get auth token
|
||||
auth_header = request.headers.get('Authorization')
|
||||
if not auth_header:
|
||||
return UnauthorizedErrorResponse('provide a valid auth token')
|
||||
|
||||
auth_token = auth_header.split(' ')[1]
|
||||
resp = User.decode_auth_token(auth_token)
|
||||
if isinstance(resp, str):
|
||||
return UnauthorizedErrorResponse(resp)
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
'message': 'successfully logged out',
|
||||
}
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/profile', methods=['GET'])
|
||||
@authenticate
|
||||
def get_authenticated_user_profile(
|
||||
|
@ -31,7 +31,6 @@ from .decorators import authenticate, authenticate_as_admin
|
||||
from .exceptions import UserNotFoundException
|
||||
from .models import User, UserSportPreference
|
||||
from .utils.admin import set_admin_rights
|
||||
from .utils.random import random_string
|
||||
|
||||
users_blueprint = Blueprint('users', __name__)
|
||||
|
||||
@ -514,7 +513,7 @@ def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
|
||||
'reset_password' in user_data
|
||||
and user_data['reset_password'] is True
|
||||
):
|
||||
new_password = random_string(length=random.randint(10, 20))
|
||||
new_password = secrets.token_urlsafe(random.randint(16, 20))
|
||||
user.password = bcrypt.generate_password_hash(
|
||||
new_password, current_app.config.get('BCRYPT_LOG_ROUNDS')
|
||||
).decode()
|
||||
|
@ -1,12 +0,0 @@
|
||||
import random
|
||||
import string
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def random_string(length: Optional[int] = None) -> str:
|
||||
if length is None:
|
||||
length = 10
|
||||
return ''.join(
|
||||
random.choice(string.ascii_letters + string.digits)
|
||||
for _ in range(length)
|
||||
)
|
Loading…
Reference in New Issue
Block a user