From 238fcf905adffcc663b553ae7fa86e2be7369444 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 19 Jun 2007 08:22:50 +0000
Subject: [PATCH] LMS-34 DBMigrationEngine: Improved logging
 DBMigrationEngineTests: More tests

SVN: 573
---
 .../cisd/dbmigration/DBMigrationEngine.java   |  27 +-
 .../dbmigration/DBMigrationEngineTest.java    | 292 +++++++++++++++++-
 2 files changed, 303 insertions(+), 16 deletions(-)

diff --git a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java
index 54d446bb61e..47fc3491f06 100644
--- a/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java
+++ b/dbmigration/source/java/ch/systemsx/cisd/dbmigration/DBMigrationEngine.java
@@ -75,28 +75,28 @@ public class DBMigrationEngine
                 operationLog.info("No migration needed for database '" + databaseName + "'. Current version: " 
                                   + version + ".");
             }
-            return;
-        }
-        if (version.compareTo(databaseVersion) > 0)
+        } else if (version.compareTo(databaseVersion) > 0)
         {
             if (operationLog.isInfoEnabled())
             {
                 String databaseName = adminDAO.getDatabaseName();
-                operationLog.info("Migrating database '" + databaseName + "' from version '" + databaseVersion 
-                                   + "' to '" + version + "'.");
+                operationLog.info("Migrating database '" + databaseName + "' from version " + databaseVersion 
+                                   + " to " + version + ".");
             }
             migrate(databaseVersion, version);
             if (operationLog.isInfoEnabled())
             {
                 String databaseName = adminDAO.getDatabaseName();
-                operationLog.info("Database '" + databaseName + "' successfully migrated from version '" 
-                                  + databaseVersion + "' to '" + version + "'.");
+                operationLog.info("Database '" + databaseName + "' successfully migrated from version " 
+                                  + databaseVersion + " to " + version + ".");
             }
         } else
         {
             String databaseName = adminDAO.getDatabaseName();
-            throw new EnvironmentFailureException("Couldn't revert database '" + databaseName + "' from version " 
-                                                  + databaseVersion + " to previous version " + version + ".");
+            String message = "Couldn't revert database '" + databaseName + "' from version " 
+                             + databaseVersion + " to previous version " + version + ".";
+            operationLog.error(message);
+            throw new EnvironmentFailureException(message);
         }
     }
 
@@ -120,7 +120,7 @@ public class DBMigrationEngine
             databaseDAO.createOwner();
         } catch (DataAccessException ex)
         {
-            if (userAlreadyExists(ex))
+            if (ownerAlreadyExists(ex))
             {
                 if (operationLog.isInfoEnabled())
                 {
@@ -128,6 +128,7 @@ public class DBMigrationEngine
                 }
             } else {
                 operationLog.error("Database owner couldn't be created:", ex);
+                throw ex;
             }
         }
     }
@@ -140,7 +141,9 @@ public class DBMigrationEngine
         Script script = scriptProvider.getSchemaScript(version);
         if (script == null)
         {
-            throw new EnvironmentFailureException("No schema script found for version " + version);
+            String message = "No schema script found for version " + version;
+            operationLog.error(message);
+            throw new EnvironmentFailureException(message);
         }
         executeScript(script, version);
     }
@@ -238,7 +241,7 @@ public class DBMigrationEngine
      * This is database specific.
      * </p>
      */
-    protected boolean userAlreadyExists(DataAccessException ex) {
+    protected boolean ownerAlreadyExists(DataAccessException ex) {
         // 42710 DUPLICATE OBJECT
         return SQLStateUtils.isDuplicateObject(SQLStateUtils.getSqlState(ex));
     }
diff --git a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java
index be67a104a94..c932e6d0a8a 100644
--- a/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java
+++ b/dbmigration/sourceTest/java/ch/systemsx/cisd/dbmigration/DBMigrationEngineTest.java
@@ -17,10 +17,14 @@
 package ch.systemsx.cisd.dbmigration;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.fail;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.SQLException;
 import java.util.Properties;
 
 import org.apache.log4j.ConsoleAppender;
@@ -28,10 +32,13 @@ import org.apache.log4j.PatternLayout;
 import org.apache.log4j.PropertyConfigurator;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
+import org.springframework.jdbc.BadSqlGrammarException;
+import org.springframework.jdbc.UncategorizedSQLException;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.common.db.SQLStateUtils;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.utilities.OSUtilities;
 
@@ -144,7 +151,7 @@ public class DBMigrationEngineTest
     }
     
     @Test
