|
23 | 23 | import os |
24 | 24 | import logging |
25 | 25 | import json |
| 26 | + |
| 27 | +import psycopg2 |
| 28 | +import psycopg2.sql |
| 29 | +import psycopg2.extensions |
26 | 30 | import pytest |
27 | 31 |
|
28 | 32 | from typing import Optional, List, Dict |
29 | 33 |
|
30 | 34 | import jwt |
31 | 35 | import oauthlib.oauth2 |
32 | 36 | import requests_oauthlib |
| 37 | +from flask_migrate import upgrade |
33 | 38 |
|
34 | | -from mrmat_python_api_flask import create_app, db |
| 39 | +from mrmat_python_api_flask import create_app, db, migrate |
35 | 40 |
|
36 | 41 | LOGGER = logging.getLogger(__name__) |
37 | 42 |
|
@@ -59,10 +64,7 @@ def test_config() -> Optional[Dict]: |
59 | 64 | Returns: |
60 | 65 | A dictionary of configuration or None if not configuration file is set |
61 | 66 | """ |
62 | | - if 'FLASK_CONFIG' not in os.environ: |
63 | | - LOGGER.info('Missing test configuration via FLASK_CONFIG environment variable. Tests are limited') |
64 | | - return None |
65 | | - config_file = os.path.expanduser(os.environ['FLASK_CONFIG']) |
| 67 | + config_file = os.path.expanduser(os.getenv('FLASK_CONFIG')) |
66 | 68 | if not os.path.exists(config_file): |
67 | 69 | LOGGER.info('Configuration set via FLASK_CONFIG environment variable does not exist. Tests are limited') |
68 | 70 | return None |
@@ -148,3 +150,92 @@ def oidc_token_write(test_config) -> Optional[Dict]: |
148 | 150 | token = oidc_token(test_config, ['mrmat-python-api-flask-resource-write']) |
149 | 151 | token['jwt'] = jwt.decode(token['access_token'], options={"verify_signature": False}) |
150 | 152 | return token |
| 153 | + |
| 154 | + |
| 155 | +class TEDException(Exception): |
| 156 | + skip: bool = False |
| 157 | + msg: str = 'An unexpected exception occurred' |
| 158 | + |
| 159 | + def __init__(self, msg: str, skip: Optional[bool] = False): |
| 160 | + self.skip = skip |
| 161 | + self.msg = msg |
| 162 | + |
| 163 | + |
| 164 | +class TED: |
| 165 | + available: bool = False |
| 166 | + config_file: str = None |
| 167 | + config: dict = {} |
| 168 | + |
| 169 | + _admin_conn: psycopg2.extensions.connection = None |
| 170 | + |
| 171 | + def __init__(self): |
| 172 | + if 'TED_CONFIG' not in os.environ: |
| 173 | + raise TEDException(skip=True, |
| 174 | + msg='There is no TED_CONFIG environment variable to configure the environment') |
| 175 | + self.config_file = os.path.expanduser(os.getenv('TED_CONFIG')) |
| 176 | + if not os.path.exists(self.config_file): |
| 177 | + raise TEDException(skip=True, msg='Configuration from TED_CONFIG environment variable is not readable or ' |
| 178 | + 'does not exist') |
| 179 | + with open(self.config_file) as C: |
| 180 | + self.config = json.load(C) |
| 181 | + if 'db' in self.config: |
| 182 | + self.admin_dsn = self.config['db']['admin_dsn'] |
| 183 | + self.user_dsn = self.config['db']['user_dsn'] |
| 184 | + self.assert_db() |
| 185 | + |
| 186 | + def teardown(self): |
| 187 | + if self._admin_conn is not None: |
| 188 | + self._admin_conn.close() |
| 189 | + |
| 190 | + def assert_db(self): |
| 191 | + user_dsn_info: psycopg2.extensions.ConnectionInfo = psycopg2.extensions.parse_dsn(self.user_dsn) |
| 192 | + role = user_dsn_info['user'] |
| 193 | + password = user_dsn_info['password'] |
| 194 | + schema = self.config['db']['user_force_schema'] if 'user_force_schema' in self.config['db'] else role |
| 195 | + self._admin_conn = psycopg2.connect(self.config['db']['admin_dsn']) |
| 196 | + with self._admin_conn.cursor() as cur: |
| 197 | + cur.execute("SELECT COUNT(rolname) FROM pg_roles WHERE rolname = %(role_name)s;", |
| 198 | + {'role_name': role}) |
| 199 | + role_count = cur.fetchone() |
| 200 | + if role_count[0] == 0: |
| 201 | + cur.execute( |
| 202 | + psycopg2.sql.SQL('CREATE ROLE {} ENCRYPTED PASSWORD %(password)s LOGIN').format( |
| 203 | + psycopg2.sql.Identifier(role)), |
| 204 | + {'password': password}) |
| 205 | + self._admin_conn.commit() |
| 206 | + cur.execute(psycopg2.sql.SQL('CREATE SCHEMA IF NOT EXISTS {} AUTHORIZATION {}').format( |
| 207 | + psycopg2.sql.Identifier(schema), |
| 208 | + psycopg2.sql.Identifier(role))) |
| 209 | + self._admin_conn.commit() |
| 210 | + cur.execute(psycopg2.sql.SQL('ALTER ROLE {} SET search_path TO {}').format( |
| 211 | + psycopg2.sql.Identifier(role), |
| 212 | + psycopg2.sql.Identifier(schema))) |
| 213 | + self._admin_conn.commit() |
| 214 | + |
| 215 | + |
| 216 | +@pytest.fixture(scope='session', autouse=False) |
| 217 | +def ted() -> TED: |
| 218 | + """Test Environment on Demand |
| 219 | +
|
| 220 | + Read a config file to establish a class containing information about the test environment to be injected into |
| 221 | + all tests that require one. |
| 222 | + """ |
| 223 | + try: |
| 224 | + ted = TED() |
| 225 | + yield ted |
| 226 | + LOGGER.info('Teardown') |
| 227 | + except TEDException as te: |
| 228 | + if te.skip: |
| 229 | + pytest.skip(msg=te.msg) |
| 230 | + |
| 231 | + |
| 232 | +@pytest.fixture(scope='module') |
| 233 | +def ted_client(ted): |
| 234 | + ted.assert_db() |
| 235 | + app = create_app({'TESTING': True, |
| 236 | + 'SQLALCHEMY_DATABASE_URI': ted.config['db']['user_dsn']}) |
| 237 | + with app.app_context(): |
| 238 | + upgrade(directory=os.path.join(os.path.dirname(__file__), '..', 'migrations')) |
| 239 | + db.create_all() |
| 240 | + with app.test_client() as client: |
| 241 | + yield client |
0 commit comments