Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Разработка веб-приложений с использованием фреймворка Flask. В 2 частях. Ч.2. Учебное пособие-1.pdf
Скачиваний:
2
Добавлен:
06.09.2026
Размер:
421 Кб
Скачать
☆

def test_app_is_production(self):

self.assertTrue( app.config['SECRET_KEY'] == \

os.environ.get('SECRET_KEY'))

self.assertFalse(app.config['TESTING'])

self.assertFalse(app.config['DEBUG_TB_ENABLED'])

self.assertTrue(app.config['BCRYPT_LOG_ROUNDS'] == 13)

self.assertTrue(app.config['TOKEN_EXPIRATION_DAYS'] == 30)

self.assertTrue(app.config['TOKEN_EXPIRATION_SECONDS'] == 0)

Затем необходимо обновить метод encode_auth_token() в модели:

def encode_auth_token(self, user_id):

"""Создание токена аутентификации"""

try:

payload = {

'exp': datetime.datetime.utcnow() + datetime.timedelta( days=current_app.config.get('TOKEN_EXPIRATION_DAYS'), se-

conds=current_app.config.get('TOKEN_EXPIRATION_SECONDS') ),

'iat': datetime.datetime.utcnow(), 'sub': user_id

}

return jwt.encode( payload,

current_app.config.get('SECRET_KEY'), algorithm='HS256'

)

except Exception as e: return e

Сейчас самое время написать тест, в котором будет выполняться декоди-

рование токена.

3.5.2. РАСШИФРОВКА ТОКЕНА

Двигаясь дальше, добавим в файл test_user_model.py следующий тест для декодирования токена:

60

def test_decode_auth_token(self):

user = add_user('justatest', 'test@test.com', 'test') auth_token = user.encode_auth_token(user.id) self.assertTrue(isinstance(auth_token, bytes)) self.assertEqual(User.decode_auth_token(auth_token), user.id)

Добавим в класс User() следующий метод:

@staticmethod

def decode_auth_token(auth_token):

"""Расшифровывает токен аутентификации -: param auth_token: -: return: integer | string"""

try:

payload = jwt.decode(

auth_token, current_app.config.get('SECRET_KEY')) return payload['sub']

except jwt.ExpiredSignatureError:

return 'Signature expired. Please log in again.' except jwt.InvalidTokenError:

return 'Invalid token. Please log in again.'

Каждый аутентифицированный запрос должен включать токен аутенти-

фикации для проверки подлинности пользователя. Прежде чем двигаться даль-

ше, необходимо убедиться, что тесты выполняются успешно.

3.6. МАРШРУТЫ ДЛЯ АУТЕНТИФИКАЦИИ

Теперь можно настроить маршруты аутентификации. Прежде чем писать код, необходимо убедиться, что покрытие тестами не уменьшается по мере до-

бавления новых маршрутов. Проверим качество покрытия тестами:

Coverage Summary:

 

Name

Stmts Miss Branch BrPart Cover

---------------------------------------------------------

project/__init__.py

24

12

0

0

50%

project/api/models.py

31

21

2

0

30%

project/api/users.py

50

10

10

0

83%

---------------------------------------------------------

TOTAL

105 43

12

0

62%

В этом параграфе мы создадим следующие маршруты:

61

Точка

HTTP-метод

Необходимость

Результат

доступа

 

аутентификации

 

 

 

 

 

/auth/register

POST

Нет

Регистрация пользователя

/auth/login

POST

Нет

Вход пользователя

/auth/logout

GET

Да

Выход пользователя

/auth/status

GET

Да

Получение статуса

 

 

 

пользователя

Добавим новый файл в каталог services/users/project/api с именем auth.py:

# services/users/project/api/auth.py

from flask import Blueprint, jsonify, request from sqlalchemy import exc, or_

from project.api.models import User from project import db, bcrypt

auth_blueprint = Blueprint('auth', __name__)

Затем зарегистрируем новый Blueprint в приложении в файле services/users /project /__init__.py:

