def test_tracer_reuse_configuration(service_name): # GIVEN tracer A is initialized tracer_a = Tracer(disabled=True, service=service_name) # WHEN tracer B is initialized afterwards tracer_b = Tracer() # THEN tracer B attributes should be equal to tracer A assert tracer_a.__dict__.items() == tracer_b.__dict__.items()
def test_tracer_reuse(): # GIVEN tracer A, B were initialized # WHEN tracer B explicitly reuses A config # THEN tracer B attributes should be equal to tracer A service_name = "booking" tracer_a = Tracer(disabled=True, service=service_name) tracer_b = Tracer() assert id(tracer_a) != id(tracer_b) assert tracer_a.__dict__.items() == tracer_b.__dict__.items()
def test_tracer_explicit_service(monkeypatch, service_name): # GIVEN tracer is disabled # WHEN service is explicitly defined tracer_explicit = Tracer(disabled=True, service=service_name) assert tracer_explicit.service == service_name monkeypatch.setenv("POWERTOOLS_TRACE_DISABLED", "true") tracer = Tracer() # THEN tracer should have use that service name assert tracer.service == service_name
def test_tracer_custom_metadata(mocker, dummy_response, provider_stub): put_metadata_mock = mocker.MagicMock() annotation_key = "Booking response" annotation_value = {"bookingStatus": "CONFIRMED"} provider = provider_stub(put_metadata_mock=put_metadata_mock) tracer = Tracer(provider=provider, service="booking") tracer.put_metadata(annotation_key, annotation_value) assert put_metadata_mock.call_count == 1 assert put_metadata_mock.call_args_list[0] == mocker.call( key=annotation_key, value=annotation_value, namespace="booking")
def test_tracer_custom_annotation(mocker, dummy_response, provider_stub): put_annotation_mock = mocker.MagicMock() annotation_key = "BookingId" annotation_value = "123456" provider = provider_stub(put_annotation_mock=put_annotation_mock) tracer = Tracer(provider=provider, service="booking") tracer.put_annotation(annotation_key, annotation_value) assert put_annotation_mock.call_count == 1 assert put_annotation_mock.call_args == mocker.call(key=annotation_key, value=annotation_value)
def test_tracer_patch(xray_patch_all_mock, xray_patch_mock, mocker): # GIVEN tracer is instantiated # WHEN default X-Ray provider client is mocked # THEN tracer should run just fine Tracer() assert xray_patch_all_mock.call_count == 1 modules = ["boto3"] Tracer(service="booking", patch_modules=modules) assert xray_patch_mock.call_count == 1 assert xray_patch_mock.call_args == mocker.call(modules)
def test_tracer_custom_annotation(mocker, dummy_response, provider_stub): # GIVEN Tracer is initialized put_annotation_mock = mocker.MagicMock() provider = provider_stub(put_annotation_mock=put_annotation_mock) tracer = Tracer(provider=provider) # WHEN put_metadata is used annotation_key = "BookingId" annotation_value = "123456" tracer.put_annotation(annotation_key, annotation_value) # THEN we should have an annotation as expected assert put_annotation_mock.call_count == 1 assert put_annotation_mock.call_args == mocker.call(key=annotation_key, value=annotation_value)
def test_tracer_custom_metadata(mocker, dummy_response, provider_stub): # GIVEN Tracer is initialized with booking as the service name put_metadata_mock = mocker.MagicMock() provider = provider_stub(put_metadata_mock=put_metadata_mock) tracer = Tracer(provider=provider, service="booking") # WHEN put_metadata is used annotation_key = "Booking response" annotation_value = {"bookingStatus": "CONFIRMED"} tracer.put_metadata(annotation_key, annotation_value) # THEN we should have metadata expected and booking as namespace assert put_metadata_mock.call_count == 1 assert put_metadata_mock.call_args_list[0] == mocker.call( key=annotation_key, value=annotation_value, namespace="booking")
def test_tracer_autopatch(patch_mock): # GIVEN tracer is initialized # WHEN auto_patch hasn't been explicitly disabled Tracer(disabled=True) # THEN tracer should patch all modules assert patch_mock.call_count == 1
async def test_tracer_method_nested_async(mocker, dummy_response, provider_stub, in_subsegment_mock): provider = provider_stub( in_subsegment_async=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider, service="booking") @tracer.capture_method async def greeting_2(name, message): return dummy_response @tracer.capture_method async def greeting(name, message): await greeting_2(name, message) return dummy_response await greeting(name="Foo", message="Bar") ( in_subsegment_greeting_call_args, in_subsegment_greeting2_call_args, ) = in_subsegment_mock.in_subsegment.call_args_list put_metadata_greeting2_call_args, put_metadata_greeting_call_args = in_subsegment_mock.put_metadata.call_args_list assert in_subsegment_mock.in_subsegment.call_count == 2 assert in_subsegment_greeting_call_args == mocker.call(name="## greeting") assert in_subsegment_greeting2_call_args == mocker.call( name="## greeting_2") assert in_subsegment_mock.put_metadata.call_count == 2 assert put_metadata_greeting2_call_args == mocker.call( key="greeting_2 response", value=dummy_response, namespace="booking") assert put_metadata_greeting_call_args == mocker.call( key="greeting response", value=dummy_response, namespace="booking")
def test_tracer_yield_from_context_manager(mocker, provider_stub, in_subsegment_mock): # GIVEN tracer is initialized provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider, service="booking") # WHEN capture_method decorator is used on a context manager @tracer.capture_method @contextlib.contextmanager def yield_with_capture(): yield "test result" @tracer.capture_lambda_handler def handler(event, context): response = [] with yield_with_capture() as yielded_value: response.append(yielded_value) return response result = handler({}, {}) # THEN we should have a subsegment named after the method name # and add its response as trace metadata handler_trace, yield_function_trace = in_subsegment_mock.in_subsegment.call_args_list assert "test result" in in_subsegment_mock.put_metadata.call_args[1]["value"] assert in_subsegment_mock.in_subsegment.call_count == 2 assert handler_trace == mocker.call(name="## handler") assert yield_function_trace == mocker.call(name="## yield_with_capture") assert "test result" in result
def test_package_logger(capsys): set_package_logger() Tracer(disabled=True) output = capsys.readouterr() assert "Tracing has been disabled" in output.out
def test_tracer_yield_with_capture(): # GIVEN tracer method decorator is used tracer = Tracer(disabled=True) # WHEN capture_method decorator is applied to a context manager @tracer.capture_method @contextlib.contextmanager def yield_with_capture(): yield "testresult" # Or WHEN capture_method decorator is applied to a generator function @tracer.capture_method def generator_func(): yield "testresult2" @tracer.capture_lambda_handler def handler(event, context): result = [] with yield_with_capture() as yielded_value: result.append(yielded_value) gen = generator_func() result.append(next(gen)) return result # THEN no exception is thrown, and the functions properly return values result = handler({}, {}) assert "testresult" in result assert "testresult2" in result
def test_tracer_no_autopatch(patch_mock): # GIVEN tracer is initialized # WHEN auto_patch is disabled Tracer(disabled=True, auto_patch=False) # THEN tracer should not patch any module assert patch_mock.call_count == 0
def test_tracer_yield_from_generator(mocker, provider_stub, in_subsegment_mock): # GIVEN tracer is initialized provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider, service="booking") # WHEN capture_method decorator is used on a generator function @tracer.capture_method def generator_fn(): yield "test result" @tracer.capture_lambda_handler def handler(event, context): gen = generator_fn() response = list(gen) return response result = handler({}, {}) # THEN we should have a subsegment named after the method name # and add its response as trace metadata handler_trace, generator_fn_trace = in_subsegment_mock.in_subsegment.call_args_list assert "test result" in in_subsegment_mock.put_metadata.call_args[1]["value"] assert in_subsegment_mock.in_subsegment.call_count == 2 assert handler_trace == mocker.call(name="## handler") assert generator_fn_trace == mocker.call(name="## generator_fn") assert "test result" in result
async def test_tracer_method_nested_async(mocker, dummy_response, provider_stub, in_subsegment_mock): # GIVEN tracer is initialized provider = provider_stub(in_subsegment_async=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider, service="booking") # WHEN capture_method decorator is used for nested async methods @tracer.capture_method async def greeting_2(name, message): return dummy_response @tracer.capture_method async def greeting(name, message): await greeting_2(name, message) return dummy_response await greeting(name="Foo", message="Bar") ( in_subsegment_greeting_call_args, in_subsegment_greeting2_call_args, ) = in_subsegment_mock.in_subsegment.call_args_list put_metadata_greeting2_call_args, put_metadata_greeting_call_args = in_subsegment_mock.put_metadata.call_args_list # THEN we should add metadata for each response like we would for a sync decorated method assert in_subsegment_mock.in_subsegment.call_count == 2 assert in_subsegment_greeting_call_args == mocker.call(name="## greeting") assert in_subsegment_greeting2_call_args == mocker.call(name="## greeting_2") assert in_subsegment_mock.put_metadata.call_count == 2 assert put_metadata_greeting2_call_args == mocker.call( key="greeting_2 response", value=dummy_response, namespace="booking" ) assert put_metadata_greeting_call_args == mocker.call( key="greeting response", value=dummy_response, namespace="booking" )
def test_package_logger_format(stdout, capsys): set_package_logger(stream=stdout, formatter=JsonFormatter(formatter="test")) Tracer(disabled=True) output = json.loads(stdout.getvalue().split("\n")[0]) assert "test" in output["formatter"]
def test_tracer_env_vars(monkeypatch): # GIVEN tracer disabled, is run without parameters # WHEN service is explicitly defined # THEN tracer should have use that service name service_name = "booking" monkeypatch.setenv("POWERTOOLS_SERVICE_NAME", service_name) tracer_env_var = Tracer(disabled=True) assert tracer_env_var.service == service_name tracer_explicit = Tracer(disabled=True, service=service_name) assert tracer_explicit.service == service_name monkeypatch.setenv("POWERTOOLS_TRACE_DISABLED", "true") tracer = Tracer() assert bool(tracer.disabled) is True
def test_tracer_service_env_var(monkeypatch, service_name): # GIVEN tracer is run without parameters # WHEN service is implicitly defined via env var monkeypatch.setenv("POWERTOOLS_SERVICE_NAME", service_name) tracer = Tracer(disabled=True) # THEN tracer should have use that service name assert tracer.service == service_name
def test_tracer_patch_modules(xray_patch_mock, mocker): # GIVEN tracer is initialized with a list of modules to patch modules = ["boto3"] # WHEN modules are supported by X-Ray Tracer(service="booking", patch_modules=modules) # THEN tracer should run just fine assert xray_patch_mock.call_count == 1 assert xray_patch_mock.call_args == mocker.call(modules)
def test_capture_method(dummy_response): # GIVEN tracer method decorator is used tracer = Tracer(disabled=True) # WHEN a function is run @tracer.capture_method def greeting(name, message): return dummy_response # THEN tracer should not raise an Exception greeting(name="Foo", message="Bar")
def test_capture_lambda_handler(dummy_response): # GIVEN tracer lambda handler decorator is used tracer = Tracer(disabled=True) # WHEN a lambda handler is run @tracer.capture_lambda_handler def handler(event, context): return dummy_response # THEN tracer should not raise an Exception handler({}, {})
def test_tracer_chalice_cli_mode(monkeypatch, dummy_response): # GIVEN tracer runs locally monkeypatch.setenv("AWS_CHALICE_CLI_MODE", "true") tracer = Tracer() # WHEN a lambda function is run through the Chalice CLI. @tracer.capture_lambda_handler def handler(event, context): return dummy_response # THEN tracer should run in disabled mode, and not raise an Exception handler({}, {})
def test_tracer_lambda_handler_empty_response_metadata(mocker, provider_stub): put_metadata_mock = mocker.MagicMock() provider = provider_stub(put_metadata_mock=put_metadata_mock) tracer = Tracer(provider=provider) @tracer.capture_lambda_handler def handler(event, context): return handler({}, mocker.MagicMock()) assert put_metadata_mock.call_count == 0
def test_tracer_lambda_emulator(monkeypatch, dummy_response): # GIVEN tracer runs locally monkeypatch.setenv("AWS_SAM_LOCAL", "true") tracer = Tracer() # WHEN a lambda function is run through SAM CLI @tracer.capture_lambda_handler def handler(event, context): return dummy_response # THEN tracer should run in disabled mode, and not raise an Exception handler({}, {})
def test_tracer_method_empty_response_metadata(mocker, provider_stub): put_metadata_mock = mocker.MagicMock() provider = provider_stub(put_metadata_mock=put_metadata_mock) tracer = Tracer(provider=provider) @tracer.capture_method def greeting(name, message): return greeting(name="Foo", message="Bar") assert put_metadata_mock.call_count == 0
def test_package_logger_stream(stdout): # GIVEN package logger "aws_lambda_powertools" is explicitly set with no params set_package_logger(stream=stdout) # WHEN Tracer is initialized in disabled mode Tracer(disabled=True) # THEN Tracer debug log statement should be logged output = stdout.getvalue() logger = logging.getLogger("aws_lambda_powertools") assert "Tracing has been disabled" in output assert logger.level == logging.DEBUG
def test_tracer_lambda_emulator(monkeypatch, dummy_response): # GIVEN tracer is run locally # WHEN a lambda function is run through SAM CLI # THEN tracer should not raise an Exception monkeypatch.setenv("AWS_SAM_LOCAL", "true") tracer = Tracer() @tracer.capture_lambda_handler def handler(event, context): return dummy_response handler({}, {}) monkeypatch.delenv("AWS_SAM_LOCAL")
def test_tracer_metadata_disabled(dummy_response): # GIVEN tracer is disabled, and annotations/metadata are used tracer = Tracer(disabled=True) # WHEN a lambda handler is run @tracer.capture_lambda_handler def handler(event, context): tracer.put_annotation("PaymentStatus", "SUCCESS") tracer.put_metadata("PaymentMetadata", "Metadata") return dummy_response # THEN tracer should not raise any Exception handler({}, {})
def test_tracer_method_override_response_as_metadata(provider_stub, in_subsegment_mock): # GIVEN tracer is initialized provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment) tracer = Tracer(provider=provider, auto_patch=False) # WHEN capture_method decorator is used with capture_response set to False @tracer.capture_method(capture_response=False) def greeting(name, message): return "response" greeting(name="Foo", message="Bar") # THEN we should not add any metadata assert in_subsegment_mock.put_metadata.call_count == 0