package net.sourceforge.mayfly; import junit.framework.TestCase; import net.sourceforge.mayfly.acceptance.SqlTestCase; import net.sourceforge.mayfly.datastore.DataStore; import net.sourceforge.mayfly.dump.SqlDumper; import net.sourceforge.mayfly.jdbc.JdbcConnection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JdbcTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); Class.forName("net.sourceforge.mayfly.JdbcDriver"); } @Override protected void tearDown() throws Exception { JdbcDriver.shutdown(); super.tearDown(); } public void testConnectViaDriverManager() throws Exception { Connection connection = DriverManager.getConnection("jdbc:mayfly:"); PreparedStatement createTable = connection.prepareStatement("CREATE TABLE FOO (a integer)"); assertEquals(0, createTable.executeUpdate()); createTable.close(); PreparedStatement select = connection.prepareStatement("SELECT A FROM FOO"); ResultSet results = select.executeQuery(); assertFalse(results.next()); results.close(); select.close(); connection.close(); } public void testBadJdbcUrl() throws Exception { expectedFailedConnection("Mayfly JDBC URL jdbc:mayfly:x not recognized", "jdbc:mayfly:x"); } public void testDoNotAcceptNull() throws Exception { JdbcDriver driver = new JdbcDriver(); assertFalse(driver.acceptsURL(null)); } public void testAccessSnapshotViaDriverManager() throws Exception { Database original = new Database(); original.execute("create table foo (a integer)"); original.execute("insert into foo(a) values(6)"); DataStore dataStore = original.dataStore(); String restored1 = JdbcDriver.create(dataStore); update(restored1, 1, "insert into foo(a) values(1)"); query(restored1, new String[] {"1", "6"}, "select a from foo"); String restored2 = JdbcDriver.create(dataStore); update(restored2, 1, "insert into foo(a) values(2)"); query(restored2, new String[] {"2", "6"}, "select a from foo"); } public void testSnapshot() throws Exception { String url = JdbcDriver.create(new DataStore()); update(url, 0, "create table foo(a integer)"); DataStore store = JdbcDriver.snapshot(url); String dump = new SqlDumper().dump(store); assertEquals("CREATE TABLE foo(\n a INTEGER\n);\n\n", dump); } public void testSnapshotOfDefault() throws Exception { update("jdbc:mayfly:", 0, "create table foo(b integer)"); DataStore store = JdbcDriver.snapshot("jdbc:mayfly:"); String dump = new SqlDumper().dump(store); assertEquals("CREATE TABLE foo(\n b INTEGER\n);\n\n", dump); } public void testSnapshotOfUnrecognized() throws Exception { try { JdbcDriver.snapshot("jdbc:mayfly:random:thing"); fail(); } catch (MayflyException e) { assertEquals( "Mayfly JDBC URL jdbc:mayfly:random:thing not recognized", e.getMessage()); } } public void testSnapshotViaConnection() throws Exception { String url = JdbcDriver.create(new DataStore()); update(url, 0, "create table foo(a integer)"); Connection connection = DriverManager.getConnection(url); passToSomeOtherPartOfTheCode(connection); } private void passToSomeOtherPartOfTheCode(Connection connection) { JdbcConnection mayflyConnection = (JdbcConnection) connection; DataStore store = mayflyConnection.snapshot(); String dump = new SqlDumper().dump(store); assertEquals("CREATE TABLE foo(\n a INTEGER\n);\n\n", dump); } public void testShutdown() throws Exception { String first = JdbcDriver.create(new Database().dataStore()); JdbcDriver.shutdown(); expectedFailedConnection("Mayfly JDBC URL " + first + " not recognized", first); String second = JdbcDriver.create(new Database().dataStore()); expectedFailedConnection("Mayfly JDBC URL " + first + " not recognized", first); update(second, 0, "create table foo (a integer)"); } public void testConnectionsOpenAtShutdownTime() throws Exception { /** * For the moment, a Connection contains a reference to the Database * it came from, and so will just operate against that old Database. * What we really want is that shutdown checks that all connections * are closed, and complains if not. Probably likewise for * connections which came from {@link Database#openConnection()} * (we'd need a shutdown method in Database which people would need * to call). * * Oh, and when we complain about a connection which hasn't been * closed, we probably want to include a stack trace to where it * was opened (or provide a mechanism for getting said stack trace). */ update("jdbc:mayfly:", 0, "create table foo (a integer)"); update("jdbc:mayfly:", 1, "insert into foo (a) values (5)"); Connection connection = DriverManager.getConnection("jdbc:mayfly:"); JdbcDriver.shutdown(); Statement statement = connection.createStatement(); SqlTestCase.assertResultSet((new String[] {"5"}), statement.executeQuery("select a from foo")); statement.close(); connection.close(); } public void testShutdownAndDefaultUrl() throws Exception { update("jdbc:mayfly:", 0, "create table foo (a integer)"); update("jdbc:mayfly:", 1, "insert into foo (a) values (5)"); query("jdbc:mayfly:", new String[] {"5"}, "select a from foo"); JdbcDriver.shutdown(); Connection connection = DriverManager.getConnection("jdbc:mayfly:"); Statement statement = connection.createStatement(); try { statement.executeQuery("select a from foo"); fail(); } catch (SQLException e) { assertEquals("no table foo", e.getMessage()); } statement.close(); connection.close(); } public void testIsClosed() throws Exception { Database database = new Database(); Connection connection = database.openConnection(); Connection secondConnection = database.openConnection(); assertFalse(connection.isClosed()); connection.close(); assertTrue(connection.isClosed()); // multiple close calls have no effect connection.close(); assertTrue(connection.isClosed()); assertFalse(secondConnection.isClosed()); } public void testOperationsOnClosedConnection() throws Exception { Database database = new Database(); Connection connection = database.openConnection(); connection.close(); try { connection.createStatement(); fail(); } catch (SQLException e) { assertEquals("connection is closed", e.getMessage()); } try { connection.prepareStatement("even syntax errors not detected"); fail(); } catch (SQLException e) { assertEquals("connection is closed", e.getMessage()); } } public void testFailingCommandViaStatement() throws Exception { Database database = new Database(); database.execute("create table foo(x integer not null)"); Connection connection = database.openConnection(); Statement statement = connection.createStatement(); try { statement.executeUpdate("insert into foo(x) values(null)"); fail(); } catch (MayflySqlException e) { assertEquals("column x cannot be null", e.getMessage()); assertEquals("insert into foo(x) values(null)", e.failingCommand()); } } public void testFailingCommandViaPreparedStatement() throws Exception { Database database = new Database(); database.execute("create table foo(x integer not null)"); Connection connection = database.openConnection(); PreparedStatement statement = connection.prepareStatement( "insert into foo(x) values(null)"); try { statement.executeUpdate(); fail(); } catch (MayflySqlException e) { assertEquals("column x cannot be null", e.getMessage()); assertEquals("insert into foo(x) values(null)", e.failingCommand()); } } private void query(String jdbcUrl, String[] expectedResults, String sql) throws SQLException { Connection connection = DriverManager.getConnection(jdbcUrl); Statement statement = connection.createStatement(); SqlTestCase.assertResultSet(expectedResults, statement.executeQuery(sql)); statement.close(); connection.close(); } private void update(String jdbcUrl, int expectedRowsAffected, String sql) throws SQLException { Connection connection = DriverManager.getConnection(jdbcUrl); Statement statement = connection.createStatement(); assertEquals(expectedRowsAffected, statement.executeUpdate(sql)); statement.close(); connection.close(); } private void expectedFailedConnection(String expectedMessage, String url) { try { DriverManager.getConnection(url); fail(); } catch (SQLException expected) { assertEquals(expectedMessage, expected.getMessage()); } } }