예제 #1
0
def test_create_index():
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_ELASTICSEARCH_PATH_DEPTH, '3')
    author1 = {"name": "Sidney Sheldon", "novels_count": 18}
    try:
        es = Elasticsearch([{'host': 'test', 'port': 3737}], max_retries=0)
        es.index(index='authors', doc_type='authors', body=author1, id=1)
    except ElasticsearchException as e:
        pass
    finally:
        tracer = ThundraTracer.get_instance()
        span = tracer.get_spans()[1]

        assert span.operation_name == '/authors/authors/1'
        assert span.class_name == constants.ClassNames['ELASTICSEARCH']
        assert span.domain_name == constants.DomainNames['DB']

        assert span.get_tag(
            constants.ESTags['ES_HOSTS']) == ['http://test:3737']
        assert span.get_tag(constants.ESTags['ES_URI']) == '/authors/authors/1'
        assert span.get_tag(constants.ESTags['ES_BODY']) == author1

        assert span.get_tag(constants.DBTags['DB_TYPE']) == 'elasticsearch'

        assert span.get_tag(constants.SpanTags['TOPOLOGY_VERTEX'])
예제 #2
0
def test_refresh():
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_ELASTICSEARCH_PATH_DEPTH, '2')
    try:
        es = Elasticsearch([{'host': 'test', 'port': 3737}], max_retries=0)
        res = es.indices.refresh(index='test-index')
        print(res)
    except ElasticsearchException as e:
        pass
    finally:
        tracer = ThundraTracer.get_instance()
        span = tracer.get_spans()[1]

        assert span.operation_name == '/test-index/_refresh'
        assert span.class_name == constants.ClassNames['ELASTICSEARCH']
        assert span.domain_name == constants.DomainNames['DB']

        assert span.get_tag(
            constants.ESTags['ES_HOSTS']) == ['http://test:3737']
        assert span.get_tag(
            constants.ESTags['ES_URI']) == '/test-index/_refresh'
        assert span.get_tag(constants.ESTags['ES_BODY']) == {}

        assert span.get_tag(constants.DBTags['DB_TYPE']) == 'elasticsearch'

        assert span.get_tag(constants.SpanTags['TOPOLOGY_VERTEX'])
예제 #3
0
    def test_retrieving_function_prefix():
        ConfigProvider.set(config_names.THUNDRA_TRACE_INSTRUMENT_TRACEABLECONFIG, \
                "{}.{}*{}".format(target_module_name ,target_function_prefix, target_trace_arguments))
        patcher = ImportPatcher()

        assert patcher.get_module_function_prefix(
            target_module_name) == target_function_prefix
예제 #4
0
def test_mysql_integration_mask_statement():
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_RDB_STATEMENT_MASK, 'true')

    query = "SELECT 1 + 1 AS solution"
    connection = mysql.connector.connect(user='******',
                                         host='localhost',
                                         password='******',
                                         database='db',
                                         auth_plugin='mysql_native_password')

    try:
        cursor = connection.cursor()
        cursor.execute(query)
        for table in cursor.fetchall():
            print(table)

    finally:
        tracer = ThundraTracer.get_instance()
        mysql_span = tracer.get_spans()[1]

        assert mysql_span.domain_name == constants.DomainNames['DB']
        assert mysql_span.class_name == constants.ClassNames['MYSQL']
        assert mysql_span.operation_name == 'db'
        assert mysql_span.get_tag(
            constants.SpanTags['OPERATION_TYPE']) == 'READ'
        assert mysql_span.get_tag(constants.SpanTags['DB_INSTANCE']) == 'db'
        assert mysql_span.get_tag(constants.SpanTags['DB_HOST']) == 'localhost'
        assert mysql_span.get_tag(constants.SpanTags['DB_STATEMENT']) == None
        assert mysql_span.get_tag(
            constants.SpanTags['DB_STATEMENT_TYPE']) == 'SELECT'

        connection.close()
