def test_postgresql_connects_with_schema(self):
     dburi = next(iter(get_test_dburis(only={"postgresql"})), None)
     if dburi is None:
         pytest.skip("PostgreSQL backend not available")
     backend = get_backend(dburi)
     with backend.transaction():
         backend.execute("CREATE SCHEMA foo")
     try:
         assert get_backend(dburi + "?schema=foo").execute(
             "SHOW search_path").fetchone() == ("foo", )
     finally:
         with backend.transaction():
             backend.execute("DROP SCHEMA foo CASCADE")
    def test_post_apply_hooks_are_run_every_time(self):

        backend = get_backend(dburi)
        migrations = migrations_dir(
            **{
                "a": "step('create table postapply (i int)')",
                "post-apply": "step('insert into postapply values (1)')",
            })

        with migrations as tmp:

            def count_postapply_calls():
                cursor = backend.cursor()
                cursor.execute("SELECT count(1) FROM postapply")
                return cursor.fetchone()[0]

            def _apply_migrations():
                backend.apply_migrations(backend.to_apply(
                    read_migrations(tmp)))

            # Should apply migration 'a' and call the post-apply hook
            _apply_migrations()
            assert count_postapply_calls() == 1

            # No outstanding migrations: post-apply hook should not be called
            _apply_migrations()
            assert count_postapply_calls() == 1

            # New migration added: post-apply should be called a second time
            migrations.add_migration("b", "")
            _apply_migrations()
            assert count_postapply_calls() == 2
