Improve database creation for testing
Create MySQL database before starting tests and cleanup after that. MySQL database is able to work in standalone mode where establish connection using a Unix domain socket. This patch makes it possible to perform functional tests with `tox -e py27-func-mysql`. If an enviroment variable REFSTACK_TEST_MYSQL_URL is set then tests will run with it and a database will not be created. Update test requirements. Change-Id: I0cdba19701bbf06109e78f78f275038caeecd881
This commit is contained in:
parent
8ea0a1ace9
commit
16affe2d12
@ -1,4 +1,4 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./refstack -s ./refstack/tests/unit $LISTOPT $IDOPTION
|
||||
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./refstack -s ./refstack/tests $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
||||
|
@ -15,7 +15,7 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
# commands auto generated by Alembic - please adjust!
|
||||
op.create_table(
|
||||
'test',
|
||||
sa.Column('updated_at', sa.DateTime()),
|
||||
@ -56,12 +56,12 @@ def upgrade():
|
||||
sa.UniqueConstraint('test_id', 'name'),
|
||||
sa.UniqueConstraint('test_id', 'uid')
|
||||
)
|
||||
### end Alembic commands ###
|
||||
# end Alembic commands
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
# commands auto generated by Alembic - please adjust!
|
||||
op.drop_table('results')
|
||||
op.drop_table('meta')
|
||||
op.drop_table('test')
|
||||
### end Alembic commands ###
|
||||
# end Alembic commands
|
||||
|
@ -29,9 +29,7 @@ CONF = cfg.CONF
|
||||
|
||||
_FACADE = None
|
||||
|
||||
_DEFAULT_SQL_CONNECTION = 'sqlite://'
|
||||
db_options.set_defaults(cfg.CONF,
|
||||
connection=_DEFAULT_SQL_CONNECTION)
|
||||
db_options.set_defaults(cfg.CONF)
|
||||
|
||||
|
||||
def _create_facade_lazily():
|
||||
|
@ -14,26 +14,28 @@
|
||||
# under the License.
|
||||
|
||||
"""Base classes for API tests."""
|
||||
import inspect
|
||||
import os
|
||||
|
||||
import alembic
|
||||
import alembic.config
|
||||
from oslo_config import cfg
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.exc
|
||||
from unittest import TestCase
|
||||
from webtest import TestApp
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslotest import base
|
||||
import pecan.testing
|
||||
from sqlalchemy.engine import reflection
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.schema import (
|
||||
MetaData,
|
||||
Table,
|
||||
DropTable,
|
||||
ForeignKeyConstraint,
|
||||
DropConstraint,
|
||||
)
|
||||
from testtools import testcase
|
||||
|
||||
import refstack
|
||||
from refstack.api import app
|
||||
|
||||
CONF = cfg.CONF
|
||||
from refstack.db import migration
|
||||
|
||||
|
||||
class FunctionalTest(TestCase):
|
||||
class FunctionalTest(base.BaseTestCase):
|
||||
|
||||
"""Functional test case.
|
||||
"""Base class for functional test case.
|
||||
|
||||
Used for functional tests where you need to test your.
|
||||
literal application and its integration with the framework.
|
||||
@ -41,60 +43,75 @@ class FunctionalTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Test setup."""
|
||||
class TestConfig(object):
|
||||
app = {
|
||||
super(FunctionalTest, self).setUp()
|
||||
|
||||
# Skip integration/functional tests
|
||||
# if database has not been created
|
||||
self.connection = os.environ.get("REFSTACK_TEST_MYSQL_URL")
|
||||
if self.connection is None:
|
||||
raise testcase.TestSkipped("Database connection url was not found")
|
||||
|
||||
self.config = {
|
||||
'app': {
|
||||
'root': 'refstack.api.controllers.root.RootController',
|
||||
'modules': ['refstack.api'],
|
||||
'static_root': '%(confdir)s/public',
|
||||
'template_path': '%(confdir)s/${package}/templates',
|
||||
}
|
||||
}
|
||||
self.config_fixture = config_fixture.Config()
|
||||
self.CONF = self.useFixture(self.config_fixture).conf
|
||||
self.CONF.set_override('connection',
|
||||
self.connection,
|
||||
'database')
|
||||
|
||||
test_config = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
'refstack.test.conf'
|
||||
)
|
||||
os.environ['REFSTACK_OSLO_CONFIG'] = test_config
|
||||
self.project_path = os.path.abspath(
|
||||
os.path.join(inspect.getabsfile(refstack), '..', '..'))
|
||||
self.app = TestApp(app.setup_app(TestConfig()))
|
||||
self.prepare_test_db()
|
||||
self.migrate_test_db()
|
||||
self.app = pecan.testing.load_test_app(self.config)
|
||||
|
||||
self.drop_all_tables_and_constraints()
|
||||
migration.upgrade('head')
|
||||
|
||||
def tearDown(self):
|
||||
"""Test teardown."""
|
||||
super(FunctionalTest, self).tearDown()
|
||||
pecan.set_config({}, overwrite=True)
|
||||
self.app.reset()
|
||||
|
||||
def prepare_test_db(self):
|
||||
"""Create/clear test database."""
|
||||
db_url = CONF.database.connection
|
||||
db_name = db_url.split('/')[-1]
|
||||
short_db_url = '/'.join(db_url.split('/')[0:-1])
|
||||
try:
|
||||
engine = sa.create_engine(db_url)
|
||||
conn = engine.connect()
|
||||
conn.execute('commit')
|
||||
conn.execute('drop database %s' % db_name)
|
||||
conn.close()
|
||||
except sqlalchemy.exc.OperationalError:
|
||||
pass
|
||||
finally:
|
||||
engine = sa.create_engine('/'.join((short_db_url, 'mysql')))
|
||||
conn = engine.connect()
|
||||
conn.execute('commit')
|
||||
conn.execute('create database %s' % db_name)
|
||||
conn.close()
|
||||
def drop_all_tables_and_constraints(self):
|
||||
"""Drop tables and cyclical constraints between tables"""
|
||||
engine = create_engine(self.connection)
|
||||
conn = engine.connect()
|
||||
trans = conn.begin()
|
||||
|
||||
def migrate_test_db(self):
|
||||
"""Apply migrations to test database."""
|
||||
alembic_cfg = alembic.config.Config()
|
||||
alembic_cfg.set_main_option(
|
||||
"script_location",
|
||||
os.path.join(self.project_path, 'refstack', 'db',
|
||||
'migrations', 'alembic')
|
||||
)
|
||||
alembic_cfg.set_main_option("sqlalchemy.url",
|
||||
CONF.database.connection)
|
||||
alembic.command.upgrade(alembic_cfg, 'head')
|
||||
inspector = reflection.Inspector.from_engine(engine)
|
||||
metadata = MetaData()
|
||||
|
||||
tbs = []
|
||||
all_fks = []
|
||||
|
||||
try:
|
||||
for table_name in inspector.get_table_names():
|
||||
fks = []
|
||||
for fk in inspector.get_foreign_keys(table_name):
|
||||
if not fk['name']:
|
||||
continue
|
||||
fks.append(
|
||||
ForeignKeyConstraint((), (), name=fk['name']))
|
||||
|
||||
t = Table(table_name, metadata, *fks)
|
||||
tbs.append(t)
|
||||
all_fks.extend(fks)
|
||||
|
||||
for fkc in all_fks:
|
||||
conn.execute(DropConstraint(fkc))
|
||||
|
||||
for table in tbs:
|
||||
conn.execute(DropTable(table))
|
||||
|
||||
trans.commit()
|
||||
trans.close()
|
||||
conn.close()
|
||||
except:
|
||||
trans.rollback()
|
||||
conn.close()
|
||||
raise
|
||||
|
||||
def get_json(self, url, headers=None, extra_environ=None,
|
||||
status=None, expect_errors=False, **params):
|
||||
|
@ -1,2 +0,0 @@
|
||||
[DEFAULT]
|
||||
sql_connection = mysql://root:passw0rd@127.0.0.1/refstack
|
37
setup-mysql-tests.sh
Executable file
37
setup-mysql-tests.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
wait_for_line () {
|
||||
while read line
|
||||
do
|
||||
echo "$line" | grep -q "$1" && break
|
||||
done < "$2"
|
||||
# Read the fifo for ever otherwise process would block
|
||||
cat "$2" >/dev/null &
|
||||
}
|
||||
|
||||
# If test DB url is provided, run tests with it
|
||||
if [[ "$REFSTACK_TEST_MYSQL_URL" ]]
|
||||
then
|
||||
$*
|
||||
exit $?
|
||||
fi
|
||||
# Else setup mysql base for tests.
|
||||
# Start MySQL process for tests
|
||||
MYSQL_DATA=`mktemp -d /tmp/refstack-mysql-XXXXX`
|
||||
mkfifo ${MYSQL_DATA}/out
|
||||
# On systems like Fedora here's where mysqld can be found
|
||||
PATH=$PATH:/usr/libexec
|
||||
mysqld --no-defaults --datadir=${MYSQL_DATA} --pid-file=${MYSQL_DATA}/mysql.pid --socket=${MYSQL_DATA}/mysql.socket --skip-networking --skip-grant-tables &> ${MYSQL_DATA}/out &
|
||||
# Wait for MySQL to start listening to connections
|
||||
wait_for_line "mysqld: ready for connections." ${MYSQL_DATA}/out
|
||||
export REFSTACK_TEST_MYSQL_URL="mysql://root@localhost/test?unix_socket=${MYSQL_DATA}/mysql.socket&charset=utf8"
|
||||
mysql --no-defaults -S ${MYSQL_DATA}/mysql.socket -e 'CREATE DATABASE test;'
|
||||
|
||||
# Yield execution to venv command
|
||||
$*
|
||||
|
||||
# Cleanup after tests
|
||||
ret=$?
|
||||
kill $(jobs -p)
|
||||
rm -rf "${MYSQL_DATA}"
|
||||
exit $ret
|
@ -1,7 +1,8 @@
|
||||
pep8==1.4.5
|
||||
pyflakes>=0.7.2,<0.7.4
|
||||
flake8==2.0
|
||||
pep8==1.5.7
|
||||
pyflakes==0.8.1
|
||||
flake8==2.2.4
|
||||
oslotest>=1.2.0 # Apache-2.0
|
||||
python-subunit>=0.0.18
|
||||
testrepository>=0.0.18
|
||||
testtools>=0.9.34
|
||||
mysqlclient
|
||||
mysqlclient
|
||||
|
18
tox.ini
18
tox.ini
@ -18,17 +18,13 @@ deps = -r{toxinidir}/requirements.txt
|
||||
commands = python setup.py testr --testr-args='{posargs}'
|
||||
distribute = false
|
||||
|
||||
[testenv:func]
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
LANG=en_US.UTF-8
|
||||
LANGUAGE=en_US:en
|
||||
LC_ALL=C
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python -m unittest discover ./refstack/tests/api
|
||||
distribute = false
|
||||
[testenv:py27-func-mysql]
|
||||
basepython = python2.7
|
||||
# Integration/functional tests
|
||||
# must not be run in parallel (--concurrency=1),
|
||||
# because each of these tests
|
||||
# require cleanup of database
|
||||
commands = {toxinidir}/setup-mysql-tests.sh python setup.py testr --slowest --testr-args='{posargs:--concurrency=1}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands =
|
||||
|
Loading…
Reference in New Issue
Block a user