예제 #5
0
def test_postgre_integration_mask_statement():
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_RDB_STATEMENT_MASK, 'true')
    query = "select 1 + 1 AS solution"

    connection = psycopg2.connect(user='******',
                                  host='localhost',
                                  password='******',
                                  dbname='db')

    try:
        cursor = connection.cursor()
        cursor.execute(query)
        for table in cursor.fetchall():
            print(table)

    finally:
        tracer = ThundraTracer.get_instance()
        postgre_span = tracer.get_spans()[1]

        assert postgre_span.domain_name == constants.DomainNames['DB']
        assert postgre_span.class_name == constants.ClassNames['POSTGRESQL']
        assert postgre_span.operation_name == 'db'
        assert postgre_span.get_tag(
            constants.SpanTags['OPERATION_TYPE']) == 'READ'
        assert postgre_span.get_tag(constants.SpanTags['DB_INSTANCE']) == 'db'
        assert postgre_span.get_tag(
            constants.SpanTags['DB_HOST']) == 'localhost'
        assert postgre_span.get_tag(constants.SpanTags['DB_STATEMENT']) is None
        assert postgre_span.get_tag(
            constants.SpanTags['DB_STATEMENT_TYPE']) == 'SELECT'

        connection.close()
예제 #6
0
def test_http_put_body_masked():
    try:
        ConfigProvider.set(
            config_names.THUNDRA_TRACE_INTEGRATIONS_HTTP_BODY_MASK, 'true')
        url = 'https://jsonplaceholder.typicode.com/users/3'
        parsed_url = urlparse(url)
        path = parsed_url.path
        normalized_path = "/users"
        query = parsed_url.query
        host = parsed_url.netloc

        requests.put(url, data={"message": "test"})
        tracer = ThundraTracer.get_instance()
        http_span = tracer.get_spans()[1]

        assert http_span.operation_name == host + normalized_path
        assert http_span.domain_name == constants.DomainNames['API']
        assert http_span.class_name == constants.ClassNames['HTTP']

        assert http_span.get_tag(constants.SpanTags['OPERATION_TYPE']) == 'PUT'
        assert http_span.get_tag(constants.HttpTags['HTTP_METHOD']) == 'PUT'
        assert http_span.get_tag(constants.HttpTags['HTTP_URL']) == host + path
        assert http_span.get_tag(constants.HttpTags['HTTP_HOST']) == host
        assert http_span.get_tag(constants.HttpTags['HTTP_PATH']) == path
        assert http_span.get_tag(constants.HttpTags['QUERY_PARAMS']) == query
        assert http_span.get_tag(constants.HttpTags['BODY']) is None
    except Exception:
        raise
    finally:
        tracer.clear()
예제 #7
0
def test_config_correct_default_value():
    ConfigProvider.__init__()

    assert ConfigProvider.get('thundra.agent.debug.enable') is False
    assert ConfigProvider.get('thundra.agent.debug.enable', True) is True
    assert ConfigProvider.get(
        'thundra.agent.lambda.debugger.logs.enable') is False
예제 #8
0
def test_http_4xx_error_with_min_status_500(mock_actual_call, monkeypatch):
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_HTTP_ERROR_STATUS_CODE_MIN,
        '500')
    mock_actual_call.return_value = requests.Response()
    mock_actual_call.return_value.status_code = 404
    mock_actual_call.return_value.reason = "Not Found"

    url = 'http://adummyurlthatnotexists.xyz/'
    parsed_url = urlparse(url)
    path = parsed_url.path
    query = parsed_url.query
    host = parsed_url.netloc

    requests.get(url)

    tracer = ThundraTracer.get_instance()
    http_span = tracer.get_spans()[1]

    assert http_span.operation_name == host + path
    assert http_span.domain_name == constants.DomainNames['API']
    assert http_span.class_name == constants.ClassNames['HTTP']

    assert http_span.get_tag(constants.SpanTags['OPERATION_TYPE']) == 'GET'
    assert http_span.get_tag(constants.HttpTags['HTTP_METHOD']) == 'GET'
    assert http_span.get_tag(constants.HttpTags['HTTP_URL']) == host + path
    assert http_span.get_tag(constants.HttpTags['HTTP_HOST']) == host
    assert http_span.get_tag(constants.HttpTags['HTTP_PATH']) == path
    assert http_span.get_tag(constants.HttpTags['QUERY_PARAMS']) == query
    assert http_span.get_tag('error') is None
    assert http_span.get_tag('error.kind') is None
    assert http_span.get_tag('error.message') is None
