kevin1024 / vcrpy

Automatically mock your HTTP interactions to simplify and speed up testing
MIT License
2.65k stars 377 forks source link

Chunked body as an iterator doesn't work when body is a streamed iterator #846

Open sathieu opened 1 week ago

sathieu commented 1 week ago

https://github.com/kevin1024/vcrpy/pull/739 fixed chunked response for most of the usecases, but this is not enough :wink: .

When list(body) is processed, this consumes the iterator.:

https://github.com/kevin1024/vcrpy/blob/86b114f2f569a9914b182718ab62090ad5913ba6/vcr/matchers.py#L91

But this code is called many times, and all but the first call will return an empty list.

This could be worked-around with:

diff --git a/vcr/stubs/__init__.py b/vcr/stubs/__init__.py
index 4d4bb39..c2af59d 100644
--- a/vcr/stubs/__init__.py
+++ b/vcr/stubs/__init__.py
@@ -254,6 +254,9 @@ class VCRConnection:
         """Retrieve the response"""
         # Check to see if the cassette has a response for this request. If so,
         # then return it
+        if self._vcr_request.headers.get('Transfer-Encoding', '') == 'chunked':
+            if not (isinstance(self._vcr_request.body, str) or isinstance(self._vcr_request.body, bytes) or isinstance(self._vcr_request.body, bytearray)):
+                self._vcr_request.body = list(self._vcr_request.body)
         if self.cassette.can_play_response_for(self._vcr_request):
             log.info(f"Playing response for {self._vcr_request} from cassette")
             response = self.cassette.play_response(self._vcr_request)

But this is hackish and probably not the right place ...

sathieu commented 1 week ago

Here is a better patch, for stream objects (BytesIO) too:

diff --git a/vcr/stubs/__init__.py b/vcr/stubs/__init__.py
index 4d4bb39..2ea9fdf 100644
--- a/vcr/stubs/__init__.py
+++ b/vcr/stubs/__init__.py
@@ -254,6 +254,12 @@ class VCRConnection:
         """Retrieve the response"""
         # Check to see if the cassette has a response for this request. If so,
         # then return it
+        if self._vcr_request.headers.get('Transfer-Encoding', '') == 'chunked':
+            if not (isinstance(self._vcr_request.body, str) or isinstance(self._vcr_request.body, bytes) or isinstance(self._vcr_request.body, bytearray)):
+                if hasattr(self._vcr_request.body, 'read'):
+                    self._vcr_request.body = self._vcr_request.body.read()
+                else:
+                    self._vcr_request.body = list(self._vcr_request.body)
         if self.cassette.can_play_response_for(self._vcr_request):
             log.info(f"Playing response for {self._vcr_request} from cassette")
             response = self.cassette.play_response(self._vcr_request)
sathieu commented 1 week ago

Created: https://github.com/kevin1024/vcrpy/pull/847