You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2019/12/31 12:45:52 UTC

[incubator-superset] branch master updated: Enable running of tests in tests/db_engine_specs (#8902)

This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 8fc814f  Enable running of tests in tests/db_engine_specs (#8902)
8fc814f is described below

commit 8fc814fc62d8f96f93aaac8223a660e4f9e6d28e
Author: Rob DiCiuccio <ro...@gmail.com>
AuthorDate: Tue Dec 31 12:45:33 2019 +0000

    Enable running of tests in tests/db_engine_specs (#8902)
    
    * Enable running of tests in tests/db_engine_specs
    
    * Resolve application context errors
    
    * Refactor and add tests for pyodbc.Row conversion
    
    * Appease isort, regardless of isort:skip
---
 superset/db_engine_specs/base.py                   | 12 ++++++++++
 superset/db_engine_specs/exasol.py                 |  4 +---
 superset/db_engine_specs/mssql.py                  |  5 ++--
 .../{athena_tests.py => __init__.py}               | 17 -------------
 tests/db_engine_specs/athena_tests.py              |  2 ++
 tests/db_engine_specs/base_engine_spec_tests.py    | 28 +++++++++++++++++++++-
 tests/db_engine_specs/base_tests.py                |  2 ++
 tests/db_engine_specs/mssql_tests.py               | 15 ++++++++++++
 8 files changed, 61 insertions(+), 24 deletions(-)

diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py
index 90dd89b..b267412 100644
--- a/superset/db_engine_specs/base.py
+++ b/superset/db_engine_specs/base.py
@@ -856,3 +856,15 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
         :return: Compiled column type
         """
         return sqla_column_type.compile(dialect=dialect).upper()
+
+    @staticmethod
+    def pyodbc_rows_to_tuples(data: List[Any]) -> List[Tuple]:
+        """
+        Convert pyodbc.Row objects from `fetch_data` to tuples.
+
+        :param data: List of tuples or pyodbc.Row objects
+        :return: List of tuples
+        """
+        if data and type(data[0]).__name__ == "Row":
+            data = [tuple(row) for row in data]
+        return data
diff --git a/superset/db_engine_specs/exasol.py b/superset/db_engine_specs/exasol.py
index 380b598..ea4a400 100644
--- a/superset/db_engine_specs/exasol.py
+++ b/superset/db_engine_specs/exasol.py
@@ -42,6 +42,4 @@ class ExasolEngineSpec(BaseEngineSpec):  # pylint: disable=abstract-method
     def fetch_data(cls, cursor, limit: int) -> List[Tuple]:
         data = super().fetch_data(cursor, limit)
         # Lists of `pyodbc.Row` need to be unpacked further
-        if data and type(data[0]).__name__ == "Row":
-            data = [tuple(row) for row in data]
-        return data
+        return cls.pyodbc_rows_to_tuples(data)
diff --git a/superset/db_engine_specs/mssql.py b/superset/db_engine_specs/mssql.py
index c774077..94555b0 100644
--- a/superset/db_engine_specs/mssql.py
+++ b/superset/db_engine_specs/mssql.py
@@ -63,9 +63,8 @@ class MssqlEngineSpec(BaseEngineSpec):
     @classmethod
     def fetch_data(cls, cursor, limit: int) -> List[Tuple]:
         data = super().fetch_data(cursor, limit)
-        if data and type(data[0]).__name__ == "Row":
-            data = [tuple(row) for row in data]
-        return data
+        # Lists of `pyodbc.Row` need to be unpacked further
+        return cls.pyodbc_rows_to_tuples(data)
 
     column_types = [
         (String(), re.compile(r"^(?<!N)((VAR){0,1}CHAR|TEXT|STRING)", re.IGNORECASE)),
diff --git a/tests/db_engine_specs/athena_tests.py b/tests/db_engine_specs/__init__.py
similarity index 59%
copy from tests/db_engine_specs/athena_tests.py
copy to tests/db_engine_specs/__init__.py
index 81b790b..13a8339 100644
--- a/tests/db_engine_specs/athena_tests.py
+++ b/tests/db_engine_specs/__init__.py
@@ -14,20 +14,3 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-from superset.db_engine_specs.athena import AthenaEngineSpec
-from tests.db_engine_specs.base_tests import DbEngineSpecTestCase
-
-
-class AthenaTestCase(DbEngineSpecTestCase):
-    def test_convert_dttm(self):
-        dttm = self.get_dttm()
-
-        self.assertEqual(
-            AthenaEngineSpec.convert_dttm("DATE", dttm),
-            "from_iso8601_date('2019-01-02')",
-        )
-
-        self.assertEqual(
-            AthenaEngineSpec.convert_dttm("TIMESTAMP", dttm),
-            "from_iso8601_timestamp('2019-01-02T03:04:05.678900')",
-        )
diff --git a/tests/db_engine_specs/athena_tests.py b/tests/db_engine_specs/athena_tests.py
index 81b790b..b59f500 100644
--- a/tests/db_engine_specs/athena_tests.py
+++ b/tests/db_engine_specs/athena_tests.py
@@ -14,6 +14,8 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from tests.test_app import app  # isort:skip
+
 from superset.db_engine_specs.athena import AthenaEngineSpec
 from tests.db_engine_specs.base_tests import DbEngineSpecTestCase
 
diff --git a/tests/db_engine_specs/base_engine_spec_tests.py b/tests/db_engine_specs/base_engine_spec_tests.py
index c595792..4ba4e31 100644
--- a/tests/db_engine_specs/base_engine_spec_tests.py
+++ b/tests/db_engine_specs/base_engine_spec_tests.py
@@ -14,15 +14,19 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from tests.test_app import app  # isort:skip
+
+import datetime
 from unittest import mock
 
-from superset import app
 from superset.db_engine_specs import engines
 from superset.db_engine_specs.base import BaseEngineSpec, builtin_time_grains
 from superset.db_engine_specs.sqlite import SqliteEngineSpec
 from superset.utils.core import get_example_database
 from tests.db_engine_specs.base_tests import DbEngineSpecTestCase
 
+from ..fixtures.pyodbcRow import Row
+
 
 class DbEngineSpecsTests(DbEngineSpecTestCase):
     def test_extract_limit_from_query(self, engine_spec_class=BaseEngineSpec):
@@ -206,3 +210,25 @@ class DbEngineSpecsTests(DbEngineSpecTestCase):
     def test_convert_dttm(self):
         dttm = self.get_dttm()
         self.assertIsNone(BaseEngineSpec.convert_dttm("", dttm))
+
+    def test_pyodbc_rows_to_tuples(self):
+        # Test for case when pyodbc.Row is returned (odbc driver)
+        data = [
+            Row((1, 1, datetime.datetime(2017, 10, 19, 23, 39, 16, 660000))),
+            Row((2, 2, datetime.datetime(2018, 10, 19, 23, 39, 16, 660000))),
+        ]
+        expected = [
+            (1, 1, datetime.datetime(2017, 10, 19, 23, 39, 16, 660000)),
+            (2, 2, datetime.datetime(2018, 10, 19, 23, 39, 16, 660000)),
+        ]
+        result = BaseEngineSpec.pyodbc_rows_to_tuples(data)
+        self.assertListEqual(result, expected)
+
+    def test_pyodbc_rows_to_tuples_passthrough(self):
+        # Test for case when tuples are returned
+        data = [
+            (1, 1, datetime.datetime(2017, 10, 19, 23, 39, 16, 660000)),
+            (2, 2, datetime.datetime(2018, 10, 19, 23, 39, 16, 660000)),
+        ]
+        result = BaseEngineSpec.pyodbc_rows_to_tuples(data)
+        self.assertListEqual(result, data)
diff --git a/tests/db_engine_specs/base_tests.py b/tests/db_engine_specs/base_tests.py
index d7a57ee..5b08dbf 100644
--- a/tests/db_engine_specs/base_tests.py
+++ b/tests/db_engine_specs/base_tests.py
@@ -20,6 +20,8 @@ from superset.db_engine_specs.mysql import MySQLEngineSpec
 from superset.models.core import Database
 from tests.base_tests import SupersetTestCase
 
+from tests.test_app import app  # isort:skip
+
 
 class DbEngineSpecTestCase(SupersetTestCase):
     def sql_limit_regex(
diff --git a/tests/db_engine_specs/mssql_tests.py b/tests/db_engine_specs/mssql_tests.py
index fabdee1..95cde00 100644
--- a/tests/db_engine_specs/mssql_tests.py
+++ b/tests/db_engine_specs/mssql_tests.py
@@ -14,11 +14,14 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+import unittest.mock as mock
+
 from sqlalchemy import column, table
 from sqlalchemy.dialects import mssql
 from sqlalchemy.sql import select
 from sqlalchemy.types import String, UnicodeText
 
+from superset.db_engine_specs.base import BaseEngineSpec
 from superset.db_engine_specs.mssql import MssqlEngineSpec
 from tests.db_engine_specs.base_tests import DbEngineSpecTestCase
 
@@ -87,3 +90,15 @@ class MssqlEngineSpecTest(DbEngineSpecTestCase):
             MssqlEngineSpec.convert_dttm("SMALLDATETIME", dttm),
             "CONVERT(SMALLDATETIME, '2019-01-02 03:04:05', 20)",
         )
+
+    @mock.patch.object(
+        MssqlEngineSpec, "pyodbc_rows_to_tuples", return_value="converted"
+    )
+    def test_fetch_data(self, mock_pyodbc_rows_to_tuples):
+        data = [(1, "foo")]
+        with mock.patch.object(
+            BaseEngineSpec, "fetch_data", return_value=data
+        ) as mock_fetch:
+            result = MssqlEngineSpec.fetch_data(None, 0)
+            mock_pyodbc_rows_to_tuples.assert_called_once_with(data)
+            self.assertEqual(result, "converted")