1# ------------------------------------ 2# Copyright (c) Microsoft Corporation. 3# Licensed under the MIT License. 4# ------------------------------------ 5"""Tests for the HttpLoggingPolicy.""" 6import pytest 7import logging 8import types 9try: 10 from unittest.mock import Mock 11except ImportError: # python < 3.3 12 from mock import Mock # type: ignore 13from azure.core.pipeline import ( 14 PipelineResponse, 15 PipelineRequest, 16 PipelineContext 17) 18from azure.core.pipeline.policies import ( 19 HttpLoggingPolicy, 20) 21from utils import HTTP_RESPONSES, create_http_response, request_and_responses_product 22from azure.core.pipeline._tools import is_rest 23 24@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES)) 25def test_http_logger(http_request, http_response): 26 27 class MockHandler(logging.Handler): 28 def __init__(self): 29 super(MockHandler, self).__init__() 30 self.messages = [] 31 def reset(self): 32 self.messages = [] 33 def emit(self, record): 34 self.messages.append(record) 35 mock_handler = MockHandler() 36 37 logger = logging.getLogger("testlogger") 38 logger.addHandler(mock_handler) 39 logger.setLevel(logging.DEBUG) 40 41 policy = HttpLoggingPolicy(logger=logger) 42 43 universal_request = http_request('GET', 'http://localhost/') 44 http_response = create_http_response(http_response, universal_request, None) 45 http_response.status_code = 202 46 request = PipelineRequest(universal_request, PipelineContext(None)) 47 48 # Basics 49 50 policy.on_request(request) 51 response = PipelineResponse(request, http_response, request.context) 52 policy.on_response(request, response) 53 54 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 55 assert len(mock_handler.messages) == 2 56 messages_request = mock_handler.messages[0].message.split("/n") 57 messages_response = mock_handler.messages[1].message.split("/n") 58 assert messages_request[0] == "Request URL: 'http://localhost/'" 59 assert messages_request[1] == "Request method: 'GET'" 60 assert messages_request[2] == 'Request headers:' 61 assert messages_request[3] == 'No body was attached to the request' 62 assert messages_response[0] == 'Response status: 202' 63 assert messages_response[1] == 'Response headers:' 64 65 mock_handler.reset() 66 67 # Let's make this request a failure, retried twice 68 69 policy.on_request(request) 70 response = PipelineResponse(request, http_response, request.context) 71 policy.on_response(request, response) 72 73 policy.on_request(request) 74 response = PipelineResponse(request, http_response, request.context) 75 policy.on_response(request, response) 76 77 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 78 assert len(mock_handler.messages) == 4 79 messages_request1 = mock_handler.messages[0].message.split("/n") 80 messages_response1 = mock_handler.messages[1].message.split("/n") 81 messages_request2 = mock_handler.messages[2].message.split("/n") 82 messages_response2 = mock_handler.messages[3].message.split("/n") 83 assert messages_request1[0] == "Request URL: 'http://localhost/'" 84 assert messages_request1[1] == "Request method: 'GET'" 85 assert messages_request1[2] == 'Request headers:' 86 assert messages_request1[3] == 'No body was attached to the request' 87 assert messages_response1[0] == 'Response status: 202' 88 assert messages_response1[1] == 'Response headers:' 89 assert messages_request2[0] == "Request URL: 'http://localhost/'" 90 assert messages_request2[1] == "Request method: 'GET'" 91 assert messages_request2[2] == 'Request headers:' 92 assert messages_request2[3] == 'No body was attached to the request' 93 assert messages_response2[0] == 'Response status: 202' 94 assert messages_response2[1] == 'Response headers:' 95 96 mock_handler.reset() 97 98 # Headers and query parameters 99 100 policy.allowed_query_params = ['country'] 101 102 universal_request.headers = { 103 "Accept": "Caramel", 104 "Hate": "Chocolat", 105 } 106 http_response.headers = { 107 "Content-Type": "Caramel", 108 "HateToo": "Chocolat", 109 } 110 universal_request.url = "http://localhost/?country=france&city=aix" 111 112 policy.on_request(request) 113 response = PipelineResponse(request, http_response, request.context) 114 policy.on_response(request, response) 115 116 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 117 assert len(mock_handler.messages) == 2 118 messages_request = mock_handler.messages[0].message.split("/n") 119 messages_response = mock_handler.messages[1].message.split("/n") 120 assert messages_request[0] == "Request URL: 'http://localhost/?country=france&city=REDACTED'" 121 assert messages_request[1] == "Request method: 'GET'" 122 assert messages_request[2] == "Request headers:" 123 # Dict not ordered in Python, exact logging order doesn't matter 124 assert set([ 125 messages_request[3], 126 messages_request[4] 127 ]) == set([ 128 " 'Accept': 'Caramel'", 129 " 'Hate': 'REDACTED'" 130 ]) 131 assert messages_request[5] == 'No body was attached to the request' 132 assert messages_response[0] == "Response status: 202" 133 assert messages_response[1] == "Response headers:" 134 # Dict not ordered in Python, exact logging order doesn't matter 135 assert set([ 136 messages_response[2], 137 messages_response[3] 138 ]) == set([ 139 " 'Content-Type': 'Caramel'", 140 " 'HateToo': 'REDACTED'" 141 ]) 142 143 mock_handler.reset() 144 145 146 147@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES)) 148def test_http_logger_operation_level(http_request, http_response): 149 150 class MockHandler(logging.Handler): 151 def __init__(self): 152 super(MockHandler, self).__init__() 153 self.messages = [] 154 def reset(self): 155 self.messages = [] 156 def emit(self, record): 157 self.messages.append(record) 158 mock_handler = MockHandler() 159 160 logger = logging.getLogger("testlogger") 161 logger.addHandler(mock_handler) 162 logger.setLevel(logging.DEBUG) 163 164 policy = HttpLoggingPolicy() 165 kwargs={'logger': logger} 166 167 universal_request = http_request('GET', 'http://localhost/') 168 http_response = create_http_response(http_response, universal_request, None) 169 http_response.status_code = 202 170 request = PipelineRequest(universal_request, PipelineContext(None, **kwargs)) 171 172 # Basics 173 174 policy.on_request(request) 175 response = PipelineResponse(request, http_response, request.context) 176 policy.on_response(request, response) 177 178 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 179 assert len(mock_handler.messages) == 2 180 messages_request = mock_handler.messages[0].message.split("/n") 181 messages_response = mock_handler.messages[1].message.split("/n") 182 assert messages_request[0] == "Request URL: 'http://localhost/'" 183 assert messages_request[1] == "Request method: 'GET'" 184 assert messages_request[2] == 'Request headers:' 185 assert messages_request[3] == 'No body was attached to the request' 186 assert messages_response[0] == 'Response status: 202' 187 assert messages_response[1] == 'Response headers:' 188 189 mock_handler.reset() 190 191 # Let's make this request a failure, retried twice 192 193 request = PipelineRequest(universal_request, PipelineContext(None, **kwargs)) 194 195 policy.on_request(request) 196 response = PipelineResponse(request, http_response, request.context) 197 policy.on_response(request, response) 198 199 policy.on_request(request) 200 response = PipelineResponse(request, http_response, request.context) 201 policy.on_response(request, response) 202 203 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 204 assert len(mock_handler.messages) == 4 205 messages_request1 = mock_handler.messages[0].message.split("/n") 206 messages_response1 = mock_handler.messages[1].message.split("/n") 207 messages_request2 = mock_handler.messages[2].message.split("/n") 208 messages_response2 = mock_handler.messages[3].message.split("/n") 209 assert messages_request1[0] == "Request URL: 'http://localhost/'" 210 assert messages_request1[1] == "Request method: 'GET'" 211 assert messages_request1[2] == 'Request headers:' 212 assert messages_request1[3] == 'No body was attached to the request' 213 assert messages_response1[0] == 'Response status: 202' 214 assert messages_response1[1] == 'Response headers:' 215 assert messages_request2[0] == "Request URL: 'http://localhost/'" 216 assert messages_request2[1] == "Request method: 'GET'" 217 assert messages_request2[2] == 'Request headers:' 218 assert messages_request2[3] == 'No body was attached to the request' 219 assert messages_response2[0] == 'Response status: 202' 220 assert messages_response2[1] == 'Response headers:' 221 222 mock_handler.reset() 223 224 225@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES)) 226def test_http_logger_with_body(http_request, http_response): 227 228 class MockHandler(logging.Handler): 229 def __init__(self): 230 super(MockHandler, self).__init__() 231 self.messages = [] 232 def reset(self): 233 self.messages = [] 234 def emit(self, record): 235 self.messages.append(record) 236 mock_handler = MockHandler() 237 238 logger = logging.getLogger("testlogger") 239 logger.addHandler(mock_handler) 240 logger.setLevel(logging.DEBUG) 241 242 policy = HttpLoggingPolicy(logger=logger) 243 244 universal_request = http_request('GET', 'http://localhost/') 245 universal_request.body = "testbody" 246 http_response = create_http_response(http_response, universal_request, None) 247 http_response.status_code = 202 248 request = PipelineRequest(universal_request, PipelineContext(None)) 249 250 policy.on_request(request) 251 response = PipelineResponse(request, http_response, request.context) 252 policy.on_response(request, response) 253 254 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 255 assert len(mock_handler.messages) == 2 256 messages_request = mock_handler.messages[0].message.split("/n") 257 messages_response = mock_handler.messages[1].message.split("/n") 258 assert messages_request[0] == "Request URL: 'http://localhost/'" 259 assert messages_request[1] == "Request method: 'GET'" 260 assert messages_request[2] == 'Request headers:' 261 assert messages_request[3] == 'A body is sent with the request' 262 assert messages_response[0] == 'Response status: 202' 263 assert messages_response[1] == 'Response headers:' 264 265 mock_handler.reset() 266 267 268@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES)) 269def test_http_logger_with_generator_body(http_request, http_response): 270 271 class MockHandler(logging.Handler): 272 def __init__(self): 273 super(MockHandler, self).__init__() 274 self.messages = [] 275 def reset(self): 276 self.messages = [] 277 def emit(self, record): 278 self.messages.append(record) 279 mock_handler = MockHandler() 280 281 logger = logging.getLogger("testlogger") 282 logger.addHandler(mock_handler) 283 logger.setLevel(logging.DEBUG) 284 285 policy = HttpLoggingPolicy(logger=logger) 286 287 universal_request = http_request('GET', 'http://localhost/') 288 mock = Mock() 289 mock.__class__ = types.GeneratorType 290 universal_request.body = mock 291 http_response = create_http_response(http_response, universal_request, None) 292 http_response.status_code = 202 293 request = PipelineRequest(universal_request, PipelineContext(None)) 294 295 policy.on_request(request) 296 response = PipelineResponse(request, http_response, request.context) 297 policy.on_response(request, response) 298 299 assert all(m.levelname == 'INFO' for m in mock_handler.messages) 300 assert len(mock_handler.messages) == 2 301 messages_request = mock_handler.messages[0].message.split("/n") 302 messages_response = mock_handler.messages[1].message.split("/n") 303 assert messages_request[0] == "Request URL: 'http://localhost/'" 304 assert messages_request[1] == "Request method: 'GET'" 305 assert messages_request[2] == 'Request headers:' 306 assert messages_request[3] == 'File upload' 307 assert messages_response[0] == 'Response status: 202' 308 assert messages_response[1] == 'Response headers:' 309 310 mock_handler.reset() 311