Пример #1
0
def test_valid_settings():
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['VALIDATE_GUID'] = False
    mocked_settings['GUID_HEADER_NAME'] = 'Correlation-ID-TEST'
    mocked_settings['RETURN_HEADER'] = False
    mocked_settings['EXPOSE_HEADER'] = False
    with override_settings(DJANGO_GUID=mocked_settings):
        assert not Settings().validate_guid
        assert Settings().guid_header_name == 'Correlation-ID-TEST'
        assert not Settings().return_header
Пример #2
0
def test_valid_settings(monkeypatch):
    monkeypatch.setattr(
        django_settings,
        'DJANGO_GUID',
        {
            'VALIDATE_GUID': False,
            'GUID_HEADER_NAME': 'Correlation-ID-TEST',
            'RETURN_HEADER': False,
            'EXPOSE_HEADER': False,
        },
    )
    assert not Settings().VALIDATE_GUID
    assert Settings().GUID_HEADER_NAME == 'Correlation-ID-TEST'
    assert not Settings().RETURN_HEADER
def test_sentry_integration(client, caplog, mocker, monkeypatch):
    """
    Tests the sentry integration
    """
    mock_scope = mocker.patch.object(Scope, 'set_tag')
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.middleware.settings', settings)
        client.get(
            '/api',
            **{'HTTP_Correlation-ID': '97c304252fd14b25b72d6aee31565842'})
        expected = [
            (None, 'sync middleware called'),
            (None,
             'Correlation-ID found in the header: 97c304252fd14b25b72d6aee31565842'
             ),
            (None, '97c304252fd14b25b72d6aee31565842 is a valid GUID'),
            ('97c304252fd14b25b72d6aee31565842',
             'Running integration: `SentryIntegration`'),
            ('97c304252fd14b25b72d6aee31565842',
             'Setting Sentry transaction_id to 97c304252fd14b25b72d6aee31565842'
             ),
            ('97c304252fd14b25b72d6aee31565842',
             'This is a DRF view log, and should have a GUID.'),
            ('97c304252fd14b25b72d6aee31565842', 'Some warning in a function'),
            ('97c304252fd14b25b72d6aee31565842',
             'Running tear down for integration: `SentryIntegration`'),
            ('97c304252fd14b25b72d6aee31565842',
             'Received signal `request_finished`, clearing guid'),
        ]
        mock_scope.assert_called_with('transaction_id',
                                      '97c304252fd14b25b72d6aee31565842')
        assert [(x.correlation_id, x.message)
                for x in caplog.records] == expected
Пример #4
0
def test_no_return_header_and_drf_url(client, caplog, mock_uuid, monkeypatch):
    """
    Tests that it does not return the GUID if RETURN_HEADER is false.
    This test also tests a DRF response, just to confirm everything works in both worlds.
    """
    mocked_settings = {
        'GUID_HEADER_NAME': 'Correlation-ID',
        'VALIDATE_GUID': True,
        'INTEGRATIONS': [],
        'IGNORE_URLS': ['no-guid'],
        'RETURN_HEADER': False,
    }
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.middleware.settings', settings)
        response = client.get('/api')
        expected = [
            ('sync middleware called', None),
            (
                'Header `Correlation-ID` was not found in the incoming request. Generated new GUID: 704ae5472cae4f8daa8f2cc5a5a8mock',
                None,
            ),
            ('This is a DRF view log, and should have a GUID.',
             '704ae5472cae4f8daa8f2cc5a5a8mock'),
            ('Some warning in a function', '704ae5472cae4f8daa8f2cc5a5a8mock'),
            ('Received signal `request_finished`, clearing guid',
             '704ae5472cae4f8daa8f2cc5a5a8mock'),
        ]
        assert [(x.message, x.correlation_id)
                for x in caplog.records] == expected
        assert not response.get('Correlation-ID')
Пример #5
0
def test_invalid_setting(monkeypatch):
    monkeypatch.setattr(django_settings, 'DJANGO_GUID',
                        {'invalid_setting': 'some_value'})
    with pytest.raises(
            ImproperlyConfigured,
            match='invalid_setting is not a valid setting for django_guid'):
        Settings()
Пример #6
0
def test_invalid_guid():
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['VALIDATE_GUID'] = 'string'
    with override_settings(DJANGO_GUID=mocked_settings):
        with pytest.raises(ImproperlyConfigured,
                           match='VALIDATE_GUID must be a boolean'):
            Settings().validate()
Пример #7
0
def test_invalid_expose_header_setting():
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['EXPOSE_HEADER'] = 'string'
    with override_settings(DJANGO_GUID=mocked_settings):
        with pytest.raises(ImproperlyConfigured,
                           match='EXPOSE_HEADER must be a boolean'):
            Settings().validate()
