def test_it_prompts_migrations(self, tmpdir): with patch("yoyo.scripts.migrate.prompt_migrations" ) as prompt_migrations, patch( "yoyo.scripts.migrate.get_backend") as get_backend: main(["apply", tmpdir, "--database", dburi]) migrations = get_backend().to_apply() assert migrations in prompt_migrations.call_args[0]
def test_it_invokes_correct_editor_binary_from_config(self, tmpdir): self.writeconfig(editor="vim {} -c +10") main(["new", tmpdir, "--database", dburi]) assert self.subprocess.call.call_args == call([ "vim", tms.Matcher(partial(is_tmpfile, directory=tmpdir)), "-c", "+10" ])
def test_it_calls_post_create_command(self, tmpdir): self.writeconfig(post_create_command="/bin/ls -l {} {}") with frozendate.freeze(2001, 1, 1): main(["new", "-b", tmpdir, "--database", dburi]) is_filename = tms.Str( lambda s: os.path.basename(s).startswith("20010101_01_")) assert self.subprocess.call.call_args == call( ["/bin/ls", "-l", is_filename, is_filename])
def test_it_pulls_message_from_docstring(self, tmpdir): def write_migration(argv): with io.open(argv[-1], "w", encoding="utf8") as f: f.write('"""\ntest docstring\nsplit over\n\nlines\n"""\n') self.subprocess.call = write_migration main(["new", tmpdir, "--database", dburi]) names = [n for n in sorted(os.listdir(tmpdir)) if n.endswith(".py")] assert "test-docstring" in names[0]
def test_it_applies_from_multiple_sources(self, t1, t2): with patch("yoyo.backends.DatabaseBackend.apply_migrations") as apply: main(["-b", "apply", t1, t2, "--database", dburi]) call_posargs, call_kwargs = apply.call_args migrations, _ = call_posargs assert [m.path for m in migrations] == [ os.path.join(t1, "m1.py"), os.path.join(t2, "m2.py"), ]
def test_it_prompts_password(self, tmpdir): dburi = "sqlite://user@/:memory" with patch("yoyo.scripts.main.getpass", return_value="fish") as getpass, patch( "yoyo.connections.get_backend") as get_backend: main(["apply", tmpdir, "--database", dburi, "--prompt-password"]) assert getpass.call_count == 1 assert get_backend.call_args == call("sqlite://*****:*****@/:memory", "_yoyo_migration")
def test_it_names_file_by_date_and_sequence(self, tmpdir): with frozendate.freeze(2001, 1, 1): main(["new", "-b", "-m", "foo", tmpdir, "--database", dburi]) main(["new", "-b", "-m", "bar", tmpdir, "--database", dburi]) names = [n for n in sorted(os.listdir(tmpdir)) if n.endswith(".py")] assert names[0].startswith("20010101_01_") assert names[0].endswith("-foo.py") assert names[1].startswith("20010101_02_") assert names[1].endswith("-bar.py")
def test_it_prompts_to_reedit_bad_migration(self, tmpdir): def write_migration(argv): with io.open(argv[-1], "w", encoding="utf8") as f: f.write("this is not valid python!") self.subprocess.call = write_migration main(["new", tmpdir, "--database", dburi]) prompts = [ args[0].lower() for args, kwargs in self.prompt.call_args_list ] assert "retry editing?" in prompts[0]
def test_it_upgrades_migration_table_None(self, tmpdir): legacy_config_path = os.path.join(tmpdir, LEGACY_CONFIG_FILENAME) with io.open(legacy_config_path, "w", encoding="utf-8") as f: f.write("[DEFAULT]\n") f.write("migration_table=None\n") f.write("dburi=sqlite:///\n") self.confirm.return_value = True main(["apply", tmpdir]) with open("yoyo.ini", "r") as f: config = f.read() assert "migration_table = _yoyo_migration\n" in config
def test_it_unmarks_to_selected_revision(self, tmpdir): from yoyo.connections import get_backend self.confirm.return_value = True migrations = read_migrations(tmpdir) backend = get_backend(self.dburi) backend.apply_migrations(migrations) main(["unmark", "-r", "m2", tmpdir, "--database", self.dburi]) assert backend.is_applied(migrations[0]) assert not backend.is_applied(migrations[1]) assert not backend.is_applied(migrations[2])
def test_it_prompts_only_unapplied(self, tmpdir): from yoyo.connections import get_backend migrations = read_migrations(tmpdir) backend = get_backend(self.dburi) backend.apply_migrations(migrations[:1]) with patch( "yoyo.scripts.migrate.prompt_migrations") as prompt_migrations: main(["mark", tmpdir, "--database", self.dburi]) _, prompted, _ = prompt_migrations.call_args[0] prompted = [m.id for m in prompted] assert prompted == ["m2", "m3"]
def test_it_breaks_lock(self, dburi): if dburi.startswith("sqlite"): pytest.skip("Test not supported for sqlite databases") backend = get_backend(dburi) backend.execute( "INSERT INTO yoyo_lock (locked, ctime, pid) " "VALUES (1, :now, 1)", {"now": datetime.utcnow()}, ) backend.commit() main(["break-lock", "--database", dburi]) assert backend.execute( "SELECT COUNT(1) FROM yoyo_lock").fetchone()[0] == 0
def execute(self): targets = self.context.targets(self.is_yoyo) for target in targets: db_string = os.environ.get( target.payload.prod_db_envvar) or target.payload.db_string command_args = self.get_passthru_args() or ['apply'] args = command_args + [ '--database=%s' % db_string, os.path.abspath(target.address.spec_path) ] logger.info('Running pants migrate %s' % ' '.join(args)) main(args)
def test_it_marks_at_selected_version(self, tmpdir): from yoyo.connections import get_backend self.confirm.return_value = True migrations = read_migrations(tmpdir) backend = get_backend(self.dburi) with backend.transaction(): backend.execute("CREATE TABLE t (id INT)") main(["mark", "-r", "m2", tmpdir, "--database", self.dburi]) assert backend.is_applied(migrations[0]) assert backend.is_applied(migrations[1]) assert not backend.is_applied(migrations[2]) # Check that migration steps have not been applied c = backend.execute("SELECT * FROM t") assert len(c.fetchall()) == 0
def test_it_creates_sql_file(self, tmpdir): main([ "new", "-b", "-m", "comment", "--sql", tmpdir, "--database", dburi, ]) name = next(n for n in sorted(os.listdir(tmpdir)) if n.endswith(".sql")) with io.open(os.path.join(tmpdir, name), "r", encoding="utf-8") as f: assert f.read() == textwrap.dedent("""\ -- comment -- depends: m1 """)
def test_it_offers_to_upgrade(self, tmpdir): legacy_config_path = os.path.join(tmpdir, LEGACY_CONFIG_FILENAME) with io.open(legacy_config_path, "w", encoding="utf-8") as f: f.write("[DEFAULT]\n") f.write("migration_table=_yoyo_migration\n") f.write("dburi=sqlite:///\n") self.confirm.return_value = True main(["apply", tmpdir]) prompts = [ args[0].lower() for args, kwargs in self.confirm.call_args_list ] assert len(prompts) == 2 assert prompts[0].startswith("move legacy configuration") assert prompts[1].startswith("delete legacy configuration") assert not os.path.exists(legacy_config_path) with open("yoyo.ini", "r") as f: config = f.read() assert "database = sqlite:///\n" in config assert "migration_table = _yoyo_migration\n" in config assert "batch_mode = off\n" in config assert "verbosity = 0\n" in config
def test_it_invokes_correct_editor_binary_from_env(self, tmpdir): # default to $VISUAL with patch("os.environ", {"EDITOR": "ed", "VISUAL": "visualed"}): main(["new", tmpdir, "--database", dburi]) assert self.subprocess.call.call_args == call( ["visualed", tms.Unicode()]) # fallback to $EDITOR with patch("os.environ", {"EDITOR": "ed"}): main(["new", tmpdir, "--database", dburi]) assert self.subprocess.call.call_args == call( ["ed", tms.Unicode()]) # Otherwise, vi with patch("os.environ", {}): main(["new", tmpdir, "--database", dburi]) == call(["vi", tms.Unicode()]) # Prompts should only appear if there is an error reading the migration # file, which should not be the case. assert self.prompt.call_args_list == []
def test_it_prompts_to_create_config_file(self, tmpdir): main(["apply", tmpdir, "--database", dburi]) assert "save migration config" in self.confirm.call_args[0][0].lower()
def test_it_sets_verbosity_level(self, tmpdir): with patch("yoyo.scripts.main.configure_logging") as m: main(["apply", tmpdir, "--database", dburi]) assert m.call_args == call(0) main(["-vvv", "apply", tmpdir, "--database", dburi]) assert m.call_args == call(3)
def test_it_uses_configured_prefix(self, tmpdir): self.writeconfig(prefix="foo_") main(["new", "-b", "-m", "bar", tmpdir, "--database", dburi]) names = [n for n in sorted(os.listdir(tmpdir)) if n.endswith(".py")] assert re.match("foo_.*-bar", names[0]) is not None
def test_it_ignores_config_file(self, tmpdir): self.writeconfig(batch_mode="on") with patch("yoyo.scripts.migrate.apply") as apply: main(["apply", "--no-config-file", tmpdir, "--database", dburi]) args_used = apply.call_args[0][0] assert args_used.batch_mode is False
def test_it_defaults_docstring_to_message(self, tmpdir): main(["new", "-b", "-m", "your ad here", tmpdir, "--database", dburi]) names = [n for n in sorted(os.listdir(tmpdir)) if n.endswith(".py")] with io.open(os.path.join(tmpdir, names[0]), "r", encoding="utf-8") as f: assert "your ad here" in f.read()
def test_it_reapplies_migrations(self, tmpdir): with patch("yoyo.scripts.migrate.get_backend") as get_backend: main(["-b", "reapply", tmpdir, "--database", dburi]) assert get_backend().rollback_migrations.call_count == 1 assert get_backend().apply_migrations.call_count == 1
def test_it_forces_batch_mode_if_not_running_in_a_tty(self, tmpdir): with patch("sys.stdout", isatty=lambda: False): main(["apply", tmpdir, "--database", dburi]) assert self.prompt.call_count == 0 assert self.confirm.call_count == 0
def test_it_creates_config_file(self, tmpdir): self.confirm.return_value = True main(["apply", tmpdir, "--database", dburi]) assert os.path.exists("yoyo.ini") with open("yoyo.ini") as f: assert "database = {0}".format(dburi) in f.read()
def test_it_depends_on_all_current_heads(self, tmpdir): main(["new", "-b", "-m", "foo", tmpdir, "--database", dburi]) m = next(f for f in os.listdir(tmpdir) if "-foo.py" in f) with io.open(os.path.join(tmpdir, m), encoding="utf-8") as f: assert "__depends__ = {'m2', 'm3'}" in f.read()
def test_it_creates_an_empty_migration(self, tmpdir): main(["new", "-b", "-m", "foo", tmpdir, "--database", dburi]) assert any("-foo.py" in f for f in os.listdir(tmpdir))
from yoyo.scripts.main import main main()
def patched_yoyo(argv): main(argv)