1919import gzip
2020import io
2121import json
22+ import re
2223import ssl
2324import warnings
2425from platform import python_version
3031
3132from elasticsearch import AIOHttpConnection , AsyncElasticsearch , __versionstr__
3233from elasticsearch .compat import reraise_exceptions
33- from elasticsearch .exceptions import ConnectionError
34+ from elasticsearch .exceptions import ConnectionError , NotFoundError
3435
3536pytestmark = pytest .mark .asyncio
3637
@@ -41,7 +42,9 @@ def gzip_decompress(data):
4142
4243
4344class TestAIOHttpConnection :
44- async def _get_mock_connection (self , connection_params = {}, response_body = b"{}" ):
45+ async def _get_mock_connection (
46+ self , connection_params = {}, status_code = 200 , response_body = b"{}"
47+ ):
4548 con = AIOHttpConnection (** connection_params )
4649 await con ._create_aiohttp_session ()
4750
@@ -61,7 +64,7 @@ async def read(self):
6164
6265 dummy_response = DummyResponse ()
6366 dummy_response .headers = CIMultiDict ()
64- dummy_response .status = 200
67+ dummy_response .status = status_code
6568 _dummy_request .call_args = (args , kwargs )
6669 return dummy_response
6770
@@ -313,6 +316,91 @@ async def test_uncompressed_body_logged(self, logger):
313316 assert '> {"example": "body"}' == req [0 ][0 ] % req [0 ][1 :]
314317 assert "< {}" == resp [0 ][0 ] % resp [0 ][1 :]
315318
319+ @patch ("elasticsearch.connection.base.logger" )
320+ async def test_full_url_logged (self , logger ):
321+ conn = await self ._get_mock_connection ()
322+ await conn .perform_request (
323+ "GET" , "/" , params = {"key" : "val" }, body = b'{"example": "body"}'
324+ )
325+
326+ assert logger .info .call_count == 1
327+ assert (
328+ logger .info .call_args_list [0 ][0 ][0 ] % logger .info .call_args_list [0 ][0 ][1 :]
329+ == "GET http://localhost:9200/?key=val [status:200 request:0.000s]"
330+ )
331+
332+ conn = await self ._get_mock_connection (status_code = 404 )
333+ with pytest .raises (NotFoundError ):
334+ await conn .perform_request (
335+ "GET" , "/" , params = {"key" : "val" }, body = b'{"example": "body"}'
336+ )
337+
338+ assert logger .warning .call_count == 1
339+ assert (
340+ logger .warning .call_args_list [0 ][0 ][0 ]
341+ % logger .warning .call_args_list [0 ][0 ][1 :]
342+ == "GET http://localhost:9200/?key=val [status:404 request:0.000s]"
343+ )
344+
345+ @patch ("elasticsearch.connection.base.tracer" )
346+ @patch ("elasticsearch.connection.base.logger" )
347+ async def test_failed_request_logs_and_traces (self , logger , tracer ):
348+ conn = await self ._get_mock_connection (
349+ response_body = b'{"answer": 42}' , status_code = 404
350+ )
351+ with pytest .raises (NotFoundError ):
352+ await conn .perform_request ("GET" , "/" , params = {"param" : 42 }, body = b"{}" )
353+
354+ # trace request
355+ assert 1 == tracer .info .call_count
356+ # trace response
357+ assert 1 == tracer .debug .call_count
358+ # log url and duration
359+ assert 1 == logger .warning .call_count
360+ assert re .match (
361+ r"^GET http://localhost:9200/\?param=42 \[status:404 request:0.[0-9]{3}s\]" ,
362+ logger .warning .call_args [0 ][0 ] % logger .warning .call_args [0 ][1 :],
363+ )
364+
365+ @patch ("elasticsearch.connection.base.tracer" )
366+ @patch ("elasticsearch.connection.base.logger" )
367+ async def test_success_logs_and_traces (self , logger , tracer ):
368+ conn = await self ._get_mock_connection (
369+ response_body = b"""{"answer": "that's it!"}"""
370+ )
371+ await conn .perform_request (
372+ "GET" ,
373+ "/" ,
374+ {"param" : 42 },
375+ """{"question": "what's that?"}""" .encode ("utf-8" ),
376+ )
377+
378+ # trace request
379+ assert 1 == tracer .info .call_count
380+ assert (
381+ """curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/?pretty¶m=42' -d '{\n "question": "what\\ u0027s that?"\n }'"""
382+ == tracer .info .call_args [0 ][0 ] % tracer .info .call_args [0 ][1 :]
383+ )
384+ # trace response
385+ assert 1 == tracer .debug .call_count
386+ assert re .match (
387+ r'#\[200\] \(0.[0-9]{3}s\)\n#{\n# "answer": "that\\u0027s it!"\n#}' ,
388+ tracer .debug .call_args [0 ][0 ] % tracer .debug .call_args [0 ][1 :],
389+ )
390+
391+ # log url and duration
392+ assert 1 == logger .info .call_count
393+ assert re .match (
394+ r"GET http://localhost:9200/\?param=42 \[status:200 request:0.[0-9]{3}s\]" ,
395+ logger .info .call_args [0 ][0 ] % logger .info .call_args [0 ][1 :],
396+ )
397+
398+ # log request body and response
399+ assert 2 == logger .debug .call_count
400+ req , resp = logger .debug .call_args_list
401+ assert '> {"question": "what\' s that?"}' == req [0 ][0 ] % req [0 ][1 :]
402+ assert '< {"answer": "that\' s it!"}' == resp [0 ][0 ] % resp [0 ][1 :]
403+
316404 async def test_surrogatepass_into_bytes (self ):
317405 buf = b"\xe4 \xbd \xa0 \xe5 \xa5 \xbd \xed \xa9 \xaa "
318406 con = await self ._get_mock_connection (response_body = buf )
0 commit comments