예제 #9
0
def test_http_path_depth():
    ConfigProvider.set(config_names.THUNDRA_TRACE_INTEGRATIONS_HTTP_URL_DEPTH,
                       '2')
    try:
        url = 'https://jsonplaceholder.typicode.com/asd/qwe/xyz'
        parsed_url = urlparse(url)
        normalized_path = "/asd/qwe"
        path = parsed_url.path
        query = parsed_url.query
        host = parsed_url.netloc

        requests.get(url)
        tracer = ThundraTracer.get_instance()
        http_span = tracer.get_spans()[1]

        assert http_span.operation_name == host + normalized_path
        assert http_span.domain_name == constants.DomainNames['API']
        assert http_span.class_name == constants.ClassNames['HTTP']

        assert http_span.get_tag(constants.SpanTags['OPERATION_TYPE']) == 'GET'
        assert http_span.get_tag(constants.HttpTags['HTTP_METHOD']) == 'GET'
        assert http_span.get_tag(constants.HttpTags['HTTP_URL']) == host + path
        assert http_span.get_tag(constants.HttpTags['HTTP_HOST']) == host
        assert http_span.get_tag(constants.HttpTags['HTTP_PATH']) == path
        assert http_span.get_tag(constants.HttpTags['QUERY_PARAMS']) == query
    except Exception:
        raise
예제 #10
0
def test_create_span_listener_with_multiple_filter_and_listener(
        span_listener_with_multiple_filterers_and_listeners):
    sl_env_var = to_json(span_listener_with_multiple_filterers_and_listeners)
    ConfigProvider.set(config_names.THUNDRA_TRACE_SPAN_LISTENERCONFIG,
                       sl_env_var)

    trace_support._parse_span_listeners()

    sl = trace_support.get_span_listeners()[0]
    f1 = sl.filterer.span_filters[0]
    f2 = sl.filterer.span_filters[1]

    assert type(sl) is FilteringSpanListener
    assert type(sl.listener) is LatencyInjectorSpanListener
    assert sl.listener.delay == 370
    assert sl.listener.distribution == 'normal'
    assert sl.listener.sigma == 73
    assert sl.listener.variation == 37

    assert f1.class_name == 'AWS-SQS'
    assert f1.domain_name == 'Messaging'
    assert f1.tags == {'foo': 'bar'}

    assert f2.class_name == 'HTTP'
    assert f2.operation_name == 'http_request'
    assert f2.tags == {'http.host': 'foobar.com'}
예제 #11
0
    def __init__(self, plugin_context=None, config=None):
        self.hooks = {
            'before:invocation': self.before_invocation,
            'after:invocation': self.after_invocation
        }
        self.plugin_context = plugin_context
        self.logger = logging.getLogger('STDOUT')

        if isinstance(config, LogConfig):
            self.config = config
        else:
            self.config = LogConfig()

        with LogPlugin.lock:
            if (not LogPlugin.wrapped) and (not ConfigProvider.get(
                    config_names.THUNDRA_LOG_CONSOLE_DISABLE)):
                if PY37 or PY38:
                    wrapt.wrap_function_wrapper('builtins', 'print',
                                                self._wrapper)
                else:
                    sys.stdout = StreamToLogger(self.logger, sys.stdout)
                LogPlugin.wrapped = True

        if not ConfigProvider.get(config_names.THUNDRA_LOG_CONSOLE_DISABLE):
            handler = ThundraLogHandler()
            has_thundra_log_handler = False
            for log_handlers in self.logger.handlers:
                if isinstance(log_handlers, ThundraLogHandler):
                    has_thundra_log_handler = True
            if not has_thundra_log_handler:
                self.logger.addHandler(handler)
            self.logger.setLevel(logging.INFO)
            handler.setLevel(logging.INFO)
            self.logger.propagate = False
