An ebook/comic library service and web client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
3.8 KiB

  1. import base64
  2. import os
  3. import random
  4. import string
  5. import tempfile
  6. from typing import Tuple, Any
  7. import pytest
  8. from flask import Flask
  9. from flask.testing import FlaskClient, FlaskCliRunner
  10. from werkzeug.test import Client
  11. from atheneum import create_app, register_blueprints
  12. from atheneum.db import init_db
  13. from atheneum.model import User
  14. from atheneum.service import user_service
  15. def add_test_user() -> Tuple[str, str]:
  16. test_username = 'test_' + ''.join(
  17. random.choices(string.ascii_letters + string.digits, k=17)).strip()
  18. test_password = ''.join(
  19. random.choices(string.ascii_letters + string.digits, k=32)).strip()
  20. user_service.register(test_username, test_password, User.ROLE_ADMIN)
  21. return test_username, test_password
  22. @pytest.fixture
  23. def app() -> Flask:
  24. """Create and configure a new atheneum_app instance for each test."""
  25. # create a temporary file to isolate the database for each test
  26. db_fd, db_path = tempfile.mkstemp()
  27. # create the atheneum_app with common test config
  28. atheneum_app = create_app({
  29. 'TESTING': True,
  30. 'DATABASE': db_path,
  31. })
  32. register_blueprints(atheneum_app)
  33. # create the database and load test data
  34. with atheneum_app.app_context():
  35. init_db()
  36. test_username, test_password = add_test_user()
  37. atheneum_app.config['test_username'] = test_username
  38. atheneum_app.config['test_password'] = test_password
  39. # get_db().executescript(_data_sql)
  40. yield atheneum_app
  41. # close and remove the temporary database
  42. os.close(db_fd)
  43. os.unlink(db_path)
  44. @pytest.fixture
  45. def client(app: Flask) -> FlaskClient:
  46. """A test client for the app."""
  47. return app.test_client()
  48. @pytest.fixture
  49. def runner(app: Flask) -> FlaskCliRunner:
  50. """A test runner for the app's Click commands."""
  51. return app.test_cli_runner()
  52. class AuthActions(object):
  53. def __init__(self,
  54. client: Client,
  55. username: str = "",
  56. password: str = "") -> None:
  57. self._client = client
  58. self.username: str = username
  59. self.password: str = password
  60. self.token: str = ""
  61. def configure(self, username: str, password: str) -> Any:
  62. self.username = username
  63. self.password = password
  64. return self
  65. def login(self) -> Any:
  66. auth_header = self.get_authorization_header_basic()
  67. result = self._client.post(
  68. '/auth/login',
  69. headers={
  70. auth_header[0]: auth_header[1]
  71. }
  72. )
  73. self.token = result.json['token']
  74. return result
  75. def bump(self) -> Any:
  76. auth_header = self.get_authorization_header_token()
  77. return self._client.post(
  78. '/auth/bump',
  79. headers={
  80. auth_header[0]: auth_header[1]
  81. }
  82. )
  83. def logout(self) -> Any:
  84. auth_header = self.get_authorization_header_token()
  85. return self._client.post(
  86. '/auth/logout',
  87. headers={
  88. auth_header[0]: auth_header[1]
  89. }
  90. )
  91. def get_authorization_header_basic(self) -> Tuple[str, str]:
  92. credentials = base64.b64encode(
  93. '{}:{}'.format(self.username, self.password).encode('utf8')) \
  94. .decode('utf8').strip()
  95. return 'Authorization', 'Basic {}'.format(credentials)
  96. def get_authorization_header_token(self) -> Tuple[str, str]:
  97. credentials = base64.b64encode(
  98. '{}:{}'.format(self.username, self.token).encode('utf8')) \
  99. .decode('utf8').strip()
  100. return 'Authorization', 'Token {}'.format(credentials)
  101. @pytest.fixture
  102. def auth(client: Client) -> AuthActions:
  103. return AuthActions(client,
  104. client.application.config.get('test_username'),
  105. client.application.config.get('test_password'))