|
| 1 | +import base64 |
| 2 | +import json |
| 3 | +import os |
| 4 | +import shutil |
| 5 | +import subprocess |
| 6 | +import sys |
| 7 | +import uuid |
| 8 | + |
| 9 | +import pytest |
| 10 | + |
| 11 | +boto3 = pytest.importorskip("boto3") |
| 12 | + |
| 13 | +LAMBDA_TEMPLATE = """ |
| 14 | +from __future__ import print_function |
| 15 | +
|
| 16 | +from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration |
| 17 | +import sentry_sdk |
| 18 | +import json |
| 19 | +from sentry_sdk.transport import Transport |
| 20 | +
|
| 21 | +class TestTransport(Transport): |
| 22 | + def __init__(self): |
| 23 | + Transport.__init__(self) |
| 24 | + self._queue = [] |
| 25 | +
|
| 26 | + def capture_event(self, event): |
| 27 | + self._queue.append(event) |
| 28 | +
|
| 29 | + def shutdown(self, timeout, callback=None): |
| 30 | + # Delay event output like this to test proper shutdown |
| 31 | + for event in self._queue: |
| 32 | + print("EVENT:", json.dumps(event)) |
| 33 | +
|
| 34 | +sentry_sdk.init( |
| 35 | + |
| 36 | + transport=TestTransport(), |
| 37 | + integrations=[AwsLambdaIntegration()], |
| 38 | + **{extra_init_args} |
| 39 | +) |
| 40 | +
|
| 41 | +
|
| 42 | +def test_handler(event, context): |
| 43 | +{code} |
| 44 | +""" |
| 45 | + |
| 46 | + |
| 47 | +@pytest.fixture |
| 48 | +def lambda_client(): |
| 49 | + if "AWS_ACCESS_KEY_ID" not in os.environ: |
| 50 | + pytest.skip("AWS environ vars not set") |
| 51 | + |
| 52 | + return boto3.client( |
| 53 | + "lambda", |
| 54 | + aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], |
| 55 | + aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"], |
| 56 | + region_name="us-east-1", |
| 57 | + ) |
| 58 | + |
| 59 | + |
| 60 | +@pytest.fixture(params=["python3.6", "python2.7"]) |
| 61 | +def run_lambda_function(tmpdir, lambda_client, request, assert_semaphore_acceptance): |
| 62 | + def inner(lambda_body, payload, extra_init_args=None): |
| 63 | + tmpdir.ensure_dir("lambda_tmp").remove() |
| 64 | + tmp = tmpdir.ensure_dir("lambda_tmp") |
| 65 | + |
| 66 | + # https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html |
| 67 | + tmp.join("test_lambda.py").write( |
| 68 | + LAMBDA_TEMPLATE.format( |
| 69 | + code="\n".join(" " + x.strip() for x in lambda_body.splitlines()), |
| 70 | + extra_init_args=repr(extra_init_args or {}), |
| 71 | + ) |
| 72 | + ) |
| 73 | + tmp.join("setup.cfg").write("[install]\nprefix=") |
| 74 | + subprocess.check_call([sys.executable, "setup.py", "sdist", "-d", str(tmpdir)]) |
| 75 | + subprocess.check_call("pip install ../*.tar.gz -t .", cwd=str(tmp), shell=True) |
| 76 | + shutil.make_archive(tmpdir.join("ball"), "zip", str(tmp)) |
| 77 | + |
| 78 | + fn_name = "test_function_{}".format(uuid.uuid4()) |
| 79 | + |
| 80 | + lambda_client.create_function( |
| 81 | + FunctionName=fn_name, |
| 82 | + Runtime=request.param, |
| 83 | + Role=os.environ["AWS_IAM_ROLE"], |
| 84 | + Handler="test_lambda.test_handler", |
| 85 | + Code={"ZipFile": tmpdir.join("ball.zip").read(mode="rb")}, |
| 86 | + Description="Created as part of testsuite for getsentry/sentry-python", |
| 87 | + ) |
| 88 | + |
| 89 | + @request.addfinalizer |
| 90 | + def delete_function(): |
| 91 | + lambda_client.delete_function(FunctionName=fn_name) |
| 92 | + |
| 93 | + response = lambda_client.invoke( |
| 94 | + FunctionName=fn_name, |
| 95 | + InvocationType="RequestResponse", |
| 96 | + LogType="Tail", |
| 97 | + Payload=payload, |
| 98 | + ) |
| 99 | + |
| 100 | + assert 200 <= response["StatusCode"] < 300, response |
| 101 | + |
| 102 | + events = [] |
| 103 | + |
| 104 | + for line in base64.b64decode(response["LogResult"]).splitlines(): |
| 105 | + print("AWS:", line) |
| 106 | + if not line.startswith(b"EVENT: "): |
| 107 | + continue |
| 108 | + line = line[len(b"EVENT: ") :] |
| 109 | + events.append(json.loads(line.decode("utf-8"))) |
| 110 | + assert_semaphore_acceptance(events[-1]) |
| 111 | + |
| 112 | + return events, response |
| 113 | + |
| 114 | + return inner |
| 115 | + |
| 116 | + |
| 117 | +def test_basic(run_lambda_function): |
| 118 | + events, response = run_lambda_function( |
| 119 | + 'raise Exception("something went wrong")\n', b'{"foo": "bar"}' |
| 120 | + ) |
| 121 | + |
| 122 | + assert response["FunctionError"] == "Unhandled" |
| 123 | + |
| 124 | + event, = events |
| 125 | + assert event["level"] == "error" |
| 126 | + exception, = event["exception"]["values"] |
| 127 | + assert exception["type"] == "Exception" |
| 128 | + assert exception["value"] == "something went wrong" |
| 129 | + |
| 130 | + frame1, = exception["stacktrace"]["frames"] |
| 131 | + assert frame1["filename"] == "test_lambda.py" |
| 132 | + assert frame1["abs_path"] == "/var/task/test_lambda.py" |
| 133 | + assert frame1["function"] == "test_handler" |
| 134 | + |
| 135 | + assert frame1["in_app"] is True |
| 136 | + |
| 137 | + assert exception["mechanism"] == {"type": "aws_lambda", "handled": False} |
| 138 | + |
| 139 | + assert event["extra"]["lambda"]["function_name"].startswith("test_function_") |
| 140 | + |
| 141 | + |
| 142 | +def test_request_data(run_lambda_function): |
| 143 | + events, _response = run_lambda_function( |
| 144 | + 'sentry_sdk.capture_message("hi")\nreturn "ok"', |
| 145 | + payload=b""" |
| 146 | + { |
| 147 | + "resource": "/asd", |
| 148 | + "path": "/asd", |
| 149 | + "httpMethod": "GET", |
| 150 | + "headers": { |
| 151 | + "Host": "iwsz2c7uwi.execute-api.us-east-1.amazonaws.com", |
| 152 | + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0", |
| 153 | + "X-Forwarded-Proto": "https" |
| 154 | + }, |
| 155 | + "queryStringParameters": { |
| 156 | + "bonkers": "true" |
| 157 | + }, |
| 158 | + "pathParameters": null, |
| 159 | + "stageVariables": null, |
| 160 | + "requestContext": { |
| 161 | + "identity": { |
| 162 | + "sourceIp": "213.47.147.207", |
| 163 | + "userArn": "42" |
| 164 | + } |
| 165 | + }, |
| 166 | + "body": null, |
| 167 | + "isBase64Encoded": false |
| 168 | + } |
| 169 | + """, |
| 170 | + ) |
| 171 | + |
| 172 | + event, = events |
| 173 | + |
| 174 | + assert event["request"] == { |
| 175 | + "headers": { |
| 176 | + "Host": "iwsz2c7uwi.execute-api.us-east-1.amazonaws.com", |
| 177 | + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0", |
| 178 | + "X-Forwarded-Proto": "https", |
| 179 | + }, |
| 180 | + "method": "GET", |
| 181 | + "query_string": {"bonkers": "true"}, |
| 182 | + "url": "https://iwsz2c7uwi.execute-api.us-east-1.amazonaws.com/asd", |
| 183 | + } |
0 commit comments