예제 #12
0
def test_get_doc():
    ConfigProvider.set(
        config_names.THUNDRA_TRACE_INTEGRATIONS_ELASTICSEARCH_PATH_DEPTH, '3')
    try:
        es = Elasticsearch(['one_host', 'another_host'], max_retries=0)
        es.get(index='test-index', doc_type='tweet', id=1)
    except ElasticsearchException as e:
        pass
    finally:
        tracer = ThundraTracer.get_instance()
        span = tracer.get_spans()[1]

        hosts = span.get_tag(constants.ESTags['ES_HOSTS'])

        assert span.operation_name == '/test-index/tweet/1'
        assert span.class_name == constants.ClassNames['ELASTICSEARCH']
        assert span.domain_name == constants.DomainNames['DB']

        assert len(hosts) == 2
        assert 'http://one_host:9200' in hosts
        assert 'http://another_host:9200' in hosts
        assert span.get_tag(constants.ESTags['ES_METHOD']) == 'GET'
        assert span.get_tag(
            constants.ESTags['ES_URI']) == '/test-index/tweet/1'
        assert span.get_tag(constants.ESTags['ES_BODY']) == {}

        assert span.get_tag(constants.DBTags['DB_TYPE']) == 'elasticsearch'

        assert span.get_tag(constants.SpanTags['OPERATION_TYPE']) == 'GET'
        assert span.get_tag(constants.SpanTags['TOPOLOGY_VERTEX'])
예제 #13
0
    def get_collector_url():
        use_local = ConfigProvider.get(config_names.THUNDRA_REPORT_REST_LOCAL)

        if use_local:
            return 'http://' + constants.LOCAL_COLLECTOR_ENDPOINT + '/v1'
        return ConfigProvider.get(
            config_names.THUNDRA_REPORT_REST_BASEURL,
            'https://' + utils.get_nearest_collector() + '/v1')
예제 #14
0
def test_freq_from_env():
    count_freq = 37
    ConfigProvider.set(config_names.THUNDRA_SAMPLER_COUNTAWARE_COUNTFREQ,
                       '{}'.format(count_freq))

    cams = CountAwareSampler()

    assert cams.count_freq == count_freq
예제 #15
0
def test_if_can_get_integer_tag():
    tag_name = 'integerField'
    (env_key, env_val) = (config_names.THUNDRA_APPLICATION_TAG_PREFIX + tag_name, 3773)
    ConfigProvider.set(env_key, str(env_val))

    application_tags = ApplicationInfoProvider.parse_application_tags()

    assert application_tags[tag_name] == env_val
예제 #16
0
def test_with_non_existing_listener_type():
    sl_env_var = '{"type": "NonExistingSpanListener", "config": {"config": {}}}'
    ConfigProvider.set(config_names.THUNDRA_TRACE_SPAN_LISTENERCONFIG,
                       sl_env_var)

    trace_support._parse_span_listeners()

    assert len(trace_support.get_span_listeners()) == 0
예제 #17
0
def test_get_report_batches(mock_report):
    ConfigProvider.set(config_names.THUNDRA_REPORT_REST_COMPOSITE_BATCH_SIZE, '2')

    reporter = Reporter('api key')
    batches = reporter.get_report_batches([mock_report] * 3)

    assert len(batches) == 2
    assert batches[0] == [mock_report, mock_report]
    assert batches[1] == [mock_report]
예제 #18
0
    def send_reports(self, reports, **opts):
        if not self.api_key:
            debug_logger("API key not set, not sending report to Thundra.")
            return []

        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'ApiKey ' + self.api_key
        }
        test_run_event = opts.get("test_run_event", False)
        rest_composite_data_enabled = ConfigProvider.get(
            config_names.THUNDRA_REPORT_REST_COMPOSITE_ENABLE, True)
        if not test_run_event:
            path = constants.COMPOSITE_DATA_PATH if rest_composite_data_enabled else constants.PATH
        else:
            path = constants.PATH
        base_url = self.get_collector_url()
        request_url = base_url + path

        if ConfigProvider.get(config_names.THUNDRA_REPORT_CLOUDWATCH_ENABLE):
            if ConfigProvider.get(
                    config_names.THUNDRA_REPORT_CLOUDWATCH_COMPOSITE_ENABLE,
                    True):
                if not test_run_event:
                    reports_json = self.prepare_composite_report_json(reports)
                else:
                    reports_json = self.prepare_report_json(reports)
                for report in reports_json:
                    print(report)
            else:
                for report in reports:
                    try:
                        print(to_json(report, separators=(',', ':')))
                    except TypeError:
                        logger.error((
                            "Couldn't dump report with type {} to json string, "
                            "probably it contains a byte array").format(
                                report.get('type')))

            return []
        if not test_run_event and rest_composite_data_enabled:
            reports_json = self.prepare_composite_report_json(reports)
        else:
            reports_json = self.prepare_report_json(reports)
        responses = []
        if len(reports_json) > 0:
            _futures = [
                self.pool.submit(self.send_batch, (request_url, headers, data))
                for data in reports_json
            ]
            responses = [
                future.result() for future in futures.as_completed(_futures)
            ]

        if ConfigProvider.get(config_names.THUNDRA_DEBUG_ENABLE):
            debug_logger("Thundra API responses: " + str(responses))
        return responses