def test_migrations_can_import_step_and_group(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT id FROM yoyo_test")
    assert cursor.fetchall() == [(1, )]
 def test_apply_migrations_only_does_not_run_hooks(self, tmpdir):
     backend = get_backend(dburi)
     backend.apply_migrations_only(backend.to_apply(
         read_migrations(tmpdir)))
     cursor = backend.cursor()
     cursor.execute("SELECT * FROM postapply")
     assert cursor.fetchall() == []
def test_specify_migration_table(tmpdir):
    backend = get_backend(dburi, migration_table="another_migration_table")
    migrations = read_migrations(tmpdir)
    backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT migration_id FROM another_migration_table")
    assert cursor.fetchall() == [("0", )]
def test_execution_continues_with_ignore_errors_in_transaction(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT * FROM yoyo_test")
    assert cursor.fetchall() == [(2, )]
def test_transaction_is_not_committed_on_error(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    with pytest.raises(backend.DatabaseError):
        backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT count(1) FROM yoyo_test")
    assert cursor.fetchone() == (0, )
def test_migration_functions_have_namespace_access(tmpdir):
    """
    Test that functions called via step have access to the script namespace
    """
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT id FROM foo_test")
    assert cursor.fetchall() == [(1, )]
def test_rollbacks_happen_in_reverse(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    backend.apply_migrations(migrations)
    cursor = backend.cursor()
    cursor.execute("SELECT * FROM yoyo_test")
    assert cursor.fetchall() == [(2, )]
    backend.rollback_migrations(migrations)
    cursor.execute("SELECT * FROM yoyo_test")
    assert cursor.fetchall() == []
def test_limits(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    # Should insert a single 1
    backend.apply_migrations(migrations, limit=2)
    cursor = backend.cursor()

    cursor.execute("SELECT * FROM yoyo_limit_test")
    all_results = cursor.fetchall()
    assert len(all_results) == 1

    cursor.execute("SELECT * FROM yoyo_limit_test where id = 0")
    all_results = cursor.fetchall()
    assert len(all_results) == 0

    cursor.execute("SELECT * FROM yoyo_limit_test where id = 1")
    all_results = cursor.fetchall()
    assert len(all_results) == 1

    # Should insert Zero
    backend2 = get_backend(dburi)
    forward_migrations = read_migrations(tmpdir)
    # Should Apply the Last Migration
    backend2.apply_migrations(forward_migrations, limit=1)
    cursor2 = backend2.cursor()
    cursor2.execute("SELECT * FROM yoyo_limit_test where id = 0")
    all_results = cursor2.fetchall()
    assert len(all_results) == 1

    # Round 3 Let's Do a limited Rollback
    # Should Remove the Zero
    backend3 = get_backend(dburi)
    backward_migrations = read_migrations(tmpdir)
    backend3.apply_migrations(backward_migrations, limit=1)
    backend3.rollback_migrations(migrations, limit=1)
    cursor3 = backend3.cursor()
    cursor3.execute("SELECT * FROM yoyo_limit_test where id = 0")
    all_results = cursor3.fetchall()
    assert len(all_results) == 0
    cursor3.execute("SELECT * FROM yoyo_limit_test where id = 1")
    all_results = cursor3.fetchall()
    assert len(all_results) == 1
def test_show_migrations(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)

    backend.apply_migrations(migrations)
    migrations = backend.get_migrations_with_applied_status(migrations)
    assert migrations[0].applied

    backend.rollback_migrations(migrations)
    migrations = backend.get_migrations_with_applied_status(migrations)
    assert not migrations[0].applied
def test_migrations_display_selected_data(tmpdir):
    backend = get_backend(dburi)
    migrations = read_migrations(tmpdir)
    with patch("yoyo.migrations.stdout") as stdout:
        backend.apply_migrations(migrations)
        written = "".join(a[0] for a, kw in stdout.write.call_args_list)
        assert written == (" id | c \n"
                           "----+---\n"
                           " 1  | a \n"
                           " 2  | b \n"
                           "(2 rows)\n")
    def test_postgresql_list_table_uses_current_schema(self):
        dburi = next(iter(get_test_dburis(only={"postgresql"})), None)
        if dburi is None:
            pytest.skip("PostgreSQL backend not available")
        backend = get_backend(dburi)
        dbname = backend.uri.database
        with backend.transaction():
            backend.execute(
                "ALTER DATABASE {} SET SEARCH_PATH = custom_schema,public".
                format(dbname))
        try:
            with backend.transaction():
                backend.execute("CREATE SCHEMA custom_schema")
                backend.execute("CREATE TABLE custom_schema.foo (x int)")
            assert "foo" in get_backend(dburi).list_tables()

        finally:
            with backend.transaction():
                backend.execute(
                    "ALTER DATABASE {} RESET SEARCH_PATH".format(dbname))
                backend.execute("DROP SCHEMA custom_schema CASCADE")
    def test_disabling_transactions_in_sqlite(self, tmpdir):
        """
        Transactions cause sqlite databases to become locked, preventing
        other tools from accessing them:

        https://bitbucket.org/ollyc/yoyo/issues/43/run-step-outside-of-transaction
        """
        with NamedTemporaryFile() as tmp:
            backend = get_backend("sqlite:///" + tmp.name)
            backend.apply_migrations(read_migrations(tmpdir))
            assert "yoyo_test_a" in backend.list_tables()
            assert "yoyo_test_b" in backend.list_tables()
            assert "yoyo_test_c" in backend.list_tables()
    def test_lock_times_out(self, dburi):

        backend = get_backend(dburi)
        self.skip_if_not_concurrency_safe(backend)

        thread = Thread(target=partial(self.do_something_with_lock, dburi))
        thread.start()
        # Give the thread time to acquire the lock, but not enough
        # to complete
        time.sleep(self.lock_duration * 0.6)
        with pytest.raises(exceptions.LockTimeout):
            with backend.lock(timeout=0.001):
                assert False, "Execution should never reach this point"

        thread.join()
Exemple #16
0
def backend(request):
    """
    Return all backends configured in ``test_databases.ini``
    """
    backend = get_backend(request.param)
    with backend.transaction():
        if backend.__class__ is backends.MySQLBackend:
            backend.execute("CREATE TABLE yoyo_t "
                            "(id CHAR(1) primary key) "
                            "ENGINE=InnoDB")
        else:
            backend.execute("CREATE TABLE yoyo_t " "(id CHAR(1) primary key)")
    try:
        yield backend
    finally:
        backend.rollback()
        drop_yoyo_tables(backend)
    def test_lock(self, dburi):
        """
        Test that :meth:`~yoyo.backends.DatabaseBackend.lock`
        acquires an exclusive lock
        """
        backend = get_backend(dburi)
        self.skip_if_not_concurrency_safe(backend)
        thread = Thread(target=partial(self.do_something_with_lock, dburi))
        t = time.time()
        thread.start()

        # Give the thread time to acquire the lock, but not enough
        # to complete
        time.sleep(self.lock_duration * 0.6)

        with backend.lock():
            delta = time.time() - t
            assert delta >= self.lock_duration

        thread.join()
Exemple #18
0
def get_backend(args, config):
    try:
        dburi = args.database
    except AttributeError:
        dburi = config.get("DEFAULT", "database")

    try:
        migration_table = args.migration_table
    except AttributeError:
        migration_table = config.get("DEFAULT", "migration_table")

    if dburi is None:
        raise InvalidArgument("Please specify a database uri")

    try:
        if args.prompt_password:
            password = getpass("Password for %s: " % dburi)
            parsed = connections.parse_uri(dburi)
            dburi = parsed._replace(password=password).uri
    except AttributeError:
        pass

    return connections.get_backend(dburi, migration_table)
 def test_it_runs_multiple_post_apply_hooks(self, tmpdir):
     backend = get_backend(dburi)
     backend.apply_migrations(backend.to_apply(read_migrations(tmpdir)))
     cursor = backend.cursor()
     cursor.execute("SELECT * FROM postapply")
     assert cursor.fetchall() == [(1, ), (2, )]
 def do_something_with_lock(self, dburi):
     with get_backend(dburi).lock():
         time.sleep(self.lock_duration)
Exemple #21
0
def dburi(request):
    try:
        yield request.param
    finally:
        drop_yoyo_tables(get_backend(request.param))
Exemple #22
0
def get_test_backends(only=frozenset(), exclude=frozenset()):
    return [get_backend(dburi) for dburi in get_test_dburis(only, exclude)]