import base64 import os import random import string import tempfile from typing import Tuple, Any, Generator import pytest from flask import Flask from flask.testing import FlaskClient, FlaskCliRunner from werkzeug.test import Client from corvus import create_app, register_blueprints, register_error_handlers from corvus.db import init_db from corvus.model import User from corvus.service import user_service def add_test_user() -> Tuple[str, str]: test_username = 'test_' + ''.join( random.choices(string.ascii_letters + string.digits, k=17)).strip() test_password = ''.join( random.choices(string.ascii_letters + string.digits, k=32)).strip() user_service.register(test_username, test_password, User.ROLE_ADMIN, False) return test_username, test_password @pytest.fixture def app() -> Generator[Flask, Any, Any]: """Create and configure a new corvus_app instance for each test.""" # create a temporary file to isolate the database for each test db_fd, db_path = tempfile.mkstemp(suffix='.db') test_database_uri = 'sqlite:///{}'.format(db_path) # create the corvus_app with common test config corvus_app = create_app({ 'TESTING': True, 'SQLALCHEMY_DATABASE_URI': test_database_uri, }) register_blueprints(corvus_app) register_error_handlers(corvus_app) # create the database and load test data with corvus_app.app_context(): init_db() test_username, test_password = add_test_user() corvus_app.config['test_username'] = test_username corvus_app.config['test_password'] = test_password # get_db().executescript(_data_sql) yield corvus_app # close and remove the temporary database os.close(db_fd) os.unlink(db_path) @pytest.fixture def client(app: Flask) -> FlaskClient: """A test client for the app.""" return app.test_client() @pytest.fixture def runner(app: Flask) -> FlaskCliRunner: """A test runner for the app's Click commands.""" return app.test_cli_runner() class AuthActions(object): def __init__(self, client: Client, username: str = "", password: str = "") -> None: self._client = client self.username: str = username self.password: str = password self.token: str = "" def configure(self, username: str, password: str) -> Any: self.username = username self.password = password return self def login(self) -> Any: auth_header = self.get_authorization_header_basic() result = self._client.post( '/auth/login', headers={ auth_header[0]: auth_header[1] } ) self.token = result.json['token'] return result def bump(self) -> Any: auth_header = self.get_authorization_header_token() return self._client.post( '/auth/bump', headers={ auth_header[0]: auth_header[1] } ) def logout(self) -> Any: auth_header = self.get_authorization_header_token() return self._client.post( '/auth/logout', headers={ auth_header[0]: auth_header[1] } ) def get_authorization_header_basic(self) -> Tuple[str, str]: credentials = base64.b64encode( '{}:{}'.format(self.username, self.password).encode('utf8')) \ .decode('utf8').strip() return 'Authorization', 'Basic {}'.format(credentials) def get_authorization_header_token(self) -> Tuple[str, str]: credentials = base64.b64encode( '{}:{}'.format(self.username, self.token).encode('utf8')) \ .decode('utf8').strip() return 'X-Auth-Token', '{}'.format(credentials) @pytest.fixture def auth(client: Client) -> AuthActions: return AuthActions(client, client.application.config.get('test_username'), client.application.config.get('test_password'))