def test_build_migration_plan_unknown_version(known_versions): settings = configuration.Settings( target_version=Version.from_string("1.5")) from_version = Version.from_string("0") with pytest.raises(ValueError): list(core.build_migration_plan(settings, from_version=from_version))
def migrate(settings: configuration.Settings, stylist: style.Stylist = style.noop_stylist) -> None: logger.info("Starting migrations") 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) from_version = schema_version else: _from_version = db.get_current_schema_version(settings=settings) assert _from_version # mypy shenanigans from_version = _from_version # play migrations with stylist.activate("title") as echo: echo("Applying migrations") for plan in core.build_migration_plan(settings=settings, from_version=from_version): version = plan["version"] logger.info("Processing version %s", version) with stylist.activate("subtitle") as echo: echo("Version {}".format(version)) for mig, applied, path, is_manual in plan["plan"]: logger.debug( "Processing migration %(mig)s, applied: %(applied)s, " "path: %(path)s, manual: %(is_manual)s", { "mig": mig, "applied": applied, "path": path, "is_manual": is_manual }, ) title = mig if is_manual: title += " (manual)" title += " " if applied: stylist.draw_checkbox(checked=True, content="Already applied") stylist.echo("") # new line else: with stylist.checkbox( content="Applying {}...".format(title), content_after="Applied {}".format(title), ): run_script(settings=settings, path=path) logger.info("Saving operation in the database") db.write_migration(settings=settings, version=version, name=mig)
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") from_version = Version.from_string("1.1") plan = list( core.build_migration_plan(settings=settings, from_version=from_version)) expected = [ { "plan": [], "version": Version.from_string("1.1") }, { "plan": [], "version": Version.from_string("1.2") }, ] assert list(plan) == expected
def test_build_migration_plan_db(mocker, known_versions): # What a mock hell >< # So first, we mock db.get_applied_migrations to tell the following story: # - on 1.1, only migration "a" was previously applied. # - on 1.2, no migration was previously applied. mocker.patch( "septentrion.db.get_applied_migrations", side_effect=lambda settings, version: { Version.from_string("1.1"): ["a"], Version.from_string("1.2"): [], }[version], ) # Then, regarding the migration files that exist on the disk: # - There are 2 files for 1.1 (so one already applied and one new) # - 1 file for 1.2 # - 1 file for 1.3 mocker.patch( "septentrion.files.get_migrations_files_mapping", side_effect=lambda settings, version: { Version.from_string("1.1"): { "a": pathlib.Path("a"), "b": pathlib.Path("b"), }, Version.from_string("1.2"): { "c": pathlib.Path("c"), }, Version.from_string("1.3"): { "d": pathlib.Path("d") }, }[version], ) # The contents of each migration is ignored mocker.patch("septentrion.files.file_lines_generator", return_value="") # Migration "c" is a manual migration mocker.patch( "septentrion.files.is_manual_migration", side_effect=(lambda migration_path, migration_contents: str( migration_path) == "c"), ) # We'll apply migrations up until 1.2 included settings = configuration.Settings( target_version=Version.from_string("1.2"), ) # And we'll start at version 1.1 included from_version = Version.from_string("1.1") plan = core.build_migration_plan(settings=settings, from_version=from_version) expected = [ { "version": Version.from_string("1.1"), "plan": [ # On 1.1, migration a is already applied. It's not manual ("a", True, pathlib.Path("a"), False), # migration b, though needs to be applied. It's not manual ("b", False, pathlib.Path("b"), False), ], }, { "version": Version.from_string("1.2"), "plan": [ # migration c also needs to be applied. It's manual (the last True) ("c", False, pathlib.Path("c"), True), ], }, ] assert list(plan) == expected
def build_migration_plan(**settings_kwargs): lib_kwargs = initialize(settings_kwargs) schema_version = core.get_best_schema_version( settings=lib_kwargs["settings"]) return core.build_migration_plan(settings=lib_kwargs["settings"], from_version=schema_version)