FitTrackee/fittrackee/tests/users/test_users_api.py

1406 lines
47 KiB
Python

import json
from datetime import datetime, timedelta
from io import BytesIO
from unittest.mock import MagicMock, patch
from flask import Flask
from fittrackee.users.models import User, UserSportPreference
from fittrackee.utils import get_readable_duration
from fittrackee.workouts.models import Sport, Workout
from ..mixins import ApiTestCaseMixin
class TestGetUser(ApiTestCaseMixin):
def test_it_returns_error_if_user_has_no_admin_rights(
self, app: Flask, user_1: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.get(
f'/api/users/{user_2.username}',
content_type='application/json',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_403(response)
def test_it_gets_single_user_without_workouts(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
f'/api/users/{user_2.username}',
content_type='application/json',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert data['status'] == 'success'
assert len(data['data']['users']) == 1
user = data['data']['users'][0]
assert user['username'] == 'toto'
assert user['email'] == 'toto@toto.com'
assert user['created_at']
assert not user['admin']
assert user['first_name'] is None
assert user['last_name'] is None
assert user['birth_date'] is None
assert user['bio'] is None
assert user['location'] is None
assert user['nb_sports'] == 0
assert user['nb_workouts'] == 0
assert user['records'] == []
assert user['sports_list'] == []
assert user['total_distance'] == 0
assert user['total_duration'] == '0:00:00'
def test_it_gets_single_user_with_workouts(
self,
app: Flask,
user_1: User,
user_2_admin: User,
sport_1_cycling: Sport,
sport_2_running: Sport,
workout_cycling_user_1: Workout,
workout_running_user_1: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_2_admin.email
)
response = client.get(
f'/api/users/{user_1.username}',
content_type='application/json',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert data['status'] == 'success'
assert len(data['data']['users']) == 1
user = data['data']['users'][0]
assert user['username'] == 'test'
assert user['email'] == 'test@test.com'
assert user['created_at']
assert not user['admin']
assert user['first_name'] is None
assert user['last_name'] is None
assert user['birth_date'] is None
assert user['bio'] is None
assert user['location'] is None
assert len(user['records']) == 8
assert user['nb_sports'] == 2
assert user['nb_workouts'] == 2
assert user['sports_list'] == [1, 2]
assert user['total_distance'] == 22
assert user['total_duration'] == '2:40:00'
def test_it_returns_error_if_user_does_not_exist(
self, app: Flask, user_1_admin: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users/not_existing',
content_type='application/json',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_404_with_entity(response, 'user')
class TestGetUsers(ApiTestCaseMixin):
def test_it_returns_error_if_user_has_no_admin_rights(
self, app: Flask, user_1: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.get(
'/api/users',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_403(response)
def test_it_get_users_list(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'created_at' in data['data']['users'][0]
assert 'created_at' in data['data']['users'][1]
assert 'created_at' in data['data']['users'][2]
assert 'admin' in data['data']['users'][0]['username']
assert 'toto' in data['data']['users'][1]['username']
assert 'sam' in data['data']['users'][2]['username']
assert 'admin@example.com' in data['data']['users'][0]['email']
assert 'toto@toto.com' in data['data']['users'][1]['email']
assert 'sam@test.com' in data['data']['users'][2]['email']
assert data['data']['users'][0]['imperial_units'] is False
assert data['data']['users'][0]['timezone'] is None
assert data['data']['users'][0]['weekm'] is False
assert data['data']['users'][0]['language'] is None
assert data['data']['users'][0]['nb_sports'] == 0
assert data['data']['users'][0]['nb_workouts'] == 0
assert data['data']['users'][0]['records'] == []
assert data['data']['users'][0]['sports_list'] == []
assert data['data']['users'][0]['total_distance'] == 0
assert data['data']['users'][0]['total_duration'] == '0:00:00'
assert data['data']['users'][1]['nb_sports'] == 0
assert data['data']['users'][1]['nb_workouts'] == 0
assert data['data']['users'][1]['records'] == []
assert data['data']['users'][1]['sports_list'] == []
assert data['data']['users'][1]['total_distance'] == 0
assert data['data']['users'][1]['total_duration'] == '0:00:00'
assert data['data']['users'][2]['records'] == []
assert data['data']['users'][2]['nb_sports'] == 0
assert data['data']['users'][2]['nb_workouts'] == 0
assert data['data']['users'][2]['sports_list'] == []
assert data['data']['users'][2]['total_distance'] == 0
assert data['data']['users'][2]['total_duration'] == '0:00:00'
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_with_workouts(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
sport_1_cycling: Sport,
workout_cycling_user_1: Workout,
sport_2_running: Sport,
workout_running_user_1: Workout,
workout_cycling_user_2: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'created_at' in data['data']['users'][0]
assert 'created_at' in data['data']['users'][1]
assert 'created_at' in data['data']['users'][2]
assert 'admin' in data['data']['users'][0]['username']
assert 'toto' in data['data']['users'][1]['username']
assert 'sam' in data['data']['users'][2]['username']
assert 'admin@example.com' in data['data']['users'][0]['email']
assert 'toto@toto.com' in data['data']['users'][1]['email']
assert 'sam@test.com' in data['data']['users'][2]['email']
assert data['data']['users'][0]['imperial_units'] is False
assert data['data']['users'][0]['timezone'] is None
assert data['data']['users'][0]['weekm'] is False
assert data['data']['users'][0]['nb_sports'] == 2
assert data['data']['users'][0]['nb_workouts'] == 2
assert len(data['data']['users'][0]['records']) == 8
assert data['data']['users'][0]['sports_list'] == [1, 2]
assert data['data']['users'][0]['total_distance'] == 22.0
assert data['data']['users'][0]['total_duration'] == '2:40:00'
assert data['data']['users'][1]['nb_sports'] == 1
assert data['data']['users'][1]['nb_workouts'] == 1
assert len(data['data']['users'][1]['records']) == 4
assert data['data']['users'][1]['sports_list'] == [1]
assert data['data']['users'][1]['total_distance'] == 15
assert data['data']['users'][1]['total_duration'] == '1:00:00'
assert data['data']['users'][2]['nb_sports'] == 0
assert data['data']['users'][2]['nb_workouts'] == 0
assert len(data['data']['users'][2]['records']) == 0
assert data['data']['users'][2]['sports_list'] == []
assert data['data']['users'][2]['total_distance'] == 0
assert data['data']['users'][2]['total_duration'] == '0:00:00'
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
@patch('fittrackee.users.users.USER_PER_PAGE', 2)
def test_it_gets_first_page_on_users_list(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?page=1',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 2
assert data['pagination'] == {
'has_next': True,
'has_prev': False,
'page': 1,
'pages': 2,
'total': 3,
}
@patch('fittrackee.users.users.USER_PER_PAGE', 2)
def test_it_gets_next_page_on_users_list(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?page=2',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert data['pagination'] == {
'has_next': False,
'has_prev': True,
'page': 2,
'pages': 2,
'total': 3,
}
def test_it_gets_empty_next_page_on_users_list(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?page=2',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 0
assert data['pagination'] == {
'has_next': False,
'has_prev': True,
'page': 2,
'pages': 1,
'total': 3,
}
def test_it_gets_user_list_with_2_per_page(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?per_page=2',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 2
assert data['pagination'] == {
'has_next': True,
'has_prev': False,
'page': 1,
'pages': 2,
'total': 3,
}
def test_it_gets_next_page_on_user_list_with_2_per_page(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?page=2&per_page=2',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert data['pagination'] == {
'has_next': False,
'has_prev': True,
'page': 2,
'pages': 2,
'total': 3,
}
def test_it_gets_users_list_ordered_by_username(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=username',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'toto' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_username_ascending(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=username&order=asc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'toto' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_username_descending(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=username&order=desc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'admin' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_creation_date(
self, app: Flask, user_2: User, user_3: User, user_1_admin: User
) -> None:
user_2.created_at = datetime.utcnow() - timedelta(days=1)
user_3.created_at = datetime.utcnow() - timedelta(hours=1)
user_1_admin.created_at = datetime.utcnow()
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=created_at',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'admin' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_creation_date_ascending(
self, app: Flask, user_2: User, user_3: User, user_1_admin: User
) -> None:
user_2.created_at = datetime.utcnow() - timedelta(days=1)
user_3.created_at = datetime.utcnow() - timedelta(hours=1)
user_1_admin.created_at = datetime.utcnow()
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=created_at&order=asc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'admin' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_creation_date_descending(
self, app: Flask, user_2: User, user_3: User, user_1_admin: User
) -> None:
user_2.created_at = datetime.utcnow() - timedelta(days=1)
user_3.created_at = datetime.utcnow() - timedelta(hours=1)
user_1_admin.created_at = datetime.utcnow()
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=created_at&order=desc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'toto' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_admin_rights(
self, app: Flask, user_2: User, user_1_admin: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=admin',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'admin' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_admin_rights_ascending(
self, app: Flask, user_2: User, user_1_admin: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=admin&order=asc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 'sam' in data['data']['users'][1]['username']
assert 'admin' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_admin_rights_descending(
self, app: Flask, user_2: User, user_3: User, user_1_admin: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=admin&order=desc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 'toto' in data['data']['users'][1]['username']
assert 'sam' in data['data']['users'][2]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_workouts_count(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
sport_1_cycling: Sport,
workout_cycling_user_2: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=workouts_count',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 0 == data['data']['users'][0]['nb_workouts']
assert 'sam' in data['data']['users'][1]['username']
assert 0 == data['data']['users'][1]['nb_workouts']
assert 'toto' in data['data']['users'][2]['username']
assert 1 == data['data']['users'][2]['nb_workouts']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_workouts_count_ascending(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
sport_1_cycling: Sport,
workout_cycling_user_2: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=workouts_count&order=asc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'admin' in data['data']['users'][0]['username']
assert 0 == data['data']['users'][0]['nb_workouts']
assert 'sam' in data['data']['users'][1]['username']
assert 0 == data['data']['users'][1]['nb_workouts']
assert 'toto' in data['data']['users'][2]['username']
assert 1 == data['data']['users'][2]['nb_workouts']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_ordered_by_workouts_count_descending(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
sport_1_cycling: Sport,
workout_cycling_user_2: Workout,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=workouts_count&order=desc',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 3
assert 'toto' in data['data']['users'][0]['username']
assert 1 == data['data']['users'][0]['nb_workouts']
assert 'admin' in data['data']['users'][1]['username']
assert 0 == data['data']['users'][1]['nb_workouts']
assert 'sam' in data['data']['users'][2]['username']
assert 0 == data['data']['users'][2]['nb_workouts']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 3,
}
def test_it_gets_users_list_filtering_on_username(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?q=toto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert 'toto' in data['data']['users'][0]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 1,
'total': 1,
}
def test_it_returns_username_matching_query(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?q=oto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert 'toto' in data['data']['users'][0]['username']
def test_it_filtering_on_username_is_case_insensitive(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?q=TOTO',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert 'toto' in data['data']['users'][0]['username']
def test_it_returns_empty_users_list_filtering_on_username(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?q=not_existing',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 0
assert data['pagination'] == {
'has_next': False,
'has_prev': False,
'page': 1,
'pages': 0,
'total': 0,
}
def test_it_users_list_with_complex_query(
self, app: Flask, user_1_admin: User, user_2: User, user_3: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.get(
'/api/users?order_by=username&order=desc&page=2&per_page=2',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['users']) == 1
assert 'admin' in data['data']['users'][0]['username']
assert data['pagination'] == {
'has_next': False,
'has_prev': True,
'page': 2,
'pages': 2,
'total': 3,
}
class TestGetUserPicture(ApiTestCaseMixin):
def test_it_return_error_if_user_has_no_picture(
self, app: Flask, user_1: User
) -> None:
client = app.test_client()
response = client.get(f'/api/users/{user_1.username}/picture')
self.assert_404_with_message(response, 'No picture.')
def test_it_returns_error_if_user_does_not_exist(
self, app: Flask, user_1: User
) -> None:
client = app.test_client()
response = client.get('/api/users/not_existing/picture')
self.assert_404_with_entity(response, 'user')
class TestUpdateUser(ApiTestCaseMixin):
def test_it_returns_error_if_payload_is_empty(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict()),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_400(response)
def test_it_returns_error_if_payload_for_admin_rights_is_invalid(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(admin="")),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 500
data = json.loads(response.data.decode())
assert 'error' in data['status']
assert (
'error, please try again or contact the administrator'
in data['message']
)
def test_it_returns_error_if_user_can_not_change_admin_rights(
self, app: Flask, user_1: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(admin=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_403(response)
def test_it_adds_admin_rights_to_a_user(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(admin=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
data = json.loads(response.data.decode())
assert 'success' in data['status']
assert len(data['data']['users']) == 1
user = data['data']['users'][0]
assert user['email'] == 'toto@toto.com'
assert user['admin'] is True
def test_it_removes_admin_rights_to_a_user(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(admin=False)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
data = json.loads(response.data.decode())
assert 'success' in data['status']
assert len(data['data']['users']) == 1
user = data['data']['users'][0]
assert user['email'] == 'toto@toto.com'
assert user['admin'] is False
def test_it_does_not_send_email_when_only_admin_rights_update(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_password_change_email_mock: MagicMock,
user_reset_password_email: MagicMock,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(admin=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
user_password_change_email_mock.send.assert_not_called()
user_reset_password_email.send.assert_not_called()
def test_it_resets_user_password(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
user_2_password = user_2.password
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(reset_password=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
assert user_2.password != user_2_password
def test_it_calls_password_change_email_when_password_reset_is_successful(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_password_change_email_mock: MagicMock,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(reset_password=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
user_password_change_email_mock.send.assert_called_once_with(
{
'language': 'en',
'email': user_2.email,
},
{
'username': user_2.username,
'fittrackee_url': 'http://0.0.0.0:5000',
},
)
def test_it_calls_reset_password_email_when_password_reset_is_successful(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_reset_password_email: MagicMock,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
with patch(
'fittrackee.users.users.User.encode_password_reset_token',
return_value='xxx',
):
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(reset_password=True)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
user_reset_password_email.send.assert_called_once_with(
{
'language': 'en',
'email': user_2.email,
},
{
'expiration_delay': get_readable_duration(
app.config['PASSWORD_TOKEN_EXPIRATION_SECONDS'],
'en',
),
'username': user_2.username,
'password_reset_url': (
'http://0.0.0.0:5000/password-reset?token=xxx'
),
'fittrackee_url': 'http://0.0.0.0:5000',
},
)
def test_it_returns_error_when_updating_email_with_invalid_address(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(new_email=self.random_string())),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_400(response, 'valid email must be provided')
def test_it_does_not_send_email_when_error_on_updating_email(
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_email_updated_to_new_address_mock: MagicMock,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(new_email=self.random_string())),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
user_email_updated_to_new_address_mock.send.assert_not_called()
def test_it_updates_user_email(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
user_2_email = user_2.email
user_2_confirmation_token = user_2.confirmation_token
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(new_email='new.' + user_2.email)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
assert user_2.email == user_2_email
assert user_2.email_to_confirm == 'new.' + user_2.email
assert user_2.confirmation_token != user_2_confirmation_token
def test_it_calls_email_updated_to_new_address_when_password_reset_is_successful( # noqa
self,
app: Flask,
user_1_admin: User,
user_2: User,
user_email_updated_to_new_address_mock: MagicMock,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
new_email = 'new.' + user_2.email
expected_token = self.random_string()
with patch('secrets.token_urlsafe', return_value=expected_token):
response = client.patch(
f'/api/users/{user_2.username}',
content_type='application/json',
data=json.dumps(dict(new_email=new_email)),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
user_email_updated_to_new_address_mock.send.assert_called_once_with(
{
'language': 'en',
'email': new_email,
},
{
'username': user_2.username,
'fittrackee_url': 'http://0.0.0.0:5000',
'email_confirmation_url': (
f'http://0.0.0.0:5000/email-update?token={expected_token}'
),
},
)
class TestDeleteUser(ApiTestCaseMixin):
def test_user_can_delete_its_own_account(
self, app: Flask, user_1: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.delete(
'/api/users/test',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_user_with_workout_can_delete_its_own_account(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
client.post(
'/api/workouts',
data=dict(
file=(BytesIO(str.encode(gpx_file)), 'example.gpx'),
data='{"sport_id": 1}',
),
headers=dict(
content_type='multipart/form-data',
Authorization=f'Bearer {auth_token}',
),
)
response = client.delete(
'/api/users/test',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_user_with_preferences_can_delete_its_own_account(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
user_sport_1_preference: UserSportPreference,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.delete(
'/api/users/test',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_user_with_picture_can_delete_its_own_account(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
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}',
),
)
response = client.delete(
'/api/users/test',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_user_can_not_delete_another_user_account(
self, app: Flask, user_1: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.delete(
'/api/users/toto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_403(response)
def test_it_returns_error_when_deleting_non_existing_user(
self, app: Flask, user_1: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.delete(
'/api/users/not_existing',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_404_with_entity(response, 'user')
def test_admin_can_delete_another_user_account(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.delete(
'/api/users/toto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_admin_can_delete_its_own_account(
self, app: Flask, user_1_admin: User, user_2_admin: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.delete(
'/api/users/admin',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 204
def test_admin_can_not_delete_its_own_account_if_no_other_admin(
self, app: Flask, user_1_admin: User, user_2: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.delete(
'/api/users/admin',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_403(
response,
'you can not delete your account, no other user has admin rights',
)
def test_it_enables_registration_on_user_delete(
self,
app_with_3_users_max: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app_with_3_users_max, user_1_admin.email
)
client.delete(
'/api/users/toto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
response = client.post(
'/api/auth/register',
data=json.dumps(
dict(
username='justatest',
email='test@test.com',
password='12345678',
password_conf='12345678',
)
),
content_type='application/json',
)
assert response.status_code == 201
def test_it_does_not_enable_registration_on_user_delete(
self,
app_with_3_users_max: Flask,
user_1_admin: User,
user_2: User,
user_3: User,
user_1_paris: User,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app_with_3_users_max, user_1_admin.email
)
client.delete(
'/api/users/toto',
headers=dict(Authorization=f'Bearer {auth_token}'),
)
response = client.post(
'/api/auth/register',
data=json.dumps(
dict(
username='justatest',
email='test@test.com',
password='12345678',
password_conf='12345678',
)
),
content_type='application/json',
)
self.assert_403(response, 'error, registration is disabled')