From 60a6fe61db9648598ebf5ca9fc198023100c344f Mon Sep 17 00:00:00 2001 From: Joachim Jablon Date: Thu, 23 Jul 2020 12:03:27 +0200 Subject: [PATCH] Disregard migrations older than the schema --- septentrion/core.py | 17 +++++++++++++-- septentrion/migration.py | 8 +++++-- septentrion/utils.py | 13 +++++++++++ tests/unit/test_core.py | 47 ++++++++++++++++++++++++++++++---------- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/septentrion/core.py b/septentrion/core.py index b87095d..6da08c8 100644 --- a/septentrion/core.py +++ b/septentrion/core.py @@ -128,7 +128,9 @@ def get_fixtures_version( return version -def build_migration_plan(settings: configuration.Settings) -> Iterable[Dict[str, Any]]: +def build_migration_plan( + settings: configuration.Settings, schema_version: versions.Version +) -> Iterable[Dict[str, Any]]: """ Return the list of migrations by version, from the version used to init the DB to the current target version. @@ -145,6 +147,9 @@ def build_migration_plan(settings: configuration.Settings) -> Iterable[Dict[str, "version {} not found.".format(settings.TARGET_VERSION) ) + if schema_version: + versions_to_apply = list(utils.since(versions_to_apply, schema_version)) + # get plan for each version to apply for version in versions_to_apply: version_plan = [] @@ -173,6 +178,14 @@ def build_migration_plan(settings: configuration.Settings) -> Iterable[Dict[str, def describe_migration_plan( settings: configuration.Settings, stylist: style.Stylist = style.noop_stylist ) -> None: + + schema_version = get_best_schema_version(settings=settings) + with stylist.activate("title") as echo: + echo("Schema file version is {}".format(schema_version)) + + with stylist.activate("subtitle") as echo: + echo(" Migrations will start after {}".format(schema_version)) + current_version = db.get_current_schema_version(settings=settings) with stylist.activate("title") as echo: echo("Current version is {}".format(current_version)) @@ -181,7 +194,7 @@ def describe_migration_plan( with stylist.activate("title") as echo: echo("Target version is {}".format(target_version)) - for plan in build_migration_plan(settings=settings): + for plan in build_migration_plan(settings=settings, schema_version=schema_version): version = plan["version"] migrations = plan["plan"] diff --git a/septentrion/migration.py b/septentrion/migration.py index b280598..4b8701b 100644 --- a/septentrion/migration.py +++ b/septentrion/migration.py @@ -25,17 +25,21 @@ def migrate( ) -> None: logger.info("Starting migrations") + + schema_version = core.get_best_schema_version(settings=settings) + if not db.is_schema_initialized(settings=settings): logger.info("Migration table is empty, loading a schema") # schema not inited - schema_version = core.get_best_schema_version(settings=settings) init_schema(settings=settings, init_version=schema_version, stylist=stylist) # play migrations with stylist.activate("title") as echo: echo("Applying migrations") - for plan in core.build_migration_plan(settings=settings): + for plan in core.build_migration_plan( + settings=settings, schema_version=schema_version + ): version = plan["version"] logger.info("Processing version %s", version) with stylist.activate("subtitle") as echo: diff --git a/septentrion/utils.py b/septentrion/utils.py index 0c23ef1..4139ee1 100644 --- a/septentrion/utils.py +++ b/septentrion/utils.py @@ -2,6 +2,7 @@ All functions in here should be easily unit testable """ +import itertools from typing import Iterable, TypeVar from septentrion import exceptions, versions @@ -37,3 +38,15 @@ def until(iterable: Iterable[T], value: T) -> Iterable[T]: break else: raise ValueError("{} not found".format(value)) + + +def since(iterable: Iterable[T], value: T) -> Iterable[T]: + """ + Returns the values from iterable starting after the element is found + >>> list(until(range(300), 297)) + [298, 299] + """ + it = itertools.dropwhile((lambda x: x != value), iterable) + # Drop the first element + next(it) + yield from it diff --git a/tests/unit/test_core.py b/tests/unit/test_core.py index 7cc2260..cb36627 100644 --- a/tests/unit/test_core.py +++ b/tests/unit/test_core.py @@ -1,3 +1,5 @@ +import pathlib + import pytest from septentrion import configuration, core, exceptions, versions @@ -6,7 +8,7 @@ @pytest.fixture def known_versions(mocker): - versions_ = [versions.Version.from_string(v) for v in ("1.1", "1.2", "1.3")] + versions_ = [versions.Version.from_string(v) for v in ("0", "1.1", "1.2", "1.3")] mocker.patch("septentrion.core.files.get_known_versions", return_value=versions_) return versions_ @@ -158,9 +160,10 @@ def test_build_migration_plan_unknown_version(known_versions): settings = configuration.Settings( target_version=versions.Version.from_string("1.5") ) + schema_version = versions.Version.from_string("0") with pytest.raises(ValueError): - list(core.build_migration_plan(settings)) + list(core.build_migration_plan(settings, schema_version=schema_version)) def test_build_migration_plan_ok(mocker, known_versions): @@ -168,16 +171,20 @@ def test_build_migration_plan_ok(mocker, known_versions): mocker.patch( "septentrion.core.files.get_migrations_files_mapping", return_value={ - "file.ddl.sql": "tests/test_data/sql/17.1/manual/file.ddl.sql", - "file.dml.sql": "tests/test_data/sql/17.1/manual/file.dml.sql", + "file.ddl.sql": pathlib.Path( + "tests/test_data/sql/17.1/manual/file.ddl.sql" + ), + "file.dml.sql": pathlib.Path( + "tests/test_data/sql/17.1/manual/file.dml.sql" + ), }, ) mocker.patch("septentrion.core.files.is_manual_migration", return_value=True) settings = configuration.Settings( target_version=versions.Version.from_string("1.2") ) - - plan = core.build_migration_plan(settings=settings) + schema_version = versions.Version.from_string("0") + plan = core.build_migration_plan(settings=settings, schema_version=schema_version) expected = [ { @@ -185,13 +192,13 @@ def test_build_migration_plan_ok(mocker, known_versions): ( "file.ddl.sql", False, - "tests/test_data/sql/17.1/manual/file.ddl.sql", + pathlib.Path("tests/test_data/sql/17.1/manual/file.ddl.sql"), True, ), ( "file.dml.sql", False, - "tests/test_data/sql/17.1/manual/file.dml.sql", + pathlib.Path("tests/test_data/sql/17.1/manual/file.dml.sql"), True, ), ], @@ -202,13 +209,13 @@ def test_build_migration_plan_ok(mocker, known_versions): ( "file.ddl.sql", False, - "tests/test_data/sql/17.1/manual/file.ddl.sql", + pathlib.Path("tests/test_data/sql/17.1/manual/file.ddl.sql"), True, ), ( "file.dml.sql", False, - "tests/test_data/sql/17.1/manual/file.dml.sql", + pathlib.Path("tests/test_data/sql/17.1/manual/file.dml.sql"), True, ), ], @@ -227,10 +234,28 @@ def test_build_migration_plan_db_uptodate(mocker, known_versions): target_version=versions.Version.from_string("1.2"), ) - plan = core.build_migration_plan(settings=settings) + schema_version = versions.Version.from_string("0") + plan = core.build_migration_plan(settings=settings, schema_version=schema_version) expected = [ {"plan": [], "version": versions.Version.from_string("1.1")}, {"plan": [], "version": versions.Version.from_string("1.2")}, ] assert list(plan) == expected + + +def test_build_migration_plan_with_schema(mocker, known_versions): + mocker.patch( + "septentrion.core.db.get_applied_migrations", return_value=[], + ) + settings = configuration.Settings(target_version="1.2") + schema_version = versions.Version.from_string("1.1") + + plan = list( + core.build_migration_plan(settings=settings, schema_version=schema_version) + ) + + expected = [ + {"plan": [], "version": versions.Version.from_string("1.2")}, + ] + assert list(plan) == expected