197 lines
5.5 KiB
Python
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)
|