예제 #19
0
def thundra_with_request_response_skip(monkeypatch):
    monkeypatch.setitem(os.environ, constants.AWS_REGION, 'region')
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_ID, '[]test')
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_VERSION, 'version')
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_STAGE, 'dev')
    ConfigProvider.set(config_names.THUNDRA_TRACE_REQUEST_SKIP, 'true')
    ConfigProvider.set(config_names.THUNDRA_TRACE_RESPONSE_SKIP, 'true')
    thundra = Thundra('api key', disable_metric=True)
    return thundra
예제 #20
0
def test_if_thundra_is_disabled(mock_reporter, handler, mock_event,
                                mock_context):
    ConfigProvider.set(config_names.THUNDRA_TRACE_DISABLE, 'true')
    _, handler = handler

    handler(mock_event, mock_context)

    assert not mock_reporter.add_report.called
    assert not mock_reporter.send_reports.called
예제 #21
0
def test_invocation_support_error_set(handler_with_user_error, mock_context, mock_event):
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_STAGE, 'dev')
    thundra, handler = handler_with_user_error

    handler(mock_event, mock_context)
    execution_context = ExecutionContextManager.get()

    assert execution_context.invocation_data['erroneous'] is True
    assert execution_context.invocation_data['errorType'] == 'Exception'
    assert execution_context.invocation_data['errorMessage'] == 'test'
예제 #22
0
def test_prepare_report_json_batch(mock_report):
    ConfigProvider.set(config_names.THUNDRA_REPORT_REST_COMPOSITE_BATCH_SIZE, '1')

    reporter = Reporter('api key')

    batched_reports = reporter.prepare_report_json([mock_report] * 2)
    assert len(batched_reports) == 2

    reports = json.loads(batched_reports[0])
    assert len(reports) == 1
예제 #23
0
def test_if_enable_trace_plugin_from_environment_variable_is_prior():
    ConfigProvider.set(config_names.THUNDRA_TRACE_DISABLE, 'false')
    thundra = Thundra('api key', disable_trace=True)

    trace_exist = False
    for plugin in thundra.plugins:
        if isinstance(plugin, TracePlugin):
            trace_exist = True

    assert trace_exist is True
예제 #24
0
    def before_call(self, scope, wrapped, instance, args, kwargs, response,
                    exception):
        scope.span.domain_name = constants.DomainNames['DB']
        scope.span.class_name = constants.ClassNames['DYNAMODB']
        operation_name, request_data = args
        operation_type = get_operation_type(scope.span.class_name,
                                            operation_name)

        self.request_data = request_data.copy()
        self.endpoint = instance._endpoint.host.split('/')[-1]

        tags = {
            constants.SpanTags['OPERATION_TYPE']:
            operation_type,
            constants.DBTags['DB_INSTANCE']:
            self.endpoint,
            constants.DBTags['DB_TYPE']:
            constants.DBTypes['DYNAMODB'],
            constants.AwsDynamoTags['TABLE_NAME']:
            str(self.request_data['TableName'])
            if 'TableName' in self.request_data else None,
            constants.DBTags['DB_STATEMENT_TYPE']:
            operation_type,
            constants.AwsSDKTags['REQUEST_NAME']:
            operation_name,
        }
        scope.span.tags = tags

        # Check if Key and Item fields have any byte field and convert to string
        if 'Key' in self.request_data:
            self.escape_byte_fields(self.request_data['Key'])
        if 'Item' in self.request_data:
            self.escape_byte_fields(self.request_data['Item'])

        # DB statement tags should not be set on span if masked
        if not ConfigProvider.get(
                config_names.
                THUNDRA_TRACE_INTEGRATIONS_AWS_DYNAMODB_STATEMENT_MASK):
            self.OPERATION.get(operation_name, dummy_func)(scope)

        scope.span.set_tag(constants.SpanTags['TOPOLOGY_VERTEX'], True)

        if ConfigProvider.get(
                config_names.
                THUNDRA_TRACE_INTEGRATIONS_AWS_DYNAMODB_TRACEINJECTION_ENABLE):
            if operation_name == 'PutItem':
                self.inject_trace_link_on_put(scope.span, request_data,
                                              instance)

            if operation_name == 'UpdateItem':
                self.inject_trace_link_on_update(scope.span, request_data,
                                                 instance)

            if operation_name == 'DeleteItem':
                self.inject_trace_link_on_delete(request_data)
