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.

136 lines
4.1 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. register_blueprints(server_app)
  34. register_error_handlers(server_app)
  35. # create the database and load test data
  36. with server_app.app_context():
  37. init_db()
  38. test_username, test_password = add_test_user()
  39. server_app.config['test_username'] = test_username
  40. server_app.config['test_password'] = test_password
  41. # get_db().executescript(_data_sql)
  42. yield server_app
  43. # close and remove the temporary database
  44. os.close(db_fd)
  45. os.unlink(db_path)
  46. @pytest.fixture
  47. def client(app: Flask) -> FlaskClient:
  48. """A test client for the app."""
  49. return app.test_client()
  50. @pytest.fixture
  51. def runner(app: Flask) -> FlaskCliRunner:
  52. """A test runner for the app's Click commands."""
  53. return app.test_cli_runner()
  54. class AuthActions(object):
  55. def __init__(self,
  56. client: Client,
  57. username: str = "",
  58. password: str = "") -> None:
  59. self._client = client
  60. self.username: str = username
  61. self.password: str = password
  62. self.token: str = ""
  63. def configure(self, username: str, password: str) -> Any:
  64. self.username = username
  65. self.password = password
  66. return self
  67. def login(self) -> Any:
  68. auth_header = self.get_authorization_header_basic()
  69. result = self._client.post(
  70. '/auth/login',
  71. headers={
  72. auth_header[0]: auth_header[1]
  73. }
  74. )
  75. self.token = result.json['token']
  76. return result
  77. def bump(self) -> Any:
  78. auth_header = self.get_authorization_header_token()
  79. return self._client.post(
  80. '/auth/bump',
  81. headers={
  82. auth_header[0]: auth_header[1]
  83. }
  84. )
  85. def logout(self) -> Any:
  86. auth_header = self.get_authorization_header_token()
  87. return self._client.post(
  88. '/auth/logout',
  89. headers={
  90. auth_header[0]: auth_header[1]
  91. }
  92. )
  93. def get_authorization_header_basic(self) -> Tuple[str, str]:
  94. credentials = base64.b64encode(
  95. '{}:{}'.format(self.username, self.password).encode('utf8')) \
  96. .decode('utf8').strip()
  97. return 'Authorization', 'Basic {}'.format(credentials)
  98. def get_authorization_header_token(self) -> Tuple[str, str]:
  99. credentials = base64.b64encode(
  100. '{}:{}'.format(self.username, self.token).encode('utf8')) \
  101. .decode('utf8').strip()
  102. return 'X-Auth-Token', '{}'.format(credentials)
  103. def __enter__(self) -> 'AuthActions':
  104. return self.login()
  105. def __exit__(self, type: Any, value: Any, traceback: Any) -> None:
  106. self.logout()
  107. @pytest.fixture
  108. def auth(client: Client) -> AuthActions:
  109. return AuthActions(client,
  110. client.application.config.get('test_username'),
  111. client.application.config.get('test_password'))