-    public void testCreateFromScratchWithFromScratchFlagFalse()
+    public void testCreateFromScratchForExistingOwner()
     {
         final String version = "042";
         context.checking(new MyExpectations()
@@ -157,11 +164,16 @@ public class DBMigrationEngineTest
                 one(daoFactory).getSqlScriptExecutor();
                 will(returnValue(scriptExecutor));
                 
+                one(adminDAO).dropDatabase();
                 one(logDAO).canConnectToDatabase();
                 will(returnValue(false));
                 one(adminDAO).getDatabaseName();
                 will(returnValue("my 1. database"));
                 one(adminDAO).createOwner();
+                SQLException exception = new SQLException("owner exists", SQLStateUtils.DUPLICATE_OBJECT);
+                will(throwException(new UncategorizedSQLException("", "", exception)));
+                one(adminDAO).getOwner();
+                will(returnValue("Albert"));
                 one(adminDAO).createDatabase();
                 one(logDAO).createTable();
                 one(scriptProvider).getSchemaScript(version);
@@ -171,7 +183,94 @@ public class DBMigrationEngineTest
                 one(adminDAO).getDatabaseName();
                 will(returnValue("my 2. database"));
             }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, true);
+        
+        logRecorder.reset();
+        migrationEngine.migrateTo(version);
+        assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 1. database' does not exist." + OSUtilities.LINE_SEPARATOR
+                + "INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Owner 'Albert' already exists." + OSUtilities.LINE_SEPARATOR
+                + "INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 2. database' version 042 has been successfully created.", getLogContent());
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testCreateFromScratchButCouldNotCreateOwner()
+    {
+        final String version = "042";
+        final BadSqlGrammarException exception = new BadSqlGrammarException("", "", new SQLException("owner"));
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(adminDAO).dropDatabase();
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(false));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 1. database"));
+                one(adminDAO).createOwner();
+                will(throwException(exception));
+            }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, true);
+        
+        logRecorder.reset();
+        try
+        {
+            migrationEngine.migrateTo(version);
+            fail("BadSqlGrammarException expected because owner couldn't be created");
+        } catch (BadSqlGrammarException e)
+        {
+            assertSame(exception, e);
+        }
+        assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 1. database' does not exist." + OSUtilities.LINE_SEPARATOR
+                + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " 
+                + "Database owner couldn't be created:" + OSUtilities.LINE_SEPARATOR 
+                + exception + OSUtilities.LINE_SEPARATOR + "Caused by: " + OSUtilities.LINE_SEPARATOR 
+                + getStackTrace(exception.getCause()), getLogContent());
+        
+        context.assertIsSatisfied();
+    }
 
+    @Test
+    public void testCreateFromScratchWithFromScratchFlagFalse()
+    {
+        final String version = "042";
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(false));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 1. database"));
+                one(adminDAO).createOwner();
+                one(adminDAO).createDatabase();
+                one(logDAO).createTable();
+                one(scriptProvider).getSchemaScript(version);
+                expectSuccessfulExecution(new Script("schema", "schema code"), version);
+                one(scriptProvider).getDataScript(version);
+                expectSuccessfulExecution(new Script("data", "data code"), version);
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 2. database"));
+            }
         });
         DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, false);
         
@@ -185,6 +284,89 @@ public class DBMigrationEngineTest
         context.assertIsSatisfied();
     }
     
+    @Test
+    public void testCreateFromScratchWithMissingDataScript()
+    {
+        final String version = "042";
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(false));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 1. database"));
+                one(adminDAO).createOwner();
+                one(adminDAO).createDatabase();
+                one(logDAO).createTable();
+                one(scriptProvider).getSchemaScript(version);
+                expectSuccessfulExecution(new Script("schema", "schema code"), version);
+                one(scriptProvider).getDataScript(version);
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 2. database"));
+            }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, false);
+        
+        logRecorder.reset();
+        migrationEngine.migrateTo(version);
+        assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 1. database' does not exist." + OSUtilities.LINE_SEPARATOR
+                + "INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 2. database' version 042 has been successfully created.", getLogContent());
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testCreateFromScratchWithMissingSchemaScript()
+    {
+        final String version = "042";
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(false));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my 1. database"));
+                one(adminDAO).createOwner();
+                one(adminDAO).createDatabase();
+                one(logDAO).createTable();
+                one(scriptProvider).getSchemaScript(version);
+            }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, false);
+        
+        logRecorder.reset();
+        String message = "No schema script found for version 042";
+        try
+        {
+            migrationEngine.migrateTo(version);
+            fail("EnvironmentFailureException expected because of missing schema script");
+        } catch (EnvironmentFailureException e)
+        {
+            assertEquals(message, e.getMessage());
+        }
+        assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Database 'my 1. database' does not exist." + OSUtilities.LINE_SEPARATOR
+                + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + message, getLogContent());
+        
+        context.assertIsSatisfied();
+    }
+    
     @Test
     public void testCaseNoMigrationNeeded()
     {
@@ -255,9 +437,9 @@ public class DBMigrationEngineTest
         logRecorder.reset();
         migrationEngine.migrateTo(toVersion);
         assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
-                + "Migrating database 'my 1. database' from version '099' to '101'." + OSUtilities.LINE_SEPARATOR
+                + "Migrating database 'my 1. database' from version 099 to 101." + OSUtilities.LINE_SEPARATOR
                 + "INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " 
-                + "Database 'my 2. database' successfully migrated from version '099' to '101'.", getLogContent());
+                + "Database 'my 2. database' successfully migrated from version 099 to 101.", getLogContent());
         
         context.assertIsSatisfied();
     }
