// Copyright (c) 2005 Scott Lamb <slamb@slamb.org> // Copyright (c) 2005 Dustin Sallings <dustin@spy.net> package net.spy.db; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Map; import net.spy.db.sp.SelectPrimaryKey; import net.spy.db.sp.UpdatePrimaryKey; import net.spy.test.db.BooleanTest; import net.spy.test.db.DialectTest; import net.spy.test.db.ImplTest; import net.spy.util.SpyConfig; import org.jmock.Mock; import org.jmock.MockObjectTestCase; import org.jmock.core.constraint.IsEqual; import org.jmock.core.matcher.InvokeAtLeastOnceMatcher; import org.jmock.core.matcher.InvokeOnceMatcher; import org.jmock.core.stub.ReturnStub; public class SPTTest extends MockObjectTestCase { private Mock connMock; private Connection conn; private Mock stMock; @Override public void setUp() { connMock = mock(Connection.class); conn = (Connection) connMock.proxy(); stMock = mock(PreparedStatement.class); } private void testCallSequence(boolean[] booleans) throws Exception { stMock.expects(once()).method("setQueryTimeout"); stMock.expects(once()).method("setMaxRows").with(eq(10)); BooleanTest bt = new BooleanTest(conn); connMock.expects(once()) .method("prepareStatement") .with(eq("select ? as a_boolean\n")) .will(returnValue(stMock.proxy())); Mock mdMock=mock(DatabaseMetaData.class); mdMock.expects(once()) .method("getDatabaseProductName") .will(returnValue("Unknown database")); connMock.expects(once()) .method("getMetaData") .will(returnValue(mdMock.proxy())); bt.setMaxRows(10); boolean tryCursor=false; for(int i=0; i<booleans.length; i++) { boolean aBoolean=booleans[i]; bt.setABoolean(aBoolean); // Meta data handling Mock rsmdMock=mock(ResultSetMetaData.class); rsmdMock.expects(atLeastOnce()) .method("getColumnCount") .will(returnValue(1)); rsmdMock.expects(atLeastOnce()) .method("getColumnName") .with(eq(1)) .will(returnValue("a_boolean")); rsmdMock.expects(atLeastOnce()) .method("getColumnTypeName") .with(eq(1)) .will(returnValue("BIT")); // Result set handling Mock rsMock = mock(ResultSet.class); ResultSet rs = (ResultSet) rsMock.proxy(); rsMock.expects(once()) .method("getMetaData") .will(returnValue(rsmdMock.proxy())); // What does the statement expect to happen each invocation? stMock.expects(once()) .method("setBoolean") .with(eq(1), eq(aBoolean)); stMock.expects(once()) .method("executeQuery") .will(returnValue(rs)); if(tryCursor) { stMock.expects(once()) .method("setCursorName") .with(eq("testCursor")); bt.setCursorName("testCursor"); } ResultSet actual = bt.executeQuery(); // The result should be the same assertSame(rs, actual); tryCursor=true; } stMock.expects(once()).method("close"); // After execution, we're going to look at more stuff. stMock.expects(once()) .method("getWarnings") .will(returnValue(null)); // Check for warnings. assertNull(bt.getWarnings()); bt.close(); } public void testSingleCalls() throws Exception { testCallSequence(new boolean[] { false }); testCallSequence(new boolean[] { true }); } public void testMultipleCalls() throws Exception { testCallSequence(new boolean[] { false, true }); } private void runQuerySelectorTest(SpyConfig conf) throws Exception { DialectTest dt=new DialectTest(conf); dt.setAnInt(13); ResultSet rs=dt.executeQuery(); rs.close(); dt.close(); // Validate all of the queries Map<String, String> queries=dt.getRegisteredQueries(); assertEquals("select ? as the_int\n", queries.get(QuerySelector.DEFAULT_QUERY)); assertEquals("select ? as the_int from unused\n", queries.get("another")); assertEquals("select ? as the_int from dual\n", queries.get("oracle")); } /** * Test query selection within SPTs. */ public void testQuerySelectionDefaultByDriver() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbConnectionSource", GeneralConnectionSource.class.getName()); runQuerySelectorTest(conf); } /** * Test query selection within SPTs (by driver name). */ public void testQuerySelectionDefault() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbDriverName", "blah.some.driver.name"); conf.put("dbConnectionSource", GeneralConnectionSource.class.getName()); runQuerySelectorTest(conf); } /** * Test a non-existent query source (should select default). */ public void testQuerySelectionNonExistent() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbConnectionSource", GeneralConnectionSource.class.getName()); conf.put("queryName", "martha"); runQuerySelectorTest(conf); } /** * Test an existing query selector by driver name for a driver whose query * we don't have. */ public void testQuerySelectionByMissingDriver() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbDriverName", "org.postgresql.Driver"); conf.put("dbConnectionSource", GeneralConnectionSource.class.getName()); runQuerySelectorTest(conf); } /** * Test a default query with a name close to another one. */ public void testQuerySelectionCloseToKnownDriver() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbDriverName", "oracle.jdbc.dr"); conf.put("dbConnectionSource", GeneralConnectionSource.class.getName()); runQuerySelectorTest(conf); } /** * Test the primary key selection spt. */ public void testSelectPrimaryKey() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbConnectionSource", PKConnectionSource.class.getName()); SelectPrimaryKey spk1=new SelectPrimaryKey(conf); spk1.setTableName("test"); Mock cMock=mock(Connection.class); SelectPrimaryKey spk2=new SelectPrimaryKey((Connection)cMock.proxy()); spk2.setTableName("test"); } /** * Test the primary key update spt. */ public void testUpdatePrimaryKey() throws Exception { SpyConfig conf=new SpyConfig(); conf.put("dbConnectionSource", PKConnectionSource.class.getName()); UpdatePrimaryKey upk1=new UpdatePrimaryKey(conf); upk1.setTableName("test"); Mock cMock=mock(Connection.class); UpdatePrimaryKey upk2=new UpdatePrimaryKey((Connection)cMock.proxy()); upk2.setTableName("test"); } private void successfulCoercion(DBSP db, String field, String val) throws Exception { db.setCoerced(field, null); db.setCoerced(field, val); } private void failedCoercion(DBSP db, String field, String val, Class<?> expectedException, String expectedMessage) throws SQLException { // This should be OK db.setCoerced(field, null); try { db.setCoerced(field, val); fail("Expected exception setting " + field + " to " + val); } catch(Exception e) { assertTrue("Unexpected exception", e.getClass().isAssignableFrom(expectedException)); if(expectedMessage != null) { assertEquals(expectedMessage, e.getMessage()); } } } /** * Test coercion routines. * * @throws Exception */ public void testCoercion() throws Exception { Mock cMock=mock(Connection.class); ImplTest it=new ImplTest((Connection)cMock.proxy()); successfulCoercion(it, "param1", "true"); successfulCoercion(it, "param1", "false"); failedCoercion(it, "param2", "some date", SQLException.class, "Date types not currently handled"); successfulCoercion(it, "param3", "13.523"); successfulCoercion(it, "param3", "13"); successfulCoercion(it, "param4", "35.2352"); successfulCoercion(it, "param4", "35"); successfulCoercion(it, "param5", "2532"); failedCoercion(it, "param5", "3.526", NumberFormatException.class, null); successfulCoercion(it, "param6", "83259252390528"); successfulCoercion(it, "param7", "992359236826722.835382"); successfulCoercion(it, "param8", "8835823952390523.23852"); successfulCoercion(it, "param9", "2"); successfulCoercion(it, "param10", "2"); successfulCoercion(it, "param11", "some obj"); successfulCoercion(it, "param12", "This is a string"); failedCoercion(it, "param13", "some date", SQLException.class, "Date types not currently handled"); failedCoercion(it, "param14", "some date", SQLException.class, "Date types not currently handled"); } // A dumb connection source that's just used to make the PK related SPT // constructors happy. public static class PKConnectionSource extends MockConnectionSource { @Override protected void setupMock(Mock connMock, SpyConfig conf) { // nothing } } public abstract static class AbsConnSrc extends MockConnectionSource { protected abstract String getQuery(); @Override protected void setupMock(Mock connMock, SpyConfig conf) { Mock dbMdMock=new Mock(DatabaseMetaData.class); dbMdMock.expects(new InvokeAtLeastOnceMatcher()) .method("getDatabaseProductName") .will(new ReturnStub("UnknownProduct")); connMock.expects(new InvokeAtLeastOnceMatcher()) .method("getMetaData") .will(new ReturnStub(dbMdMock.proxy())); Mock rsmdMock=new Mock(ResultSetMetaData.class); rsmdMock.expects(new InvokeAtLeastOnceMatcher()) .method("getColumnCount") .will(new ReturnStub(new Integer(1))); rsmdMock.expects(new InvokeAtLeastOnceMatcher()) .method("getColumnName") .with(new IsEqual(new Integer(1))) .will(new ReturnStub("the_int")); rsmdMock.expects(new InvokeAtLeastOnceMatcher()) .method("getColumnTypeName") .with(new IsEqual(new Integer(1))) .will(new ReturnStub("INTEGER")); Mock rsMock=new Mock(ResultSet.class); rsMock.expects(new InvokeOnceMatcher()).method("close"); rsMock.expects(new InvokeOnceMatcher()).method("getMetaData") .will(new ReturnStub(rsmdMock.proxy())); Mock pstMock=new Mock(PreparedStatement.class); pstMock.expects(new InvokeOnceMatcher()).method("setQueryTimeout"); pstMock.expects(new InvokeOnceMatcher()).method("setMaxRows") .with(new IsEqual(new Integer(0))); pstMock.expects(new InvokeOnceMatcher()).method("setInt") .with(new IsEqual(new Integer(1)), new IsEqual(new Integer(13))); pstMock.expects(new InvokeOnceMatcher()).method("executeQuery") .will(new ReturnStub(rsMock.proxy())); pstMock.expects(new InvokeOnceMatcher()).method("close"); connMock.expects(new InvokeOnceMatcher()).method("prepareStatement") .with(new IsEqual(getQuery())) .will(new ReturnStub(pstMock.proxy())); connMock.expects(new InvokeOnceMatcher()).method("close"); } } public static class GeneralConnectionSource extends AbsConnSrc { @Override public String getQuery() { return("select ? as the_int\n"); } } public static class OracleConnectionSource extends AbsConnSrc { @Override public String getQuery() { return("select ? as the_int from dual\n"); } } }