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]
|
[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_id_option=--load-list $IDFILE
|
||||||
test_list_option=--list
|
test_list_option=--list
|
||||||
|
@ -15,7 +15,7 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
### commands auto generated by Alembic - please adjust! ###
|
# commands auto generated by Alembic - please adjust!
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'test',
|
'test',
|
||||||
sa.Column('updated_at', sa.DateTime()),
|
sa.Column('updated_at', sa.DateTime()),
|
||||||
@ -56,12 +56,12 @@ def upgrade():
|
|||||||
sa.UniqueConstraint('test_id', 'name'),
|
sa.UniqueConstraint('test_id', 'name'),
|
||||||
sa.UniqueConstraint('test_id', 'uid')
|
sa.UniqueConstraint('test_id', 'uid')
|
||||||
)
|
)
|
||||||
### end Alembic commands ###
|
# end Alembic commands
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
### commands auto generated by Alembic - please adjust! ###
|
# commands auto generated by Alembic - please adjust!
|
||||||
op.drop_table('results')
|
op.drop_table('results')
|
||||||
op.drop_table('meta')
|
op.drop_table('meta')
|
||||||
op.drop_table('test')
|
op.drop_table('test')
|
||||||
### end Alembic commands ###
|
# end Alembic commands
|
||||||
|
@ -29,9 +29,7 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
_FACADE = None
|
_FACADE = None
|
||||||
|
|
||||||
_DEFAULT_SQL_CONNECTION = 'sqlite://'
|
db_options.set_defaults(cfg.CONF)
|
||||||
db_options.set_defaults(cfg.CONF,
|
|
||||||
connection=_DEFAULT_SQL_CONNECTION)
|
|
||||||
|
|
||||||
|
|
||||||
def _create_facade_lazily():
|
def _create_facade_lazily():
|
||||||
|
@ -14,26 +14,28 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Base classes for API tests."""
|
"""Base classes for API tests."""
|
||||||
import inspect
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import alembic
|
from oslo_config import fixture as config_fixture
|
||||||
import alembic.config
|
from oslotest import base
|
||||||
from oslo_config import cfg
|
import pecan.testing
|
||||||
import sqlalchemy as sa
|
from sqlalchemy.engine import reflection
|
||||||
import sqlalchemy.exc
|
from sqlalchemy import create_engine
|
||||||
from unittest import TestCase
|
from sqlalchemy.schema import (
|
||||||
from webtest import TestApp
|
MetaData,
|
||||||
|
Table,
|
||||||
|
DropTable,
|
||||||
|
ForeignKeyConstraint,
|
||||||
|
DropConstraint,
|
||||||
|
)
|
||||||
|
from testtools import testcase
|
||||||
|
|
||||||
import refstack
|
from refstack.db import migration
|
||||||
from refstack.api import app
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
Used for functional tests where you need to test your.
|
||||||
literal application and its integration with the framework.
|
literal application and its integration with the framework.
|
||||||
@ -41,60 +43,75 @@ class FunctionalTest(TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Test setup."""
|
"""Test setup."""
|
||||||
class TestConfig(object):
|
super(FunctionalTest, self).setUp()
|
||||||
app = {
|
|
||||||
|
# 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',
|
'root': 'refstack.api.controllers.root.RootController',
|
||||||
'modules': ['refstack.api'],
|
'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(
|
self.app = pecan.testing.load_test_app(self.config)
|
||||||
os.path.dirname(os.path.realpath(__file__)),
|
|
||||||
'refstack.test.conf'
|
self.drop_all_tables_and_constraints()
|
||||||
)
|
migration.upgrade('head')
|
||||||
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()
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Test teardown."""
|
"""Test teardown."""
|
||||||
|
super(FunctionalTest, self).tearDown()
|
||||||
|
pecan.set_config({}, overwrite=True)
|
||||||
self.app.reset()
|
self.app.reset()
|
||||||
|
|
||||||
def prepare_test_db(self):
|
def drop_all_tables_and_constraints(self):
|
||||||
"""Create/clear test database."""
|
"""Drop tables and cyclical constraints between tables"""
|
||||||
db_url = CONF.database.connection
|
engine = create_engine(self.connection)
|
||||||
db_name = db_url.split('/')[-1]
|
conn = engine.connect()
|
||||||
short_db_url = '/'.join(db_url.split('/')[0:-1])
|
trans = conn.begin()
|
||||||
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 migrate_test_db(self):
|
inspector = reflection.Inspector.from_engine(engine)
|
||||||
"""Apply migrations to test database."""
|
metadata = MetaData()
|
||||||
alembic_cfg = alembic.config.Config()
|
|
||||||
alembic_cfg.set_main_option(
|
tbs = []
|
||||||
"script_location",
|
all_fks = []
|
||||||
os.path.join(self.project_path, 'refstack', 'db',
|
|
||||||
'migrations', 'alembic')
|
try:
|
||||||
)
|
for table_name in inspector.get_table_names():
|
||||||
alembic_cfg.set_main_option("sqlalchemy.url",
|
fks = []
|
||||||
CONF.database.connection)
|
for fk in inspector.get_foreign_keys(table_name):
|
||||||
alembic.command.upgrade(alembic_cfg, 'head')
|
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,
|
def get_json(self, url, headers=None, extra_environ=None,
|
||||||
status=None, expect_errors=False, **params):
|
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,6 +1,7 @@
|
|||||||
pep8==1.4.5
|
pep8==1.5.7
|
||||||
pyflakes>=0.7.2,<0.7.4
|
pyflakes==0.8.1
|
||||||
flake8==2.0
|
flake8==2.2.4
|
||||||
|
oslotest>=1.2.0 # Apache-2.0
|
||||||
python-subunit>=0.0.18
|
python-subunit>=0.0.18
|
||||||
testrepository>=0.0.18
|
testrepository>=0.0.18
|
||||||
testtools>=0.9.34
|
testtools>=0.9.34
|
||||||
|
18
tox.ini
18
tox.ini
@ -18,17 +18,13 @@ deps = -r{toxinidir}/requirements.txt
|
|||||||
commands = python setup.py testr --testr-args='{posargs}'
|
commands = python setup.py testr --testr-args='{posargs}'
|
||||||
distribute = false
|
distribute = false
|
||||||
|
|
||||||
[testenv:func]
|
[testenv:py27-func-mysql]
|
||||||
usedevelop = True
|
basepython = python2.7
|
||||||
install_command = pip install -U {opts} {packages}
|
# Integration/functional tests
|
||||||
setenv = VIRTUAL_ENV={envdir}
|
# must not be run in parallel (--concurrency=1),
|
||||||
LANG=en_US.UTF-8
|
# because each of these tests
|
||||||
LANGUAGE=en_US:en
|
# require cleanup of database
|
||||||
LC_ALL=C
|
commands = {toxinidir}/setup-mysql-tests.sh python setup.py testr --slowest --testr-args='{posargs:--concurrency=1}'
|
||||||
deps = -r{toxinidir}/requirements.txt
|
|
||||||
-r{toxinidir}/test-requirements.txt
|
|
||||||
commands = python -m unittest discover ./refstack/tests/api
|
|
||||||
distribute = false
|
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
commands =
|
commands =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user