Added base for functional tests

This change allows to run functional tests with in-memory sqlite DB.
Tests will use DB schema memoization. Here is the algorithm:
1. Statically create global DB fixture
2. Run migration script (only once)
3. Copy DDL and save it in the global state
4. Run DDL to initialize DB for each test

This algorithm allows to skip long-running migration scripts and initialize
database in one step.

Based on openstack/ironic tests.

Co-Authored-By: Ruslan Kamaldinov <rkamaldinov@mirantis.com>

Change-Id: If80abd1b616548495d3466037aef54ade8f1614d
This commit is contained in:
Nikita Konovalov 2014-01-27 19:19:37 +04:00 committed by Ruslan Kamaldinov
parent 4f062f129e
commit 5908bb7cc4
3 changed files with 104 additions and 19 deletions

View File

@ -21,9 +21,6 @@ Tests for `storyboard` module.
import copy
import json
from mock import patch
from storyboard.tests.api.utils import FakeSession
from storyboard.tests import base
SAMPLE_STORY = {
@ -38,19 +35,11 @@ SAMPLE_STORY_REQUEST = {
class TestStories(base.FunctionalTest):
@patch("storyboard.openstack.common.db.sqlalchemy.session.get_session")
def test_stories_endpoint(self, session_mock):
fake_session = FakeSession()
session_mock.return_value = fake_session
def test_stories_endpoint(self):
response = self.get_json(path="/stories")
self.assertEqual([], response)
@patch("storyboard.openstack.common.db.sqlalchemy.session.get_session")
def test_create(self, session_mock):
fake_session = FakeSession()
session_mock.return_value = fake_session
def test_create(self):
response = self.post_json("/stories", SAMPLE_STORY_REQUEST)
story = json.loads(response.body)
@ -59,11 +48,7 @@ class TestStories(base.FunctionalTest):
self.assertEqual(story["title"], SAMPLE_STORY["title"])
self.assertEqual(story["description"], SAMPLE_STORY["description"])
@patch("storyboard.openstack.common.db.sqlalchemy.session.get_session")
def test_update(self, session_mock):
fake_session = FakeSession()
session_mock.return_value = fake_session
def test_update(self):
response = self.post_json("/stories", SAMPLE_STORY_REQUEST)
old_story = json.loads(response.body)

View File

@ -21,13 +21,32 @@ import fixtures
from oslo.config import cfg
import pecan
import pecan.testing
from storyboard.openstack.common import lockutils
from storyboard.openstack.common import log as logging
import testtools
from storyboard.tests.db.db_fixture import Database
cfg.set_defaults(lockutils.util_opts, lock_path='/tmp')
CONF = cfg.CONF
_TRUE_VALUES = ('true', '1', 'yes')
_DB_CACHE = None
test_opts = [
cfg.StrOpt('sqlite_clean_db',
default='clean.sqlite',
help='File name of clean sqlite db')]
CONF.register_opts(test_opts)
CONF.import_opt('connection',
'storyboard.openstack.common.db.sqlalchemy.session',
group='database')
CONF.import_opt('sqlite_db',
'storyboard.openstack.common.db.sqlalchemy.session')
logging.setup('storyboard')
@ -90,10 +109,19 @@ class FunctionalTest(TestCase):
def setUp(self):
super(FunctionalTest, self).setUp()
self.app = self._make_app()
CONF.set_override("connection", "sqlite://", "database")
self.init_db_cache()
self.app = self._make_app()
self.addCleanup(self._reset_pecan)
@lockutils.synchronized("storyboard", "db_init", True)
def init_db_cache(self):
global _DB_CACHE
if not _DB_CACHE:
_DB_CACHE = Database()
self.useFixture(_DB_CACHE)
def _make_app(self):
config = {
'app': {

View File

@ -0,0 +1,72 @@
# Copyright (c) 2014 Mirantis Inc.
#
# 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.
import eventlet
eventlet.monkey_patch(os=False)
import os
import warnings
from alembic import context, script, environment
from alembic import config as alembic_config
import fixtures
from oslo.config import cfg
from sqlalchemy.exc import SADeprecationWarning
from storyboard.openstack.common.db.sqlalchemy import session
CONF = cfg.CONF
warnings.simplefilter("ignore", SADeprecationWarning)
class Database(fixtures.Fixture):
def __init__(self):
config = alembic_config.Config(os.path.join(
os.path.dirname(__file__),
"../../db/migration/alembic.ini"))
config.set_main_option(
'script_location',
'storyboard.db.migration:alembic_migrations')
config.storyboard_config = CONF
self.engine = session.get_engine()
conn = self.engine.connect()
self._sync_db(config, conn)
self._DB = "".join(line for line in conn.connection.iterdump())
self.engine.dispose()
def _sync_db(self, config, conn):
_script = script.ScriptDirectory.from_config(config)
def upgrade(rev, context):
return _script._upgrade_revs('head', rev)
with environment.EnvironmentContext(
config,
_script,
fn=upgrade,
):
context.configure(connection=conn)
with context.begin_transaction():
context.run_migrations()
def setUp(self):
super(Database, self).setUp()
conn = self.engine.connect()
conn.connection.executescript(self._DB)
self.addCleanup(self.engine.dispose)