From 4df6414d27a675e390b6e525b64b4467f446dbfe Mon Sep 17 00:00:00 2001 From: baztian Date: Sun, 12 Mar 2017 21:06:12 +0100 Subject: [PATCH 1/4] Write testcase for #18. (Very rough version. MockConnection method should be more flexible and use less copy code) --- mockdriver/pom.xml | 5 ++++ .../jaydebeapi/mockdriver/MockConnection.java | 29 ++++++++++++++++++- test/test_mock.py | 8 +++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mockdriver/pom.xml b/mockdriver/pom.xml index 8a78498..2e9f37b 100644 --- a/mockdriver/pom.xml +++ b/mockdriver/pom.xml @@ -7,6 +7,11 @@ 1.0-SNAPSHOT jar + + 1.8 + 1.8 + + org.mockito diff --git a/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java b/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java index 8ed0703..0bb4cb5 100644 --- a/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java +++ b/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java @@ -1,28 +1,35 @@ + package org.jaydebeapi.mockdriver; import java.lang.reflect.Field; import java.sql.Connection; +import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.time.LocalDate; + import org.mockito.Mockito; public abstract class MockConnection implements Connection { ResultSet mockResultSet; + public final void mockExceptionOnCommit(String className, String exceptionMessage) throws SQLException { Throwable exception = createException(className, exceptionMessage); Mockito.doThrow(exception).when(this).commit(); } + public final void mockExceptionOnRollback(String className, String exceptionMessage) throws SQLException { Throwable exception = createException(className, exceptionMessage); Mockito.doThrow(exception).when(this).rollback(); } + public final void mockExceptionOnExecute(String className, String exceptionMessage) throws SQLException { PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); Throwable exception = createException(className, exceptionMessage); @@ -30,6 +37,23 @@ public abstract class MockConnection implements Connection { Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); } + + public final void mockDateResult(int year, int month, int day) throws SQLException { + PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(mockPreparedStatement.execute()).thenReturn(true); + mockResultSet = Mockito.mock(ResultSet.class, "ResultSet(for date)"); + Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(mockResultSet); + Mockito.when(mockResultSet.next()).thenReturn(true); + ResultSetMetaData mockMetaData = Mockito.mock(ResultSetMetaData.class); + Mockito.when(mockResultSet.getMetaData()).thenReturn(mockMetaData); + Mockito.when(mockMetaData.getColumnCount()).thenReturn(1); + Date ancientDate = Date.valueOf(LocalDate.of(year, month, day)); + Mockito.when(mockResultSet.getDate(1)).thenReturn(ancientDate); + Mockito.when(mockMetaData.getColumnType(1)).thenReturn(Types.DATE); + Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); + } + + public final void mockType(String sqlTypesName) throws SQLException { PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); Mockito.when(mockPreparedStatement.execute()).thenReturn(true); @@ -44,11 +68,13 @@ public abstract class MockConnection implements Connection { Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); } + public final ResultSet verifyResultSet() { return Mockito.verify(mockResultSet); } - private static Throwable createException(String className, String exceptionMessage) { + + private static Throwable createException(String className, String exceptionMessage) { try { return (Throwable) Class.forName(className).getConstructor(String.class).newInstance(exceptionMessage); } catch (Exception e) { @@ -56,6 +82,7 @@ public abstract class MockConnection implements Connection { } } + private static int extractTypeCodeForName(String sqlTypesName) { try { Field field = Types.class.getField(sqlTypesName); diff --git a/test/test_mock.py b/test/test_mock.py index 6dcb702..0afc907 100644 --- a/test/test_mock.py +++ b/test/test_mock.py @@ -50,6 +50,13 @@ class MockTest(unittest.TestCase): 'getObject')) verify_get(1) + def test_ancient_date_mapped(self): + self.conn.jconn.mockDateResult(1899, 12, 31) + cursor = self.conn.cursor() + cursor.execute("dummy stmt") + result = cursor.fetchone() + self.assertEquals(result[0], "1899-12-31") + def test_sql_exception_on_execute(self): self.conn.jconn.mockExceptionOnExecute("java.sql.SQLException", "expected") cursor = self.conn.cursor() @@ -59,6 +66,7 @@ class MockTest(unittest.TestCase): except jaydebeapi.DatabaseError as e: self.assertEquals(str(e), "java.sql.SQLException: expected") + def test_runtime_exception_on_execute(self): self.conn.jconn.mockExceptionOnExecute("java.lang.RuntimeException", "expected") cursor = self.conn.cursor() From 8f3345bcdfa9b9b69672ba98647db4b7cef8e0af Mon Sep 17 00:00:00 2001 From: baztian Date: Sun, 12 Mar 2017 21:47:10 +0100 Subject: [PATCH 2/4] Make mock driver Java 7 compatible again (Travis standard JDK). --- mockdriver/pom.xml | 4 +- .../jaydebeapi/mockdriver/MockConnection.java | 150 +++++++++--------- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/mockdriver/pom.xml b/mockdriver/pom.xml index 2e9f37b..e261790 100644 --- a/mockdriver/pom.xml +++ b/mockdriver/pom.xml @@ -8,8 +8,8 @@ jar - 1.8 - 1.8 + 1.7 + 1.7 diff --git a/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java b/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java index 0bb4cb5..6c959fd 100644 --- a/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java +++ b/mockdriver/src/main/java/org/jaydebeapi/mockdriver/MockConnection.java @@ -1,4 +1,3 @@ - package org.jaydebeapi.mockdriver; import java.lang.reflect.Field; @@ -9,93 +8,92 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; -import java.time.LocalDate; - +import java.util.Calendar; import org.mockito.Mockito; public abstract class MockConnection implements Connection { - ResultSet mockResultSet; + ResultSet mockResultSet; - - public final void mockExceptionOnCommit(String className, String exceptionMessage) throws SQLException { - Throwable exception = createException(className, exceptionMessage); - Mockito.doThrow(exception).when(this).commit(); + private static Throwable createException(String className, String exceptionMessage) { + try { + return (Throwable) Class.forName(className).getConstructor(String.class) + .newInstance(exceptionMessage); + } catch (Exception e) { + throw new RuntimeException("Couldn't initialize class " + className + ".", e); } + } - - public final void mockExceptionOnRollback(String className, String exceptionMessage) throws SQLException { - Throwable exception = createException(className, exceptionMessage); - Mockito.doThrow(exception).when(this).rollback(); + private static int extractTypeCodeForName(String sqlTypesName) { + try { + Field field = Types.class.getField(sqlTypesName); + return field.getInt(null); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException("Type " + sqlTypesName + " not found in Types class.", e); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); } + } + public final void mockExceptionOnCommit(String className, String exceptionMessage) + throws SQLException { + Throwable exception = createException(className, exceptionMessage); + Mockito.doThrow(exception).when(this).commit(); + } - public final void mockExceptionOnExecute(String className, String exceptionMessage) throws SQLException { - PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); - Throwable exception = createException(className, exceptionMessage); - Mockito.when(mockPreparedStatement.execute()).thenThrow(exception); - Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); - } + public final void mockExceptionOnRollback(String className, String exceptionMessage) + throws SQLException { + Throwable exception = createException(className, exceptionMessage); + Mockito.doThrow(exception).when(this).rollback(); + } + public final void mockExceptionOnExecute(String className, String exceptionMessage) + throws SQLException { + PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); + Throwable exception = createException(className, exceptionMessage); + Mockito.when(mockPreparedStatement.execute()).thenThrow(exception); + Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); + } - public final void mockDateResult(int year, int month, int day) throws SQLException { - PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); - Mockito.when(mockPreparedStatement.execute()).thenReturn(true); - mockResultSet = Mockito.mock(ResultSet.class, "ResultSet(for date)"); - Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(mockResultSet); - Mockito.when(mockResultSet.next()).thenReturn(true); - ResultSetMetaData mockMetaData = Mockito.mock(ResultSetMetaData.class); - Mockito.when(mockResultSet.getMetaData()).thenReturn(mockMetaData); - Mockito.when(mockMetaData.getColumnCount()).thenReturn(1); - Date ancientDate = Date.valueOf(LocalDate.of(year, month, day)); - Mockito.when(mockResultSet.getDate(1)).thenReturn(ancientDate); - Mockito.when(mockMetaData.getColumnType(1)).thenReturn(Types.DATE); - Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); - } + public final void mockDateResult(int year, int month, int day) throws SQLException { + PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(mockPreparedStatement.execute()).thenReturn(true); + mockResultSet = Mockito.mock(ResultSet.class, "ResultSet(for date)"); + Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(mockResultSet); + Mockito.when(mockResultSet.next()).thenReturn(true); + ResultSetMetaData mockMetaData = Mockito.mock(ResultSetMetaData.class); + Mockito.when(mockResultSet.getMetaData()).thenReturn(mockMetaData); + Mockito.when(mockMetaData.getColumnCount()).thenReturn(1); + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month - 1); + cal.set(Calendar.DAY_OF_MONTH, day); + Date ancientDate = new Date(cal.getTime().getTime()); + Mockito.when(mockResultSet.getDate(1)).thenReturn(ancientDate); + Mockito.when(mockMetaData.getColumnType(1)).thenReturn(Types.DATE); + Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); + } + public final void mockType(String sqlTypesName) throws SQLException { + PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); + Mockito.when(mockPreparedStatement.execute()).thenReturn(true); + mockResultSet = Mockito.mock(ResultSet.class, "ResultSet(for type " + sqlTypesName + ")"); + Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(mockResultSet); + Mockito.when(mockResultSet.next()).thenReturn(true); + ResultSetMetaData mockMetaData = Mockito.mock(ResultSetMetaData.class); + Mockito.when(mockResultSet.getMetaData()).thenReturn(mockMetaData); + Mockito.when(mockMetaData.getColumnCount()).thenReturn(1); + int sqlTypeCode = extractTypeCodeForName(sqlTypesName); + Mockito.when(mockMetaData.getColumnType(1)).thenReturn(sqlTypeCode); + Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); + } - public final void mockType(String sqlTypesName) throws SQLException { - PreparedStatement mockPreparedStatement = Mockito.mock(PreparedStatement.class); - Mockito.when(mockPreparedStatement.execute()).thenReturn(true); - mockResultSet = Mockito.mock(ResultSet.class, "ResultSet(for type " + sqlTypesName + ")"); - Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(mockResultSet); - Mockito.when(mockResultSet.next()).thenReturn(true); - ResultSetMetaData mockMetaData = Mockito.mock(ResultSetMetaData.class); - Mockito.when(mockResultSet.getMetaData()).thenReturn(mockMetaData); - Mockito.when(mockMetaData.getColumnCount()).thenReturn(1); - int sqlTypeCode = extractTypeCodeForName(sqlTypesName); - Mockito.when(mockMetaData.getColumnType(1)).thenReturn(sqlTypeCode); - Mockito.when(this.prepareStatement(Mockito.anyString())).thenReturn(mockPreparedStatement); - } - - - public final ResultSet verifyResultSet() { - return Mockito.verify(mockResultSet); - } - - - private static Throwable createException(String className, String exceptionMessage) { - try { - return (Throwable) Class.forName(className).getConstructor(String.class).newInstance(exceptionMessage); - } catch (Exception e) { - throw new RuntimeException("Couldn't initialize class " + className + ".", e); - } - } - - - private static int extractTypeCodeForName(String sqlTypesName) { - try { - Field field = Types.class.getField(sqlTypesName); - return field.getInt(null); - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Type " + sqlTypesName + " not found in Types class.", e); - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - + public final ResultSet verifyResultSet() { + return Mockito.verify(mockResultSet); + } } From 364a3584d55d18ccf49f17b3a89d11f61c8e62a2 Mon Sep 17 00:00:00 2001 From: baztian Date: Sun, 12 Mar 2017 21:47:29 +0100 Subject: [PATCH 3/4] Work around bug. Fixes #18. --- jaydebeapi/__init__.py | 8 ++++++-- test/test_mock.py | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jaydebeapi/__init__.py b/jaydebeapi/__init__.py index bb77016..4e1c597 100644 --- a/jaydebeapi/__init__.py +++ b/jaydebeapi/__init__.py @@ -595,8 +595,12 @@ def _to_date(rs, col): java_val = rs.getDate(col) if not java_val: return - d = datetime.datetime.strptime(str(java_val)[:10], "%Y-%m-%d") - return d.strftime("%Y-%m-%d") + # The following code requires Python 3.3+ on dates before year 1900. + # d = datetime.datetime.strptime(str(java_val)[:10], "%Y-%m-%d") + # return d.strftime("%Y-%m-%d") + # Workaround / simpler soltution (see + # https://github.com/baztian/jaydebeapi/issues/18): + return str(java_val)[:10] def _to_binary(rs, col): java_val = rs.getObject(col) diff --git a/test/test_mock.py b/test/test_mock.py index 0afc907..09443a1 100644 --- a/test/test_mock.py +++ b/test/test_mock.py @@ -66,7 +66,6 @@ class MockTest(unittest.TestCase): except jaydebeapi.DatabaseError as e: self.assertEquals(str(e), "java.sql.SQLException: expected") - def test_runtime_exception_on_execute(self): self.conn.jconn.mockExceptionOnExecute("java.lang.RuntimeException", "expected") cursor = self.conn.cursor() From 2f0eaeb5e09a6e85fba08997839a4f4f7f994b55 Mon Sep 17 00:00:00 2001 From: baztian Date: Tue, 21 Mar 2017 20:03:44 +0100 Subject: [PATCH 4/4] Maintain changelog. --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 28e9eb8..13ab23c 100644 --- a/README.rst +++ b/README.rst @@ -159,6 +159,9 @@ Changelog ========= - Next version - unreleased + + - Don't fail on dates before 1900 on Python < 3. + - 1.1.0 - 2017-03-19 - Support BIT and TINYINT type mappings (thanks @Mokubyow for