diff --git a/.circleci/config.yml b/.circleci/config.yml index addfd6629..7aaf14d20 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -202,6 +202,38 @@ jobs: - store-pytest-results - store-coverage-report + python312: + docker: + - image: python:3.12 + - image: cimg/postgres:9.6.24 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: '' + POSTGRES_DB: circle_test + - image: cimg/mariadb:10.11.2 + - image: cimg/redis:5.0.14 + - image: rabbitmq:3.9.13 + - image: mongo:4.2.3 + - image: egymgmbh/pubsub-emulator + command: + - test-project + - test-topic + - test-subscription + working_directory: ~/repo + steps: + - run: + name: Install extra Python Dependencies for 3.12 + command: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source ~/.cargo/env + + - checkout + - pip-install-deps: + requirements: "tests/requirements-312.txt" + - run-tests-with-coverage-report + - store-pytest-results + - store-coverage-report + py38couchbase: docker: - image: cimg/python:3.8.17 @@ -259,6 +291,7 @@ workflows: - python39 - python310 - python311 + - python312 - py37cassandra - py38couchbase - py38gevent diff --git a/.gitignore b/.gitignore index 08885779c..ee7693faf 100644 --- a/.gitignore +++ b/.gitignore @@ -41,8 +41,6 @@ htmlcov/ .coverage .coverage.* .cache -nosetests.xml -nosetests.json coverage.xml *,cover .hypothesis/ diff --git a/tests/clients/test_pymongo.py b/tests/clients/test_pymongo.py index ca0a7bccf..6a6913825 100644 --- a/tests/clients/test_pymongo.py +++ b/tests/clients/test_pymongo.py @@ -8,9 +8,6 @@ import logging import pytest -from nose.tools import (assert_is_none, assert_is_not_none, - assert_false, assert_true, assert_list_equal) - from ..helpers import testenv from instana.singletons import tracer @@ -26,21 +23,21 @@ class TestPyMongoTracer(unittest.TestCase): def setUp(self): - self.conn = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), - username=testenv['mongodb_user'], password=testenv['mongodb_pw']) - self.conn.test.records.delete_many(filter={}) + self.client = pymongo.MongoClient(host=testenv['mongodb_host'], port=int(testenv['mongodb_port']), + username=testenv['mongodb_user'], password=testenv['mongodb_pw']) + self.client.test.records.delete_many(filter={}) self.recorder = tracer.recorder self.recorder.clear_spans() def tearDown(self): - return None + self.client.close() def test_successful_find_query(self): with tracer.start_active_span("test"): - self.conn.test.records.find_one({"type": "string"}) + self.client.test.records.find_one({"type": "string"}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -51,7 +48,7 @@ def test_successful_find_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) @@ -59,13 +56,13 @@ def test_successful_find_query(self): self.assertEqual(db_span.data["mongo"]["command"], "find") self.assertEqual(db_span.data["mongo"]["filter"], '{"type": "string"}') - assert_is_none(db_span.data["mongo"]["json"]) + self.assertIsNone(db_span.data["mongo"]["json"]) def test_successful_insert_query(self): with tracer.start_active_span("test"): - self.conn.test.records.insert_one({"type": "string"}) + self.client.test.records.insert_one({"type": "string"}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -76,20 +73,20 @@ def test_successful_insert_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") self.assertEqual(db_span.data["mongo"]["command"], "insert") - assert_is_none(db_span.data["mongo"]["filter"]) + self.assertIsNone(db_span.data["mongo"]["filter"]) def test_successful_update_query(self): with tracer.start_active_span("test"): - self.conn.test.records.update_one({"type": "string"}, {"$set": {"type": "int"}}) + self.client.test.records.update_one({"type": "string"}, {"$set": {"type": "int"}}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -100,29 +97,29 @@ def test_successful_update_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") self.assertEqual(db_span.data["mongo"]["command"], "update") - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) + self.assertIsNone(db_span.data["mongo"]["filter"]) + self.assertIsNotNone(db_span.data["mongo"]["json"]) payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({ + self.assertIn({ "q": {"type": "string"}, "u": {"$set": {"type": "int"}}, "multi": False, "upsert": False - } in payload, db_span.data["mongo"]["json"]) + }, payload) def test_successful_delete_query(self): with tracer.start_active_span("test"): - self.conn.test.records.delete_one(filter={"type": "string"}) + self.client.test.records.delete_one(filter={"type": "string"}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -133,24 +130,24 @@ def test_successful_delete_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") self.assertEqual(db_span.data["mongo"]["command"], "delete") - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) + self.assertIsNone(db_span.data["mongo"]["filter"]) + self.assertIsNotNone(db_span.data["mongo"]["json"]) payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({"q": {"type": "string"}, "limit": 1} in payload, db_span.data["mongo"]["json"]) + self.assertIn({"q": {"type": "string"}, "limit": 1}, payload) def test_successful_aggregate_query(self): with tracer.start_active_span("test"): - self.conn.test.records.count_documents({"type": "string"}) + self.client.test.records.count_documents({"type": "string"}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -161,18 +158,18 @@ def test_successful_aggregate_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) self.assertEqual(db_span.data["mongo"]["namespace"], "test.records") self.assertEqual(db_span.data["mongo"]["command"], "aggregate") - assert_is_none(db_span.data["mongo"]["filter"]) - assert_is_not_none(db_span.data["mongo"]["json"]) + self.assertIsNone(db_span.data["mongo"]["filter"]) + self.assertIsNotNone(db_span.data["mongo"]["json"]) payload = json.loads(db_span.data["mongo"]["json"]) - assert_true({"$match": {"type": "string"}} in payload, db_span.data["mongo"]["json"]) + self.assertIn({"$match": {"type": "string"}}, payload) @pymongoversion def test_successful_map_reduce_query(self): @@ -180,10 +177,10 @@ def test_successful_map_reduce_query(self): reducer = "function (key, values) { return len(values); }" with tracer.start_active_span("test"): - self.conn.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", + self.client.test.records.map_reduce(bson.code.Code(mapper), bson.code.Code(reducer), "results", query={"x": {"$lt": 2}}) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 2) @@ -194,7 +191,7 @@ def test_successful_map_reduce_query(self): self.assertEqual(test_span.t, db_span.t) self.assertEqual(db_span.p, test_span.s) - assert_is_none(db_span.ec) + self.assertIsNone(db_span.ec) self.assertEqual(db_span.n, "mongo") self.assertEqual(db_span.data["mongo"]["service"], "%s:%s" % (testenv['mongodb_host'], testenv['mongodb_port'])) @@ -203,7 +200,7 @@ def test_successful_map_reduce_query(self): "mapreduce") # mapreduce command was renamed to mapReduce in pymongo 3.9.0 self.assertEqual(db_span.data["mongo"]["filter"], '{"x": {"$lt": 2}}') - assert_is_not_none(db_span.data["mongo"]["json"]) + self.assertIsNotNone(db_span.data["mongo"]["json"]) payload = json.loads(db_span.data["mongo"]["json"]) self.assertEqual(payload["map"], {"$code": mapper}, db_span.data["mongo"]["json"]) @@ -211,11 +208,11 @@ def test_successful_map_reduce_query(self): def test_successful_mutiple_queries(self): with tracer.start_active_span("test"): - self.conn.test.records.bulk_write([pymongo.InsertOne({"type": "string"}), - pymongo.UpdateOne({"type": "string"}, {"$set": {"type": "int"}}), - pymongo.DeleteOne({"type": "string"})]) + self.client.test.records.bulk_write([pymongo.InsertOne({"type": "string"}), + pymongo.UpdateOne({"type": "string"}, {"$set": {"type": "int"}}), + pymongo.DeleteOne({"type": "string"})]) - assert_is_none(tracer.active_span) + self.assertIsNone(tracer.active_span) spans = self.recorder.queued_spans() self.assertEqual(len(spans), 4) @@ -229,11 +226,11 @@ def test_successful_mutiple_queries(self): self.assertEqual(span.p, test_span.s) # check if all spans got a unique id - assert_false(span.s in seen_span_ids) + self.assertNotIn(span.s, seen_span_ids) seen_span_ids.add(span.s) commands.append(span.data["mongo"]["command"]) # ensure spans are ordered the same way as commands - assert_list_equal(commands, ["insert", "update", "delete"]) + self.assertListEqual(commands, ["insert", "update", "delete"]) diff --git a/tests/conftest.py b/tests/conftest.py index c37d5a399..157f6636b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,13 @@ import sys import pytest +# Set our testing flags +os.environ["INSTANA_TEST"] = "true" +# os.environ["INSTANA_DEBUG"] = "true" + +# Make sure the instana package is fully loaded +import instana + collect_ignore_glob = [] # Cassandra and gevent tests are run in dedicated jobs on CircleCI and will @@ -28,13 +35,13 @@ # tests/opentracing/test_ot_span.py::TestOTSpan::test_stacks # TODO: Remove that once we find a workaround or DROP opentracing! -# Set our testing flags -os.environ["INSTANA_TEST"] = "true" -# os.environ["INSTANA_DEBUG"] = "true" - -# Make sure the instana package is fully loaded -import instana - +if sys.version_info.minor >= 12: + # Currently the dependencies of sanic and aiohttp are not installable on 3.12 + # PyLongObject’ {aka ‘struct _longobject’} has no member named ‘ob_digit’ + collect_ignore_glob.append("*test_sanic*") + collect_ignore_glob.append("*test_aiohttp*") + # The asyncio also depends on aiohttp + collect_ignore_glob.append("*test_asyncio*") @pytest.fixture(scope='session') def celery_config(): diff --git a/tests/frameworks/test_tornado_client.py b/tests/frameworks/test_tornado_client.py index 80df0cefd..20a2392c6 100644 --- a/tests/frameworks/test_tornado_client.py +++ b/tests/frameworks/test_tornado_client.py @@ -14,8 +14,7 @@ import tests.apps.tornado_server from ..helpers import testenv -from nose.plugins.skip import SkipTest -raise SkipTest("Non deterministic tests TBR") +raise unittest.SkipTest("Non deterministic tests TBR") class TestTornadoClient(unittest.TestCase): diff --git a/tests/opentracing/test_opentracing.py b/tests/opentracing/test_opentracing.py index e4c5f8c0a..0ed9e5082 100644 --- a/tests/opentracing/test_opentracing.py +++ b/tests/opentracing/test_opentracing.py @@ -1,7 +1,7 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2020 -from nose.plugins.skip import SkipTest +from unittest import SkipTest from opentracing.harness.api_check import APICompatibilityCheckMixin from instana.tracer import InstanaTracer diff --git a/tests/opentracing/test_ot_propagators.py b/tests/opentracing/test_ot_propagators.py index dc92839a7..01626e2cf 100644 --- a/tests/opentracing/test_ot_propagators.py +++ b/tests/opentracing/test_ot_propagators.py @@ -28,7 +28,7 @@ def test_http_inject_with_dict(): ot.tracer = InstanaTracer() carrier = {} - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.HTTP_HEADERS, carrier) assert 'X-INSTANA-T' in carrier @@ -43,7 +43,7 @@ def test_http_inject_with_list(): ot.tracer = InstanaTracer() carrier = [] - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.HTTP_HEADERS, carrier) assert ('X-INSTANA-T', span.context.trace_id) in carrier @@ -152,7 +152,7 @@ def test_text_inject_with_dict(): ot.tracer = InstanaTracer() carrier = {} - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.TEXT_MAP, carrier) assert 'x-instana-t' in carrier @@ -167,7 +167,7 @@ def test_text_inject_with_list(): ot.tracer = InstanaTracer() carrier = [] - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.TEXT_MAP, carrier) assert ('x-instana-t', span.context.trace_id) in carrier @@ -237,7 +237,7 @@ def test_binary_inject_with_dict(): ot.tracer = InstanaTracer() carrier = {} - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.BINARY, carrier) assert b'x-instana-t' in carrier @@ -252,7 +252,7 @@ def test_binary_inject_with_list(): ot.tracer = InstanaTracer() carrier = [] - span = ot.tracer.start_span("nosetests") + span = ot.tracer.start_span("unittest") ot.tracer.inject(span.context, ot.Format.BINARY, carrier) assert (b'x-instana-t', str.encode(span.context.trace_id)) in carrier diff --git a/tests/requirements-307.txt b/tests/requirements-307.txt index 9b9827399..7d0b33b57 100644 --- a/tests/requirements-307.txt +++ b/tests/requirements-307.txt @@ -22,7 +22,6 @@ lxml>=4.9.2 mock>=4.0.3 moto>=4.1.2 mysqlclient>=2.0.3 -nose>=1.3.7 PyMySQL[rsa]>=1.0.2 psycopg2-binary>=2.8.6 pika>=1.2.0 diff --git a/tests/requirements-310-with-tornado.txt b/tests/requirements-310-with-tornado.txt index 7c33f6052..3c51cdc31 100644 --- a/tests/requirements-310-with-tornado.txt +++ b/tests/requirements-310-with-tornado.txt @@ -21,7 +21,6 @@ lxml>=4.9.2 mock>=4.0.3 moto>=4.1.2 mysqlclient>=2.0.3 -nose>=1.3.7 PyMySQL[rsa]>=1.0.2 psycopg2-binary>=2.8.6 pika>=1.2.0 diff --git a/tests/requirements-310.txt b/tests/requirements-310.txt index b54f265fe..0b7229597 100644 --- a/tests/requirements-310.txt +++ b/tests/requirements-310.txt @@ -14,7 +14,6 @@ lxml>=4.9.2 mock>=4.0.3 moto>=4.1.2 mysqlclient>=2.0.3 -nose>=1.3.7 PyMySQL[rsa]>=1.0.2 psycopg2-binary>=2.8.6 pika>=1.2.0 diff --git a/tests/requirements-312.txt b/tests/requirements-312.txt new file mode 100644 index 000000000..a5c0f6f25 --- /dev/null +++ b/tests/requirements-312.txt @@ -0,0 +1,45 @@ +aiofiles>=0.5.0 +#aiohttp currently depends on yarl which can't be installed: +#PyLongObject’ {aka ‘struct _longobject’} has no member named ‘ob_digit’ +#aiohttp>=3.8.3 +boto3>=1.17.74 +celery>=5.2.7 +coverage>=5.5 +Django>=5.0a1 --pre +fastapi>=0.92.0 +flask>=2.3.2 +markupsafe>=2.1.0 +grpcio>=1.37.1 +google-cloud-pubsub<=2.1.0 +google-cloud-storage>=1.24.0 +lxml>=4.9.2 +mock>=4.0.3 +moto>=4.1.2 +mysqlclient>=2.0.3 +PyMySQL[rsa]>=1.0.2 +psycopg2-binary>=2.8.6 +pika>=1.2.0 + +# protobuf is pulled in and also `basictracer`, a core instana dependency +# and also by google-cloud-storage +# but also directly needed by tests/apps/grpc_server/stan_pb2.py +# On 4.0.0 we currently get: +# AttributeError: module 'google._upb._message' has no attribute 'Message' +# TODO: Remove this when support for 4.0.0 is done +protobuf<4.0.0 + +pymongo>=3.11.4 +pyramid>=2.0.1 +pytest>=6.2.4 +pytest-celery +redis>=3.5.3 +requests-mock +responses<=0.17.0 +#Sanic depends on uvloop which can't be installed: +#PyLongObject’ {aka ‘struct _longobject’} has no member named ‘ob_digit’ +#sanic==21.6.2 +sqlalchemy>=2.0.0 +spyne>=2.14.0 + +uvicorn>=0.13.4 +urllib3[secure]<1.27,>=1.26.5 diff --git a/tests/requirements-cassandra.txt b/tests/requirements-cassandra.txt index f1342103f..2f2be3e99 100644 --- a/tests/requirements-cassandra.txt +++ b/tests/requirements-cassandra.txt @@ -1,6 +1,5 @@ cassandra-driver>=3.20.2 coverage>=5.5 mock>=2.0.0 -nose>=1.0 pytest>=4.6 urllib3[secure]<1.27,>=1.26.5 diff --git a/tests/requirements-gevent.txt b/tests/requirements-gevent.txt index bb49a4ec8..e59d8bf4a 100644 --- a/tests/requirements-gevent.txt +++ b/tests/requirements-gevent.txt @@ -2,7 +2,6 @@ coverage>=5.5 flask>=0.12.2 gevent>=1.4.0 mock>=2.0.0 -nose>=1.0 pyramid>=2.0.1 pytest>=4.6 urllib3[secure]<1.27,>=1.26.5 diff --git a/tests/requirements.txt b/tests/requirements.txt index 3c6269133..42de661ea 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -13,7 +13,6 @@ lxml>=4.9.2 mock>=4.0.3 moto>=4.1.2 mysqlclient>=2.0.3 -nose>=1.3.7 PyMySQL[rsa]>=1.0.2 psycopg2-binary>=2.8.6 pika>=1.2.0