Пример #8
0
def test_cleanup_signal(client, caplog, monkeypatch):
    """
    Tests that a request cleans up a request after finishing.
    :param client: Django client
    :param caplog: Caplog fixture
    """
    from django.conf import settings as django_settings

    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['VALIDATE_GUID'] = False
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.utils.settings', settings)
        client.get('/', **{'HTTP_Correlation-ID': 'bad-guid'})
        client.get('/', **{'HTTP_Correlation-ID': 'another-bad-guid'})

        expected = [
            # First request
            ('sync middleware called', None),
            ('Correlation-ID found in the header: bad-guid', None),
            ('Returning ID from header without validating it as a GUID', None),
            ('This log message should have a GUID', 'bad-guid'),
            ('Some warning in a function', 'bad-guid'),
            ('Received signal `request_finished`, clearing guid', 'bad-guid'),
            # Second request
            ('sync middleware called', None),
            ('Correlation-ID found in the header: another-bad-guid', None),
            ('Returning ID from header without validating it as a GUID', None),
            ('This log message should have a GUID', 'another-bad-guid'),
            ('Some warning in a function', 'another-bad-guid'),
            ('Received signal `request_finished`, clearing guid',
             'another-bad-guid'),
        ]
        assert [(x.message, x.correlation_id)
                for x in caplog.records] == expected
Пример #9
0
def test_invalid_header_name():
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['GUID_HEADER_NAME'] = True
    with override_settings(DJANGO_GUID=mocked_settings):
        with pytest.raises(ImproperlyConfigured,
                           match='GUID_HEADER_NAME must be a string'):
            Settings().validate()
Пример #10
0
def test_bad_integrations_type(monkeypatch):
    for item in [{}, '', 2, None, -2]:
        monkeypatch.setattr(django_settings, 'DJANGO_GUID',
                            {'INTEGRATIONS': item})
        with pytest.raises(ImproperlyConfigured,
                           match='INTEGRATIONS must be an array'):
            Settings()
Пример #11
0
def test_invalid_skip_guid_setting(monkeypatch):
    """
    Assert that a deprecation warning is called when settings are instantiated with SKIP_CLEANUP == True or False
    """
    monkeypatch.setattr(django_settings, 'DJANGO_GUID', {'SKIP_CLEANUP': True})
    with pytest.deprecated_call():
        Settings()
Пример #12
0
def test_request_with_invalid_correlation_id_without_validation(
        client, caplog, monkeypatch):
    """
    Tests that a request with an invalid GUID is replaced when VALIDATE_GUID is False.
    :param client: Django client
    :param caplog: Caplog fixture
    """
    mocked_settings = {
        'GUID_HEADER_NAME': 'Correlation-ID',
        'VALIDATE_GUID': False,
        'INTEGRATIONS': [],
        'IGNORE_URLS': ['no-guid'],
    }
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.utils.settings', settings)

        client.get('/', **{'HTTP_Correlation-ID': 'bad-guid'})
        expected = [
            ('sync middleware called', None),
            ('Correlation-ID found in the header: bad-guid', None),
            ('Returning ID from header without validating it as a GUID', None),
            ('This log message should have a GUID', 'bad-guid'),
            ('Some warning in a function', 'bad-guid'),
            ('Received signal `request_finished`, clearing guid', 'bad-guid'),
        ]
        assert [(x.message, x.correlation_id)
                for x in caplog.records] == expected
Пример #13
0
def test_non_callable_methods(monkeypatch, subtests):
    """
    Tests that an exception is raised when any of the integration base methods are non-callable.
    """
    from django_guid.integrations import SentryIntegration
    from django.conf import settings
    from django_guid.config import Settings

    mock_integration = SentryIntegration()

    to_test = [
        {
            'function_name': 'cleanup',
            'error': 'Integration method `cleanup` needs to be made callable for `SentryIntegration`.',
        },
        {
            'function_name': 'run',
            'error': 'Integration method `run` needs to be made callable for `SentryIntegration`.',
        },
        {
            'function_name': 'setup',
            'error': 'Integration method `setup` needs to be made callable for `SentryIntegration`.',
        },
    ]

    for test in to_test:
        setattr(mock_integration, test.get('function_name'), 'test')
        monkeypatch.setattr(settings, 'DJANGO_GUID', {'INTEGRATIONS': [mock_integration]})
        with subtests.test(msg=f'Testing function {test.get("function_name")}'):
            with pytest.raises(ImproperlyConfigured, match=test.get('error')):
                Settings()
