# Path: bugsnag/asgi.py
# class BugsnagMiddleware:
#     """
#     Sends unhandled exceptions to Bugsnag which happen while processing
#     requests.
# 
#     >>> async def app(scope, receive, send):
#     ...     await send({
#     ...         "type": "http.request",
#     ...         "body": b"Hello World",
#     ...         "more_body": False,
#     ...     })
#     >>> app = BugsnagMiddleware(app)
#     """
#     def __init__(self, app):
#         self.app = app
#         stack = bugsnag.configure().internal_middleware
# 
#         def add_request_info(event):
#             if not hasattr(event.request_config, 'asgi_scope'):
#                 return
# 
#             scope = event.request_config.asgi_scope
#             request = dict()
#             server = []
# 
#             client = scope.get('client')
#             if client is not None and len(client) > 0:
#                 request['clientIp'] = scope['client'][0]
# 
#             if 'server' in scope and type(scope['server']) in [list, tuple]:
#                 server = scope['server']
#             if 'headers' in scope:
#                 request['headers'] = dict(
#                         [i for i in scope['headers'] if len(i) == 2])
#             for prop in RequestMetadata:
#                 for item in prop.scope_keys:
#                     if item in scope:
#                         request[prop.metadata_key] = scope[item]
#                         break
# 
#             request['url'] = parse_url(request, server)
# 
#             event.add_tab("request", request)
#             if bugsnag.configure().send_environment:
#                 event.add_tab("environment", scope)
# 
#         stack.before_notify(add_request_info)
# 
#     async def __call__(self, scope, receive, send):
#         bugsnag.configure()._breadcrumbs.create_copy_for_context()
#         bugsnag.configure_request(asgi_scope=scope)
#         try:
#             if bugsnag.configuration.auto_capture_sessions:
#                 bugsnag.start_session()
# 
#             # only HTTP and Websocket requests have headers
#             if scope['type'] in ('http', 'websocket'):
#                 _auto_leave_breadcrumb(
#                     '{} request'.format(scope['type']),
#                     _get_breadcrumb_metadata(scope),
#                     BreadcrumbType.NAVIGATION
#                 )
# 
#             await self.app(scope, receive, send)
#         except Exception as e:
#             bugsnag.auto_notify(e, severity_reason=SEVERITY_REASON)
#             raise
# 
# Path: tests/async_utils.py
# class AsyncIntegrationTest(IsolatedAsyncioTestCase):
#     async def asyncSetUp(self):
#         self.server = FakeBugsnag()
#         bugsnag.configure(asynchronous=True,
#                           endpoint=self.server.events_url,
#                           session_endpoint=self.server.sessions_url,
#                           api_key='ffffffffffffffffff')
# 
#     async def asyncTearDown(self):
#         bugsnag.legacy.default_client.uninstall_sys_hook()
#         self.server.shutdown()
# 
#     async def last_event_request(self):
#         """
#         Waits for a request to be received by the event server, timing out
#         after a few seconds
#         """
#         async def poll():
#             while len(self.server.events_received) == 0:
#                 await asyncio.sleep(0.1)
#             return self.server.events_received[0]
#         try:
#             return await asyncio.wait_for(poll(), timeout=1.0)
#         except asyncio.TimeoutError:
#             assert 0, 'Timeout while waiting for a request'
# 
# class ASGITestClient:
#     def __init__(self, app):
#         self.app = app
# 
#     async def invoke(self, scope):
#         received = None
# 
#         async def send(message: Dict[str, Any]):
#             nonlocal received
#             received = message
# 
#         async def receive():
#             pass
# 
#         await self.app(scope, receive, send)
#         return received
# 
#     async def request(self, path: str, query: str = '', **kwargs):
#         return await self.invoke({
#             'method': 'GET',
#             'path': path,
#             'query_string': query.encode(),
#             'scheme': 'http',
#             'type': 'http',
#             'server': ['testserver', 80],
#             'client': ['testclient', 8080],
#             'headers': [
#                 [b'user-agent', b'testclient'],
#             ],
#             **kwargs
#         })
# 
#     async def websocket_request(self, path: str):
#         return await self.invoke({
#             'path': path,
#             'scheme': 'ws',
#             'type': 'websocket',
#             'server': ['testserver', 80],
#             'client': ['testclient', 8080],
#             'headers': [
#                 [b'user-agent', b'testclient'],
#                 [b'sec-websocket-version', b'13'],
#             ],
#         })


import bugsnag
from bugsnag.asgi import BugsnagMiddleware
from tests.async_utils import AsyncIntegrationTest, ASGITestClient
        app = ASGITestClient(BugsnagMiddleware(app))

        try:
            await app.request('/')
            assert 0, 'An exception should have been raised'
        except CustomException:
            pass

        payload = await self.last_event_request()
        metadata = payload['events'][0]['metaData']
        request = metadata['request']
        self.assertEqual('/', request['path'])
        self.assertEqual('GET', request['httpMethod'])
        self.assertEqual('http', request['type'])
        self.assertEqual('http://testserver/', request['url'])
        self.assertEqual('35b', metadata['wave']['size'])

        exception = payload['events'][0]['exceptions'][0]
        self.assertEqual('test_async_asgi.CustomException',
                         exception['errorClass'])
        self.assertEqual('fell winds!', exception['message'])
        self.assertEqual('next_func', exception['stacktrace'][0]['method'])
        self.assertEqual('app', exception['stacktrace'][1]['method'])

    async def test_websocket_crash(self):
        async def app(scope, receive, send):
            await send({'type': 'websocket.accept'})
            raise CustomException('invalid inputs')
            await send({'type': 'websocket.close'})


