betamaxpy / betamax

A VCR imitation designed only for python-requests.
https://betamax.readthedocs.io/en/latest/
Other
565 stars 62 forks source link

Only one cookie is stored if define_cassette_placeholder is used #128

Closed Lukas0907 closed 6 years ago

Lukas0907 commented 7 years ago

Hi,

the following code produces a cassette that does not contain a list for every header in the recorded cassette:

from betamax import Betamax
import requests

with Betamax.configure() as config:
    config.define_cassette_placeholder('<USERNAME>', 'foo')

session = requests.Session()
recorder = Betamax(session)
with recorder.use_cassette('cookie_test'):
    res = session.get('http://httpbin.org/cookies/set?k2=v2&k1=v1')

The cassette:

{
   "recorded_with" : "betamax/0.8.0",
   "http_interactions" : [
      {
         "response" : {
            "headers" : {
               "Server" : "nginx",
               "Content-Type" : "text/html; charset=utf-8",
               "Access-Control-Allow-Credentials" : "true",
               "Content-Length" : "223",
               "Connection" : "keep-alive",
               "Date" : "Tue, 03 Jan 2017 03:15:09 GMT",
               "Set-Cookie" : "k2=v2; Path=/",
               "Access-Control-Allow-Origin" : "*",
               "Location" : "/cookies"
            },
[...]

The result is that Set-Cookie is only included once, i.e. the first cookie is lost. If I remove the cassette placeholder, the cassette looks different:

{
   "http_interactions" : [
      {
         "response" : {
            "headers" : {
               "Location" : [
                  "/cookies"
               ],
               "Content-Length" : [
                  "223"
               ],
               "Date" : [
                  "Tue, 03 Jan 2017 03:16:33 GMT"
               ],
               "Access-Control-Allow-Origin" : [
                  "*"
               ],
               "Server" : [
                  "nginx"
               ],
               "Content-Type" : [
                  "text/html; charset=utf-8"
               ],
               "Access-Control-Allow-Credentials" : [
                  "true"
               ],
               "Connection" : [
                  "keep-alive"
               ],
               "Set-Cookie" : [
                  "k2=v2; Path=/",
                  "k1=v1; Path=/"
               ]
            },
[...]

Cheers,

Lukas

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/40571627-only-one-cookie-is-stored-if-define_cassette_placeholder-is-used?utm_campaign=plugin&utm_content=tracker%2F198445&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F198445&utm_medium=issues&utm_source=github).
Lukas0907 commented 7 years ago

I debugged this a bit and I think I found the problem.

    def replace_in_headers(self, text_to_replace, placeholder):
        for obj in ('request', 'response'):
            headers = self.data[obj]['headers']
            for k, v in list(headers.items()):
                v = util.from_list(v)
                headers[k] = v.replace(text_to_replace, placeholder)

(from cassette/interaction.py)

v = util.from_list(v) returns the first element of the list (if it is a list), or the element itself. This way, we lose all elements in the list except the first one.

sigmavirus24 commented 7 years ago

Well done, @Lukas0907! Would you like to send a PR?

Lukas0907 commented 7 years ago

Thanks! :) Yes, I'm currently testing a fix locally and will send a PR soon.

Lukas0907 commented 7 years ago

I've created a PR #129 which works for me and also added this scenario to existing tests.

hroncok commented 6 years ago

Fixed in #129.