def test_span_namer(http_request, http_response): settings.tracing_implementation.set_value(FakeSpan) with FakeSpan(name="parent") as root_span: request = http_request("GET", "http://localhost/temp?query=query") pipeline_request = PipelineRequest(request, PipelineContext(None)) def fixed_namer(http_request): assert http_request is request return "overridenname" policy = DistributedTracingPolicy(network_span_namer=fixed_namer) policy.on_request(pipeline_request) response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 policy.on_response( pipeline_request, PipelineResponse(request, response, PipelineContext(None))) def operation_namer(http_request): assert http_request is request return "operation level name" pipeline_request.context.options[ 'network_span_namer'] = operation_namer policy.on_request(pipeline_request) response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 policy.on_response( pipeline_request, PipelineResponse(request, response, PipelineContext(None))) # Check init kwargs network_span = root_span.children[0] assert network_span.name == "overridenname" # Check operation kwargs network_span = root_span.children[1] assert network_span.name == "operation level name"
def send(self, request, **kwargs): # type: (PipelineRequest, Any) -> PipelineResponse self._count += 1 response = create_http_response(http_response, request, None) response.status_code = 201 headers = {"Retry-After": "1"} response.headers = headers return response
def test_error_map(http_request, http_response): request = http_request("GET", "") response = create_http_response(http_response, request, None) error_map = { 404: ResourceNotFoundError } with pytest.raises(ResourceNotFoundError): map_error(404, response, error_map)
def test_error_map_with_default(http_request, http_response): request = http_request("GET", "") response = create_http_response(http_response, request, None) error_map = ErrorMap({ 404: ResourceNotFoundError }, default_error=ResourceExistsError) with pytest.raises(ResourceExistsError): map_error(401, response, error_map)
def test_distributed_tracing_policy_solo(http_request, http_response): """Test policy with no other policy and happy path""" settings.tracing_implementation.set_value(FakeSpan) with FakeSpan(name="parent") as root_span: policy = DistributedTracingPolicy() request = http_request("GET", "http://localhost/temp?query=query") request.headers["x-ms-client-request-id"] = "some client request id" pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 response.headers["x-ms-request-id"] = "some request id" assert request.headers.get("traceparent") == '123456789' policy.on_response( pipeline_request, PipelineResponse(request, response, PipelineContext(None))) time.sleep(0.001) policy.on_request(pipeline_request) try: raise ValueError("Transport trouble") except: policy.on_exception(pipeline_request) # Check on_response network_span = root_span.children[0] assert network_span.name == "/temp" assert network_span.attributes.get("http.method") == "GET" assert network_span.attributes.get("component") == "http" assert network_span.attributes.get( "http.url") == "http://localhost/temp?query=query" assert network_span.attributes.get("http.user_agent") is None assert network_span.attributes.get("x-ms-request-id") == "some request id" assert network_span.attributes.get( "x-ms-client-request-id") == "some client request id" assert network_span.attributes.get("http.status_code") == 202 # Check on_exception network_span = root_span.children[1] assert network_span.name == "/temp" assert network_span.attributes.get("http.method") == "GET" assert network_span.attributes.get("component") == "http" assert network_span.attributes.get( "http.url") == "http://localhost/temp?query=query" assert network_span.attributes.get( "x-ms-client-request-id") == "some client request id" assert network_span.attributes.get("http.user_agent") is None assert network_span.attributes.get("x-ms-request-id") == None assert network_span.attributes.get("http.status_code") == 504
def send(self, request, **kwargs): # type: (PipelineRequest, Any) -> PipelineResponse if self._first: self._first = False request.body.seek(0, 2) raise AzureError('fail on first') position = request.body.tell() assert position == 0 response = create_http_response(http_response, request, None) response.status_code = 400 return response
def test_connection_error_response(http_request, http_response): class MockTransport(HttpTransport): def __init__(self): self._count = 0 def __exit__(self, exc_type, exc_val, exc_tb): pass def close(self): pass def open(self): pass def send(self, request, **kwargs): request = http_request('GET', 'http://localhost/') response = create_http_response(http_response, request, None) response.status_code = 200 return response def next(self): self.__next__() def __next__(self): if self._count == 0: self._count += 1 raise requests.exceptions.ConnectionError def stream(self, chunk_size, decode_content=False): if self._count == 0: self._count += 1 raise requests.exceptions.ConnectionError while True: yield b"test" class MockInternalResponse(): def __init__(self): self.raw = MockTransport() def close(self): pass http_request = http_request('GET', 'http://localhost/') pipeline = Pipeline(MockTransport()) http_response = create_http_response(http_response, http_request, None) http_response.internal_response = MockInternalResponse() stream = StreamDownloadGenerator(pipeline, http_response, decompress=False) with mock.patch('time.sleep', return_value=None): with pytest.raises(requests.exceptions.ConnectionError): stream.__next__()
def test_x_ms_retry_after(retry_after_input, http_request, http_response): retry_policy = RetryPolicy() request = http_request("GET", "http://localhost") response = create_http_response(http_response, request, None) response.headers["x-ms-retry-after-ms"] = retry_after_input pipeline_response = PipelineResponse(request, response, None) retry_after = retry_policy.get_retry_after(pipeline_response) seconds = float(retry_after_input) assert retry_after == seconds / 1000.0 response.headers.pop("x-ms-retry-after-ms") response.headers["Retry-After"] = retry_after_input retry_after = retry_policy.get_retry_after(pipeline_response) assert retry_after == float(retry_after_input) response.headers["x-ms-retry-after-ms"] = 500 retry_after = retry_policy.get_retry_after(pipeline_response) assert retry_after == float(retry_after_input)
def test_http_logger_with_generator_body(http_request, http_response): class MockHandler(logging.Handler): def __init__(self): super(MockHandler, self).__init__() self.messages = [] def reset(self): self.messages = [] def emit(self, record): self.messages.append(record) mock_handler = MockHandler() logger = logging.getLogger("testlogger") logger.addHandler(mock_handler) logger.setLevel(logging.DEBUG) policy = HttpLoggingPolicy(logger=logger) universal_request = http_request('GET', 'http://localhost/') mock = Mock() mock.__class__ = types.AsyncGeneratorType universal_request.body = mock http_response = create_http_response(http_response, universal_request, None) http_response.status_code = 202 request = PipelineRequest(universal_request, PipelineContext(None)) policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 2 messages_request = mock_handler.messages[0].message.split("\n") messages_response = mock_handler.messages[1].message.split("\n") assert messages_request[0] == "Request URL: 'http://localhost/'" assert messages_request[1] == "Request method: 'GET'" assert messages_request[2] == 'Request headers:' assert messages_request[3] == 'File upload' assert messages_response[0] == 'Response status: 202' assert messages_response[1] == 'Response headers:' mock_handler.reset()
def send(self, request, **kwargs): # type: (PipelineRequest, Any) -> PipelineResponse if self._first: self._first = False for value in request.files.values(): name, body = value[0], value[1] if name and body and hasattr(body, 'read'): body.seek(0, 2) raise AzureError('fail on first') for value in request.files.values(): name, body = value[0], value[1] if name and body and hasattr(body, 'read'): position = body.tell() assert not position response = create_http_response(http_response, request, None) response.status_code = 400 return response
def test_distributed_tracing_policy_attributes(http_request, http_response): """Test policy with no other policy and happy path""" settings.tracing_implementation.set_value(FakeSpan) with FakeSpan(name="parent") as root_span: policy = DistributedTracingPolicy( tracing_attributes={'myattr': 'myvalue'}) request = http_request("GET", "http://localhost/temp?query=query") pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 policy.on_response( pipeline_request, PipelineResponse(request, response, PipelineContext(None))) # Check on_response network_span = root_span.children[0] assert network_span.attributes.get("myattr") == "myvalue"
def test_distributed_tracing_policy_badurl(caplog, http_request, http_response): """Test policy with a bad url that will throw, and be sure policy ignores it""" settings.tracing_implementation.set_value(FakeSpan) with FakeSpan(name="parent") as root_span: policy = DistributedTracingPolicy() request = http_request("GET", "http://[[[") request.headers["x-ms-client-request-id"] = "some client request id" pipeline_request = PipelineRequest(request, PipelineContext(None)) with caplog.at_level( logging.WARNING, logger="azure.core.pipeline.policies.distributed_tracing"): policy.on_request(pipeline_request) assert "Unable to start network span" in caplog.text response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 response.headers["x-ms-request-id"] = "some request id" assert request.headers.get( "traceparent") is None # Got not network trace policy.on_response( pipeline_request, PipelineResponse(request, response, PipelineContext(None))) time.sleep(0.001) policy.on_request(pipeline_request) try: raise ValueError("Transport trouble") except: policy.on_exception(pipeline_request) assert len(root_span.children) == 0
def test_no_log(mock_http_logger, http_request, http_response): universal_request = http_request('GET', 'http://localhost/') request = PipelineRequest(universal_request, PipelineContext(None)) http_logger = NetworkTraceLoggingPolicy() response = PipelineResponse( request, create_http_response(http_response, universal_request, None), request.context) # By default, no log handler for HTTP http_logger.on_request(request) mock_http_logger.debug.assert_not_called() http_logger.on_response(request, response) mock_http_logger.debug.assert_not_called() mock_http_logger.reset_mock() # I can enable it per request request.context.options['logging_enable'] = True http_logger.on_request(request) assert mock_http_logger.debug.call_count >= 1 mock_http_logger.reset_mock() request.context.options['logging_enable'] = True http_logger.on_response(request, response) assert mock_http_logger.debug.call_count >= 1 mock_http_logger.reset_mock() # I can enable it per request (bool value should be honored) request.context.options['logging_enable'] = False http_logger.on_request(request) mock_http_logger.debug.assert_not_called() request.context.options['logging_enable'] = False http_logger.on_response(request, response) mock_http_logger.debug.assert_not_called() mock_http_logger.reset_mock() # I can enable it globally request.context.options = {} http_logger.enable_http_logger = True http_logger.on_request(request) assert mock_http_logger.debug.call_count >= 1 http_logger.on_response(request, response) assert mock_http_logger.debug.call_count >= 1 mock_http_logger.reset_mock() # I can enable it globally and override it locally http_logger.enable_http_logger = True request.context.options['logging_enable'] = False http_logger.on_request(request) mock_http_logger.debug.assert_not_called() response.context['logging_enable'] = False http_logger.on_response(request, response) mock_http_logger.debug.assert_not_called() mock_http_logger.reset_mock() # Let's make this request a failure, retried twice request.context.options['logging_enable'] = True http_logger.on_request(request) http_logger.on_response(request, response) first_count = mock_http_logger.debug.call_count assert first_count >= 1 http_logger.on_request(request) http_logger.on_response(request, response) second_count = mock_http_logger.debug.call_count assert second_count == first_count * 2
def test_distributed_tracing_policy_with_user_agent(http_request, http_response): """Test policy working with user agent.""" settings.tracing_implementation.set_value(FakeSpan) with mock.patch.dict('os.environ', {"AZURE_HTTP_USER_AGENT": "mytools"}): with FakeSpan(name="parent") as root_span: policy = DistributedTracingPolicy() request = http_request("GET", "http://localhost") request.headers[ "x-ms-client-request-id"] = "some client request id" pipeline_request = PipelineRequest(request, PipelineContext(None)) user_agent = UserAgentPolicy() user_agent.on_request(pipeline_request) policy.on_request(pipeline_request) response = create_http_response(http_response, request, None) response.headers = request.headers response.status_code = 202 response.headers["x-ms-request-id"] = "some request id" pipeline_response = PipelineResponse(request, response, PipelineContext(None)) assert request.headers.get("traceparent") == '123456789' policy.on_response(pipeline_request, pipeline_response) time.sleep(0.001) policy.on_request(pipeline_request) try: raise ValueError("Transport trouble") except: policy.on_exception(pipeline_request) user_agent.on_response(pipeline_request, pipeline_response) network_span = root_span.children[0] assert network_span.name == "/" assert network_span.attributes.get("http.method") == "GET" assert network_span.attributes.get("component") == "http" assert network_span.attributes.get("http.url") == "http://localhost" assert network_span.attributes.get("http.user_agent").endswith( "mytools") assert network_span.attributes.get( "x-ms-request-id") == "some request id" assert network_span.attributes.get( "x-ms-client-request-id") == "some client request id" assert network_span.attributes.get("http.status_code") == 202 network_span = root_span.children[1] assert network_span.name == "/" assert network_span.attributes.get("http.method") == "GET" assert network_span.attributes.get("component") == "http" assert network_span.attributes.get("http.url") == "http://localhost" assert network_span.attributes.get("http.user_agent").endswith( "mytools") assert network_span.attributes.get( "x-ms-client-request-id") == "some client request id" assert network_span.attributes.get("x-ms-request-id") is None assert network_span.attributes.get("http.status_code") == 504 # Exception should propagate status for Opencensus assert network_span.status == 'Transport trouble'
def test_http_logger(http_request, http_response): class MockHandler(logging.Handler): def __init__(self): super(MockHandler, self).__init__() self.messages = [] def reset(self): self.messages = [] def emit(self, record): self.messages.append(record) mock_handler = MockHandler() logger = logging.getLogger("testlogger") logger.addHandler(mock_handler) logger.setLevel(logging.DEBUG) policy = HttpLoggingPolicy(logger=logger) universal_request = http_request('GET', 'http://localhost/') http_response = create_http_response(http_response, universal_request, None) http_response.status_code = 202 request = PipelineRequest(universal_request, PipelineContext(None)) # Basics policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 2 messages_request = mock_handler.messages[0].message.split("\n") messages_response = mock_handler.messages[1].message.split("\n") assert messages_request[0] == "Request URL: 'http://localhost/'" assert messages_request[1] == "Request method: 'GET'" assert messages_request[2] == 'Request headers:' assert messages_request[3] == 'No body was attached to the request' assert messages_response[0] == 'Response status: 202' assert messages_response[1] == 'Response headers:' mock_handler.reset() # Let's make this request a failure, retried twice policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 4 messages_request1 = mock_handler.messages[0].message.split("\n") messages_response1 = mock_handler.messages[1].message.split("\n") messages_request2 = mock_handler.messages[2].message.split("\n") messages_response2 = mock_handler.messages[3].message.split("\n") assert messages_request1[0] == "Request URL: 'http://localhost/'" assert messages_request1[1] == "Request method: 'GET'" assert messages_request1[2] == 'Request headers:' assert messages_request1[3] == 'No body was attached to the request' assert messages_response1[0] == 'Response status: 202' assert messages_response1[1] == 'Response headers:' assert messages_request2[0] == "Request URL: 'http://localhost/'" assert messages_request2[1] == "Request method: 'GET'" assert messages_request2[2] == 'Request headers:' assert messages_request2[3] == 'No body was attached to the request' assert messages_response2[0] == 'Response status: 202' assert messages_response2[1] == 'Response headers:' mock_handler.reset() # Headers and query parameters policy.allowed_query_params = ['country'] universal_request.headers = { "Accept": "Caramel", "Hate": "Chocolat", } http_response.headers = { "Content-Type": "Caramel", "HateToo": "Chocolat", } universal_request.url = "http://localhost/?country=france&city=aix" policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 2 messages_request = mock_handler.messages[0].message.split("\n") messages_response = mock_handler.messages[1].message.split("\n") assert messages_request[ 0] == "Request URL: 'http://localhost/?country=france&city=REDACTED'" assert messages_request[1] == "Request method: 'GET'" assert messages_request[2] == "Request headers:" # Dict not ordered in Python, exact logging order doesn't matter assert set([messages_request[3], messages_request[4]]) == set( [" 'Accept': 'Caramel'", " 'Hate': 'REDACTED'"]) assert messages_request[5] == 'No body was attached to the request' assert messages_response[0] == "Response status: 202" assert messages_response[1] == "Response headers:" # Dict not ordered in Python, exact logging order doesn't matter assert set([messages_response[2], messages_response[3]]) == set( [" 'Content-Type': 'Caramel'", " 'HateToo': 'REDACTED'"]) mock_handler.reset()
def test_http_logger_operation_level(http_request, http_response): class MockHandler(logging.Handler): def __init__(self): super(MockHandler, self).__init__() self.messages = [] def reset(self): self.messages = [] def emit(self, record): self.messages.append(record) mock_handler = MockHandler() logger = logging.getLogger("testlogger") logger.addHandler(mock_handler) logger.setLevel(logging.DEBUG) policy = HttpLoggingPolicy() kwargs = {'logger': logger} universal_request = http_request('GET', 'http://localhost/') http_response = create_http_response(http_response, universal_request, None) http_response.status_code = 202 request = PipelineRequest(universal_request, PipelineContext(None, **kwargs)) # Basics policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 2 messages_request = mock_handler.messages[0].message.split("\n") messages_response = mock_handler.messages[1].message.split("\n") assert messages_request[0] == "Request URL: 'http://localhost/'" assert messages_request[1] == "Request method: 'GET'" assert messages_request[2] == 'Request headers:' assert messages_request[3] == 'No body was attached to the request' assert messages_response[0] == 'Response status: 202' assert messages_response[1] == 'Response headers:' mock_handler.reset() # Let's make this request a failure, retried twice request = PipelineRequest(universal_request, PipelineContext(None, **kwargs)) policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) policy.on_request(request) response = PipelineResponse(request, http_response, request.context) policy.on_response(request, response) assert all(m.levelname == 'INFO' for m in mock_handler.messages) assert len(mock_handler.messages) == 4 messages_request1 = mock_handler.messages[0].message.split("\n") messages_response1 = mock_handler.messages[1].message.split("\n") messages_request2 = mock_handler.messages[2].message.split("\n") messages_response2 = mock_handler.messages[3].message.split("\n") assert messages_request1[0] == "Request URL: 'http://localhost/'" assert messages_request1[1] == "Request method: 'GET'" assert messages_request1[2] == 'Request headers:' assert messages_request1[3] == 'No body was attached to the request' assert messages_response1[0] == 'Response status: 202' assert messages_response1[1] == 'Response headers:' assert messages_request2[0] == "Request URL: 'http://localhost/'" assert messages_request2[1] == "Request method: 'GET'" assert messages_request2[2] == 'Request headers:' assert messages_request2[3] == 'No body was attached to the request' assert messages_response2[0] == 'Response status: 202' assert messages_response2[1] == 'Response headers:' mock_handler.reset()
def send(self, request, **kwargs): request = http_request('GET', 'http://localhost/') response = create_http_response(http_response, request, None) response.status_code = 200 return response
def send(self, request, **kwargs): # type: (PipelineRequest, Any) -> PipelineResponse self._count += 1 response = create_http_response(http_response, request, None) response.status_code = 429 return response
def send(request, **kwargs): for arg in ("connection_timeout", "read_timeout"): assert arg not in kwargs, "policy should defer to transport configuration when not given a timeout" response = create_http_response(http_response, request, None) response.status_code = 200 return response
async def test_connection_error_response(http_request, http_response): class MockSession(object): def __init__(self): self.auto_decompress = True @property def auto_decompress(self): return self.auto_decompress class MockTransport(AsyncHttpTransport): def __init__(self): self._count = 0 self.session = MockSession async def __aexit__(self, exc_type, exc_val, exc_tb): pass async def close(self): pass async def open(self): pass async def send(self, request, **kwargs): request = http_request('GET', 'http://localhost/') response = create_http_response(http_response, request, None) response.status_code = 200 return response class MockContent(): def __init__(self): self._first = True async def read(self, block_size): if self._first: self._first = False raise ConnectionError return None class MockInternalResponse(): def __init__(self): self.headers = {} self.content = MockContent() async def close(self): pass class AsyncMock(mock.MagicMock): async def __call__(self, *args, **kwargs): return super(AsyncMock, self).__call__(*args, **kwargs) http_request = http_request('GET', 'http://localhost/') pipeline = AsyncPipeline(MockTransport()) http_response = create_http_response(http_response, http_request, None) http_response.internal_response = MockInternalResponse() stream = AioHttpStreamDownloadGenerator(pipeline, http_response, decompress=False) with mock.patch('asyncio.sleep', new_callable=AsyncMock): with pytest.raises(ConnectionError): await stream.__anext__()