# Path: bugsnag/asgi.py
# class BugsnagMiddleware:
#     """
#     Sends unhandled exceptions to Bugsnag which happen while processing
#     requests.
# 
#     >>> async def app(scope, receive, send):
#     ...     await send({
#     ...         "type": "http.request",
#     ...         "body": b"Hello World",
#     ...         "more_body": False,
#     ...     })
#     >>> app = BugsnagMiddleware(app)
#     """
#     def __init__(self, app):
#         self.app = app
#         stack = bugsnag.configure().internal_middleware
# 
#         def add_request_info(event):
#             if not hasattr(event.request_config, 'asgi_scope'):
#                 return
# 
#             scope = event.request_config.asgi_scope
#             request = dict()
#             server = []
# 
#             client = scope.get('client')
#             if client is not None and len(client) > 0:
#                 request['clientIp'] = scope['client'][0]
# 
#             if 'server' in scope and type(scope['server']) in [list, tuple]:
#                 server = scope['server']
#             if 'headers' in scope:
#                 request['headers'] = dict(
#                         [i for i in scope['headers'] if len(i) == 2])
#             for prop in RequestMetadata:
#                 for item in prop.scope_keys:
#                     if item in scope:
#                         request[prop.metadata_key] = scope[item]
#                         break
# 
#             request['url'] = parse_url(request, server)
# 
#             event.add_tab("request", request)
#             if bugsnag.configure().send_environment:
#                 event.add_tab("environment", scope)
# 
#         stack.before_notify(add_request_info)
# 
#     async def __call__(self, scope, receive, send):
#         bugsnag.configure()._breadcrumbs.create_copy_for_context()
#         bugsnag.configure_request(asgi_scope=scope)
#         try:
#             if bugsnag.configuration.auto_capture_sessions:
#                 bugsnag.start_session()
# 
#             # only HTTP and Websocket requests have headers
#             if scope['type'] in ('http', 'websocket'):
#                 _auto_leave_breadcrumb(
#                     '{} request'.format(scope['type']),
#                     _get_breadcrumb_metadata(scope),
#                     BreadcrumbType.NAVIGATION
#                 )
# 
#             await self.app(scope, receive, send)
#         except Exception as e:
#             bugsnag.auto_notify(e, severity_reason=SEVERITY_REASON)
#             raise
# 
# Path: tests/async_utils.py
# class AsyncIntegrationTest(IsolatedAsyncioTestCase):
#     async def asyncSetUp(self):
#         self.server = FakeBugsnag()
#         bugsnag.configure(asynchronous=True,
#                           endpoint=self.server.events_url,
#                           session_endpoint=self.server.sessions_url,
#                           api_key='ffffffffffffffffff')
# 
#     async def asyncTearDown(self):
#         bugsnag.legacy.default_client.uninstall_sys_hook()
#         self.server.shutdown()
# 
#     async def last_event_request(self):
#         """
#         Waits for a request to be received by the event server, timing out
#         after a few seconds
#         """
#         async def poll():
#             while len(self.server.events_received) == 0:
#                 await asyncio.sleep(0.1)
#             return self.server.events_received[0]
#         try:
#             return await asyncio.wait_for(poll(), timeout=1.0)
#         except asyncio.TimeoutError:
#             assert 0, 'Timeout while waiting for a request'
# 
# class ASGITestClient:
#     def __init__(self, app):
#         self.app = app
# 
#     async def invoke(self, scope):
#         received = None
# 
#         async def send(message: Dict[str, Any]):
#             nonlocal received
#             received = message
# 
#         async def receive():
#             pass
# 
#         await self.app(scope, receive, send)
#         return received
# 
#     async def request(self, path: str, query: str = '', **kwargs):
#         return await self.invoke({
#             'method': 'GET',
#             'path': path,
#             'query_string': query.encode(),
#             'scheme': 'http',
#             'type': 'http',
#             'server': ['testserver', 80],
#             'client': ['testclient', 8080],
#             'headers': [
#                 [b'user-agent', b'testclient'],
#             ],
#             **kwargs
#         })
# 
#     async def websocket_request(self, path: str):
#         return await self.invoke({
#             'path': path,
#             'scheme': 'ws',
#             'type': 'websocket',
#             'server': ['testserver', 80],
#             'client': ['testclient', 8080],
#             'headers': [
#                 [b'user-agent', b'testclient'],
#                 [b'sec-websocket-version', b'13'],
#             ],
#         })


# Path: tests/integrations/test_async_asgi.py
import bugsnag
from bugsnag.asgi import BugsnagMiddleware
from tests.async_utils import AsyncIntegrationTest, ASGITestClient

        app = ASGITestClient(BugsnagMiddleware(app))

        try:
            await app.request('/')
            assert 0, 'An exception should have been raised'
        except CustomException:
            pass

        payload = await self.last_event_request()
        metadata = payload['events'][0]['metaData']
        request = metadata['request']
        self.assertEqual('/', request['path'])
        self.assertEqual('GET', request['httpMethod'])
        self.assertEqual('http', request['type'])
        self.assertEqual('http://testserver/', request['url'])
        self.assertEqual('35b', metadata['wave']['size'])

        exception = payload['events'][0]['exceptions'][0]
        self.assertEqual('test_async_asgi.CustomException',
                         exception['errorClass'])
        self.assertEqual('fell winds!', exception['message'])
        self.assertEqual('next_func', exception['stacktrace'][0]['method'])
        self.assertEqual('app', exception['stacktrace'][1]['method'])

    async def test_websocket_crash(self):
        async def app(scope, receive, send):
            await send({'type': 'websocket.accept'})
            raise CustomException('invalid inputs')
            await send({'type': 'websocket.close'})


        app = ASGITestClient(BugsnagMiddleware(app))