package net.ttddyy.dsproxy.proxy; import net.ttddyy.dsproxy.ExecutionInfo; import net.ttddyy.dsproxy.QueryInfo; import net.ttddyy.dsproxy.listener.QueryExecutionListener; import net.ttddyy.dsproxy.proxy.jdk.ConnectionInvocationHandler; import net.ttddyy.dsproxy.proxy.jdk.JdkJdbcProxyFactory; import net.ttddyy.dsproxy.transform.QueryTransformer; import org.junit.Test; import org.mockito.ArgumentCaptor; import java.io.InputStream; import java.io.Reader; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.Ref; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * @author Tadaya Tsuyukubo */ public class PreparedStatementProxyLogicMockTest { private static final String DS_NAME = "myDS"; @Test public void testExecuteQuery() throws Throwable { final String query = "select * from emp where id = ?"; PreparedStatement stat = mock(PreparedStatement.class); QueryExecutionListener listener = mock(QueryExecutionListener.class); PreparedStatementProxyLogic logic = getProxyLogic(stat, query, listener); Array array = mock(Array.class); InputStream inputStream = mock(InputStream.class); BigDecimal bigDecimal = mock(BigDecimal.class); InputStream binaryStream = mock(InputStream.class); Blob blob = mock(Blob.class); boolean booleanValue = true; Reader reader = mock(Reader.class); Clob clob = mock(Clob.class); Date date = mock(Date.class); double doubleValue = 10.0d; Float floatValue = 20f; Integer intvalue = 30; Long longValue = 40L; Object object = mock(Object.class); Ref ref = mock(Ref.class); Short shortValue = 50; String stringValue = "str"; Time time = mock(Time.class); Timestamp timestamp = mock(Timestamp.class); URL url = new URL("http://foo.com"); Method setArray = PreparedStatement.class.getMethod("setArray", int.class, Array.class); Method setAsciiStream = PreparedStatement.class.getMethod("setAsciiStream", int.class, InputStream.class); Method setBigDecimal = PreparedStatement.class.getMethod("setBigDecimal", int.class, BigDecimal.class); Method setBinaryStream = PreparedStatement.class.getMethod("setBinaryStream", int.class, InputStream.class); Method setBlob = PreparedStatement.class.getMethod("setBlob", int.class, Blob.class); Method setBoolean = PreparedStatement.class.getMethod("setBoolean", int.class, boolean.class); Method setCharacterStream = PreparedStatement.class.getMethod("setCharacterStream", int.class, Reader.class); Method setClob = PreparedStatement.class.getMethod("setClob", int.class, Clob.class); Method setDate = PreparedStatement.class.getMethod("setDate", int.class, Date.class); Method setDouble = PreparedStatement.class.getMethod("setDouble", int.class, double.class); Method setFloat = PreparedStatement.class.getMethod("setFloat", int.class, float.class); Method setInt = PreparedStatement.class.getMethod("setInt", int.class, int.class); Method setLong = PreparedStatement.class.getMethod("setLong", int.class, long.class); Method setNull = PreparedStatement.class.getMethod("setNull", int.class, int.class); Method setObject = PreparedStatement.class.getMethod("setObject", int.class, Object.class); Method setRef = PreparedStatement.class.getMethod("setRef", int.class, Ref.class); Method setShort = PreparedStatement.class.getMethod("setShort", int.class, short.class); Method setString = PreparedStatement.class.getMethod("setString", int.class, String.class); Method setTime = PreparedStatement.class.getMethod("setTime", int.class, Time.class); Method setTimestamp = PreparedStatement.class.getMethod("setTimestamp", int.class, Timestamp.class); Method setURL = PreparedStatement.class.getMethod("setURL", int.class, URL.class); Method executeQuery = PreparedStatement.class.getMethod("executeQuery"); logic.invoke(setArray, new Object[]{1, array}); logic.invoke(setAsciiStream, new Object[]{2, inputStream}); logic.invoke(setBigDecimal, new Object[]{3, bigDecimal}); logic.invoke(setBinaryStream, new Object[]{4, binaryStream}); logic.invoke(setBlob, new Object[]{5, blob}); logic.invoke(setBoolean, new Object[]{6, booleanValue}); logic.invoke(setCharacterStream, new Object[]{7, reader}); logic.invoke(setClob, new Object[]{8, clob}); logic.invoke(setDate, new Object[]{9, date}); logic.invoke(setDouble, new Object[]{10, doubleValue}); logic.invoke(setFloat, new Object[]{11, floatValue}); logic.invoke(setInt, new Object[]{12, intvalue}); logic.invoke(setLong, new Object[]{13, longValue}); logic.invoke(setNull, new Object[]{14, Types.VARCHAR}); logic.invoke(setObject, new Object[]{15, object}); logic.invoke(setRef, new Object[]{16, ref}); logic.invoke(setShort, new Object[]{17, shortValue}); logic.invoke(setString, new Object[]{18, stringValue}); logic.invoke(setTime, new Object[]{19, time}); logic.invoke(setTimestamp, new Object[]{20, timestamp}); logic.invoke(setURL, new Object[]{21, url}); logic.invoke(executeQuery, null); verify(stat).setArray(1, array); verify(stat).setAsciiStream(2, inputStream); verify(stat).setBigDecimal(3, bigDecimal); verify(stat).setBinaryStream(4, binaryStream); verify(stat).setBlob(5, blob); verify(stat).setBoolean(6, booleanValue); verify(stat).setCharacterStream(7, reader); verify(stat).setClob(8, clob); verify(stat).setDate(9, date); verify(stat).setDouble(10, doubleValue); verify(stat).setFloat(11, floatValue); verify(stat).setInt(12, intvalue); verify(stat).setLong(13, longValue); verify(stat).setNull(14, Types.VARCHAR); verify(stat).setObject(15, object); verify(stat).setRef(16, ref); verify(stat).setShort(17, shortValue); verify(stat).setString(18, stringValue); verify(stat).setTime(19, time); verify(stat).setTimestamp(20, timestamp); verify(stat).setURL(21, url); verify(stat).executeQuery(); Map<String, Object> expectedQueryArgas = new LinkedHashMap<String, Object>(); expectedQueryArgas.put("1", array); expectedQueryArgas.put("2", inputStream); expectedQueryArgas.put("3", bigDecimal); expectedQueryArgas.put("4", binaryStream); expectedQueryArgas.put("5", blob); expectedQueryArgas.put("6", booleanValue); expectedQueryArgas.put("7", reader); expectedQueryArgas.put("8", clob); expectedQueryArgas.put("9", date); expectedQueryArgas.put("10", doubleValue); expectedQueryArgas.put("11", floatValue); expectedQueryArgas.put("12", intvalue); expectedQueryArgas.put("13", longValue); expectedQueryArgas.put("14", Types.VARCHAR); expectedQueryArgas.put("15", object); expectedQueryArgas.put("16", ref); expectedQueryArgas.put("17", shortValue); expectedQueryArgas.put("18", stringValue); expectedQueryArgas.put("19", time); expectedQueryArgas.put("20", timestamp); expectedQueryArgas.put("21", url); verifyListener(listener, "executeQuery", query, expectedQueryArgas); } private PreparedStatementProxyLogic getProxyLogic(PreparedStatement ps, String query, QueryExecutionListener listener) { InterceptorHolder interceptorHolder = new InterceptorHolder(listener, QueryTransformer.DEFAULT); return new PreparedStatementProxyLogic(ps, query, interceptorHolder, DS_NAME, new JdkJdbcProxyFactory()); } @SuppressWarnings("unchecked") private void verifyListener(QueryExecutionListener listener, String methodName, String query, Map<String, Object> expectedQueryArgs) { ArgumentCaptor<ExecutionInfo> executionInfoCaptor = ArgumentCaptor.forClass(ExecutionInfo.class); ArgumentCaptor<List> queryInfoListCaptor = ArgumentCaptor.forClass(List.class); verify(listener).afterQuery(executionInfoCaptor.capture(), queryInfoListCaptor.capture()); ExecutionInfo execInfo = executionInfoCaptor.getValue(); assertThat(execInfo.getMethod(), is(notNullValue())); assertThat(execInfo.getMethod().getName(), is(methodName)); assertThat(execInfo.getMethodArgs(), is(nullValue())); assertThat(execInfo.getDataSourceName(), is(DS_NAME)); assertThat(execInfo.getThrowable(), is(nullValue())); assertThat(execInfo.isBatch(), is(false)); assertThat(execInfo.getBatchSize(), is(0)); List<QueryInfo> queryInfoList = queryInfoListCaptor.getValue(); assertThat(queryInfoList.size(), is(1)); QueryInfo queryInfo = queryInfoList.get(0); assertThat(queryInfo.getQuery(), is(query)); List<List<ParameterSetOperation>> queryArgsList = queryInfo.getParametersList(); assertThat("non-batch query size is always 1", queryArgsList, hasSize(1)); Map<String, Object> queryArgs = new HashMap<String, Object>(); for (ParameterSetOperation operation : queryArgsList.get(0)) { Object[] args = operation.getArgs(); queryArgs.put(args[0].toString(), args[1]); } assertThat(queryArgs, aMapWithSize(expectedQueryArgs.size())); for (Map.Entry<String, Object> entry : expectedQueryArgs.entrySet()) { assertThat(queryArgs, hasEntry(entry.getKey(), entry.getValue())); } } @Test public void testBatch() throws Throwable { final String query = "update emp set name = ? where id = ?"; PreparedStatement stat = mock(PreparedStatement.class); when(stat.executeBatch()).thenReturn(new int[]{1, 1, 1}); QueryExecutionListener listener = mock(QueryExecutionListener.class); PreparedStatementProxyLogic logic = getProxyLogic(stat, query, listener); Method setString = PreparedStatement.class.getMethod("setString", int.class, String.class); Method setInt = PreparedStatement.class.getMethod("setInt", int.class, int.class); Method addBatch = PreparedStatement.class.getMethod("addBatch"); Method executeBatch = PreparedStatement.class.getMethod("executeBatch"); logic.invoke(setString, new Object[]{1, "foo"}); logic.invoke(setInt, new Object[]{2, 10}); logic.invoke(addBatch, null); logic.invoke(setString, new Object[]{1, "bar"}); logic.invoke(setInt, new Object[]{2, 20}); logic.invoke(addBatch, null); logic.invoke(setString, new Object[]{1, "baz"}); logic.invoke(setInt, new Object[]{2, 30}); logic.invoke(addBatch, null); Object result = logic.invoke(executeBatch, null); assertThat(result, is(instanceOf(int[].class))); assertThat(((int[]) result).length, is(3)); assertThat(((int[]) result)[0], is(1)); assertThat(((int[]) result)[1], is(1)); assertThat(((int[]) result)[2], is(1)); verify(stat).setString(1, "foo"); verify(stat).setInt(2, 10); verify(stat).setString(1, "bar"); verify(stat).setInt(2, 20); verify(stat).setString(1, "baz"); verify(stat).setInt(2, 30); verify(stat, times(3)).addBatch(); Map<String, Object> expectedArgs1 = new LinkedHashMap<String, Object>(); expectedArgs1.put("1", "foo"); expectedArgs1.put("2", 10); Map<String, Object> expectedArgs2 = new LinkedHashMap<String, Object>(); expectedArgs2.put("1", "bar"); expectedArgs2.put("2", 20); Map<String, Object> expectedArgs3 = new LinkedHashMap<String, Object>(); expectedArgs3.put("1", "baz"); expectedArgs3.put("2", 30); MockTestUtils.verifyListenerForBatch(listener, DS_NAME, query, expectedArgs1, expectedArgs2, expectedArgs3); } @Test public void testBatchWithClearBatch() throws Throwable { final String query = "update emp set name = ? where id = ?"; PreparedStatement stat = mock(PreparedStatement.class); when(stat.executeBatch()).thenReturn(new int[]{1}); QueryExecutionListener listener = mock(QueryExecutionListener.class); PreparedStatementProxyLogic logic = getProxyLogic(stat, query, listener); Method setString = PreparedStatement.class.getMethod("setString", int.class, String.class); Method setInt = PreparedStatement.class.getMethod("setInt", int.class, int.class); Method addBatch = PreparedStatement.class.getMethod("addBatch"); Method clearBatch = PreparedStatement.class.getMethod("clearBatch"); Method executeBatch = PreparedStatement.class.getMethod("executeBatch"); logic.invoke(setString, new Object[]{1, "foo"}); logic.invoke(setInt, new Object[]{2, 10}); logic.invoke(addBatch, null); logic.invoke(clearBatch, null); logic.invoke(setString, new Object[]{1, "FOO"}); logic.invoke(setInt, new Object[]{2, 20}); logic.invoke(addBatch, null); Object result = logic.invoke(executeBatch, null); assertThat(result, is(instanceOf(int[].class))); assertThat(((int[]) result).length, is(1)); assertThat(((int[]) result)[0], is(1)); verify(stat).setString(1, "foo"); verify(stat).setInt(2, 10); verify(stat).clearBatch(); verify(stat).setString(1, "FOO"); verify(stat).setInt(2, 20); verify(stat, times(2)).addBatch(); Map<String, Object> expectedArgs = new LinkedHashMap<String, Object>(); expectedArgs.put("1", "FOO"); expectedArgs.put("2", 20); MockTestUtils.verifyListenerForBatch(listener, DS_NAME, query, expectedArgs); } @Test public void testBatchWithClearParameters() throws Throwable { final String query = "update emp set name = ? where id = ?"; PreparedStatement stat = mock(PreparedStatement.class); when(stat.executeBatch()).thenReturn(new int[]{1}); QueryExecutionListener listener = mock(QueryExecutionListener.class); PreparedStatementProxyLogic logic = getProxyLogic(stat, query, listener); Method setString = PreparedStatement.class.getMethod("setString", int.class, String.class); Method setInt = PreparedStatement.class.getMethod("setInt", int.class, int.class); Method addBatch = PreparedStatement.class.getMethod("addBatch"); Method clearParametes = PreparedStatement.class.getMethod("clearParameters"); Method executeBatch = PreparedStatement.class.getMethod("executeBatch"); logic.invoke(setString, new Object[]{1, "foo"}); logic.invoke(clearParametes, null); logic.invoke(setString, new Object[]{1, "FOO"}); logic.invoke(setInt, new Object[]{2, 10}); logic.invoke(addBatch, null); Object result = logic.invoke(executeBatch, null); assertThat(result, is(instanceOf(int[].class))); assertThat(((int[]) result).length, is(1)); assertThat(((int[]) result)[0], is(1)); verify(stat).setString(1, "foo"); verify(stat).clearParameters(); verify(stat).setString(1, "FOO"); verify(stat).setInt(2, 10); verify(stat).addBatch(); Map<String, Object> expectedArgs = new LinkedHashMap<String, Object>(); expectedArgs.put("1", "FOO"); expectedArgs.put("2", 10); MockTestUtils.verifyListenerForBatch(listener, DS_NAME, query, expectedArgs); } @SuppressWarnings("unchecked") @Test public void testClearParameters() throws Throwable { final String query = "update emp set name = ? where id = ?"; PreparedStatement stat = mock(PreparedStatement.class); QueryExecutionListener listener = mock(QueryExecutionListener.class); PreparedStatementProxyLogic logic = getProxyLogic(stat, query, listener); Method setString = PreparedStatement.class.getMethod("setString", int.class, String.class); Method setInt = PreparedStatement.class.getMethod("setInt", int.class, int.class); Method addBatch = PreparedStatement.class.getMethod("addBatch"); Method clearParametes = PreparedStatement.class.getMethod("clearParameters"); Method executeBatch = PreparedStatement.class.getMethod("executeBatch"); logic.invoke(setString, new Object[]{1, "foo"}); logic.invoke(setInt, new Object[]{2, 10}); logic.invoke(clearParametes, null); logic.invoke(addBatch, null); logic.invoke(executeBatch, null); verify(stat).setString(1, "foo"); verify(stat).setInt(2, 10); verify(stat).clearParameters(); verify(stat).addBatch(); ArgumentCaptor<List> queryInfoListCaptor = ArgumentCaptor.forClass(List.class); verify(listener).afterQuery(any(ExecutionInfo.class), queryInfoListCaptor.capture()); List<QueryInfo> queryInfoList = queryInfoListCaptor.getValue(); assertThat(queryInfoList.size(), is(1)); assertThat(queryInfoList.get(0).getParametersList(), hasSize(1)); assertThat("Args should be empty", queryInfoList.get(0).getParametersList().get(0), empty()); } @Test public void testGetTarget() throws Throwable { PreparedStatement stmt = mock(PreparedStatement.class); PreparedStatementProxyLogic logic = getProxyLogic(stmt, null, null); Method method = ProxyJdbcObject.class.getMethod("getTarget"); Object result = logic.invoke(method, null); assertThat(result, is(instanceOf(PreparedStatement.class))); assertThat((PreparedStatement) result, is(sameInstance(stmt))); } @Test public void testUnwrap() throws Throwable { PreparedStatement mock = mock(PreparedStatement.class); when(mock.unwrap(String.class)).thenReturn("called"); PreparedStatementProxyLogic logic = getProxyLogic(mock, null, null); Method method = PreparedStatement.class.getMethod("unwrap", Class.class); Object result = logic.invoke(method, new Object[]{String.class}); verify(mock).unwrap(String.class); assertThat(result, is(instanceOf(String.class))); assertThat((String) result, is("called")); } @Test public void testIsWrapperFor() throws Throwable { PreparedStatement mock = mock(PreparedStatement.class); when(mock.isWrapperFor(String.class)).thenReturn(true); PreparedStatementProxyLogic logic = getProxyLogic(mock, null, null); Method method = PreparedStatement.class.getMethod("isWrapperFor", Class.class); Object result = logic.invoke(method, new Object[]{String.class}); verify(mock).isWrapperFor(String.class); assertThat(result, is(instanceOf(boolean.class))); assertThat((Boolean) result, is(true)); } @Test public void testGetConnection() throws Throwable { Connection conn = mock(Connection.class); PreparedStatement stat = mock(PreparedStatement.class); when(stat.getConnection()).thenReturn(conn); PreparedStatementProxyLogic logic = getProxyLogic(stat, null, null); Method method = PreparedStatement.class.getMethod("getConnection"); Object result = logic.invoke(method, null); assertThat(result, is(instanceOf(Connection.class))); verify(stat).getConnection(); assertThat(Proxy.isProxyClass(result.getClass()), is(true)); InvocationHandler handler = Proxy.getInvocationHandler(result); assertThat(handler, is(instanceOf(ConnectionInvocationHandler.class))); assertThat(result, is(instanceOf(ProxyJdbcObject.class))); Object obj = ((ProxyJdbcObject) result).getTarget(); assertThat(obj, is(instanceOf(Connection.class))); Connection resultConn = (Connection) obj; assertThat(resultConn, is(sameInstance(conn))); } }