From 751f1fe94b75f11dce5ddd9cef2fa194f13f28fd Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 22 Jan 2025 09:29:22 +0100 Subject: [PATCH 1/6] First running dummy function --- .gitignore | 3 + requirements-testing.txt | 4 + .../lambda_functions/hello_world/index.py | 2 + .../aws_lambda/test_aws_lambda.py | 127 ++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/integrations/aws_lambda/lambda_functions/hello_world/index.py create mode 100644 tests/integrations/aws_lambda/test_aws_lambda.py diff --git a/.gitignore b/.gitignore index 8c7a5f2174..5149586a89 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ relay pip-wheel-metadata .mypy_cache .vscode/ + +# for running AWS Lambda tests using AWS SAM +sam.template.yaml \ No newline at end of file diff --git a/requirements-testing.txt b/requirements-testing.txt index dfbd821845..2f78570d33 100644 --- a/requirements-testing.txt +++ b/requirements-testing.txt @@ -14,3 +14,7 @@ socksio httpcore[http2] setuptools Brotli + +aws-sam-cli +aws-cdk-lib +requests \ No newline at end of file diff --git a/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py new file mode 100644 index 0000000000..0857e04177 --- /dev/null +++ b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py @@ -0,0 +1,2 @@ +def handler(event, context): + return {"message": f"Hello, {event['name']}!"} diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py new file mode 100644 index 0000000000..b8a4399383 --- /dev/null +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -0,0 +1,127 @@ +import boto3 +import json +import pytest +import requests +import subprocess +import time +import yaml +from aws_cdk import ( + App, + CfnResource, + Stack, +) +from constructs import Construct + + +SAM_PORT = 3001 +SAM_REGION = "us-east-1" +SAM_TEMPLATE_FILE = "sam.template.yaml" + + +class DummyLambdaStack(Stack): + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + # Override the template synthesis + self.template_options.template_format_version = "2010-09-09" + self.template_options.transforms = ["AWS::Serverless-2016-10-31"] + + # Add the function using SAM format + CfnResource( + self, + "BasicTestFunction", + type="AWS::Serverless::Function", + properties={ + "CodeUri": "./tests/integrations/aws_lambda/lambda_functions/hello_world", + "Handler": "index.handler", + "Runtime": "python3.12", + }, + ) + + +def wait_for_sam(timeout=30, port=SAM_PORT): + """ + Wait for SAM to be ready, with timeout. + """ + start_time = time.time() + while True: + if time.time() - start_time > timeout: + raise TimeoutError("SAM failed to start within {} seconds".format(timeout)) + + try: + # Try to connect to SAM + response = requests.get(f"http://127.0.0.1:{port}/") + if response.status_code == 200 or response.status_code == 404: + return + + except requests.exceptions.ConnectionError: + time.sleep(1) + continue + + +@pytest.fixture(scope="session") +def sam_stack(): + """ + Create and deploy the SAM stack once for all tests + """ + app = App() + stack = DummyLambdaStack(app, "DummyLambdaStack", env={"region": SAM_REGION}) + + # Write template to file + template = app.synth().get_stack_by_name("DummyLambdaStack").template + with open(SAM_TEMPLATE_FILE, "w") as f: + yaml.dump(template, f) + + # Start SAM local + process = subprocess.Popen( + [ + "sam", + "local", + "start-lambda", + "--region", + SAM_REGION, + "--template", + SAM_TEMPLATE_FILE, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, # This makes stdout/stderr return strings instead of bytes + ) + + try: + # Wait for SAM to be ready + wait_for_sam() + yield stack + finally: + process.terminate() + process.wait(timeout=5) # Give it time to shut down gracefully + + # Force kill if still running + if process.poll() is None: + process.kill() + + +@pytest.fixture +def lambda_client(): + """ + Create a boto3 client configured to use SAM local + """ + return boto3.client( + "lambda", + endpoint_url=f"http://127.0.0.1:{SAM_PORT}", + region_name=SAM_REGION, + aws_access_key_id="dummy", + aws_secret_access_key="dummy", + ) + + +def test_basic(lambda_client, sam_stack): + region = boto3.Session().region_name + print("Region: ", region) + + response = lambda_client.invoke( + FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Anton"}) + ) + result = json.loads(response["Payload"].read().decode()) + + assert result == {"message": "Hello, Anton!"} From 7bb1a4a585f1695da4164b41a6550bf793bad49b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 22 Jan 2025 13:39:40 +0100 Subject: [PATCH 2/6] Full working example with layer and sentry server collecting envelopes --- .../lambda_functions/hello_world/index.py | 6 +- .../lambda_layer/python/somelayer.py | 14 ++++ .../aws_lambda/test_aws_lambda.py | 84 ++++++++++++++++++- 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 tests/integrations/aws_lambda/lambda_layer/python/somelayer.py diff --git a/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py index 0857e04177..fcf7df0eb8 100644 --- a/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py +++ b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py @@ -1,2 +1,6 @@ +from somelayer import layer_function + + def handler(event, context): - return {"message": f"Hello, {event['name']}!"} + message = f"Hello, {event['name']}! ({layer_function()})" + return {"message": message} diff --git a/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py b/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py new file mode 100644 index 0000000000..30308ed1c8 --- /dev/null +++ b/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py @@ -0,0 +1,14 @@ +import urllib.request +import urllib.parse +import json + + +def layer_function(): + data = json.dumps({"event": "test from layer"}).encode("utf-8") + req = urllib.request.Request( + "http://host.docker.internal:9999/api/0/envelope/", + data=data, + headers={"Content-Type": "application/json"}, + ) + with urllib.request.urlopen(req) as response: + return "This is from the Layer" diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py index b8a4399383..e04f6dc315 100644 --- a/tests/integrations/aws_lambda/test_aws_lambda.py +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -4,6 +4,7 @@ import requests import subprocess import time +import threading import yaml from aws_cdk import ( App, @@ -26,6 +27,25 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: self.template_options.template_format_version = "2010-09-09" self.template_options.transforms = ["AWS::Serverless-2016-10-31"] + # Create Sentry Lambda Layer + layer = CfnResource( + self, + "SentryPythonServerlessSDK", + type="AWS::Serverless::LayerVersion", + properties={ + "ContentUri": "./tests/integrations/aws_lambda/lambda_layer", + "CompatibleRuntimes": [ + "python3.7", + "python3.8", + "python3.9", + "python3.10", + "python3.11", + "python3.12", + "python3.13", + ], + }, + ) + # Add the function using SAM format CfnResource( self, @@ -35,6 +55,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: "CodeUri": "./tests/integrations/aws_lambda/lambda_functions/hello_world", "Handler": "index.handler", "Runtime": "python3.12", + "Layers": [{"Ref": layer.logical_id}], }, ) @@ -59,11 +80,55 @@ def wait_for_sam(timeout=30, port=SAM_PORT): continue +class SentryTestServer: + def __init__(self, port=9999): + self.envelopes = [] + self.port = port + + from fastapi import FastAPI, Request + + self.app = FastAPI() + + @self.app.get("/") + async def root(): + return { + "message": "Sentry Test Server. Use DSN http://123@localhost:9999/0 in your SDK." + } + + @self.app.post("/api/0/envelope/") + async def envelope(request: Request): + self.envelopes.append(await request.json()) + return {"status": "ok"} + + def run_server(self): + import uvicorn + + uvicorn.run(self.app, host="0.0.0.0", port=self.port) + + def start(self): + server_thread = threading.Thread(target=self.run_server, daemon=True) + server_thread.start() + + def clear_envelopes(self): + self.envelopes = [] + + +@pytest.fixture(scope="session") +def sentry_test_server(): + server = SentryTestServer() + server.start() + + time.sleep(1) # Give it a moment to start up + + yield server + + @pytest.fixture(scope="session") def sam_stack(): """ Create and deploy the SAM stack once for all tests """ + # Create the SAM stack app = App() stack = DummyLambdaStack(app, "DummyLambdaStack", env={"region": SAM_REGION}) @@ -92,6 +157,7 @@ def sam_stack(): # Wait for SAM to be ready wait_for_sam() yield stack + finally: process.terminate() process.wait(timeout=5) # Give it time to shut down gracefully @@ -115,13 +181,25 @@ def lambda_client(): ) -def test_basic(lambda_client, sam_stack): - region = boto3.Session().region_name - print("Region: ", region) +def test_basic(lambda_client, sam_stack, sentry_test_server): + sentry_test_server.clear_envelopes() + + response = lambda_client.invoke( + FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Anton"}) + ) + result = json.loads(response["Payload"].read().decode()) + + print(sentry_test_server.envelopes) + assert result == {"message": "Hello, Anton!"} + + +def test_basic_2(lambda_client, sam_stack, sentry_test_server): + sentry_test_server.clear_envelopes() response = lambda_client.invoke( FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Anton"}) ) result = json.loads(response["Payload"].read().decode()) + print(sentry_test_server.envelopes) assert result == {"message": "Hello, Anton!"} From 01ff49148c37c97194ff86adb472a83095ff9ce2 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 22 Jan 2025 13:52:00 +0100 Subject: [PATCH 3/6] Cleanup --- tests/integrations/aws_lambda/test_aws_lambda.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py index e04f6dc315..159366929d 100644 --- a/tests/integrations/aws_lambda/test_aws_lambda.py +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -113,7 +113,7 @@ def clear_envelopes(self): self.envelopes = [] -@pytest.fixture(scope="session") +@pytest.fixture(scope="session", autouse=True) def sentry_test_server(): server = SentryTestServer() server.start() @@ -123,7 +123,7 @@ def sentry_test_server(): yield server -@pytest.fixture(scope="session") +@pytest.fixture(scope="session", autouse=True) def sam_stack(): """ Create and deploy the SAM stack once for all tests @@ -181,7 +181,7 @@ def lambda_client(): ) -def test_basic(lambda_client, sam_stack, sentry_test_server): +def test_basic(lambda_client, sentry_test_server): sentry_test_server.clear_envelopes() response = lambda_client.invoke( @@ -193,7 +193,7 @@ def test_basic(lambda_client, sam_stack, sentry_test_server): assert result == {"message": "Hello, Anton!"} -def test_basic_2(lambda_client, sam_stack, sentry_test_server): +def test_basic_2(lambda_client, sentry_test_server): sentry_test_server.clear_envelopes() response = lambda_client.invoke( From 981562dfdad0686d1df530b153cc24176550792b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 22 Jan 2025 17:12:31 +0100 Subject: [PATCH 4/6] Using the actual Sentry Layer --- .../lambda_functions/hello_world/index.py | 5 +- .../lambda_layer/python/somelayer.py | 14 ---- .../aws_lambda/test_aws_lambda.py | 84 ++++++++++++++----- 3 files changed, 68 insertions(+), 35 deletions(-) delete mode 100644 tests/integrations/aws_lambda/lambda_layer/python/somelayer.py diff --git a/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py index fcf7df0eb8..87ba44708f 100644 --- a/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py +++ b/tests/integrations/aws_lambda/lambda_functions/hello_world/index.py @@ -1,6 +1,7 @@ -from somelayer import layer_function +import sentry_sdk def handler(event, context): - message = f"Hello, {event['name']}! ({layer_function()})" + message = f"Hello, {event['name']}!" + sentry_sdk.capture_message(f"[SENTRY MESSAGE] {message}") return {"message": message} diff --git a/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py b/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py deleted file mode 100644 index 30308ed1c8..0000000000 --- a/tests/integrations/aws_lambda/lambda_layer/python/somelayer.py +++ /dev/null @@ -1,14 +0,0 @@ -import urllib.request -import urllib.parse -import json - - -def layer_function(): - data = json.dumps({"event": "test from layer"}).encode("utf-8") - req = urllib.request.Request( - "http://host.docker.internal:9999/api/0/envelope/", - data=data, - headers={"Content-Type": "application/json"}, - ) - with urllib.request.urlopen(req) as response: - return "This is from the Layer" diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py index 159366929d..d314da4c83 100644 --- a/tests/integrations/aws_lambda/test_aws_lambda.py +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -1,17 +1,25 @@ import boto3 +import gzip import json +import os import pytest import requests import subprocess +import shutil import time import threading import yaml + from aws_cdk import ( App, CfnResource, Stack, ) from constructs import Construct +from fastapi import FastAPI, Request +import uvicorn + +from scripts.build_aws_lambda_layer import build_packaged_zip, DIST_PATH SAM_PORT = 3001 @@ -28,12 +36,18 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: self.template_options.transforms = ["AWS::Serverless-2016-10-31"] # Create Sentry Lambda Layer + filename = "sentry-sdk-lambda-layer.zip" + build_packaged_zip( + make_dist=True, + out_zip_filename=filename, + ) + layer = CfnResource( self, "SentryPythonServerlessSDK", type="AWS::Serverless::LayerVersion", properties={ - "ContentUri": "./tests/integrations/aws_lambda/lambda_layer", + "ContentUri": os.path.join(DIST_PATH, filename), "CompatibleRuntimes": [ "python3.7", "python3.8", @@ -53,9 +67,16 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: type="AWS::Serverless::Function", properties={ "CodeUri": "./tests/integrations/aws_lambda/lambda_functions/hello_world", - "Handler": "index.handler", + "Handler": "sentry_sdk.integrations.init_serverless_sdk.sentry_lambda_handler", "Runtime": "python3.12", - "Layers": [{"Ref": layer.logical_id}], + "Layers": [{"Ref": layer.logical_id}], # The layer adds the sentry-sdk + "Environment": { # The environment variables are set up the Sentry SDK to instrument the lambda function + "Variables": { + "SENTRY_DSN": "http://123@host.docker.internal:9999/0", + "SENTRY_INITIAL_HANDLER": "index.handler", + "SENTRY_TRACES_SAMPLE_RATE": "1.0", + } + }, }, ) @@ -84,32 +105,51 @@ class SentryTestServer: def __init__(self, port=9999): self.envelopes = [] self.port = port - - from fastapi import FastAPI, Request - self.app = FastAPI() - @self.app.get("/") - async def root(): - return { - "message": "Sentry Test Server. Use DSN http://123@localhost:9999/0 in your SDK." - } - @self.app.post("/api/0/envelope/") async def envelope(request: Request): - self.envelopes.append(await request.json()) + print("[SENTRY SERVER] Received envelope") + try: + raw_body = await request.body() + except: + return {"status": "no body"} + + try: + body = gzip.decompress(raw_body).decode("utf-8") + except: + # If decompression fails, assume it's plain text + body = raw_body.decode("utf-8") + + lines = body.split("\n") + + current_line = 1 # line 0 is envelope header + while current_line < len(lines): + # skip empty lines + if not lines[current_line].strip(): + current_line += 1 + continue + + # skip envelope item header + current_line += 1 + + # add envelope item to store + envelope_item = lines[current_line] + if envelope_item.strip(): + self.envelopes.append(json.loads(envelope_item)) + return {"status": "ok"} def run_server(self): - import uvicorn - uvicorn.run(self.app, host="0.0.0.0", port=self.port) def start(self): + print("[SENTRY SERVER] Starting server") server_thread = threading.Thread(target=self.run_server, daemon=True) server_thread.start() def clear_envelopes(self): + print("[SENTRY SERVER] Clear envelopes") self.envelopes = [] @@ -185,21 +225,27 @@ def test_basic(lambda_client, sentry_test_server): sentry_test_server.clear_envelopes() response = lambda_client.invoke( - FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Anton"}) + FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Ivana"}) ) result = json.loads(response["Payload"].read().decode()) + assert result == {"message": "Hello, Ivana!"} + print("envelopes:") print(sentry_test_server.envelopes) - assert result == {"message": "Hello, Anton!"} + + # assert sentry_test_server.envelopes == [{"message": "[SENTRY MESSAGE] Hello, Ivana!"}] def test_basic_2(lambda_client, sentry_test_server): sentry_test_server.clear_envelopes() response = lambda_client.invoke( - FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Anton"}) + FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Neel"}) ) result = json.loads(response["Payload"].read().decode()) + assert result == {"message": "Hello, Neel!"} + print("envelopes2:") print(sentry_test_server.envelopes) - assert result == {"message": "Hello, Anton!"} + + # assert sentry_test_server.envelopes == [{"message": "[SENTRY MESSAGE] Hello, Neel!"}] From 2d4e47db94d80d4cf39dd834bb14c8008d139aad Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 22 Jan 2025 17:14:16 +0100 Subject: [PATCH 5/6] Cleanup --- tests/integrations/aws_lambda/test_aws_lambda.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py index d314da4c83..50b67cc010 100644 --- a/tests/integrations/aws_lambda/test_aws_lambda.py +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -5,7 +5,6 @@ import pytest import requests import subprocess -import shutil import time import threading import yaml From e1d0c79f020584c6b951663d3e3c10cad2458a11 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Mon, 27 Jan 2025 16:15:01 +0100 Subject: [PATCH 6/6] Clearing envelopes between tests --- .../aws_lambda/test_aws_lambda.py | 94 +++++++++++-------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/tests/integrations/aws_lambda/test_aws_lambda.py b/tests/integrations/aws_lambda/test_aws_lambda.py index 50b67cc010..264575a171 100644 --- a/tests/integrations/aws_lambda/test_aws_lambda.py +++ b/tests/integrations/aws_lambda/test_aws_lambda.py @@ -79,25 +79,27 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: }, ) + @classmethod + def wait_for_stack(cls, timeout=30, port=SAM_PORT): + """ + Wait for SAM to be ready, with timeout. + """ + start_time = time.time() + while True: + if time.time() - start_time > timeout: + raise TimeoutError( + "SAM failed to start within {} seconds".format(timeout) + ) -def wait_for_sam(timeout=30, port=SAM_PORT): - """ - Wait for SAM to be ready, with timeout. - """ - start_time = time.time() - while True: - if time.time() - start_time > timeout: - raise TimeoutError("SAM failed to start within {} seconds".format(timeout)) - - try: - # Try to connect to SAM - response = requests.get(f"http://127.0.0.1:{port}/") - if response.status_code == 200 or response.status_code == 404: - return + try: + # Try to connect to SAM + response = requests.get(f"http://127.0.0.1:{port}/") + if response.status_code == 200 or response.status_code == 404: + return - except requests.exceptions.ConnectionError: - time.sleep(1) - continue + except requests.exceptions.ConnectionError: + time.sleep(1) + continue class SentryTestServer: @@ -153,20 +155,14 @@ def clear_envelopes(self): @pytest.fixture(scope="session", autouse=True) -def sentry_test_server(): +def test_environment(): + print("Setting up AWS Lambda test infrastructure") + + # Setup dummy relay to capture envelopes server = SentryTestServer() server.start() - time.sleep(1) # Give it a moment to start up - yield server - - -@pytest.fixture(scope="session", autouse=True) -def sam_stack(): - """ - Create and deploy the SAM stack once for all tests - """ # Create the SAM stack app = App() stack = DummyLambdaStack(app, "DummyLambdaStack", env={"region": SAM_REGION}) @@ -194,10 +190,21 @@ def sam_stack(): try: # Wait for SAM to be ready - wait_for_sam() - yield stack + DummyLambdaStack.wait_for_stack() + + def before_test(): + server.clear_envelopes() + print("[TEST] Clearing envelopes before test") + + yield { + "stack": stack, + "server": server, + "before_test": before_test, # Add this function to the yielded dict + } finally: + print("Tearing down AWS Lambda test infrastructure") + process.terminate() process.wait(timeout=5) # Give it time to shut down gracefully @@ -206,6 +213,11 @@ def sam_stack(): process.kill() +@pytest.fixture(autouse=True) +def clear_before_test(test_environment): + test_environment["before_test"]() + + @pytest.fixture def lambda_client(): """ @@ -220,23 +232,20 @@ def lambda_client(): ) -def test_basic(lambda_client, sentry_test_server): - sentry_test_server.clear_envelopes() - +def test_basic(lambda_client, test_environment): response = lambda_client.invoke( FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Ivana"}) ) result = json.loads(response["Payload"].read().decode()) assert result == {"message": "Hello, Ivana!"} - print("envelopes:") - print(sentry_test_server.envelopes) + message, transaction = test_environment["server"].envelopes + assert message["message"] == "[SENTRY MESSAGE] Hello, Ivana!" + assert transaction["type"] == "transaction" - # assert sentry_test_server.envelopes == [{"message": "[SENTRY MESSAGE] Hello, Ivana!"}] - -def test_basic_2(lambda_client, sentry_test_server): - sentry_test_server.clear_envelopes() +def test_basic_2(lambda_client, test_environment): + test_environment["server"].clear_envelopes() response = lambda_client.invoke( FunctionName="BasicTestFunction", Payload=json.dumps({"name": "Neel"}) @@ -244,7 +253,10 @@ def test_basic_2(lambda_client, sentry_test_server): result = json.loads(response["Payload"].read().decode()) assert result == {"message": "Hello, Neel!"} - print("envelopes2:") - print(sentry_test_server.envelopes) + message, transaction = test_environment["server"].envelopes + assert message["message"] == "[SENTRY MESSAGE] Hello, Neel!" + assert transaction["type"] == "transaction" + - # assert sentry_test_server.envelopes == [{"message": "[SENTRY MESSAGE] Hello, Neel!"}] +# what is not working: +# i should improve how the server are started and stopped at the beginning and end of the test session.