…

# регистрация blueprints

from project.api.users import users_blueprint app.register_blueprint(users_blueprint)

from project.api.auth import auth_blueprint app.register_blueprint(auth_blueprint)

...

Добавим новый файл с именем test_auth.py в каталог tests для хранения

всех тестов, связанных с Blueprint:

# services/users/project/tests/test_auth.py

import json

from project import db

from project.api.models import User

from project.tests.base import BaseTestCase from project.tests.utils import add_user

62

class TestAuthBlueprint(BaseTestCase):

pass

3.6.1. МАРШРУТ POST /AUTH/REGISTER

Создание маршрута начнём с написания теста:

def test_user_registration(self): with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'username': 'justatest', 'email': 'test@test.com', 'password': '123456',

}), content_type='application/json'

)

data = json.loads(response.data.decode()) self.assertTrue(data['status'] == 'success') self.assertTrue(data['message'] == 'Successfully registered.') self.assertTrue(data['auth_token']) self.assertTrue(response.content_type == 'application/json') self.assertEqual(response.status_code, 201)

Тест проверят только корректные варианты регистрации. Однако воз-

можны следующие ситуации:

1)адрес электронной почты уже существует;

2)имя пользователя уже существует;

3)полезная нагрузка является недопустимой (пустая, не содержит имени пользователя, не содержит адреса электронной почты, не содержит пароля).

Напишем для трёх перечисленных ситуаций соответствующие тесты:

def test_user_registration_duplicate_email(self): add_user('test', 'test@test.com', 'test')

with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'username': 'testname1',

63

'email': 'test@test.com', 'password': 'test'

}), content_type='application/json',

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400) self.assertIn(

'Sorry. That user already exists.', data['message']) self.assertIn('fail', data['status'])

def test_user_registration_duplicate_username(self): add_user('test', 'test@test.com', 'test')

with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'username': 'test', 'email': 'test@test.com2', 'password': 'test'

}), content_type='application/json',

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400) self.assertIn(

'Sorry. That user already exists.', data['message']) self.assertIn('fail', data['status'])

def test_user_registration_invalid_json(self): with self.client:

response = self.client.post( '/auth/register', data=json.dumps({}), content_type='application/json'

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400)

64

self.assertIn('Invalid payload.', data['message']) self.assertIn('fail', data['status'])

def test_user_registration_invalid_json_keys_no_username (self): with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'email': 'test@test.com', 'password': 'test'

}), content_type='application/json',

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400) self.assertIn('Invalid payload.', data['message']) self.assertIn('fail', data['status'])

def test_user_registration_invalid_json_keys_no_email(self): with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'username': 'justatest', 'password': 'test'

}), content_type='application/json',

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400) self.assertIn('Invalid payload.', data['message']) self.assertIn('fail', data['status'])

def test_user_registration_invalid_json_keys_no_password (self): with self.client:

response = self.client.post( '/auth/register', data=json.dumps({

'username': 'justatest',

65

'email': 'test@test.com' }),

content_type='application/json',

)

data = json.loads(response.data.decode()) self.assertEqual(response.status_code, 400) self.assertIn('Invalid payload.', data['message']) self.assertIn('fail', data['status'])

Убедимся, что после запуска тесты завершаются неудачно, а затем доба-

вим функцию-представление:

@auth_blueprint.route('/auth/register', methods=['POST']) def register_user():

# get post data

post_data = request.get_json() response_object = {

'status': 'fail',

'message': 'Invalid payload.'

}

if not post_data:

return jsonify(response_object), 400 username = post_data.get('username') email = post_data.get('email') password = post_data.get('password') try:

# проверка существования пользователя user = User.query.filter(

or_(User.username == username, User.email == email)).first() if not user:

# добавление нового пользователя в базу данных new_user = User(

username=username,

email=email,

password=password

)

db.session.add(new_user)

db.session.commit()

# генерация токена

auth_token = new_user.encode_auth_token(new_user.id)

66

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]