package io.dropwizard.migrations;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.jcip.annotations.NotThreadSafe;
import net.sourceforge.argparse4j.inf.Namespace;
import org.junit.Before;
import org.junit.Test;
import org.skife.jdbi.v2.DBI;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
@NotThreadSafe
public class DbRollbackCommandTest extends AbstractMigrationTest {
private final String migrationsFileName = "migrations-ddl.xml";
private final DbRollbackCommand<TestMigrationConfiguration> rollbackCommand = new DbRollbackCommand<>(
new TestMigrationDatabaseConfiguration(), TestMigrationConfiguration.class, migrationsFileName);
private final DbMigrateCommand<TestMigrationConfiguration> migrateCommand = new DbMigrateCommand<>(
new TestMigrationDatabaseConfiguration(), TestMigrationConfiguration.class, migrationsFileName);
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private TestMigrationConfiguration conf;
private DBI dbi;
@Before
public void setUp() throws Exception {
final String databaseUrl = getDatabaseUrl();
conf = createConfiguration(databaseUrl);
dbi = new DBI(databaseUrl, "sa", "");
}
@Test
public void testRollbackNChanges() throws Exception {
// Migrate some DDL changes to the database
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Rollback the last one (the email field)
rollbackCommand.run(null, new Namespace(ImmutableMap.of("count", 1)), conf);
// Now we can add it
dbi.useHandle(h -> h.execute("alter table persons add column email varchar(128)"));
}
@Test
public void testRollbackNChangesAsDryRun() throws Exception {
// Migrate some DDL changes to the database
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Print out the change that rollbacks the second change
rollbackCommand.setOutputStream(new PrintStream(baos, true));
rollbackCommand.run(null, new Namespace(ImmutableMap.of("count", 1, "dry-run", true)), conf);
assertThat(baos.toString(UTF_8))
.containsIgnoringCase("ALTER TABLE PUBLIC.persons DROP COLUMN email;");
}
@Test
public void testRollbackToDate() throws Exception {
// Migrate some DDL changes to the database
long migrationDate = System.currentTimeMillis();
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Rollback both changes (they're after the migration date)
rollbackCommand.run(null, new Namespace(ImmutableMap.of("date", new Date(migrationDate - 1000))),
conf);
// Verify we can creat the table
dbi.useHandle(h -> h.execute("create table persons(id int, name varchar(255))"));
}
@Test
public void testRollbackToDateAsDryRun() throws Exception {
// Migrate some DDL changes to the database
long migrationDate = System.currentTimeMillis();
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Print out a rollback script for both changes after the migration date
rollbackCommand.setOutputStream(new PrintStream(baos, true));
rollbackCommand.run(null, new Namespace(ImmutableMap.of("date", new Date(migrationDate - 1000),
"dry-run", true)), conf);
assertThat(baos.toString(UTF_8))
.containsIgnoringCase("ALTER TABLE PUBLIC.persons DROP COLUMN email;")
.containsIgnoringCase("DROP TABLE PUBLIC.persons;");
}
@Test
public void testRollbackToTag() throws Exception {
// Migrate the first DDL change to the database
migrateCommand.run(null, new Namespace(ImmutableMap.of("count", 1)), conf);
// Tag it
final DbTagCommand<TestMigrationConfiguration> tagCommand = new DbTagCommand<>(
new TestMigrationDatabaseConfiguration(), TestMigrationConfiguration.class, migrationsFileName);
tagCommand.run(null, new Namespace(ImmutableMap.of("tag-name", ImmutableList.of("v1"))), conf);
// Migrate the second change
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Rollback to the first change
rollbackCommand.run(null, new Namespace(ImmutableMap.of("tag", "v1")), conf);
// Verify we can add the second change manually
dbi.useHandle(h -> h.execute("alter table persons add column email varchar(128)"));
}
@Test
public void testRollbackToTagAsDryRun() throws Exception {
// Migrate the first DDL change to the database
migrateCommand.run(null, new Namespace(ImmutableMap.of("count", 1)), conf);
// Tag it
final DbTagCommand<TestMigrationConfiguration> tagCommand = new DbTagCommand<>(
new TestMigrationDatabaseConfiguration(), TestMigrationConfiguration.class, migrationsFileName);
tagCommand.run(null, new Namespace(ImmutableMap.of("tag-name", ImmutableList.of("v1"))), conf);
// Migrate the second change
migrateCommand.run(null, new Namespace(ImmutableMap.of()), conf);
// Print out the rollback script for the second change
rollbackCommand.setOutputStream(new PrintStream(baos, true));
rollbackCommand.run(null, new Namespace(ImmutableMap.of("tag", "v1", "dry-run", true)), conf);
assertThat(baos.toString(UTF_8))
.containsIgnoringCase("ALTER TABLE PUBLIC.persons DROP COLUMN email;");
}
@Test
public void testPrintHelp() throws Exception {
createSubparser(rollbackCommand).printHelp(new PrintWriter(new OutputStreamWriter(baos, UTF_8), true));
assertThat(baos.toString(UTF_8)).isEqualTo(String.format(
"usage: db rollback [-h] [--migrations MIGRATIONS-FILE] [--catalog CATALOG]%n" +
" [--schema SCHEMA] [-n] [-t TAG] [-d DATE] [-c COUNT]%n" +
" [-i CONTEXTS] [file]%n" +
"%n" +
"Rollback the database schema to a previous version.%n" +
"%n" +
"positional arguments:%n" +
" file application configuration file%n" +
"%n" +
"optional arguments:%n" +
" -h, --help show this help message and exit%n" +
" --migrations MIGRATIONS-FILE%n" +
" the file containing the Liquibase migrations for%n" +
" the application%n" +
" --catalog CATALOG Specify the database catalog (use database%n" +
" default if omitted)%n" +
" --schema SCHEMA Specify the database schema (use database default%n" +
" if omitted)%n" +
" -n, --dry-run Output the DDL to stdout, don't run it%n" +
" -t TAG, --tag TAG Rollback to the given tag%n" +
" -d DATE, --date DATE Rollback to the given date%n" +
" -c COUNT, --count COUNT%n" +
" Rollback the specified number of change sets%n" +
" -i CONTEXTS, --include CONTEXTS%n" +
" include change sets from the given context%n"));
}
}