diff --git a/.github/actions/setup-python-matrix/action.yml b/.github/actions/setup-python-matrix/action.yml index bcb5cbc785..299dd2e7bd 100644 --- a/.github/actions/setup-python-matrix/action.yml +++ b/.github/actions/setup-python-matrix/action.yml @@ -47,4 +47,4 @@ runs: shell: bash run: | python3.10 -m pip install -U pip - python3.10 -m pip install -U wheel setuptools 'tox<4' virtualenv!=20.0.24 + python3.10 -m pip install -U wheel setuptools tox 'virtualenv<20.22.0' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d20a7c02a7..dc73168ebf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,7 @@ jobs: - elasticsearchserver08 - gearman - grpc - - kafka + #- kafka - libcurl - memcached - mongodb @@ -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: | @@ -534,77 +542,77 @@ jobs: path: ./**/.coverage.* retention-days: 1 - kafka: - env: - TOTAL_GROUPS: 4 - - strategy: - fail-fast: false - matrix: - group-number: [1, 2, 3, 4] - - runs-on: ubuntu-20.04 - timeout-minutes: 30 - - services: - zookeeper: - image: bitnami/zookeeper:3.7 - env: - ALLOW_ANONYMOUS_LOGIN: yes - - ports: - - 2181:2181 - - kafka: - image: bitnami/kafka:3.2 - ports: - - 8080:8080 - - 8081:8081 - env: - ALLOW_PLAINTEXT_LISTENER: yes - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: true - KAFKA_CFG_LISTENERS: L1://:8080,L2://:8081 - KAFKA_CFG_ADVERTISED_LISTENERS: L1://127.0.0.1:8080,L2://kafka:8081, - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: L1:PLAINTEXT,L2:PLAINTEXT - KAFKA_CFG_INTER_BROKER_LISTENER_NAME: L2 - - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/setup-python-matrix - - # Special case packages - - name: Install librdkafka-dev - run: | - # Use lsb-release to find the codename of Ubuntu to use to install the correct library name - sudo apt-get update - sudo ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime - sudo apt-get install -y wget gnupg2 software-properties-common - sudo wget -qO - https://packages.confluent.io/deb/7.2/archive.key | sudo apt-key add - - sudo add-apt-repository "deb https://packages.confluent.io/clients/deb $(lsb_release -cs) main" - sudo apt-get update - sudo apt-get install -y librdkafka-dev/$(lsb_release -c | cut -f 2) - - - name: Get Environments - id: get-envs - run: | - echo "::set-output name=envs::$(tox -l | grep "^${{ github.job }}\-" | ./.github/workflows/get-envs.py)" - env: - GROUP_NUMBER: ${{ matrix.group-number }} - - - name: Test - run: | - tox -vv -e ${{ steps.get-envs.outputs.envs }} - env: - TOX_PARALLEL_NO_SPINNER: 1 - PY_COLORS: 0 - - - name: Upload Coverage Artifacts - uses: actions/upload-artifact@v3 - with: - name: coverage-${{ github.job }}-${{ strategy.job-index }} - path: ./**/.coverage.* - retention-days: 1 + #kafka: + # env: + # TOTAL_GROUPS: 4 + + # strategy: + # fail-fast: false + # matrix: + # group-number: [1, 2, 3, 4] + + # runs-on: ubuntu-20.04 + # timeout-minutes: 30 + + # services: + # zookeeper: + # image: bitnami/zookeeper:3.7 + # env: + # ALLOW_ANONYMOUS_LOGIN: yes + + # ports: + # - 2181:2181 + + # kafka: + # image: bitnami/kafka:3.2 + # ports: + # - 8080:8080 + # - 8081:8081 + # env: + # ALLOW_PLAINTEXT_LISTENER: yes + # KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + # KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: true + # KAFKA_CFG_LISTENERS: L1://:8080,L2://:8081 + # KAFKA_CFG_ADVERTISED_LISTENERS: L1://127.0.0.1:8080,L2://kafka:8081, + # KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: L1:PLAINTEXT,L2:PLAINTEXT + # KAFKA_CFG_INTER_BROKER_LISTENER_NAME: L2 + + # steps: + # - uses: actions/checkout@v3 + # - uses: ./.github/actions/setup-python-matrix + + # # Special case packages + # - name: Install librdkafka-dev + # run: | + # # Use lsb-release to find the codename of Ubuntu to use to install the correct library name + # sudo apt-get update + # sudo ln -fs /usr/share/zoneinfo/America/Los_Angeles /etc/localtime + # sudo apt-get install -y wget gnupg2 software-properties-common + # sudo wget -qO - https://packages.confluent.io/deb/7.2/archive.key | sudo apt-key add - + # sudo add-apt-repository "deb https://packages.confluent.io/clients/deb $(lsb_release -cs) main" + # sudo apt-get update + # sudo apt-get install -y librdkafka-dev/$(lsb_release -c | cut -f 2) + + # - name: Get Environments + # id: get-envs + # run: | + # echo "::set-output name=envs::$(tox -l | grep "^${{ github.job }}\-" | ./.github/workflows/get-envs.py)" + # env: + # GROUP_NUMBER: ${{ matrix.group-number }} + + # - name: Test + # run: | + # tox -vv -e ${{ steps.get-envs.outputs.envs }} + # env: + # TOX_PARALLEL_NO_SPINNER: 1 + # PY_COLORS: 0 + + # - name: Upload Coverage Artifacts + # uses: actions/upload-artifact@v3 + # with: + # name: coverage-${{ github.job }}-${{ strategy.job-index }} + # path: ./**/.coverage.* + # retention-days: 1 mongodb: env: diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..ec600226a2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,21 @@ +ignore: + - "newrelic/packages/**/*" + - "newrelic/packages/*" + - "newrelic/hooks/adapter_meinheld.py" + - "newrelic/hooks/adapter_flup.py" + - "newrelic/hooks/component_piston.py" + - "newrelic/hooks/datastore_pyelasticsearch.py" + - "newrelic/hooks/external_pywapi.py" + - "newrelic/hooks/external_dropbox.py" + - "newrelic/hooks/external_facepy.py" + - "newrelic/hooks/external_xmlrpclib.py" + - "newrelic/hooks/framework_pylons.py" + - "newrelic/hooks/framework_web2py.py" + - "newrelic/hooks/middleware_weberror.py" + - "newrelic/hooks/framework_webpy.py" + - "newrelic/hooks/database_oursql.py" + - "newrelic/hooks/database_psycopg2ct.py" + - "newrelic/hooks/datastore_umemcache.py" + # Temporarily disable kafka + - "newrelic/hooks/messagebroker_kafkapython.py" + - "newrelic/hooks/messagebroker_confluentkafka.py" diff --git a/newrelic/hooks/adapter_waitress.py b/newrelic/hooks/adapter_waitress.py index bdeb15b37e..2353510e3f 100644 --- a/newrelic/hooks/adapter_waitress.py +++ b/newrelic/hooks/adapter_waitress.py @@ -12,17 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import newrelic.api.wsgi_application -import newrelic.api.in_function +from newrelic.api.in_function import wrap_in_function +from newrelic.api.wsgi_application import WSGIApplicationWrapper +from newrelic.common.package_version_utils import get_package_version -def instrument_waitress_server(module): - def wrap_wsgi_application_entry_point(server, application, - *args, **kwargs): - application = newrelic.api.wsgi_application.WSGIApplicationWrapper( - application) +def instrument_waitress_server(module): + def wrap_wsgi_application_entry_point(server, application, *args, **kwargs): + dispatcher_details = ("Waitress", get_package_version("waitress")) + application = WSGIApplicationWrapper(application, dispatcher=dispatcher_details) args = [server, application] + list(args) return (args, kwargs) - newrelic.api.in_function.wrap_in_function(module, - 'WSGIServer.__init__', wrap_wsgi_application_entry_point) + wrap_in_function(module, "WSGIServer.__init__", wrap_wsgi_application_entry_point) diff --git a/tests/adapter_waitress/_application.py b/tests/adapter_waitress/_application.py new file mode 100644 index 0000000000..c3b36f0c2d --- /dev/null +++ b/tests/adapter_waitress/_application.py @@ -0,0 +1,54 @@ +# 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 threading import Thread +from time import sleep + +from testing_support.sample_applications import ( + raise_exception_application, + raise_exception_finalize, + raise_exception_response, + simple_app_raw, +) +from testing_support.util import get_open_port + + +def sample_application(environ, start_response): + path_info = environ.get("PATH_INFO") + + if path_info.startswith("/raise-exception-application"): + return raise_exception_application(environ, start_response) + elif path_info.startswith("/raise-exception-response"): + return raise_exception_response(environ, start_response) + elif path_info.startswith("/raise-exception-finalize"): + return raise_exception_finalize(environ, start_response) + + return simple_app_raw(environ, start_response) + + +def setup_application(): + port = get_open_port() + + def run_wsgi(): + from waitress import serve + + serve(sample_application, host="127.0.0.1", port=port) + + wsgi_thread = Thread(target=run_wsgi) + wsgi_thread.daemon = True + wsgi_thread.start() + + sleep(1) + + return port diff --git a/tests/adapter_waitress/conftest.py b/tests/adapter_waitress/conftest.py new file mode 100644 index 0000000000..aecbfd86d6 --- /dev/null +++ b/tests/adapter_waitress/conftest.py @@ -0,0 +1,40 @@ +# 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 +import webtest +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, +} + +collector_agent_registration = collector_agent_registration_fixture( + app_name="Python Agent Test (Waitress)", default_settings=_default_settings +) + + +@pytest.fixture(autouse=True, scope="session") +def target_application(): + import _application + + port = _application.setup_application() + return webtest.TestApp("http://localhost:%d" % port) diff --git a/tests/adapter_waitress/test_wsgi.py b/tests/adapter_waitress/test_wsgi.py new file mode 100644 index 0000000000..c9fa427196 --- /dev/null +++ b/tests/adapter_waitress/test_wsgi.py @@ -0,0 +1,101 @@ +# 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 ( + override_application_settings, + raise_background_exceptions, + wait_for_background_threads, +) +from testing_support.validators.validate_transaction_errors import ( + validate_transaction_errors, +) +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.common.package_version_utils import get_package_version + +WAITRESS_VERSION = get_package_version("waitress") + + +@override_application_settings({"transaction_name.naming_scheme": "framework"}) +def test_wsgi_application_index(target_application): + @validate_transaction_metrics( + "_application:sample_application", + custom_metrics=[ + ("Python/Dispatcher/Waitress/%s" % WAITRESS_VERSION, 1), + ], + ) + @raise_background_exceptions() + @wait_for_background_threads() + def _test(): + response = target_application.get("/") + assert response.status == "200 OK" + + _test() + + +@override_application_settings({"transaction_name.naming_scheme": "framework"}) +def test_raise_exception_application(target_application): + @validate_transaction_errors(["builtins:RuntimeError"]) + @validate_transaction_metrics( + "_application:sample_application", + custom_metrics=[ + ("Python/Dispatcher/Waitress/%s" % WAITRESS_VERSION, 1), + ], + ) + @raise_background_exceptions() + @wait_for_background_threads() + def _test(): + response = target_application.get("/raise-exception-application/", status=500) + assert response.status == "500 Internal Server Error" + + _test() + + +@override_application_settings({"transaction_name.naming_scheme": "framework"}) +def test_raise_exception_response(target_application): + @validate_transaction_errors(["builtins:RuntimeError"]) + @validate_transaction_metrics( + "_application:sample_application", + custom_metrics=[ + ("Python/Dispatcher/Waitress/%s" % WAITRESS_VERSION, 1), + ], + ) + @raise_background_exceptions() + @wait_for_background_threads() + def _test(): + response = target_application.get("/raise-exception-response/", status=500) + assert response.status == "500 Internal Server Error" + + _test() + + +@override_application_settings({"transaction_name.naming_scheme": "framework"}) +def test_raise_exception_finalize(target_application): + @validate_transaction_errors(["builtins:RuntimeError"]) + @validate_transaction_metrics( + "_application:sample_application", + custom_metrics=[ + ("Python/Dispatcher/Waitress/%s" % WAITRESS_VERSION, 1), + ], + ) + @raise_background_exceptions() + @wait_for_background_threads() + def _test(): + response = target_application.get("/raise-exception-finalize/", status=500) + assert response.status == "500 Internal Server Error" + + _test() diff --git a/tests/datastore_aioredis/test_uninstrumented_methods.py b/tests/datastore_aioredis/test_uninstrumented_methods.py index c48b5a4424..7858709c14 100644 --- a/tests/datastore_aioredis/test_uninstrumented_methods.py +++ b/tests/datastore_aioredis/test_uninstrumented_methods.py @@ -19,6 +19,7 @@ "channels", "client_tracking_off", "client_tracking_on", + "client_no_touch", "close", "closed", "connection_pool", diff --git a/tests/datastore_pyodbc/conftest.py b/tests/datastore_pyodbc/conftest.py new file mode 100644 index 0000000000..b00a0a663f --- /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 0000000000..119908e4db --- /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/tests/datastore_redis/test_uninstrumented_methods.py b/tests/datastore_redis/test_uninstrumented_methods.py index f9c6756592..ccf5a096df 100644 --- a/tests/datastore_redis/test_uninstrumented_methods.py +++ b/tests/datastore_redis/test_uninstrumented_methods.py @@ -14,19 +14,18 @@ import pytest import redis - from testing_support.db_settings import redis_settings DB_SETTINGS = redis_settings()[0] -redis_client = redis.Redis(host=DB_SETTINGS['host'], port=DB_SETTINGS['port'], db=0) -strict_redis_client = redis.StrictRedis(host=DB_SETTINGS['host'], port=DB_SETTINGS['port'], db=0) +redis_client = redis.Redis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) +strict_redis_client = redis.StrictRedis(host=DB_SETTINGS["host"], port=DB_SETTINGS["port"], db=0) IGNORED_METHODS = { - 'MODULE_CALLBACKS', - 'MODULE_VERSION', - 'NAME', + "MODULE_CALLBACKS", + "MODULE_VERSION", + "NAME", "add_edge", "add_node", "append_bucket_size", @@ -44,6 +43,7 @@ "BatchIndexer", "bulk", "call_procedure", + "client_no_touch", "client_tracking_off", "client_tracking_on", "client", diff --git a/tests/datastore_umemcache/test_memcache.py b/tests/datastore_umemcache/test_memcache.py deleted file mode 100644 index 4683a20d59..0000000000 --- a/tests/datastore_umemcache/test_memcache.py +++ /dev/null @@ -1,143 +0,0 @@ -# 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 os - -import umemcache - -from testing_support.db_settings import memcached_settings -from testing_support.validators.validate_transaction_metrics import validate_transaction_metrics - -from newrelic.api.background_task import background_task -from newrelic.api.transaction import set_background_task - - -DB_SETTINGS = memcached_settings()[0] - -MEMCACHED_HOST = DB_SETTINGS["host"] -MEMCACHED_PORT = DB_SETTINGS["port"] -MEMCACHED_NAMESPACE = DB_SETTINGS["namespace"] - -MEMCACHED_ADDR = '%s:%s' % (MEMCACHED_HOST, MEMCACHED_PORT) - -_test_bt_set_get_delete_scoped_metrics = [ - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 1), - ('Datastore/operation/Memcached/delete', 1)] - -_test_bt_set_get_delete_rollup_metrics = [ - ('Datastore/all', 3), - ('Datastore/allOther', 3), - ('Datastore/Memcached/all', 3), - ('Datastore/Memcached/allOther', 3), - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 1), - ('Datastore/operation/Memcached/delete', 1)] - -@validate_transaction_metrics( - 'test_memcache:test_bt_set_get_delete', - scoped_metrics=_test_bt_set_get_delete_scoped_metrics, - rollup_metrics=_test_bt_set_get_delete_rollup_metrics, - background_task=True) -@background_task() -def test_bt_set_get_delete(): - set_background_task(True) - client = umemcache.Client(MEMCACHED_ADDR) - client.connect() - - key = MEMCACHED_NAMESPACE + 'key' - - client.set(key, 'value') - value = client.get(key)[0] - client.delete(key) - - assert value == 'value' - -_test_wt_set_get_delete_scoped_metrics = [ - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 1), - ('Datastore/operation/Memcached/delete', 1)] - -_test_wt_set_get_delete_rollup_metrics = [ - ('Datastore/all', 3), - ('Datastore/allWeb', 3), - ('Datastore/Memcached/all', 3), - ('Datastore/Memcached/allWeb', 3), - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 1), - ('Datastore/operation/Memcached/delete', 1)] - -@validate_transaction_metrics( - 'test_memcache:test_wt_set_get_delete', - scoped_metrics=_test_wt_set_get_delete_scoped_metrics, - rollup_metrics=_test_wt_set_get_delete_rollup_metrics, - background_task=False) -@background_task() -def test_wt_set_get_delete(): - set_background_task(False) - client = umemcache.Client(MEMCACHED_ADDR) - client.connect() - - key = MEMCACHED_NAMESPACE + 'key' - - client.set(key, 'value') - value = client.get(key)[0] - client.delete(key) - - assert value == 'value' - -_test_wt_set_incr_decr_scoped_metrics = [ - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 2), - ('Datastore/operation/Memcached/incr', 2), - ('Datastore/operation/Memcached/decr', 1), - ('Datastore/operation/Memcached/stats', 1)] - -_test_wt_set_incr_decr_rollup_metrics = [ - ('Datastore/all', 7), - ('Datastore/allWeb', 7), - ('Datastore/Memcached/all', 7), - ('Datastore/Memcached/allWeb', 7), - ('Datastore/operation/Memcached/set', 1), - ('Datastore/operation/Memcached/get', 2), - ('Datastore/operation/Memcached/incr', 2), - ('Datastore/operation/Memcached/decr', 1), - ('Datastore/operation/Memcached/stats', 1)] - -@validate_transaction_metrics( - 'test_memcache:test_wt_set_incr_decr', - scoped_metrics=_test_wt_set_incr_decr_scoped_metrics, - rollup_metrics=_test_wt_set_incr_decr_rollup_metrics, - background_task=False) -@background_task() -def test_wt_set_incr_decr(): - set_background_task(False) - client = umemcache.Client(MEMCACHED_ADDR) - client.connect() - - key = MEMCACHED_NAMESPACE + 'key' - - client.set(key, '667') - value = client.get(key)[0] - client.incr(key, 1) - client.incr(key, 1) - client.decr(key, 1) - value = client.get(key)[0] - - assert value == '668' - - d = client.stats() - - assert d.has_key('uptime') - assert d.has_key('bytes') diff --git a/tests/datastore_umemcache/conftest.py b/tests/template_genshi/conftest.py similarity index 55% rename from tests/datastore_umemcache/conftest.py rename to tests/template_genshi/conftest.py index aa61ca26a8..932ec9bae2 100644 --- a/tests/datastore_umemcache/conftest.py +++ b/tests/template_genshi/conftest.py @@ -12,20 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest - -from testing_support.fixtures import collector_agent_registration_fixture, collector_available_fixture # noqa: F401; pylint: disable=W0611 - +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 + "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, } collector_agent_registration = collector_agent_registration_fixture( - app_name='Python Agent Test (datastore_umemcache)', - default_settings=_default_settings, - linked_applications=['Python Agent Test (datastore)']) + app_name="Python Agent Test (template_genshi)", default_settings=_default_settings +) diff --git a/tests/template_genshi/test_genshi.py b/tests/template_genshi/test_genshi.py new file mode 100644 index 0000000000..03420579e5 --- /dev/null +++ b/tests/template_genshi/test_genshi.py @@ -0,0 +1,38 @@ +# 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 genshi.template import MarkupTemplate +from testing_support.validators.validate_transaction_metrics import ( + validate_transaction_metrics, +) + +from newrelic.api.background_task import background_task + + +@validate_transaction_metrics( + "test_render", + background_task=True, + scoped_metrics=(("Template/Render/genshi.core:Stream.render", 1),), +) +@background_task(name="test_render") +def test_render(): + template_to_render = MarkupTemplate("