From 7103506ca5639d339e3e47dfb9e4affb546c839b Mon Sep 17 00:00:00 2001 From: Hannah Stepanek Date: Mon, 1 May 2023 14:12:31 -0700 Subject: [PATCH] Add tests for pyodbc (#796) * Add tests for pyodbc * Move imports into tests to get import coverage * Fixup: remove time import * Trigger tests --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/tests.yml | 8 ++ tests/datastore_pyodbc/conftest.py | 33 +++++++ tests/datastore_pyodbc/test_pyodbc.py | 120 ++++++++++++++++++++++++++ tox.ini | 3 + 4 files changed, 164 insertions(+) create mode 100644 tests/datastore_pyodbc/conftest.py create mode 100644 tests/datastore_pyodbc/test_pyodbc.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 15d105b83..dc73168eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -255,6 +255,14 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-python-matrix + - name: Install odbc driver for postgresql + run: | + sudo apt-get update + sudo sudo apt-get install odbc-postgresql + sudo sed -i 's/Driver=psqlodbca.so/Driver=\/usr\/lib\/x86_64-linux-gnu\/odbc\/psqlodbca.so/g' /etc/odbcinst.ini + sudo sed -i 's/Driver=psqlodbcw.so/Driver=\/usr\/lib\/x86_64-linux-gnu\/odbc\/psqlodbcw.so/g' /etc/odbcinst.ini + sudo sed -i 's/Setup=libodbcpsqlS.so/Setup=\/usr\/lib\/x86_64-linux-gnu\/odbc\/libodbcpsqlS.so/g' /etc/odbcinst.ini + - name: Get Environments id: get-envs run: | diff --git a/tests/datastore_pyodbc/conftest.py b/tests/datastore_pyodbc/conftest.py new file mode 100644 index 000000000..b00a0a663 --- /dev/null +++ b/tests/datastore_pyodbc/conftest.py @@ -0,0 +1,33 @@ +# Copyright 2010 New Relic, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from testing_support.fixtures import ( # noqa: F401; pylint: disable=W0611 + collector_agent_registration_fixture, + collector_available_fixture, +) + +_default_settings = { + "transaction_tracer.explain_threshold": 0.0, + "transaction_tracer.transaction_threshold": 0.0, + "transaction_tracer.stack_trace_threshold": 0.0, + "debug.log_data_collector_payloads": True, + "debug.record_transaction_failure": True, + "debug.log_explain_plan_queries": True, +} + +collector_agent_registration = collector_agent_registration_fixture( + app_name="Python Agent Test (datastore_pyodbc)", + default_settings=_default_settings, + linked_applications=["Python Agent Test (datastore)"], +) diff --git a/tests/datastore_pyodbc/test_pyodbc.py b/tests/datastore_pyodbc/test_pyodbc.py new file mode 100644 index 000000000..119908e4d --- /dev/null +++ b/tests/datastore_pyodbc/test_pyodbc.py @@ -0,0 +1,120 @@ +# Copyright 2010 New Relic, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest +from testing_support.db_settings import postgresql_settings +from testing_support.validators.validate_database_trace_inputs import ( + validate_database_trace_inputs, +) +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.api.background_task import background_task + +DB_SETTINGS = postgresql_settings()[0] + + +@validate_transaction_metrics( + "test_pyodbc:test_execute_via_cursor", + scoped_metrics=[ + ("Function/pyodbc:connect", 1), + ], + rollup_metrics=[ + ("Datastore/all", 1), + ("Datastore/allOther", 1), + ("Datastore/ODBC/all", 1), + ("Datastore/ODBC/allOther", 1), + ], + background_task=True, +) +@validate_database_trace_inputs(sql_parameters_type=tuple) +@background_task() +def test_execute_via_cursor(pyodbc_driver): + import pyodbc + + with pyodbc.connect( + "DRIVER={%s};SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s" + % ( + pyodbc_driver, + DB_SETTINGS["host"], + DB_SETTINGS["port"], + DB_SETTINGS["name"], + DB_SETTINGS["user"], + DB_SETTINGS["password"], + ) + ) as connection: + cursor = connection.cursor() + cursor.execute("""drop table if exists %s""" % DB_SETTINGS["table_name"]) + cursor.execute("""create table %s """ % DB_SETTINGS["table_name"] + """(a integer, b real, c text)""") + cursor.executemany( + """insert into %s """ % DB_SETTINGS["table_name"] + """values (?, ?, ?)""", + [(1, 1.0, "1.0"), (2, 2.2, "2.2"), (3, 3.3, "3.3")], + ) + cursor.execute("""select * from %s""" % DB_SETTINGS["table_name"]) + for row in cursor: + pass + cursor.execute( + """update %s """ % DB_SETTINGS["table_name"] + """set a=?, b=?, c=? where a=?""", + (4, 4.0, "4.0", 1), + ) + cursor.execute("""delete from %s where a=2""" % DB_SETTINGS["table_name"]) + connection.commit() + + cursor.execute("SELECT now()") + cursor.execute("SELECT pg_sleep(0.25)") + + connection.rollback() + connection.commit() + + +@validate_transaction_metrics( + "test_pyodbc:test_rollback_on_exception", + scoped_metrics=[ + ("Function/pyodbc:connect", 1), + ], + rollup_metrics=[ + ("Datastore/all", 1), + ("Datastore/allOther", 1), + ("Datastore/ODBC/all", 1), + ("Datastore/ODBC/allOther", 1), + ], + background_task=True, +) +@validate_database_trace_inputs(sql_parameters_type=tuple) +@background_task() +def test_rollback_on_exception(pyodbc_driver): + import pyodbc + + with pytest.raises(RuntimeError): + with pyodbc.connect( + "DRIVER={%s};SERVER=%s;PORT=%s;DATABASE=%s;UID=%s;PWD=%s" + % ( + pyodbc_driver, + DB_SETTINGS["host"], + DB_SETTINGS["port"], + DB_SETTINGS["name"], + DB_SETTINGS["user"], + DB_SETTINGS["password"], + ) + ) as connection: + raise RuntimeError("error") + + +@pytest.fixture +def pyodbc_driver(): + import pyodbc + + driver_name = "PostgreSQL Unicode" + assert driver_name in pyodbc.drivers() + return driver_name diff --git a/tox.ini b/tox.ini index 1d9110e4f..122daa68b 100644 --- a/tox.ini +++ b/tox.ini @@ -80,6 +80,7 @@ envlist = postgres-datastore_postgresql-{py37,py38,py39}, postgres-datastore_psycopg2-{py27,py37,py38,py39,py310,py311}-psycopg2latest postgres-datastore_psycopg2cffi-{py27,pypy,py37,py38,py39,py310,py311}-psycopg2cffilatest, + postgres-datastore_pyodbc-{py27,py37,py311}-pyodbclatest memcached-datastore_pylibmc-{py27,py37}, memcached-datastore_pymemcache-{py27,py37,py38,py39,py310,py311,pypy,pypy37}, mongodb-datastore_pymongo-{py27,py37,py38,py39,py310,py311,pypy}-pymongo{03}, @@ -231,6 +232,7 @@ deps = datastore_postgresql: py-postgresql<1.3 datastore_psycopg2-psycopg2latest: psycopg2-binary datastore_psycopg2cffi-psycopg2cffilatest: psycopg2cffi + datastore_pyodbc-pyodbclatest: pyodbc datastore_pylibmc: pylibmc datastore_pymemcache: pymemcache datastore_pymongo-pymongo03: pymongo<4.0 @@ -434,6 +436,7 @@ changedir = datastore_mysql: tests/datastore_mysql datastore_postgresql: tests/datastore_postgresql datastore_psycopg2: tests/datastore_psycopg2 + datastore_pyodbc: tests/datastore_pyodbc datastore_psycopg2cffi: tests/datastore_psycopg2cffi datastore_pylibmc: tests/datastore_pylibmc datastore_pymemcache: tests/datastore_pymemcache