Пример #14
0
def test_not_array_ignore_urls():
    for setting in [{}, '', 2, None, -2]:
        mocked_settings = deepcopy(django_settings.DJANGO_GUID)
        mocked_settings['IGNORE_URLS'] = setting
        with override_settings(DJANGO_GUID=mocked_settings):
            with pytest.raises(ImproperlyConfigured,
                               match='IGNORE_URLS must be an array'):
                Settings().validate()
Пример #15
0
def test_bad_integrations_type():
    for setting in [{}, '', 2, None, -2]:
        mocked_settings = deepcopy(django_settings.DJANGO_GUID)
        mocked_settings['INTEGRATIONS'] = setting
        with override_settings(DJANGO_GUID=mocked_settings):
            with pytest.raises(ImproperlyConfigured,
                               match='INTEGRATIONS must be an array'):
                Settings().validate()
Пример #16
0
def test_not_string_in_igore_urls():
    for setting in ['api/v1/test', 'api/v1/othertest', True], [1, 2, 'yup']:
        mocked_settings = deepcopy(django_settings.DJANGO_GUID)
        mocked_settings['IGNORE_URLS'] = setting
        with override_settings(DJANGO_GUID=mocked_settings):
            with pytest.raises(
                    ImproperlyConfigured,
                    match='IGNORE_URLS must be an array of strings'):
                Settings().validate()
Пример #17
0
def test_uuid_len_fail():
    for setting in [True, False, {}, [], 'asd', -1, 0, 33]:
        mocked_settings = deepcopy(django_settings.DJANGO_GUID)
        mocked_settings['UUID_LENGTH'] = setting
        with override_settings(DJANGO_GUID=mocked_settings):
            with pytest.raises(
                    ImproperlyConfigured,
                    match='UUID_LENGTH must be an integer and be between 1-32'
            ):
                Settings().validate()
Пример #18
0
def test_expose_header_return_header_true(client, monkeypatch):
    """
    Tests that it does return the Access-Control-Allow-Origin when EXPOSE_HEADER is set to True
    and RETURN_HEADER is True
    """
    from django.conf import settings as django_settings

    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['EXPOSE_HEADER'] = True
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.middleware.settings', settings)
        response = client.get('/api')
        assert response.get('Access-Control-Expose-Headers')
Пример #19
0
def test_worker_prerun_guid_does_not_exist(monkeypatch, mocker: MockerFixture,
                                           mock_uuid):
    """
    Tests that a GUID is set if it does not exist
    """
    mock_task = mocker.Mock()
    mock_task.request = {'Correlation-ID': None}
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=False)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)
        worker_prerun(mock_task)
    assert get_guid() == '704ae5472cae4f8daa8f2cc5a5a8mock'
Пример #20
0
def test_worker_prerun_guid_exists(monkeypatch, mocker: MockerFixture,
                                   two_unique_uuid4):
    """
    Tests that GUID is set to the GUID if a GUID exists in the task object.
    """
    mock_task = mocker.Mock()
    mock_task.request = {'Correlation-ID': '704ae5472cae4f8daa8f2cc5a5a8mock'}
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=False)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)
        worker_prerun(mock_task)
    assert get_guid() == '704ae5472cae4f8daa8f2cc5a5a8mock'
Пример #21
0
def test_cleanup_method_not_accepting_kwargs(monkeypatch, client):
    """
    Tests that an exception is raised when the run method doesn't accept kwargs.
    """
    from django_guid.integrations import SentryIntegration
    from django.conf import settings
    from django_guid.config import Settings

    class BadIntegration(SentryIntegration):
        def cleanup(self, guid):
            pass

    monkeypatch.setattr(settings, 'DJANGO_GUID', {'INTEGRATIONS': [BadIntegration()]})
    with pytest.raises(ImproperlyConfigured, match='Integration method `cleanup` must accept keyword arguments '):
        Settings()
Пример #22
0
def test_cleanup(monkeypatch, mocker: MockerFixture):
    """
    Test that cleanup works as expected
    """
    set_guid('123')
    celery_current.set('123')
    celery_parent.set('123')

    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=True)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)
        clean_up(task=mocker.Mock())

    assert [get_guid(), celery_current.get(),
            celery_parent.get()] == [None, None, None]
def test_sentry_validation(client, monkeypatch):
    """
    Tests that the package handles multiple header values by defaulting to one and logging a warning.
    """
    import sys
    from django_guid.integrations import SentryIntegration
    from django_guid.config import Settings
    from django.conf import settings

    # Mock away the sentry_sdk dependency
    sys.modules['sentry_sdk'] = None

    monkeypatch.setattr(settings, 'DJANGO_GUID', {'INTEGRATIONS': [SentryIntegration()]})
    with pytest.raises(
        ImproperlyConfigured,
        match='The package `sentry-sdk` is required for extending your tracing IDs to Sentry. '
        'Please run `pip install sentry-sdk` if you wish to include this integration.',
    ):
        Settings()
