valet/valet/tests/api/conftest.py
2017-05-01 19:10:48 -05:00

197 lines
5.5 KiB
Python

#
# Copyright 2014-2017 AT&T Intellectual Property
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from copy import deepcopy
import os
from pecan import conf
from pecan import configuration
from pecan.testing import load_test_app
import pytest
# TODO(JD): Make this work for music or write a separate test.
from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
import subprocess
from valet.api.db import models as _db
PARAMS = 'charset=utf8'
DBNAME = 'valettest'
BIND = 'mysql+pymysql://root:password@127.0.0.1'
def config_file():
here = os.path.abspath(os.path.dirname(__file__))
return os.path.join(here, 'config.py')
@pytest.fixture(scope='session')
def app(request):
config = configuration.conf_from_file(config_file()).to_dict()
# Add the appropriate connection string to the app config.
config['sqlalchemy'] = {
'url': '%s/%s?%s' % (BIND, DBNAME, PARAMS),
'encoding': 'utf-8',
'poolclass': NullPool
}
# Set up a fake app
app = TestApp(load_test_app(config))
return app
@pytest.fixture(scope='session')
def connection(app, request):
"""Session-wide test database."""
# Connect and create the temporary database
print("=" * 80)
print("CREATING TEMPORARY DATABASE FOR TESTS")
print("=" * 80)
subprocess.call(['mysqladmin', '-f', '-uroot', '-ppassword', 'drop', DBNAME])
subprocess.call(['mysqladmin', '-f', '-uroot', '-ppassword', 'create', DBNAME])
# Bind and create the database tables
_db.clear()
engine_url = '%s/%s?%s' % (BIND, DBNAME, PARAMS)
db_engine = create_engine(
engine_url,
encoding='utf-8',
poolclass=NullPool)
# AKA models.start()
_db.Session.bind = db_engine
_db.metadata.bind = _db.Session.bind
_db.Base.metadata.create_all(db_engine)
_db.commit()
_db.clear()
# connection = db_engine.connect()
def teardown():
_db.Base.metadata.drop_all(db_engine)
request.addfinalizer(teardown)
# Slap our test app on it
_db.app = app
return _db
@pytest.fixture(scope='function')
def session(connection, request):
"""Creates a new database session for a test."""
_config = configuration.conf_from_file(config_file()).to_dict()
config = deepcopy(_config)
# Add the appropriate connection string to the app config.
config['sqlalchemy'] = {
'url': '%s/%s?%s' % (BIND, DBNAME, PARAMS),
'encoding': 'utf-8',
'poolclass': NullPool
}
connection.start()
def teardown():
from sqlalchemy.engine import reflection
# Tear down and dispose the DB binding
connection.clear()
# start a transaction
engine = conf.sqlalchemy.engine
conn = engine.connect()
trans = conn.begin()
inspector = reflection.Inspector.from_engine(engine)
# gather all data first before dropping anything.
# some DBs lock after things have been dropped in
# a transaction.
conn.execute("SET FOREIGN_KEY_CHECKS = 0")
table_names = inspector.get_table_names()
for table in table_names:
conn.execute("TRUNCATE TABLE %s" % table)
conn.execute("SET FOREIGN_KEY_CHECKS = 1")
trans.commit()
conn.close()
request.addfinalizer(teardown)
return connection
class TestApp(object):
""" A controller test starts a database transaction and creates a fake WSGI app. """
__headers__ = {}
def __init__(self, app):
self.app = app
def _do_request(self, url, method='GET', **kwargs):
methods = {
'GET': self.app.get,
'POST': self.app.post,
'POSTJ': self.app.post_json,
'PUT': self.app.put,
'DELETE': self.app.delete
}
kwargs.setdefault('headers', {}).update(self.__headers__)
return methods.get(method, self.app.get)(str(url), **kwargs)
def post_json(self, url, **kwargs):
""" note:
@param (string) url - The URL to emulate a POST request to
@returns (paste.fixture.TestResponse)
"""
return self._do_request(url, 'POSTJ', **kwargs)
def post(self, url, **kwargs):
""" note:
@param (string) url - The URL to emulate a POST request to
@returns (paste.fixture.TestResponse)
"""
return self._do_request(url, 'POST', **kwargs)
def get(self, url, **kwargs):
""" note:
@param (string) url - The URL to emulate a GET request to
@returns (paste.fixture.TestResponse)
"""
return self._do_request(url, 'GET', **kwargs)
def put(self, url, **kwargs):
""" note:
@param (string) url - The URL to emulate a PUT request to
@returns (paste.fixture.TestResponse)
"""
return self._do_request(url, 'PUT', **kwargs)
def delete(self, url, **kwargs):
""" note:
@param (string) url - The URL to emulate a DELETE request to
@returns (paste.fixture.TestResponse)
"""
return self._do_request(url, 'DELETE', **kwargs)