def test_with_user_specified_exceptions(self, container): something_expected = get_extension(container, Consumer, method_name="something_expected") assert issubclass(self.UserException, something_expected.expected_exceptions) assert issubclass(Backoff, something_expected.expected_exceptions)
def test_with_user_specified_exceptions_including_backoff(self, container): backoff_expected = get_extension(container, Consumer, method_name="backoff_expected") assert issubclass(self.UserException, backoff_expected.expected_exceptions) assert issubclass(Backoff, backoff_expected.expected_exceptions)
def test_cron_runs(timeout, concurrency, task_time, expected_calls, container_factory, tracker): """Test running the cron main loop.""" class Service(object): name = "service" @cron('* * * * * *', concurrency=concurrency) def tick(self): tracker() eventlet.sleep(task_time) container = container_factory(Service, {}) # Check that Cron instance is initialized correctly instance = get_extension(container, Cron) assert instance.schedule == '* * * * * *' assert instance.tz is None assert instance.concurrency == concurrency with freezegun.freeze_time('2020-11-20 23:59:59.5', tick=True): container.start() eventlet.sleep(timeout) container.stop() assert tracker.call_count == expected_calls
def test_json_payload( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('POST', '/resource') def resource(self, request): raise CustomException() container = container_factory(Service, config) container.start() submitted_data = { 'foo': 'bar' } with entrypoint_waiter(container, 'resource'): rv = web_session.post('/resource', json=submitted_data) assert rv.status_code == 500 assert "CustomException" in rv.text sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args received_data = kwargs['request']['data'] assert received_data == json.dumps(submitted_data).encode('utf-8')
def test_cached_response(container): cached_rpc = get_extension(container, CachedRpcProxy) def fake_some_method(*args, **kwargs): return 'hi' with patch('nameko.rpc.MethodProxy.__call__', fake_some_method): with entrypoint_hook(container, 'cached') as hook: assert hook('test') == 'hi' def broken_some_method(*args, **kwargs): raise Exception('hmm') with patch('nameko.rpc.MethodProxy.__call__', broken_some_method): with entrypoint_hook(container, 'cached') as hook: assert hook('test') == 'hi' with patch('nameko.rpc.MethodProxy.__call__', broken_some_method): with entrypoint_hook(container, 'cached') as hook: with pytest.raises(Exception): hook('unknown') cached_rpc.cache = {} with patch('nameko.rpc.MethodProxy.__call__', broken_some_method): with entrypoint_hook(container, 'cached') as hook: with pytest.raises(Exception): hook('test')
def test_unsupported_http_entrypoint(self, container_factory, config, web_session): bogus = object() class CustomHttpEntrypoint(HttpRequestHandler): def get_entrypoint_parameters(self, request): args = (bogus, request) kwargs = request.path_values return args, kwargs custom_http = CustomHttpEntrypoint.decorator class Service(object): name = "service" sentry = SentryReporter() @custom_http('GET', '/resource') def resource(self, bogus_arg, request): raise CustomException() container = container_factory(Service, config) container.start() with entrypoint_waiter(container, 'resource'): web_session.get('/resource') sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args expected_http = {} assert kwargs['request'] == expected_http
def test_tags_custom(self, container_factory, service_cls, config): config['SENTRY']['TAG_TYPE_CONTEXT_KEYS'] = ('session', 'other_pattern') container = container_factory(service_cls, config) container.start() context_data = { 'call_id_stack': ["standalone_rpc_proxy.call.0"], 'session_id': 1, 'email_address': '*****@*****.**', } with ServiceRpcProxy('service', config, context_data=context_data) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken() sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 expected_tags = { 'site': config['SENTRY']['CLIENT_CONFIG']['site'], 'call_id': 'service.broken.1', 'parent_call_id': 'standalone_rpc_proxy.call.0', 'service_name': 'service', 'method_name': 'broken', 'session_id': '1', # extra } _, kwargs = sentry.client.send.call_args assert expected_tags == kwargs['tags']
def test_user_defaults(self, container_factory, service_cls, config): user_data = { 'user': '******', 'username': '******', 'user_id': 1, 'email': '*****@*****.**', 'email_address': '*****@*****.**', 'session_id': 1 } container = container_factory(service_cls, config) container.start() context_data = { 'language': 'en-gb' } context_data.update(user_data) with ServiceRpcProxy( 'service', config, context_data=context_data ) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken() sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args assert kwargs['user'] == user_data
def test_concurrent_workers( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('GET', '/resource') def resource(self, request): raise CustomException() container = container_factory(Service, config) container.start() called = Mock() def called_twice(worker_ctx, res, exc_info): called() return called.call_count == 2 with entrypoint_waiter(container, 'resource', callback=called_twice): eventlet.spawn(web_session.get, '/resource?q1') eventlet.spawn(web_session.get, '/resource?q2') sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 2 query_strings = { kwargs['request']['query_string'] for (_, kwargs) in sentry.client.send.call_args_list } assert query_strings == {"q1", "q2"}
def test_activate_deactivate(self, container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() with ServiceRpcProxy('service', config) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.activate_deactivate("a", "b", "c") sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args breadcrumbs = [ crumb for crumb in kwargs['breadcrumbs']['values'] if crumb['category'] == "worker" ] assert breadcrumbs == [{ 'category': "worker", 'data': None, 'level': ANY, 'message': 'a', 'timestamp': ANY, 'type': 'default' }, { 'category': "worker", 'data': None, 'level': ANY, 'message': 'c', 'timestamp': ANY, 'type': 'default' }]
def test_context_merge(self, container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() user_data = { 'user': '******' } context_data = { 'language': 'en-gb' } context_data.update(user_data) data = {'foo': 'bar'} with ServiceRpcProxy( 'service', config, context_data=context_data ) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken(data) sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args assert kwargs['user'] == user_data assert kwargs['arbitrary'] == data
def test_client_disconnect( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('POST', '/resource') def resource(self, request): raise CustomException() container = container_factory(Service, config) container.start() request = Mock( method="GET", url="http://example.com", mimetype='application/json', environ={} ) type(request).data = PropertyMock(side_effect=ClientDisconnected) with entrypoint_hook(container, 'resource') as hook: with pytest.raises(CustomException): hook(request) sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args assert kwargs['request']['data'] == {}
def test_user_custom(self, container_factory, service_cls, config): config['SENTRY']['USER_TYPE_CONTEXT_KEYS'] = ( 'user|email', # excludes session 'other_pattern') container = container_factory(service_cls, config) container.start() user_data = { 'user': '******', 'username': '******', 'user_id': 1, 'email': '*****@*****.**', 'email_address': '*****@*****.**', } context_data = { 'session_id': 1, # excluded from user data 'language': 'en-gb' } context_data.update(user_data) with ServiceRpcProxy('service', config, context_data=context_data) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken() sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args assert kwargs['user'] == user_data assert "session_id" not in kwargs['user']
def test_cached_response_on_timeout(container): cached_rpc = get_extension(container, CachedRpcProxy) def fake_some_method(*args, **kwargs): return 'hi' with patch('nameko.rpc.MethodProxy.__call__', fake_some_method): with entrypoint_hook(container, 'cached') as hook: assert hook() == 'hi' def slow_response(*args, **kwargs): eventlet.sleep(3) return 'hi' start = time.time() with patch('nameko.rpc.MethodProxy.__call__', slow_response): with entrypoint_hook(container, 'cached') as hook: assert hook() == 'hi' assert time.time() - start < 2 cached_rpc.cache = {} start = time.time() with patch('nameko.rpc.MethodProxy.__call__', slow_response): with entrypoint_hook(container, 'cached') as hook: assert hook() == 'hi' assert time.time() - start >= 3
def test_form_submission( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('POST', '/resource') def resource(self, request): raise CustomException() container = container_factory(Service, config) container.start() submitted_data = { 'foo': 'bar' } with entrypoint_waiter(container, 'resource'): web_session.post('/resource', data=submitted_data) sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args assert kwargs['request']['data'] == submitted_data
def test_extra(self, container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() context_data = { 'language': 'en-gb' } with ServiceRpcProxy( 'service', config, context_data=context_data ) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken() sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 expected_extra = { 'call_id_stack': ( repr(u"standalone_rpc_proxy.call.0"), repr(u"service.broken.1") ), 'language': repr(u"en-gb"), 'sys.argv': ANY } _, kwargs = sentry.client.send.call_args assert kwargs['extra'] == expected_extra
def test_normal_http_entrypoint( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('GET', '/resource') def resource(self, request): raise CustomException() container = container_factory(Service, config) container.start() with entrypoint_waiter(container, 'resource'): web_session.get('/resource') sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args expected_http = { 'url': ANY, 'query_string': "", 'method': 'GET', 'data': {}, 'headers': ANY, 'env': ANY } assert kwargs['request'] == expected_http
def test_expected_exception_not_reported( exception_cls, expected_count, container_factory, config ): class Service(object): name = "service" sentry = SentryReporter() @rpc(expected_exceptions=CustomException) def broken(self): raise exception_cls("Error!") config['SENTRY']['REPORT_EXPECTED_EXCEPTIONS'] = False container = container_factory(Service, config) container.start() with entrypoint_waiter(container, 'broken') as result: with ServiceRpcProxy('service', config) as rpc_proxy: with pytest.raises(RemoteError): rpc_proxy.broken() with pytest.raises(exception_cls): result.get() sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == expected_count
def test_breadcrumbs(self, method, container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() data = {'foo': 'bar'} with ServiceRpcProxy('service', config) as rpc_proxy: with pytest.raises(RemoteError): getattr(rpc_proxy, method)(data) sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 1 _, kwargs = sentry.client.send.call_args breadcrumbs = [ crumb for crumb in kwargs['breadcrumbs']['values'] if crumb['category'] == "worker" ] assert breadcrumbs == [{ 'category': 'worker', 'data': data, 'level': 'warning', 'message': 'breadcrumb message', 'timestamp': ANY, 'type': 'default' }]
def test_concurrency( self, container_factory, config, web_session ): class Service(object): name = "service" sentry = SentryReporter() @http('GET', '/resource') def resource(self, request): breadcrumbs.record(message=request.query_string) raise CustomException() container = container_factory(Service, config) container.start() called = Mock() def called_twice(worker_ctx, res, exc_info): called() return called.call_count == 2 with entrypoint_waiter(container, 'resource', callback=called_twice): eventlet.spawn(web_session.get, '/resource?q1') eventlet.spawn(web_session.get, '/resource?q2') sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 2 breadcrumbs_map = { kwargs['request']['query_string']: kwargs['breadcrumbs']['values'] for (_, kwargs) in sentry.client.send.call_args_list } expected_crumb_q1 = { 'category': None, 'data': None, 'level': ANY, 'message': 'q1'.encode('utf-8'), 'timestamp': ANY, 'type': 'default' } assert expected_crumb_q1 in breadcrumbs_map['q1'] assert expected_crumb_q1 not in breadcrumbs_map['q2'] expected_crumb_q2 = { 'category': None, 'data': None, 'level': ANY, 'message': 'q2'.encode('utf-8'), 'timestamp': ANY, 'type': 'default' } assert expected_crumb_q2 in breadcrumbs_map['q2'] assert expected_crumb_q2 not in breadcrumbs_map['q1']
def test_access_client(self, container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() with ServiceRpcProxy('service', config) as rpc_proxy: res = rpc_proxy.get_dsn() sentry = get_extension(container, SentryReporter) assert res == sentry.client.get_public_dsn()
def test_worker_result(container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() with entrypoint_hook(container, 'fine') as fine: with entrypoint_waiter(container, 'fine'): assert fine() == "OK" sentry = get_extension(container, SentryReporter) assert sentry.client.send.call_count == 0
def test_connection_not_found(container, websocket): hub = get_extension(container, WebSocketHubProvider) ws = websocket() hub.server.sockets.clear() # doesn't need to be known assert ws.rpc('unsubscribe') == 'unsubscribed!' with pytest.raises(RemoteError) as exc: ws.rpc('subscribe') assert exc.value.exc_type == 'ConnectionNotFound'
def test_disabled(container_factory, service_cls, config): config['SENTRY']['DSN'] = None container = container_factory(service_cls, config) container.start() sentry = get_extension(container, SentryReporter) # DSN applied correctly assert sentry.client.get_public_dsn() is None assert not sentry.client.is_enabled()
def test_structlog_setup(container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() struct_log = get_extension(container, StructlogDependency) with entrypoint_hook(container, 'foo') as foo: with entrypoint_waiter(container, 'foo'): assert foo() == 'OK' # StructlogDependency returns a structlog logger per service name service_logger = struct_log.logger_by_service_name['demo'] assert 'bar' == service_logger.info('bar')
def test_setup_without_sentry_section_in_config( container_factory, service_cls, config ): del config['SENTRY'] container = container_factory(service_cls, config) container.start() sentry = get_extension(container, SentryReporter) # DSN applied correctly assert sentry.client.get_public_dsn() is None assert not sentry.client.is_enabled()
def test_get_redacted_args_invocation(self, args, kwargs): class Service(object): name = "service" @rpc(sensitive_variables="a") def method(self, a, b=None): pass expected = {'a': REDACTED, 'b': 'B'} container = ServiceContainer(Service, {}) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected
def test_setup(container_factory, service_cls, config): container = container_factory(service_cls, config) container.start() sentry = get_extension(container, SentryReporter) # client config and DSN applied correctly assert sentry.client.site == "site name" assert sentry.client.get_public_dsn() == "//user@localhost:9000/1" assert sentry.client.is_enabled() # transport set correctly transport = sentry.client.remote.get_transport() assert isinstance(transport, EventletHTTPTransport)
def test_get_redacted_args_invocation(self, args, kwargs, rabbit_config): class Service(object): name = "service" @rpc(sensitive_arguments="a") def method(self, a, b=None): pass # pragma: no cover expected = {'a': REDACTED, 'b': 'B'} container = ServiceContainer(Service, rabbit_config) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected
def test_setup_without_optional_config(container_factory, service_cls, config): del config['SENTRY']['CLIENT_CONFIG'] container = container_factory(service_cls, config) container.start() sentry = get_extension(container, SentryReporter) # DSN applied correctly assert sentry.client.get_public_dsn() == "//user@localhost:9000/1" assert sentry.client.is_enabled() # transport set correctly transport = sentry.client.remote.get_transport() assert isinstance(transport, EventletHTTPTransport)
def test_get_redacted_args(self, sensitive_variables, expected): class Service(object): name = "service" @rpc(sensitive_variables=sensitive_variables) def method(self, a, b): pass args = ("A", "B") kwargs = {} container = ServiceContainer(Service, {}) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected
def test_get_redacted_args( self, sensitive_arguments, expected, rabbit_config ): class Service(object): name = "service" @rpc(sensitive_arguments=sensitive_arguments) def method(self, a, b): pass # pragma: no cover args = ("A", "B") kwargs = {} container = ServiceContainer(Service, rabbit_config) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected
def test_get_redacted_args_partial(self, sensitive_variables, expected): class Service(object): name = "service" @rpc(sensitive_variables=sensitive_variables) def method(self, a, b): pass complex_arg = { 'foo': [1, 2, 3], 'bar': "BAR" } args = ("A", complex_arg) kwargs = {} container = ServiceContainer(Service, {}) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected
def test_get_redacted_args_partial( self, sensitive_arguments, expected, rabbit_config ): class Service(object): name = "service" @rpc(sensitive_arguments=sensitive_arguments) def method(self, a, b): pass # pragma: no cover complex_arg = { 'foo': [1, 2, 3], 'bar': "BAR" } args = ("A", complex_arg) kwargs = {} container = ServiceContainer(Service, rabbit_config) entrypoint = get_extension(container, Rpc) redacted = get_redacted_args(entrypoint, *args, **kwargs) assert redacted == expected