nova/nova/tests/unit/db/test_migration_utils.py
Victor Sergeyev b930fb3a6b Use opportunistic approach for migration testing
Refactored migration tests to use OpportunisticTestCase, removed
unused code, BaseMigrationTestCase class and ``test_migrations.conf`` file.

The main feature of this approach is to create a new database with
random name for each migration test. This will avoid migration tests of
race conditions and reduce tests intersection. After this change,
``openstack_citest`` user credentials will be used only for initial connection
to the database.  TestMigrationUtils class was refactored also, because
BaseMigrationTestCase was removed.

Co-Authored-By: Roman Podoliaka <rpodolyaka@mirantis.com>

Change-Id: I5c9aaa56e5041b919b1e96a19e0395c5e03b727a
2014-11-20 11:09:44 +02:00

221 lines
8.8 KiB
Python

# Copyright (c) 2013 Boris Pavlovic (boris@pavlovic.me).
# All Rights Reserved.
#
# 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 uuid
from oslo.db.sqlalchemy import test_base
from oslo.db.sqlalchemy import utils as oslodbutils
import sqlalchemy
from sqlalchemy import Integer, String
from sqlalchemy import MetaData, Table, Column
from sqlalchemy.exc import NoSuchTableError
from sqlalchemy import sql
from sqlalchemy.types import UserDefinedType
from nova.db.sqlalchemy import api as db
from nova.db.sqlalchemy import utils
from nova import exception
SA_VERSION = tuple(map(int, sqlalchemy.__version__.split('.')))
class CustomType(UserDefinedType):
"""Dummy column type for testing unsupported types."""
def get_col_spec(self):
return "CustomType"
class TestMigrationUtilsSQLite(test_base.DbTestCase):
"""Class for testing utils that are used in db migrations."""
def setUp(self):
super(TestMigrationUtilsSQLite, self).setUp()
self.meta = MetaData(bind=self.engine)
def test_delete_from_select(self):
table_name = "__test_deletefromselect_table__"
uuidstrs = []
for unused in range(10):
uuidstrs.append(uuid.uuid4().hex)
conn = self.engine.connect()
test_table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True,
nullable=False, autoincrement=True),
Column('uuid', String(36), nullable=False))
test_table.create()
# Add 10 rows to table
for uuidstr in uuidstrs:
ins_stmt = test_table.insert().values(uuid=uuidstr)
conn.execute(ins_stmt)
# Delete 4 rows in one chunk
column = test_table.c.id
query_delete = sql.select([column],
test_table.c.id < 5).order_by(column)
delete_statement = utils.DeleteFromSelect(test_table,
query_delete, column)
result_delete = conn.execute(delete_statement)
# Verify we delete 4 rows
self.assertEqual(result_delete.rowcount, 4)
query_all = sql.select([test_table])\
.where(test_table.c.uuid.in_(uuidstrs))
rows = conn.execute(query_all).fetchall()
# Verify we still have 6 rows in table
self.assertEqual(len(rows), 6)
def test_check_shadow_table(self):
table_name = 'test_check_shadow_table'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer),
Column('c', String(256)))
table.create()
# check missing shadow table
self.assertRaises(NoSuchTableError,
utils.check_shadow_table, self.engine, table_name)
shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, self.meta,
Column('id', Integer),
Column('a', Integer))
shadow_table.create()
# check missing column
self.assertRaises(exception.NovaException,
utils.check_shadow_table, self.engine, table_name)
# check when all is ok
c = Column('c', String(256))
shadow_table.create_column(c)
self.assertTrue(utils.check_shadow_table(self.engine, table_name))
# check extra column
d = Column('d', Integer)
shadow_table.create_column(d)
self.assertRaises(exception.NovaException,
utils.check_shadow_table, self.engine, table_name)
def test_check_shadow_table_different_types(self):
table_name = 'test_check_shadow_table_different_types'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer))
table.create()
shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', String(256)))
shadow_table.create()
self.assertRaises(exception.NovaException,
utils.check_shadow_table, self.engine, table_name)
@test_base.backend_specific('sqlite')
def test_check_shadow_table_with_unsupported_sqlite_type(self):
table_name = 'test_check_shadow_table_with_unsupported_sqlite_type'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer),
Column('c', CustomType))
table.create()
shadow_table = Table(db._SHADOW_TABLE_PREFIX + table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer),
Column('c', CustomType))
shadow_table.create()
self.assertTrue(utils.check_shadow_table(self.engine, table_name))
def test_create_shadow_table_by_table_instance(self):
table_name = 'test_create_shadow_table_by_table_instance'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer),
Column('b', String(256)))
table.create()
utils.create_shadow_table(self.engine, table=table)
self.assertTrue(utils.check_shadow_table(self.engine, table_name))
def test_create_shadow_table_by_name(self):
table_name = 'test_create_shadow_table_by_name'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer),
Column('b', String(256)))
table.create()
utils.create_shadow_table(self.engine, table_name=table_name)
self.assertTrue(utils.check_shadow_table(self.engine, table_name))
@test_base.backend_specific('sqlite')
def test_create_shadow_table_not_supported_type(self):
table_name = 'test_create_shadow_table_not_supported_type'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', CustomType))
table.create()
# reflection of custom types has been fixed upstream
if SA_VERSION < (0, 9, 0):
self.assertRaises(oslodbutils.ColumnError,
utils.create_shadow_table,
self.engine, table_name=table_name)
utils.create_shadow_table(self.engine,
table_name=table_name,
a=Column('a', CustomType()))
self.assertTrue(utils.check_shadow_table(self.engine, table_name))
def test_create_shadow_both_table_and_table_name_are_none(self):
self.assertRaises(exception.NovaException,
utils.create_shadow_table, self.engine)
def test_create_shadow_both_table_and_table_name_are_specified(self):
table_name = ('test_create_shadow_both_table_and_table_name_are_'
'specified')
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer))
table.create()
self.assertRaises(exception.NovaException,
utils.create_shadow_table,
self.engine, table=table, table_name=table_name)
def test_create_duplicate_shadow_table(self):
table_name = 'test_create_duplicate_shadow_table'
table = Table(table_name, self.meta,
Column('id', Integer, primary_key=True),
Column('a', Integer))
table.create()
utils.create_shadow_table(self.engine, table_name=table_name)
self.assertRaises(exception.ShadowTableExists,
utils.create_shadow_table,
self.engine, table_name=table_name)
class TestMigrationUtilsPostgreSQL(TestMigrationUtilsSQLite,
test_base.PostgreSQLOpportunisticTestCase):
pass
class TestMigrationUtilsMySQL(TestMigrationUtilsSQLite,
test_base.MySQLOpportunisticTestCase):
pass