Repository: getsentry/sentry-python

Issue:
Redis integration tests have side effects
### How do you use Sentry?

Self-hosted/on-premise

### Version

1.9.2

### Steps to Reproduce

While working on https://github.com/getsentry/sentry-python/pull/1543, I noticed the following:

1. Checked out `sentry-sdk` for development.
2. Installed redis:
    ```
    fakeredis==1.9.0
    redis==3.5.3
    redis-py-cluster==2.1.3
    ````
3. Run redis integration tests twice, in different order:
    ```bash
    # first rediscluster, then redis
    pytest 'tests/integrations/rediscluster/test_rediscluster.py::test_rediscluster_basic[RedisCluster]' tests/integrations/redis/test_redis.py::test_basic
    # first redis, then rediscluster
    pytest tests/integrations/redis/test_redis.py::test_basic 'tests/integrations/rediscluster/test_rediscluster.py::test_rediscluster_basic[RedisCluster]'

### Expected Result

Both test runs pass.

### Actual Result

The second test run

```bash
pytest tests/integrations/redis/test_redis.py::test_basic 'tests/integrations/rediscluster/test_rediscluster.py::test_rediscluster_basic[RedisCluster]' 
```

fails with 

```pytest
tests/integrations/redis/test_redis.py .                                                                                                                                                                                                                                                    [ 50%]
tests/integrations/rediscluster/test_rediscluster.py F                                                                                                                                                                                                                                      [100%]

============================================================================================================================================ FAILURES =============================================================================================================================================
______________________________________________________________________________________________________________________________ test_rediscluster_basic[RedisCluster] ______________________________________________________________________________________________________________________________
tests/integrations/rediscluster/test_rediscluster.py:29: in test_rediscluster_basic
    (crumb,) = event["breadcrumbs"]["values"]
E   ValueError: not enough values to unpack (expected 1, got 0)
```


Discussion:
thx @jjbayer, yes there are some others too that fail intermittently. @antonpirker just turned on parallelization in https://github.com/getsentry/sentry-python/pull/1504, I'm debating whether to turn this off for now and do this properly (cleanup all side-effecty tests).

To track one more on the same issue, `sanic.test_client` starts a server on the same port so if they run in parallel, it will fail.



Resolving patch:
diff --git a/sentry_sdk/integrations/redis.py b/sentry_sdk/integrations/redis.py
--- a/sentry_sdk/integrations/redis.py
+++ b/sentry_sdk/integrations/redis.py
@@ -131,7 +131,6 @@ def patch_redis_client(cls, is_cluster):
     This function can be used to instrument custom redis client classes or
     subclasses.
     """
-
     old_execute_command = cls.execute_command
 
     def sentry_patched_execute_command(self, name, *args, **kwargs):


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -19,6 +19,7 @@
 from sentry_sdk.transport import Transport
 from sentry_sdk.envelope import Envelope
 from sentry_sdk.utils import capture_internal_exceptions
+from sentry_sdk.integrations import _installed_integrations  # noqa: F401
 
 from tests import _warning_recorder, _warning_recorder_mgr
 
@@ -165,6 +166,17 @@ def inner(event):
     return inner
 
 
+@pytest.fixture
+def reset_integrations():
+    """
+    Use with caution, sometimes we really need to start
+    with a clean slate to ensure monkeypatching works well,
+    but this also means some other stuff will be monkeypatched twice.
+    """
+    global _installed_integrations
+    _installed_integrations.clear()
+
+
 @pytest.fixture
 def sentry_init(monkeypatch_test_transport, request):
     def inner(*a, **kw):
diff --git a/tests/integrations/rediscluster/test_rediscluster.py b/tests/integrations/rediscluster/test_rediscluster.py
--- a/tests/integrations/rediscluster/test_rediscluster.py
+++ b/tests/integrations/rediscluster/test_rediscluster.py
@@ -11,8 +11,8 @@
     rediscluster_classes.append(rediscluster.StrictRedisCluster)
 
 
-@pytest.fixture(scope="module", autouse=True)
-def monkeypatch_rediscluster_classes():
+@pytest.fixture(autouse=True)
+def monkeypatch_rediscluster_classes(reset_integrations):
 
     try:
         pipeline_cls = rediscluster.ClusterPipeline
diff --git a/tests/integrations/sanic/test_sanic.py b/tests/integrations/sanic/test_sanic.py
--- a/tests/integrations/sanic/test_sanic.py
+++ b/tests/integrations/sanic/test_sanic.py
@@ -1,5 +1,5 @@
+import os
 import sys
-
 import random
 import asyncio
 from unittest.mock import Mock
@@ -18,6 +18,20 @@
 
 @pytest.fixture
 def app():
+    if SANIC_VERSION < (19,):
+        """
+        Older Sanic versions 0.8 and 18 bind to the same fixed port which
+        creates problems when we run tests concurrently.
+        """
+        old_test_client = Sanic.test_client.__get__
+
+        def new_test_client(self):
+            client = old_test_client(self, Sanic)
+            client.port += os.getpid() % 100
+            return client
+
+        Sanic.test_client = property(new_test_client)
+
     if SANIC_VERSION >= (20, 12):
         # Build (20.12.0) adds a feature where the instance is stored in an internal class
         # registry for later retrieval, and so add register=False to disable that