/*
* Copyright 2008, Unitils.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.unitils.dbmaintainer.structure;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
import org.unitils.UnitilsJUnit4;
import org.unitils.core.ConfigurationLoader;
import org.unitils.core.dbsupport.DbSupport;
import org.unitils.core.dbsupport.SQLHandler;
import static org.unitils.core.dbsupport.DbSupportFactory.getDefaultDbSupport;
import org.unitils.core.dbsupport.DefaultSQLHandler;
import static org.unitils.core.util.SQLTestUtils.dropTestSequences;
import static org.unitils.core.util.SQLTestUtils.dropTestTables;
import static org.unitils.database.SQLUnitils.executeUpdate;
import static org.unitils.database.SQLUnitils.getItemAsLong;
import org.unitils.database.annotations.TestDataSource;
import static org.unitils.dbmaintainer.structure.impl.DefaultSequenceUpdater.PROPKEY_LOWEST_ACCEPTABLE_SEQUENCE_VALUE;
import static org.unitils.dbmaintainer.util.DatabaseModuleConfigUtils.getConfiguredDatabaseTaskInstance;
import javax.sql.DataSource;
import java.util.Properties;
import org.unitils.util.PropertyUtils;
/**
* Test class for the SequenceUpdater. Contains tests that can be implemented generally for all different database dialects.
* Extended with implementations for each supported database dialect.
* <p/>
* Tests are only executed for the currently activated database dialect. By default, a hsqldb in-memory database is used,
* to avoid the need for setting up a database instance. If you want to run unit tests for other dbms's, change the
* configuration in test/resources/unitils.properties
*
* @author Filip Neven
* @author Tim Ducheyne
* @author Scott Prater
*/
public class SequenceUpdaterTest extends UnitilsJUnit4 {
/* The logger instance for this class */
private static Log logger = LogFactory.getLog(SequenceUpdaterTest.class);
/* DataSource for the test database, is injected */
@TestDataSource
private DataSource dataSource = null;
/* Tested object */
private SequenceUpdater sequenceUpdater;
/* DbSupport instance */
private DbSupport dbSupport;
private static String dialect = "h2";
private List<String> schemas;
/**
* Test fixture. Configures the implementation of the SequenceUpdater that matches the currenlty configured dialect.
* Creates a test table and test sequence.
*/
@Before
public void setUp() throws Exception {
Properties configuration = new ConfigurationLoader().loadConfiguration();
configuration.setProperty(PROPKEY_LOWEST_ACCEPTABLE_SEQUENCE_VALUE, "1000");
schemas = PropertyUtils.getStringList("database.schemaNames", configuration);
SQLHandler sqlHandler = new DefaultSQLHandler(dataSource);
sequenceUpdater = getConfiguredDatabaseTaskInstance(SequenceUpdater.class, configuration, sqlHandler, dialect, schemas);
dbSupport = getDefaultDbSupport(configuration, sqlHandler, dialect, schemas.get(0));
cleanupTestDatabase();
createTestDatabase();
}
/**
* Clears the database, to avoid interference with other tests
*/
@After
public void tearDown() throws Exception {
cleanupTestDatabase();
}
/**
* Tests the update sequences behavior
*/
@Test
public void testUpdateSequences() throws Exception {
if (!dbSupport.supportsSequences()) {
logger.warn("Current dialect does not support sequences. Skipping test.");
return;
}
assertCurrentSequenceValueBetween(0, 10);
sequenceUpdater.updateSequences();
assertCurrentSequenceValueBetween(1000, 1010);
}
/**
* Verifies that if a sequence has a value already high enough, the value is not being set to a lower value
*/
@Test
public void testUpdateSequences_valueAlreadyHighEnough() throws Exception {
if (!dbSupport.supportsSequences()) {
logger.warn("Current dialect does not support sequences. Skipping test.");
return;
}
assertCurrentSequenceValueBetween(0, 10);
sequenceUpdater.updateSequences();
assertCurrentSequenceValueBetween(1000, 1010);
sequenceUpdater.updateSequences();
assertCurrentSequenceValueBetween(1000, 1010);
}
/**
* Tests the update identity columns behavior
*/
@Test
public void testUpdateSequences_identityColumns() throws Exception {
if (!dbSupport.supportsIdentityColumns()) {
logger.warn("Current dialect does not support identity columns. Skipping test.");
return;
}
assertCurrentIdentityColumnValueBetween(0, 10);
sequenceUpdater.updateSequences();
assertCurrentIdentityColumnValueBetween(1000, 1010);
}
/**
* Verifies that if a identity columns has a value already high enough, the value is not being set to a lower value
*/
@Test
public void testUpdateSequences_identityColumnsValueAlreadyHighEnough() throws Exception {
if (!dbSupport.supportsIdentityColumns()) {
logger.warn("Current dialect does not support identity columns. Skipping test.");
return;
}
assertCurrentIdentityColumnValueBetween(0, 10);
sequenceUpdater.updateSequences();
assertCurrentIdentityColumnValueBetween(1000, 1010);
sequenceUpdater.updateSequences();
assertCurrentIdentityColumnValueBetween(1000, 1010);
}
/**
* Asserts that the current value for the test_sequence is between the given values
*
* @param minValue The minimum value (included)
* @param maxValue The maximum value (included)
*/
private void assertCurrentSequenceValueBetween(long minValue, long maxValue) {
String correctCaseSequenceName = dbSupport.toCorrectCaseIdentifier("test_sequence");
long currentValue = dbSupport.getSequenceValue(correctCaseSequenceName);
assertTrue("Current sequence value is not between " + minValue + " and " + maxValue, (currentValue >= minValue && currentValue <= maxValue));
}
/**
* Asserts that the current value for the identity column test_table.col1 is between the given values.
*
* @param minValue The minimum value (included)
* @param maxValue The maximum value (included)
*/
private void assertCurrentIdentityColumnValueBetween(long minValue, long maxValue) {
executeUpdate("delete from test_table1", dataSource);
executeUpdate("insert into test_table1(col2) values('test')", dataSource);
long currentValue = getItemAsLong("select col1 from test_table1 where col2 = 'test'", dataSource);
assertTrue("Current sequence value is not between " + minValue + " and " + maxValue, (currentValue >= minValue && currentValue <= maxValue));
}
/**
* Creates all test database structures (view, tables...)
*/
private void createTestDatabase() throws Exception {
String dialect = dbSupport.getDatabaseDialect();
if ("hsqldb".equals(dialect)) {
createTestDatabaseHsqlDb();
} else if ("mysql".equals(dialect)) {
createTestDatabaseMySql();
} else if ("oracle".equals(dialect)) {
createTestDatabaseOracle();
} else if ("postgresql".equals(dialect)) {
createTestDatabasePostgreSql();
} else if ("db2".equals(dialect)) {
createTestDatabaseDb2();
} else if ("derby".equals(dialect)) {
createTestDatabaseDerby();
} else if ("mssql".equals(dialect)) {
createTestDatabaseMsSql();
} else {
fail("This test is not implemented for current dialect: " + dialect);
}
}
/**
* Drops all created test database structures (views, tables...)
*/
private void cleanupTestDatabase() throws Exception {
String dialect = dbSupport.getDatabaseDialect();
if ("hsqldb".equals(dialect)) {
cleanupTestDatabaseHsqlDb();
} else if ("mysql".equals(dialect)) {
cleanupTestDatabaseMySql();
} else if ("oracle".equals(dialect)) {
cleanupTestDatabaseOracle();
} else if ("postgresql".equals(dialect)) {
cleanupTestDatabasePostgreSql();
} else if ("db2".equals(dialect)) {
cleanupTestDatabaseDb2();
} else if ("derby".equals(dialect)) {
cleanupTestDatabaseDerby();
} else if ("mssql".equals(dialect)) {
cleanupTestDatabaseMsSql();
}
}
//
// Database setup for HsqlDb
//
/**
* Creates all test database structures
*/
private void createTestDatabaseHsqlDb() throws Exception {
// create table containing identity
executeUpdate("create table test_table1 (col1 int not null identity, col2 varchar(12) not null)", dataSource);
// create table without identity
executeUpdate("create table test_table2 (col1 int primary key, col2 varchar(12) not null)", dataSource);
// create sequences
executeUpdate("create sequence test_sequence", dataSource);
}
/**
* Drops all created test database structures
*/
private void cleanupTestDatabaseHsqlDb() throws Exception {
dropTestTables(dbSupport, "test_table1", "test_table2");
dropTestSequences(dbSupport, "test_sequence");
}
//
// Database setup for MySql
//
/**
* Creates all test database structures
*/
private void createTestDatabaseMySql() throws Exception {
// create tables with auto increment column
executeUpdate("create table test_table1 (col1 int not null primary key AUTO_INCREMENT, col2 varchar(12) not null)", dataSource);
// create table without increment column
executeUpdate("create table test_table2 (col1 int not null primary key, col2 varchar(12) not null)", dataSource);
}
/**
* Drops all created test database structures
*/
private void cleanupTestDatabaseMySql() throws Exception {
dropTestTables(dbSupport, "test_table1", "test_table2");
}
//
// Database setup for Oracle
//
/**
* Creates all test database structures (view, tables...)
*/
private void createTestDatabaseOracle() throws Exception {
// create sequence
executeUpdate("create sequence test_sequence", dataSource);
}
/**
* Drops all created test database structures (views, tables...)
*/
private void cleanupTestDatabaseOracle() throws Exception {
dropTestSequences(dbSupport, "test_sequence");
}
//
// Database setup for PostgreSql
//
/**
* Creates all test database structures
*/
private void createTestDatabasePostgreSql() throws Exception {
// create sequence
executeUpdate("create sequence test_sequence", dataSource);
}
/**
* Drops all created test database structures
*/
private void cleanupTestDatabasePostgreSql() throws Exception {
dropTestSequences(dbSupport, "test_sequence");
}
//
// Database setup for Db2
//
/**
* Creates all test database structures (view, tables...)
*/
private void createTestDatabaseDb2() throws Exception {
// create tables with auto increment column
executeUpdate("create table test_table1 (col1 int not null primary key generated by default as identity, col2 varchar(12) not null)", dataSource);
// create table without increment column
executeUpdate("create table test_table2 (col1 int not null primary key, col2 varchar(12) not null)", dataSource);
// create sequences
executeUpdate("create sequence test_sequence", dataSource);
}
/**
* Drops all created test database structures (views, tables...)
*/
private void cleanupTestDatabaseDb2() throws Exception {
dropTestTables(dbSupport, "test_table1", "test_table2");
dropTestSequences(dbSupport, "test_sequence");
}
//
// Database setup for Derby
//
/**
* Creates all test database structures
*/
private void createTestDatabaseDerby() throws Exception {
// create table containing identity
executeUpdate("create table test_table1 (col1 int not null primary key generated always as identity, col2 varchar(12) not null)", dataSource);
// create table without identity
executeUpdate("create table test_table2 (col1 int not null primary key, col2 varchar(12) not null)", dataSource);
}
/**
* Drops all created test database structures
*/
private void cleanupTestDatabaseDerby() throws Exception {
dropTestTables(dbSupport, "test_table1", "test_table2");
}
//
// Database setup for MS-Sql
//
/**
* Creates all test database structures
*/
private void createTestDatabaseMsSql() throws Exception {
// create table containing identity
executeUpdate("create table test_table1 (col1 int not null primary key identity, col2 varchar(12) not null)", dataSource);
// create table without identity
executeUpdate("create table test_table2 (col1 int not null primary key, col2 varchar(12) not null)", dataSource);
}
/**
* Drops all created test database structures
*/
private void cleanupTestDatabaseMsSql() throws Exception {
dropTestTables(dbSupport, "test_table1", "test_table2");
}
}