def test_missing_run_method(monkeypatch, client):
    """
    Tests that an exception is raised when the run method has not been defined.
    """
    from django_guid.integrations import SentryIntegration

    monkeypatch.delattr(SentryIntegration, 'run')
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [SentryIntegration()]

    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.middleware.settings', settings)
        with pytest.raises(
                ImproperlyConfigured,
                match=
                'The integration `SentryIntegration` is missing a `run` method'
        ):
            client.get('/api')
def test_cleanup_method_not_accepting_kwargs(client):
    """
    Tests that an exception is raised when the run method doesn't accept kwargs.
    """
    from django_guid.config import Settings
    from django_guid.integrations import SentryIntegration

    class BadIntegration(SentryIntegration):
        def cleanup(self, guid):
            pass

    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [BadIntegration()]

    with override_settings(DJANGO_GUID=mocked_settings):
        with pytest.raises(
                ImproperlyConfigured,
                match=
                'Integration method `cleanup` must accept keyword arguments '):
            Settings().validate()
Пример #26
0
def test_dont_set_transaction_id(monkeypatch, caplog):
    """
    Tests that the `configure_scope()` is not executed, given `sentry_integration=False` in CeleryIntegration
    """
    logger = logging.getLogger('django_guid.celery')
    logger.addHandler(caplog.handler)

    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [
        CeleryIntegration(sentry_integration=False)
    ]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)
        guid = generate_guid()
        set_transaction_id(guid)
    logger.removeHandler(caplog.handler)
    assert f'Setting Sentry transaction_id to {guid}' not in [
        record.message for record in caplog.records
    ]
Пример #27
0
def test_worker_prerun_guid_log_parent_with_origin(monkeypatch,
                                                   mocker: MockerFixture,
                                                   mock_uuid_two_unique):
    """
    Tests that depth works when there is an origin
    """
    from django_guid.integrations.celery.signals import parent_header

    mock_task = mocker.Mock()
    mock_task.request = {
        'Correlation-ID': None,
        parent_header: '1234'
    }  # No origin
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=True)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)
        worker_prerun(mock_task)
    assert get_guid() == '704ae5472cae4f8daa8f2cc5a5a8mock'
    assert celery_current.get() == 'c494886651cd4baaa8654e4d24a8mock'
    assert celery_parent.get() == '1234'
def test_sentry_validation(client):
    """
    Tests that the package handles multiple header values by defaulting to one and logging a warning.
    """

    # Mock away the sentry_sdk dependency
    backup = None
    if 'sentry_sdk' in sys.modules:
        backup = sys.modules['sentry_sdk']
        sys.modules['sentry_sdk'] = None

    with override_settings(DJANGO_GUID=mocked_settings):
        with pytest.raises(
                ImproperlyConfigured,
                match=
                'The package `sentry-sdk` is required for extending your tracing IDs to Sentry. '
                'Please run `pip install sentry-sdk` if you wish to include this integration.',
        ):
            Settings().validate()

    # Put it back in - otherwise a bunch of downstream tests break
    if backup:
        sys.modules['sentry_sdk'] = backup
Пример #29
0
def test_task_publish_includes_correct_depth_headers(monkeypatch):
    """
    Test log_parent True
    """
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=True)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)

        headers = {}
        publish_task_from_worker_or_request(headers=headers)
        # The parent header should not be in headers, because
        # There should be no celery_current context
        assert parent_header not in headers

        for correlation_id in ['test', 123, -1]:
            headers = {}
            celery_current.set(correlation_id)
            publish_task_from_worker_or_request(headers=headers)
            # Here the celery-parent-id header should exist
            assert headers[parent_header] == correlation_id
Пример #30
0
def test_task_publish_includes_correct_headers(monkeypatch):
    """
    It's important that we include the correct headers when publishing a task
    to the celery worker pool, otherwise there's no transfer of state.
    """
    # Mocking overhead
    mocked_settings = deepcopy(django_settings.DJANGO_GUID)
    mocked_settings['INTEGRATIONS'] = [CeleryIntegration(log_parent=False)]
    with override_settings(DJANGO_GUID=mocked_settings):
        settings = Settings()
        monkeypatch.setattr('django_guid.integrations.celery.signals.settings',
                            settings)

        # Actual testing
        for correlation_id in [None, 'test', 123, -1]:
            # Set the id in our context var
            set_guid(correlation_id)

            # Run signal with empty headers
            headers = {}
            publish_task_from_worker_or_request(headers=headers)

            # Makes sure the returned headers contain the correct result
            assert headers[settings.guid_header_name] == correlation_id