예제 #25
0
def test_send_report_to_url_async(mock_requests, mock_report):
    ConfigProvider.set(config_names.THUNDRA_REPORT_REST_BASEURL, 'different_url/api')
    ConfigProvider.set(config_names.THUNDRA_REPORT_CLOUDWATCH_ENABLE, 'true')

    test_session = mock_requests.Session()
    reporter = Reporter('api key', session=test_session)

    responses = reporter.send_reports([mock_report])

    reporter.session.post.assert_not_called()
    assert responses == []
예제 #26
0
    def test_mongo_command_masked():
        ConfigProvider.set(
            config_names.THUNDRA_TRACE_INTEGRATIONS_MONGODB_COMMAND_MASK,
            'true')
        client = MongoClient('localhost', 27017)
        db = client.test
        db.list_collection_names()

        tracer = ThundraTracer.get_instance()
        span = tracer.get_spans()[1]
        assert span.get_tag(constants.MongoDBTags['MONGODB_COMMAND']) is None
예제 #27
0
def test_create_empty_span_listener(empty_span_listener):
    sl_env_var = to_json(empty_span_listener)
    ConfigProvider.set(config_names.THUNDRA_TRACE_SPAN_LISTENERCONFIG,
                       sl_env_var)

    trace_support._parse_span_listeners()

    sl = trace_support.get_span_listeners()[0]

    assert type(sl) is FilteringSpanListener
    assert type(sl.listener) is ErrorInjectorSpanListener
    assert type(sl.filterer) is StandardSpanFilterer
예제 #28
0
    def get_report_batches(self, reports):
        batch_size = ConfigProvider.get(
            config_names.THUNDRA_REPORT_REST_COMPOSITE_BATCH_SIZE)
        if ConfigProvider.get(config_names.THUNDRA_REPORT_CLOUDWATCH_ENABLE):
            batch_size = ConfigProvider.get(
                config_names.THUNDRA_REPORT_CLOUDWATCH_COMPOSITE_BATCH_SIZE)

        batches = [
            reports[i:i + batch_size]
            for i in range(0, len(reports), batch_size)
        ]
        return batches
예제 #29
0
def test_if_error_is_added_to_report(handler_with_exception, mock_context, mock_event):
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_STAGE, 'dev')
    thundra, handler = handler_with_exception

    try:
        handler(mock_event, mock_context)
    except Exception:
        pass

    execution_context = ExecutionContextManager.get()
    assert execution_context.invocation_data['erroneous'] is True
    assert execution_context.invocation_data['errorType'] == 'Exception'
    assert execution_context.invocation_data['errorMessage'] == 'hello'
예제 #30
0
def test_coldstarts(handler, mock_context, mock_event):
    ConfigProvider.set(config_names.THUNDRA_APPLICATION_STAGE, 'dev')

    thundra, handler = handler
    handler(mock_event, mock_context)
    execution_context = ExecutionContextManager.get()
    assert execution_context.invocation_data['coldStart'] is True
    assert execution_context.invocation_data['tags']['aws.lambda.invocation.coldstart'] is True

    handler(mock_event, mock_context)
    execution_context = ExecutionContextManager.get()
    assert execution_context.invocation_data['coldStart'] is False
    assert execution_context.invocation_data['tags']['aws.lambda.invocation.coldstart'] is False