From e39d67b5660cd55710be7e8e4aa2d8203078306e Mon Sep 17 00:00:00 2001 From: Joshua Fenton Date: Wed, 31 Jul 2024 19:56:12 +1000 Subject: [PATCH] Increase test coverage of instrumentation-aws-lambda --- .../mocks/alb_conventional_headers_event.py | 24 ++ .../mocks/alb_mutl_value_headers_event.py | 30 ++ .../tests/mocks/dynamo_db_event.py | 48 +++ .../tests/mocks/s3_event.py | 36 ++ .../tests/mocks/sns_event.py | 29 ++ .../tests/mocks/sqs_event.py | 24 ++ .../test_aws_lambda_instrumentation_manual.py | 387 ++++++++++++------ 7 files changed, 448 insertions(+), 130 deletions(-) create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_conventional_headers_event.py create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_mutl_value_headers_event.py create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/dynamo_db_event.py create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/s3_event.py create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sns_event.py create mode 100644 instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sqs_event.py diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_conventional_headers_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_conventional_headers_event.py new file mode 100644 index 0000000000..7462f2a64e --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_conventional_headers_event.py @@ -0,0 +1,24 @@ +MOCK_LAMBDA_ALB_EVENT = { + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09" + } + }, + "httpMethod": "GET", + "path": "/", + "queryStringParameters": {"foo": "bar"}, + "headers": { + "accept": "text/html,application/xhtml+xml", + "accept-language": "en-US,en;q=0.8", + "content-type": "text/plain", + "cookie": "cookies", + "host": "lambda-846800462-us-east-2.elb.amazonaws.com", + "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)", + "x-amzn-trace-id": "Root=1-5bdb40ca-556d8b0c50dc66f0511bf520", + "x-forwarded-for": "72.21.198.66", + "x-forwarded-port": "443", + "x-forwarded-proto": "https", + }, + "isBase64Encoded": False, + "body": "request_body", +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_mutl_value_headers_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_mutl_value_headers_event.py new file mode 100644 index 0000000000..bf7c2ae686 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/alb_mutl_value_headers_event.py @@ -0,0 +1,30 @@ +""" +https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers + +When an ALB is configured to send multi-value headers, the headers are sent as a list of values under the key in the multiValueHeaders object. +""" + +MOCK_LAMBDA_ALB_MULTI_VALUE_HEADER_EVENT = { + "requestContext": { + "elb": { + "targetGroupArn": "arn:aws:elasticloadbalancing:region:123456789012:targetgroup/my-target-group/6d0ecf831eec9f09" + } + }, + "httpMethod": "GET", + "path": "/", + "queryStringParameters": {"foo": "bar"}, + "multiValueHeaders": { + "accept": ["text/html,application/xhtml+xml"], + "accept-language": ["en-US,en;q=0.8"], + "content-type": ["text/plain"], + "cookie": ["cookies"], + "host": ["lambda-846800462-us-east-2.elb.amazonaws.com"], + "user-agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)"], + "x-amzn-trace-id": ["Root=1-5bdb40ca-556d8b0c50dc66f0511bf520"], + "x-forwarded-for": ["72.21.198.66"], + "x-forwarded-port": ["443"], + "x-forwarded-proto": ["https"], + }, + "isBase64Encoded": False, + "body": "request_body", +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/dynamo_db_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/dynamo_db_event.py new file mode 100644 index 0000000000..ea11237082 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/dynamo_db_event.py @@ -0,0 +1,48 @@ +""" +https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html +""" + +MOCK_LAMBDA_DYNAMO_DB_EVENT = { + "Records": [ + { + "eventID": "1", + "eventVersion": "1.0", + "dynamodb": { + "Keys": {"Id": {"N": "101"}}, + "NewImage": { + "Message": {"S": "New item!"}, + "Id": {"N": "101"}, + }, + "StreamViewType": "NEW_AND_OLD_IMAGES", + "SequenceNumber": "111", + "SizeBytes": 26, + }, + "awsRegion": "us-west-2", + "eventName": "INSERT", + "eventSourceARN": "arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2023-06-10T19:26:16.525", + "eventSource": "aws:dynamodb", + }, + { + "eventID": "2", + "eventVersion": "1.0", + "dynamodb": { + "OldImage": { + "Message": {"S": "New item!"}, + "Id": {"N": "101"}, + }, + "SequenceNumber": "222", + "Keys": {"Id": {"N": "101"}}, + "SizeBytes": 59, + "NewImage": { + "Message": {"S": "This item has changed"}, + "Id": {"N": "101"}, + }, + "StreamViewType": "NEW_AND_OLD_IMAGES", + }, + "awsRegion": "us-west-2", + "eventName": "MODIFY", + "eventSourceARN": "arn:aws:dynamodb:us-east-2:123456789012:table/my-table/stream/2023-06-10T19:26:16.525", + "eventSource": "aws:dynamodb", + }, + ] +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/s3_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/s3_event.py new file mode 100644 index 0000000000..8a866c0d4d --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/s3_event.py @@ -0,0 +1,36 @@ +""" +https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html +""" + +MOCK_LAMBDA_S3_EVENT = { + "Records": [ + { + "eventVersion": "2.1", + "eventSource": "aws:s3", + "awsRegion": "us-east-2", + "eventTime": "2019-09-03T19:37:27.192Z", + "eventName": "ObjectCreated:Put", + "userIdentity": {"principalId": "AWS:AIDAINPONIXQXHT3IKHL2"}, + "requestParameters": {"sourceIPAddress": "205.255.255.255"}, + "responseElements": { + "x-amz-request-id": "D82B88E5F771F645", + "x-amz-id-2": "vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo=", + }, + "s3": { + "s3SchemaVersion": "1.0", + "configurationId": "828aa6fc-f7b5-4305-8584-487c791949c1", + "bucket": { + "name": "DOC-EXAMPLE-BUCKET", + "ownerIdentity": {"principalId": "A3I5XTEXAMAI3E"}, + "arn": "arn:aws:s3:::lambda-artifacts-deafc19498e3f2df", + }, + "object": { + "key": "b21b84d653bb07b05b1e6b33684dc11b", + "size": 1305107, + "eTag": "b21b84d653bb07b05b1e6b33684dc11b", + "sequencer": "0C0F6F405D6ED209E1", + }, + }, + } + ] +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sns_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sns_event.py new file mode 100644 index 0000000000..f53155e26f --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sns_event.py @@ -0,0 +1,29 @@ +""" +https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html +""" + +MOCK_LAMBDA_SNS_EVENT = { + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": "arn:aws:sns:us-east-1:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "2019-01-02T12:45:07.000Z", + "Signature": "mock-signature", + "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": {"Type": "String", "Value": "TestString"}, + "TestBinary": {"Type": "Binary", "Value": "TestBinary"}, + }, + "Type": "Notification", + "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486", + "TopicArn": "arn:aws:sns:us-east-1:123456789012:sns-lambda", + "Subject": "TestInvoke", + }, + } + ] +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sqs_event.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sqs_event.py new file mode 100644 index 0000000000..081ff9debd --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/mocks/sqs_event.py @@ -0,0 +1,24 @@ +""" +https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html +""" + +MOCK_LAMBDA_SQS_EVENT = { + "Records": [ + { + "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d", + "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...", + "body": "Test message.", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1545082649183", + "SenderId": "AIDAIENQZJOLO23YVJ4VO", + "ApproximateFirstReceiveTimestamp": "1545082649185", + }, + "messageAttributes": {}, + "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue", + "awsRegion": "us-east-2", + } + ] +} diff --git a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py index 3a71e83aa2..bb37fe6a02 100644 --- a/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py +++ b/instrumentation/opentelemetry-instrumentation-aws-lambda/tests/test_aws_lambda_instrumentation_manual.py @@ -41,10 +41,18 @@ ) from opentelemetry.util._importlib_metadata import entry_points +from .mocks.alb_conventional_headers_event import MOCK_LAMBDA_ALB_EVENT +from .mocks.alb_mutl_value_headers_event import ( + MOCK_LAMBDA_ALB_MULTI_VALUE_HEADER_EVENT, +) from .mocks.api_gateway_http_api_event import ( MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT, ) from .mocks.api_gateway_proxy_event import MOCK_LAMBDA_API_GATEWAY_PROXY_EVENT +from .mocks.dynamo_db_event import MOCK_LAMBDA_DYNAMO_DB_EVENT +from .mocks.s3_event import MOCK_LAMBDA_S3_EVENT +from .mocks.sns_event import MOCK_LAMBDA_SNS_EVENT +from .mocks.sqs_event import MOCK_LAMBDA_SQS_EVENT class MockLambdaContext: @@ -58,8 +66,17 @@ def __init__(self, aws_request_id, invoked_function_arn): invoked_function_arn="arn:aws:lambda:us-east-1:123456:function:myfunction:myalias", ) -MOCK_XRAY_TRACE_ID = 0x5FB7331105E8BB83207FA31D4D9CDB4C +MOCK_LAMBDA_CONTEXT_ATTRIBUTES = { + SpanAttributes.CLOUD_RESOURCE_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn, + SpanAttributes.FAAS_INVOCATION_ID: MOCK_LAMBDA_CONTEXT.aws_request_id, + ResourceAttributes.CLOUD_ACCOUNT_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn.split( + ":" + )[ + 4 + ], +} +MOCK_XRAY_TRACE_ID = 0x5FB7331105E8BB83207FA31D4D9CDB4C MOCK_XRAY_TRACE_ID_STR = f"{MOCK_XRAY_TRACE_ID:x}" MOCK_XRAY_PARENT_SPAN_ID = 0x3328B8445A6DBAD2 MOCK_XRAY_TRACE_CONTEXT_COMMON = f"Root={TRACE_ID_VERSION}-{MOCK_XRAY_TRACE_ID_STR[:TRACE_ID_FIRST_PART_LENGTH]}-{MOCK_XRAY_TRACE_ID_STR[TRACE_ID_FIRST_PART_LENGTH:]};Parent={MOCK_XRAY_PARENT_SPAN_ID:x}" @@ -88,7 +105,6 @@ def mock_execute_lambda(event=None): """Mocks the AWS Lambda execution. NOTE: We don't use `moto`'s `mock_lambda` because we are not instrumenting - calls to AWS Lambda using the AWS SDK. Instead, we are instrumenting AWS Lambda itself. @@ -104,7 +120,7 @@ def mock_execute_lambda(event=None): return getattr(handler_module, handler_name)(event, MOCK_LAMBDA_CONTEXT) -class TestAwsLambdaInstrumentor(TestBase): +class TestAwsLambdaInstrumentorBase(TestBase): """AWS Lambda Instrumentation Testsuite""" def setUp(self): @@ -124,6 +140,8 @@ def tearDown(self): self.common_env_patch.stop() AwsLambdaInstrumentor().uninstrument() + +class TestAwsLambdaInstrumentor(TestAwsLambdaInstrumentorBase): def test_active_tracing(self): test_env_patch = mock.patch.dict( "os.environ", @@ -153,15 +171,7 @@ def test_active_tracing(self): self.assertEqual(span.kind, SpanKind.SERVER) self.assertSpanHasAttributes( span, - { - SpanAttributes.CLOUD_RESOURCE_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn, - SpanAttributes.FAAS_INVOCATION_ID: MOCK_LAMBDA_CONTEXT.aws_request_id, - ResourceAttributes.CLOUD_ACCOUNT_ID: MOCK_LAMBDA_CONTEXT.invoked_function_arn.split( - ":" - )[ - 4 - ], - }, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, ) parent_context = span.parent @@ -293,53 +303,51 @@ def custom_event_context_extractor(lambda_event): ), ] for test in tests: + with self.subTest(test_name=test.name): + test_env_patch = mock.patch.dict( + "os.environ", + { + **os.environ, + # NOT Active Tracing + _X_AMZN_TRACE_ID: test.xray_traceid, + OTEL_PROPAGATORS: test.propagators, + }, + ) + test_env_patch.start() + reload(propagate) - test_env_patch = mock.patch.dict( - "os.environ", - { - **os.environ, - # NOT Active Tracing - _X_AMZN_TRACE_ID: test.xray_traceid, - OTEL_PROPAGATORS: test.propagators, - }, - ) - test_env_patch.start() - reload(propagate) - - AwsLambdaInstrumentor().instrument( - event_context_extractor=test.custom_extractor, - ) - result = mock_execute_lambda(test.context) - result = json.loads(result) - - spans = self.memory_exporter.get_finished_spans() - assert spans - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual( - span.get_span_context().trace_id, test.expected_traceid - ) + AwsLambdaInstrumentor().instrument( + event_context_extractor=test.custom_extractor, + ) + result = mock_execute_lambda(test.context) + result = json.loads(result) + spans = self.memory_exporter.get_finished_spans() + assert spans + self.assertEqual(len(spans), 1) + span = spans[0] + self.assertEqual( + span.get_span_context().trace_id, test.expected_traceid + ) - parent_context = span.parent - self.assertEqual( - parent_context.trace_id, span.get_span_context().trace_id - ) - self.assertEqual(parent_context.span_id, test.expected_parentid) - self.assertEqual( - len(parent_context.trace_state), test.expected_trace_state_len - ) - self.assertEqual( - parent_context.trace_state.get(MOCK_W3C_TRACE_STATE_KEY), - test.expected_state_value, - ) - self.assertEqual( - result["baggage_content"].get(MOCK_W3C_BAGGAGE_KEY), - test.expected_baggage, - ) - self.assertTrue(parent_context.is_remote) - self.memory_exporter.clear() - AwsLambdaInstrumentor().uninstrument() - test_env_patch.stop() + parent_context = span.parent + self.assertEqual( + parent_context.trace_id, span.get_span_context().trace_id + ) + self.assertEqual( + parent_context.span_id, test.expected_parentid + ) + self.assertEqual( + len(parent_context.trace_state), + test.expected_trace_state_len, + ) + self.assertEqual( + parent_context.trace_state.get(MOCK_W3C_TRACE_STATE_KEY), + test.expected_state_value, + ) + self.assertTrue(parent_context.is_remote) + self.memory_exporter.clear() + AwsLambdaInstrumentor().uninstrument() + test_env_patch.stop() def test_lambda_no_error_with_invalid_flush_timeout(self): test_env_patch = mock.patch.dict( @@ -419,12 +427,99 @@ def test_lambda_handles_invalid_event_source(self): assert spans assert len(spans) == 1 - assert ( - spans[0].kind == SpanKind.SERVER - ) # Default to SERVER for unknown sources + # Default to SERVER for unknown sources + assert spans[0].kind == SpanKind.SERVER + + for span in spans: + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) test_env_patch.stop() + def test_lambda_handles_list_event(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda([{"message": "test"}]) + + spans = self.memory_exporter.get_finished_spans() + + assert spans + + def test_lambda_handles_handler_exception(self): + exc_env_patch = mock.patch.dict( + "os.environ", + {_HANDLER: "tests.mocks.lambda_function.handler_exc"}, + ) + exc_env_patch.start() + AwsLambdaInstrumentor().instrument() + # instrumentor re-raises the exception + with self.assertRaises(Exception): + mock_execute_lambda() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + span = spans[0] + self.assertEqual(span.status.status_code, StatusCode.ERROR) + self.assertEqual(len(span.events), 1) + event = span.events[0] + self.assertEqual(event.name, "exception") + + exc_env_patch.stop() + + def test_lambda_handles_should_do_nothing_when_environment_variables_not_present( + self, + ): + exc_env_patch = mock.patch.dict( + "os.environ", + {_HANDLER: ""}, + ) + exc_env_patch.start() + AwsLambdaInstrumentor().instrument() + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + exc_env_patch.stop() + + def test_uninstrument(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + self.memory_exporter.clear() + AwsLambdaInstrumentor().uninstrument() + + mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + def test_no_op_tracer_provider(self): + tracer_provider = NoOpTracerProvider() + AwsLambdaInstrumentor().instrument(tracer_provider=tracer_provider) + + mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) + spans = self.memory_exporter.get_finished_spans() + assert spans is not None + self.assertEqual(len(spans), 0) + + def test_load_entry_point(self): + self.assertIs( + next( + iter( + entry_points( + group="opentelemetry_instrumentor", name="aws-lambda" + ) + ) + ).load(), + AwsLambdaInstrumentor, + ) + + +class TestAwsLambdaInstrumentorMocks(TestAwsLambdaInstrumentorBase): def test_api_gateway_proxy_event_sets_attributes(self): handler_patch = mock.patch.dict( "os.environ", @@ -438,6 +533,12 @@ def test_api_gateway_proxy_event_sets_attributes(self): span = self.memory_exporter.get_finished_spans()[0] + assert span.kind == SpanKind.SERVER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) self.assertSpanHasAttributes( span, { @@ -459,6 +560,12 @@ def test_api_gateway_http_api_proxy_event_sets_attributes(self): span = self.memory_exporter.get_finished_spans()[0] + assert span.kind == SpanKind.SERVER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) self.assertSpanHasAttributes( span, { @@ -471,35 +578,105 @@ def test_api_gateway_http_api_proxy_event_sets_attributes(self): }, ) - def test_lambda_handles_list_event(self): + def test_alb_conventional_event_sets_attributes(self): AwsLambdaInstrumentor().instrument() - mock_execute_lambda([{"message": "test"}]) + mock_execute_lambda(MOCK_LAMBDA_ALB_EVENT) - spans = self.memory_exporter.get_finished_spans() + span = self.memory_exporter.get_finished_spans()[0] - assert spans + assert span.kind == SpanKind.SERVER - def test_lambda_handles_handler_exception(self): - exc_env_patch = mock.patch.dict( - "os.environ", - {_HANDLER: "tests.mocks.lambda_function.handler_exc"}, + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, ) - exc_env_patch.start() + print(span.attributes) + self.assertSpanHasAttributes( + span, + { + SpanAttributes.FAAS_TRIGGER: "http", + SpanAttributes.HTTP_METHOD: "GET", + }, + ) + + def test_alb_multi_value_header_event_sets_attributes(self): AwsLambdaInstrumentor().instrument() - # instrumentor re-raises the exception - with self.assertRaises(Exception): - mock_execute_lambda() - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - span = spans[0] - self.assertEqual(span.status.status_code, StatusCode.ERROR) - self.assertEqual(len(span.events), 1) - event = span.events[0] - self.assertEqual(event.name, "exception") + mock_execute_lambda(MOCK_LAMBDA_ALB_MULTI_VALUE_HEADER_EVENT) - exc_env_patch.stop() + span = self.memory_exporter.get_finished_spans()[0] + + assert span.kind == SpanKind.SERVER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) + print(span.attributes) + self.assertSpanHasAttributes( + span, + { + SpanAttributes.FAAS_TRIGGER: "http", + SpanAttributes.HTTP_METHOD: "GET", + }, + ) + + def test_dynamo_db_event_sets_attributes(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda(MOCK_LAMBDA_DYNAMO_DB_EVENT) + + span = self.memory_exporter.get_finished_spans()[0] + + assert span.kind == SpanKind.CONSUMER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) + + def test_s3_event_sets_attributes(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda(MOCK_LAMBDA_S3_EVENT) + + span = self.memory_exporter.get_finished_spans()[0] + + assert span.kind == SpanKind.CONSUMER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) + + def test_sns_event_sets_attributes(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda(MOCK_LAMBDA_SNS_EVENT) + + span = self.memory_exporter.get_finished_spans()[0] + + assert span.kind == SpanKind.CONSUMER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) + + def test_sqs_event_sets_attributes(self): + AwsLambdaInstrumentor().instrument() + + mock_execute_lambda(MOCK_LAMBDA_SQS_EVENT) + + span = self.memory_exporter.get_finished_spans()[0] + + assert span.kind == SpanKind.CONSUMER + + self.assertSpanHasAttributes( + span, + MOCK_LAMBDA_CONTEXT_ATTRIBUTES, + ) def test_lambda_handles_handler_exception_with_api_gateway_proxy_event( self, @@ -525,53 +702,3 @@ def test_lambda_handles_handler_exception_with_api_gateway_proxy_event( self.assertEqual(event.name, "exception") exc_env_patch.stop() - - def test_lambda_handles_should_do_nothing_when_environment_variables_not_present( - self, - ): - exc_env_patch = mock.patch.dict( - "os.environ", - {_HANDLER: ""}, - ) - exc_env_patch.start() - AwsLambdaInstrumentor().instrument() - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - exc_env_patch.stop() - - def test_uninstrument(self): - AwsLambdaInstrumentor().instrument() - - mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) - - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 1) - - self.memory_exporter.clear() - AwsLambdaInstrumentor().uninstrument() - - mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) - spans = self.memory_exporter.get_finished_spans() - self.assertEqual(len(spans), 0) - - def test_no_op_tracer_provider(self): - tracer_provider = NoOpTracerProvider() - AwsLambdaInstrumentor().instrument(tracer_provider=tracer_provider) - - mock_execute_lambda(MOCK_LAMBDA_API_GATEWAY_HTTP_API_EVENT) - spans = self.memory_exporter.get_finished_spans() - assert spans is not None - self.assertEqual(len(spans), 0) - - def test_load_entry_point(self): - self.assertIs( - next( - iter( - entry_points( - group="opentelemetry_instrumentor", name="aws-lambda" - ) - ) - ).load(), - AwsLambdaInstrumentor, - )