From 1a0f29e66621a8f17b7d31c7f7debf54bf433694 Mon Sep 17 00:00:00 2001 From: "STATION\\MF" Date: Mon, 26 Oct 2020 19:08:18 -0400 Subject: [PATCH 1/3] wip: work-in-progress --- noxfile.py | 7 +- tests/unit/django_spanner/test_compiler.py | 95 ++++++++++++++++++++++ version.py | 3 +- 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 tests/unit/django_spanner/test_compiler.py diff --git a/noxfile.py b/noxfile.py index a8e2ce58e8..75a2e55d9f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -62,7 +62,7 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. - session.install("mock", "pytest", "pytest-cov") + session.install("mock", "pytest", "pytest-cov", "django") session.install("-e", ".") # Run py.test against the unit tests. @@ -71,12 +71,13 @@ def default(session): "--quiet", "--cov=django_spanner", "--cov=google.cloud", - "--cov=tests.spanner_dbapi", + # "--cov=tests.spanner_dbapi", "--cov-append", "--cov-config=.coveragerc", "--cov-report=", "--cov-fail-under=0", - os.path.join("tests", "spanner_dbapi"), + # os.path.join("tests", "spanner_dbapi"), + os.path.join("tests", "unit"), *session.posargs ) diff --git a/tests/unit/django_spanner/test_compiler.py b/tests/unit/django_spanner/test_compiler.py new file mode 100644 index 0000000000..7f8b104cce --- /dev/null +++ b/tests/unit/django_spanner/test_compiler.py @@ -0,0 +1,95 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""Connection() class unit tests.""" + +import unittest +from unittest import mock + +# import google.cloud.spanner_dbapi.exceptions as dbapi_exceptions + +# from google.cloud.spanner_dbapi.connection import AUTOCOMMIT_MODE_WARNING + + +class TestSQLCompiler(unittest.TestCase): + def test_get_combinator_sql(self): + # from django.db.models.sql.query import Query + from django.core.exceptions import EmptyResultSet + from django.db.utils import DatabaseError + from django_spanner.compiler import SQLCompiler + # from django_spanner.features import DatabaseFeatures + + query = mock.MagicMock() + query.values_select = False + connection = mock.MagicMock() + using = 'using' + + compiler = SQLCompiler(query, connection, using) + self.assertIsInstance(compiler, SQLCompiler) + + mock_cquery = mock.MagicMock() + mock_cquery.get_compiler = mock_compiler = mock.MagicMock() + part_sql = 'part_sql' + part_args = 'part_args' + mock_compiler.return_value.as_sql.return_value = (part_sql, part_args) + mock_cquery.is_empty = lambda: False + + compiler.query = mock_query = mock.MagicMock() + mock_query.combined_queries = [mock_cquery] + + combinator = 'union' + all_ = True + + res, params = compiler.get_combinator_sql(combinator, all_) + self.assertEqual(res, ['({})'.format(part_sql)]) + self.assertEqual(params, [c for c in part_args]) + + compiler.connection.features.supports_slicing_ordering_in_compound = False + q = compiler.query.combined_queries[0] + q.low_mark = q.high_mark = True + with self.assertRaises(DatabaseError): + compiler.get_combinator_sql(combinator, all_) + q.low_mark = q.high_mark = False + mock_compiler.return_value.get_order_by.return_value = True + with self.assertRaises(DatabaseError): + compiler.get_combinator_sql(combinator, all_) + + compiler.connection.features.supports_slicing_ordering_in_compound = True + mock_compiler.return_value.query.values_select = False + compiler.query.values_select = [0] + mock_compiler.return_value.query.set_values = mock_set_values = mock.MagicMock() + compiler.get_combinator_sql(combinator, all_) + mock_set_values.assert_called_once_with((0,)) + + compiler.query.combined_queries = [] + with self.assertRaises(EmptyResultSet): + compiler.get_combinator_sql(combinator, all_) + + compiler.connection.features.supports_slicing_ordering_in_compound = False + compiler.query.combined_queries = [mock_cquery, mock_cquery] + mock_cquery.return_value.get_compiler.return_value = [0, 0] + mock_compiler.return_value.get_order_by.return_value = False + res, params = compiler.get_combinator_sql(combinator, all_) + self.assertIsInstance(res, list) + self.assertIsInstance(params, list) + + + + + + # @mock.patch("warnings.warn") + # def test_transaction_autocommit_warnings(self, warn_mock): + # connection = self._make_connection() + # connection.autocommit = True + # + # connection.commit() + # warn_mock.assert_called_with( + # AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 + # ) + # connection.rollback() + # warn_mock.assert_called_with( + # AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 + # ) diff --git a/version.py b/version.py index 3ec7fb40dd..8990b8050e 100644 --- a/version.py +++ b/version.py @@ -4,4 +4,5 @@ # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd -__version__ = "2.2.0a1" +# __version__ = "2.2.0a1" +__version__ = "3.1.0a1" From 4c0a56e77f4fc237d79dacf3dc9b4aade8945480 Mon Sep 17 00:00:00 2001 From: "STATION\\MF" Date: Mon, 26 Oct 2020 23:09:26 -0400 Subject: [PATCH 2/3] test: unit test for `SQLCompiler.get_combinator_sql` method override --- tests/unit/django_spanner/test_compiler.py | 45 ++++++++++------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/tests/unit/django_spanner/test_compiler.py b/tests/unit/django_spanner/test_compiler.py index 7f8b104cce..7f46fb1319 100644 --- a/tests/unit/django_spanner/test_compiler.py +++ b/tests/unit/django_spanner/test_compiler.py @@ -4,23 +4,22 @@ # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd -"""Connection() class unit tests.""" +"""Django-Spanner SQLCompiler class unit tests.""" import unittest from unittest import mock -# import google.cloud.spanner_dbapi.exceptions as dbapi_exceptions - -# from google.cloud.spanner_dbapi.connection import AUTOCOMMIT_MODE_WARNING - class TestSQLCompiler(unittest.TestCase): def test_get_combinator_sql(self): - # from django.db.models.sql.query import Query from django.core.exceptions import EmptyResultSet from django.db.utils import DatabaseError from django_spanner.compiler import SQLCompiler - # from django_spanner.features import DatabaseFeatures + + def _empty_result_set(): + from django.core.exceptions import EmptyResultSet + + raise EmptyResultSet query = mock.MagicMock() query.values_select = False @@ -73,23 +72,21 @@ def test_get_combinator_sql(self): mock_cquery.return_value.get_compiler.return_value = [0, 0] mock_compiler.return_value.get_order_by.return_value = False res, params = compiler.get_combinator_sql(combinator, all_) - self.assertIsInstance(res, list) - self.assertIsInstance(params, list) - - + self.assertEqual(res[0].split()[0], '({})'.format(part_sql)) + self.assertEqual(params[:9], [c for c in part_args]) + compiler.connection.features.supports_parentheses_in_compound = False + res, params = compiler.get_combinator_sql(combinator, all_) + self.assertEqual(res[0].split()[0], 'SELECT') + self.assertEqual(params[:9], [c for c in part_args]) + mock_compiler.return_value.query.combinator = False + res, params = compiler.get_combinator_sql(combinator, all_) + self.assertEqual(res[0].split()[0], part_sql) + self.assertEqual(params[:9], [c for c in part_args]) - # @mock.patch("warnings.warn") - # def test_transaction_autocommit_warnings(self, warn_mock): - # connection = self._make_connection() - # connection.autocommit = True - # - # connection.commit() - # warn_mock.assert_called_with( - # AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 - # ) - # connection.rollback() - # warn_mock.assert_called_with( - # AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 - # ) + mock_compiler.return_value.as_sql = _empty_result_set + with self.assertRaises(EmptyResultSet): + compiler.get_combinator_sql(combinator, all_) + with self.assertRaises(EmptyResultSet): + compiler.get_combinator_sql(None, all_) From ada9f80b269ea86973289f7542c3622812ca03d4 Mon Sep 17 00:00:00 2001 From: "STATION\\MF" Date: Mon, 26 Oct 2020 23:21:02 -0400 Subject: [PATCH 3/3] chore: cleanup --- noxfile.py | 5 +- tests/spanner_dbapi/test_connect.py | 135 ------------------------- tests/spanner_dbapi/test_connection.py | 79 --------------- 3 files changed, 2 insertions(+), 217 deletions(-) delete mode 100644 tests/spanner_dbapi/test_connect.py delete mode 100644 tests/spanner_dbapi/test_connection.py diff --git a/noxfile.py b/noxfile.py index 75a2e55d9f..c20e50ae37 100644 --- a/noxfile.py +++ b/noxfile.py @@ -71,12 +71,11 @@ def default(session): "--quiet", "--cov=django_spanner", "--cov=google.cloud", - # "--cov=tests.spanner_dbapi", + "--cov=tests.unit", "--cov-append", "--cov-config=.coveragerc", "--cov-report=", - "--cov-fail-under=0", - # os.path.join("tests", "spanner_dbapi"), + "--cov-fail-under=70", os.path.join("tests", "unit"), *session.posargs ) diff --git a/tests/spanner_dbapi/test_connect.py b/tests/spanner_dbapi/test_connect.py deleted file mode 100644 index fb4d89c373..0000000000 --- a/tests/spanner_dbapi/test_connect.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -"""connect() module function unit tests.""" - -import unittest -from unittest import mock - -import google.auth.credentials -from google.api_core.gapic_v1.client_info import ClientInfo -from google.cloud.spanner_dbapi import connect, Connection -from google.cloud.spanner_v1.pool import FixedSizePool - - -def _make_credentials(): - class _CredentialsWithScopes( - google.auth.credentials.Credentials, google.auth.credentials.Scoped - ): - pass - - return mock.Mock(spec=_CredentialsWithScopes) - - -class Test_connect(unittest.TestCase): - def test_connect(self): - PROJECT = "test-project" - USER_AGENT = "user-agent" - CREDENTIALS = _make_credentials() - CLIENT_INFO = ClientInfo(user_agent=USER_AGENT) - - with mock.patch( - "google.cloud.spanner_dbapi.spanner_v1.Client" - ) as client_mock: - with mock.patch( - "google.cloud.spanner_dbapi.google_client_info", - return_value=CLIENT_INFO, - ) as client_info_mock: - - connection = connect( - "test-instance", - "test-database", - PROJECT, - CREDENTIALS, - user_agent=USER_AGENT, - ) - - self.assertIsInstance(connection, Connection) - client_info_mock.assert_called_once_with(USER_AGENT) - - client_mock.assert_called_once_with( - project=PROJECT, - credentials=CREDENTIALS, - client_info=CLIENT_INFO, - ) - - def test_instance_not_found(self): - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.exists", - return_value=False, - ) as exists_mock: - - with self.assertRaises(ValueError): - connect("test-instance", "test-database") - - exists_mock.assert_called_once_with() - - def test_database_not_found(self): - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.exists", - return_value=True, - ): - with mock.patch( - "google.cloud.spanner_v1.database.Database.exists", - return_value=False, - ) as exists_mock: - - with self.assertRaises(ValueError): - connect("test-instance", "test-database") - - exists_mock.assert_called_once_with() - - def test_connect_instance_id(self): - INSTANCE = "test-instance" - - with mock.patch( - "google.cloud.spanner_v1.client.Client.instance" - ) as instance_mock: - connection = connect(INSTANCE, "test-database") - - instance_mock.assert_called_once_with(INSTANCE) - - self.assertIsInstance(connection, Connection) - - def test_connect_database_id(self): - DATABASE = "test-database" - - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.database" - ) as database_mock: - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.exists", - return_value=True, - ): - connection = connect("test-instance", DATABASE) - - database_mock.assert_called_once_with(DATABASE, pool=mock.ANY) - - self.assertIsInstance(connection, Connection) - - def test_default_sessions_pool(self): - with mock.patch("google.cloud.spanner_v1.instance.Instance.database"): - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.exists", - return_value=True, - ): - connection = connect("test-instance", "test-database") - - self.assertIsNotNone(connection.database._pool) - - def test_sessions_pool(self): - database_id = "test-database" - pool = FixedSizePool() - - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.database" - ) as database_mock: - with mock.patch( - "google.cloud.spanner_v1.instance.Instance.exists", - return_value=True, - ): - connect("test-instance", database_id, pool=pool) - database_mock.assert_called_once_with(database_id, pool=pool) diff --git a/tests/spanner_dbapi/test_connection.py b/tests/spanner_dbapi/test_connection.py deleted file mode 100644 index 24260de12e..0000000000 --- a/tests/spanner_dbapi/test_connection.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -"""Connection() class unit tests.""" - -import unittest -from unittest import mock - -# import google.cloud.spanner_dbapi.exceptions as dbapi_exceptions - -from google.cloud.spanner_dbapi import Connection, InterfaceError -from google.cloud.spanner_dbapi.connection import AUTOCOMMIT_MODE_WARNING -from google.cloud.spanner_v1.database import Database -from google.cloud.spanner_v1.instance import Instance - - -class TestConnection(unittest.TestCase): - instance_name = "instance-name" - database_name = "database-name" - - def _make_connection(self): - # we don't need real Client object to test the constructor - instance = Instance(self.instance_name, client=None) - database = instance.database(self.database_name) - return Connection(instance, database) - - def test_ctor(self): - connection = self._make_connection() - - self.assertIsInstance(connection.instance, Instance) - self.assertEqual(connection.instance.instance_id, self.instance_name) - - self.assertIsInstance(connection.database, Database) - self.assertEqual(connection.database.database_id, self.database_name) - - self.assertFalse(connection.is_closed) - - def test_close(self): - connection = self._make_connection() - - self.assertFalse(connection.is_closed) - connection.close() - self.assertTrue(connection.is_closed) - - with self.assertRaises(InterfaceError): - connection.cursor() - - @mock.patch("warnings.warn") - def test_transaction_autocommit_warnings(self, warn_mock): - connection = self._make_connection() - connection.autocommit = True - - connection.commit() - warn_mock.assert_called_with( - AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 - ) - connection.rollback() - warn_mock.assert_called_with( - AUTOCOMMIT_MODE_WARNING, UserWarning, stacklevel=2 - ) - - def test_database_property(self): - connection = self._make_connection() - self.assertIsInstance(connection.database, Database) - self.assertEqual(connection.database, connection._database) - - with self.assertRaises(AttributeError): - connection.database = None - - def test_instance_property(self): - connection = self._make_connection() - self.assertIsInstance(connection.instance, Instance) - self.assertEqual(connection.instance, connection._instance) - - with self.assertRaises(AttributeError): - connection.instance = None