@@ -304,15 +486,117 @@ public class DBMigrationEngineTest
             assertEquals(errorMessage, e.getMessage());
         }
         assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
-                + "Migrating database 'my 1. database' from version '099' to '101'." + OSUtilities.LINE_SEPARATOR
+                + "Migrating database 'my 1. database' from version 099 to 101." + OSUtilities.LINE_SEPARATOR
                 + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + errorMessage, getLogContent());
         
         context.assertIsSatisfied();
     }
     
+    @Test
+    public void testRevertMigration()
+    {
+        final String fromVersion = "101";
+        final String toVersion = "099";
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(true));
+                one(logDAO).getLastEntry();
+                LogEntry logEntry = new LogEntry();
+                logEntry.setVersion(fromVersion);
+                will(returnValue(logEntry));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my database"));
+            }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, false);
+        
+        logRecorder.reset();
+        String errorMessage = "Couldn't revert database 'my database' from version 101 to previous version 099.";
+        try
+        {
+            migrationEngine.migrateTo(toVersion);
+            fail("EnvironmentFailureException expected because migration couldn't be reverted");
+        } catch (EnvironmentFailureException e)
+        {
+            assertEquals(errorMessage, e.getMessage());
+        }
+        assertEquals("ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - " + errorMessage, 
+                     getLogContent());
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testFailureOfScriptExecution()
+    {
+        final String fromVersion = "001";
+        final String toVersion = "002";
+        final RuntimeException exception = new RuntimeException("execution of script failed");
+        context.checking(new MyExpectations()
+        {
+            {
+                one(daoFactory).getDatabaseDAO();
+                will(returnValue(adminDAO));
+                one(daoFactory).getDatabaseVersionLogDAO();
+                will(returnValue(logDAO));
+                one(daoFactory).getSqlScriptExecutor();
+                will(returnValue(scriptExecutor));
+                
+                one(logDAO).canConnectToDatabase();
+                will(returnValue(true));
+                one(logDAO).getLastEntry();
+                LogEntry logEntry = new LogEntry();
+                logEntry.setVersion(fromVersion);
+                will(returnValue(logEntry));
+                one(adminDAO).getDatabaseName();
+                will(returnValue("my database"));
+                one(scriptProvider).getMigrationScript(fromVersion, toVersion);
+                Script script = new Script("m-1-2", "code");
+                will(returnValue(script));
+                one(logDAO).logStart(toVersion, script.getName(), script.getCode());
+                one(scriptExecutor).execute(script.getCode());
+                will(throwException(exception));
+                one(logDAO).logFailure(with(equal(toVersion)), with(equal(script.getName())), with(same(exception)));
+            }
+        });
+        DBMigrationEngine migrationEngine = new DBMigrationEngine(daoFactory, scriptProvider, false);
+        
+        logRecorder.reset();
+        try
+        {
+            migrationEngine.migrateTo(toVersion);
+            fail("Exception expected");
+        } catch (RuntimeException e)
+        {
+            assertSame(exception, e);
+        }
+        assertEquals("INFO  OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - "
+                + "Migrating database 'my database' from version 001 to 002." + OSUtilities.LINE_SEPARATOR
+                + "ERROR OPERATION.ch.systemsx.cisd.dbmigration.DBMigrationEngine - Executing script 'm-1-2' failed"
+                + OSUtilities.LINE_SEPARATOR + getStackTrace(exception), getLogContent());
+        
+        context.assertIsSatisfied();
+    }
+    
     private String getLogContent()
     {
         return new String(logRecorder.toByteArray()).trim();
     }
     
+    private String getStackTrace(final Throwable throwable)
+    {
+        StringWriter stringWriter = new StringWriter();
+        throwable.printStackTrace(new PrintWriter(stringWriter));
+        return stringWriter.toString().trim();
+    }
+    
 }
-- 
GitLab