A multipurpose python flask API server and administration SPA
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.

139 lines
4.3 KiB

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