Add upgrade test coverage
Adding additional test upgrade coverage. This implementation tests things like the is_migration_needed flag. In the future we can also add support for MySQL functional coverage as well. Change-Id: I9386d1bfbfee1fe8ff41859520cdbe94381ee3fd
This commit is contained in:
parent
d90691f3a2
commit
f5a034272d
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[alembic]
|
[alembic]
|
||||||
# path to migration scripts
|
# path to migration scripts
|
||||||
script_location = alembic
|
script_location = %(here)s/alembic
|
||||||
|
|
||||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||||
# Uncomment the line below if you want the files to be prepended with date and time
|
# Uncomment the line below if you want the files to be prepended with date and time
|
||||||
|
@ -42,12 +42,12 @@ def run_migrations_offline() -> None:
|
|||||||
script output.
|
script output.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
url = config.get_main_option("sqlalchemy.url")
|
url = config.get_main_option('sqlalchemy.url')
|
||||||
context.configure(
|
context.configure(
|
||||||
url=url,
|
url=url,
|
||||||
target_metadata=target_metadata,
|
target_metadata=target_metadata,
|
||||||
literal_binds=True,
|
literal_binds=True,
|
||||||
dialect_opts={"paramstyle": "named"},
|
dialect_opts={'paramstyle': 'named'},
|
||||||
)
|
)
|
||||||
|
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
@ -61,16 +61,32 @@ def run_migrations_online() -> None:
|
|||||||
and associate a connection with the context.
|
and associate a connection with the context.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
connectable = engine_from_config(
|
connectable = config.attributes.get('connection', None)
|
||||||
config.get_section(config.config_ini_section),
|
if connectable is None:
|
||||||
prefix="sqlalchemy.",
|
# only create Engine if we don't have a Connection from the outside
|
||||||
poolclass=pool.NullPool,
|
connectable = engine_from_config(
|
||||||
)
|
config.get_section(config.config_ini_section),
|
||||||
|
prefix='sqlalchemy.',
|
||||||
|
poolclass=pool.NullPool,
|
||||||
|
)
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
render_as_batch=True,
|
||||||
|
transactional_ddl=True,
|
||||||
|
transaction_per_migration=True,
|
||||||
|
)
|
||||||
|
|
||||||
with connectable.connect() as connection:
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
else:
|
||||||
context.configure(
|
context.configure(
|
||||||
connection=connection, target_metadata=target_metadata,
|
connection=connectable,
|
||||||
transactional_ddl=True, transaction_per_migration=True
|
target_metadata=target_metadata,
|
||||||
|
render_as_batch=True,
|
||||||
|
transactional_ddl=True,
|
||||||
|
transaction_per_migration=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
|
175
designate/tests/test_storage/test_migration.py
Normal file
175
designate/tests/test_storage/test_migration.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# 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.
|
||||||
|
# Based on Nova's test_migrations.py
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from alembic import command as alembic_api
|
||||||
|
from alembic import config as alembic_config
|
||||||
|
from alembic import script as alembic_script
|
||||||
|
from oslo_db.sqlalchemy import enginefacade
|
||||||
|
from oslo_db.sqlalchemy import test_fixtures
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from designate.storage import sqlalchemy
|
||||||
|
from designate.storage.sqlalchemy.alembic import legacy_utils
|
||||||
|
from designate import tests
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
ALEMBIC_PATH = os.path.join(
|
||||||
|
os.path.dirname(sqlalchemy.__file__), 'alembic.ini'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DesignateMigrationsWalk(
|
||||||
|
test_fixtures.OpportunisticDBTestMixin, tests.TestCase,
|
||||||
|
):
|
||||||
|
# Migrations can take a long time, particularly on underpowered CI nodes.
|
||||||
|
# Give them some breathing room.
|
||||||
|
TIMEOUT_SCALING_FACTOR = 4
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.engine = enginefacade.writer.get_engine()
|
||||||
|
self.config = alembic_config.Config(ALEMBIC_PATH)
|
||||||
|
self.init_version = 'c9f427f7180a'
|
||||||
|
|
||||||
|
def _migrate_up(self, connection, revision):
|
||||||
|
if revision == self.init_version: # no tests for the initial revision
|
||||||
|
alembic_api.upgrade(self.config, revision)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.assertIsNotNone(
|
||||||
|
getattr(self, '_check_%s' % revision, None),
|
||||||
|
(
|
||||||
|
'DB Migration %s does not have a test; you must add one'
|
||||||
|
) % revision,
|
||||||
|
)
|
||||||
|
|
||||||
|
pre_upgrade = getattr(self, '_pre_upgrade_%s' % revision, None)
|
||||||
|
if pre_upgrade:
|
||||||
|
pre_upgrade(connection)
|
||||||
|
|
||||||
|
alembic_api.upgrade(self.config, revision)
|
||||||
|
|
||||||
|
post_upgrade = getattr(self, '_check_%s' % revision, None)
|
||||||
|
if post_upgrade:
|
||||||
|
post_upgrade(connection)
|
||||||
|
|
||||||
|
def _check_867a331ce1fc(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_d9a1883e93e9(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_bfcfc4a07487(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_f9f969f9d85e(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_a69b45715cc1(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_0bcf910ea823(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_d04819112169(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_304d41c3847a(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_15b34ff3ecb8(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_7977deaa5167(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_93a00a815f07(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_b8999fd10721(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_91eb1eb7c882(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_e5e2199ed76e(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_b20189fd288e(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_a005af3aa38e(self, connection):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_single_base_revision(self):
|
||||||
|
script = alembic_script.ScriptDirectory.from_config(self.config)
|
||||||
|
self.assertEqual(1, len(script.get_bases()))
|
||||||
|
|
||||||
|
def test_walk_versions(self):
|
||||||
|
with self.engine.begin() as connection:
|
||||||
|
self.config.attributes['connection'] = connection
|
||||||
|
script = alembic_script.ScriptDirectory.from_config(self.config)
|
||||||
|
revisions = [x.revision for x in script.walk_revisions()]
|
||||||
|
|
||||||
|
# for some reason, 'walk_revisions' gives us the revisions in
|
||||||
|
# reverse chronological order, so we have to invert this
|
||||||
|
revisions.reverse()
|
||||||
|
self.assertEqual(revisions[0], self.init_version)
|
||||||
|
|
||||||
|
for revision in revisions:
|
||||||
|
LOG.info('Testing revision %s', revision)
|
||||||
|
self._migrate_up(connection, revision)
|
||||||
|
|
||||||
|
def test_is_migration_needed(self):
|
||||||
|
with self.engine.begin() as connection:
|
||||||
|
self.config.attributes['connection'] = connection
|
||||||
|
script = alembic_script.ScriptDirectory.from_config(self.config)
|
||||||
|
revisions = [x.revision for x in script.walk_revisions()]
|
||||||
|
|
||||||
|
# for some reason, 'walk_revisions' gives us the revisions in
|
||||||
|
# reverse chronological order, so we have to invert this
|
||||||
|
revisions.reverse()
|
||||||
|
self.assertEqual(revisions[0], self.init_version)
|
||||||
|
|
||||||
|
for revision in revisions:
|
||||||
|
LOG.info('Testing revision %s', revision)
|
||||||
|
|
||||||
|
# Let's stop after the first revision
|
||||||
|
# without is_migration_needed.
|
||||||
|
if revision == 'b20189fd288e':
|
||||||
|
break
|
||||||
|
self._migrate_up(connection, revision)
|
||||||
|
|
||||||
|
# Reset alembic.
|
||||||
|
alembic_api.stamp(self.config, None)
|
||||||
|
|
||||||
|
# We should only need the last few revisions.
|
||||||
|
with mock.patch.object(legacy_utils,
|
||||||
|
'is_migration_needed',
|
||||||
|
return_value=False):
|
||||||
|
for revision in revisions:
|
||||||
|
LOG.info('Testing revision %s', revision)
|
||||||
|
self._migrate_up(connection, revision)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMigrationsWalkSQLite(
|
||||||
|
DesignateMigrationsWalk,
|
||||||
|
test_fixtures.OpportunisticDBTestMixin,
|
||||||
|
):
|
||||||
|
pass
|
@ -19,3 +19,4 @@ zake>=0.1.6 # Apache-2.0
|
|||||||
doc8>=0.6.0 # Apache-2.0
|
doc8>=0.6.0 # Apache-2.0
|
||||||
Pygments>=2.2.0 # BSD license
|
Pygments>=2.2.0 # BSD license
|
||||||
pymemcache!=1.3.0,>=1.2.9 # Apache 2.0 License
|
pymemcache!=1.3.0,>=1.2.9 # Apache 2.0 License
|
||||||
|
PyMySQL>=0.8.0 # MIT License
|
||||||
|
Loading…
Reference in New Issue
Block a user