Code Monkey home page Code Monkey logo

exvcr's People

Contributors

blakewilliams avatar boone avatar ciaran avatar clekstro avatar drapergeek avatar ecarnevale avatar edgurgel avatar enilsen16 avatar frekw avatar frm avatar gmile avatar gullitmiranda avatar javierjulio avatar joe-noh avatar kianmeng avatar marcandre avatar myronmarston avatar nemo157 avatar nurugger07 avatar optikfluffel avatar parroty avatar reisub avatar schnittchen avatar smeevil avatar surik avatar tt avatar vanstee avatar wojtekmach avatar zhyu avatar zoldar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exvcr's Issues

mocks seem to persist outside of use_cassette blocks

Hey thanks for bringing VCR to Elixir!

I noticed that if I have tests that use a VCR cassette that other tests in the module that hit the same URL are being intercepted by the mocks even though they aren't wrapped in use_cassette blocks.

For example https://github.com/adamkittelson/simplex/blob/feature/metadata-credentials/test/simplex_test.exs#L72 and https://github.com/adamkittelson/simplex/blob/feature/metadata-credentials/test/simplex_test.exs#L54 both cause https://github.com/adamkittelson/simplex/blob/feature/metadata-credentials/lib/simplex/config.ex#L50 to be executed. If the test without a cassette happens to be executed after the one with the cassette it will be intercepted by the mock causing the test to fail.

I'm not sure if this is a bug or just the way VCR works. I've worked around it for now by calling :meck.unload/1 after the assertions using the vcr cassette.

JSON serialization => type losses

I had issues using ExVCR because of transformations that have been made on the request / responses to save them. The JSON deserialization made all the strings as Elixir's String (no character list anymore) and my oauth lib (tim/erlang-oauth), expected a character list :-( Do you see where my problem is?

Why not using an Erlang representation instead of JSON? You'll avoid many conversions...
I tried this idea here if you're interested: https://github.com/nicoolas25/exvcr

Thank you for this work. With my minor adjustments it is very usefull!

Stub a new response from an existing HTTP dump/fixture

One of the features I'm missing (that I was using all the time in Ruby and in other languages) is the ability to load a response from an existing HTTP response fixture.

I wrote a super simple helper in a client I'm working on right now that allows you to use an existing fixture as a cassette:

use_cassette :stub, ExvcrUtils.response_fixture("auth/whoami/success.http", [url: "~r/\/v2\/whoami/$"]) do
  # ...
end

This was inspired by how Webmock behaves in Ruby. You can pass a full HTTP response as string to to_return, and it will be parsed.

      stub_request(:post, %r[/v2/#{account_id}/domains$])
          .to_return(" ... ")

rather than having to pass each response component separately (code, headers, body).

Would you be interested in having something similar built into ExVCR? If that's the case, I can provide a PR with the patch.

It could be directly integrated in prepare_stub_record: if the value of options[:response] exists and it is a string, then parse it. I would delegate to the caller the responsibility to load the response (from file, a variable, or somewhere else).

Does it make sense to you?

mix tasks doesn't take into account "cassette_library_dir" config

Hi,

If I set the cassette_library_dir directly into a module or inside a Mix.Config, the new directory is used when running the tests; however, it seems it is disregarded by the "mix" tasks.

I have set "preferred_cli_env:". Do you think I'm missing something or it is a general issue?

Everything seems fine by using the default dir - "fixture/vcr_cassettes". I'm trying to set it globally to "test/fixtures/vcr_cassettes".

Option to remove headers

It looks like filter_sensitive_data will filter header values pretty well, but what about an option to filter out certain headers from the response?

Something like:

ExVCR.Config.remove_response_headers(["Content-Type", "Content-Length", "X-Random-Custom-Header"])

Hackney Converter: Options sanitization fails

When doing a request using hackney I get the following error:

** (FunctionClauseError) no function clause matching in anonymous fn/1 in ExVCR.Adapter.Hackney.Converter.sanitize_options/1
     stacktrace:
       lib/exvcr/adapter/hackney/converter.ex:35: anonymous fn(:with_body) in ExVCR.Adapter.Hackney.Converter.sanitize_options/1
       (elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
       lib/exvcr/adapter/hackney/converter.ex:29: ExVCR.Adapter.Hackney.Converter.request_to_string/1
       lib/exvcr/adapter/hackney/converter.ex:6: ExVCR.Adapter.Hackney.Converter.convert_to_string/2
       lib/exvcr/handler.ex:137: ExVCR.Handler.get_response_from_server/2
       (hackney) :hackney.request(:get, "...", [...], "", [:with_body, {:max_body, 30000000}])

Error is happening because the function sanitize_options/1, here, is expecting a list of tuples, but hackney also accepts single atoms as values.

ExVCR and Pavlov function adapter_method/0 undefined

Hi,

I tried to use ExVCR with Pavlov.
Both works separately but together i always get something like function adapter_method/0 undefined

Here is a code example:

defmodule MyModuleTest do
  use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
  use Pavlov.Case, async: true
  import Pavlov.Syntax.Expect

  describe « .something" do
    context "given valid params » do
      it "returns response body » do
        use_cassette "sucess » do
          response = MyModule.test
          expect response |> not_to_eq %{}
        end
      end
    end
  end
end

Any idea what i'm missing ?

Compiler warnings

I'm seeing the following using elixir 1.4:

warning: variable "adapter_method" does not exist and is being expanded to "adapter_method()", please use parentheses to remove the ambiguity or change the variable name

This happens for all of my tests which use exvcr. I have them setup as follows:

defmodule SomeApp.ModuleTest do
  use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney

  setup_all do
    HTTPoison.start
  end

  test "some test that does an api call" do
    assert something == result_of_call
  end
end

My deps:

  defp deps do
    [{:phoenix, "~> 1.2.1"},
     {:phoenix_pubsub, "~> 1.0"},
     {:phoenix_ecto, "~> 3.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.8"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"},
     # App specific deps
     {:comeonin, "~> 3.0"},
     {:credo, "~> 0.7", only: [:dev]},
     {:csv, "~> 1.4.2"},
     {:dialyxir, "~> 0.3.5", only: :dev},
     {:distillery, "~> 1.0", runtime: false, warn_missing: false},
     {:edeliver, "~> 1.4.0"},
     {:excoveralls, "~> 0.5", only: :test},
     {:ex_doc, "~> 0.15", only: :dev, runtime: false},
     {:ex_machina, "~> 1.0", only: :test},
     {:exvcr, "~> 0.8", runtime: false},
     {:gen_smtp, "~> 0.11.0"},
     {:guardian, "~> 0.14.2"},
     {:httpoison, "~> 0.11.0"},
     {:timex, "~> 3.0"},
     {:sweet_xml, "~> 0.6.2"},
     {:wallaby, "~> 0.14.0"},
     {:xml_builder, "~> 0.0.6"},
     {:uuid, "~> 1.1"}
    ]
  end

EXVCR and Espec integration

I am having some problems in integrating exvcr with espec.
I did not find a clear doc. on how to integrate and adapting what provided by the documentation
Is ESpec supported as testing framework ?

Cassette is not ejected after the test

Right now this happens:

  1. I have a file with multiple test cases,
  2. each case is wrapped into with use_cassette/2 macro,
  3. bottom-most test case is called X,
  4. I add a new test case Y that performs HTTP request, but I forget to wrap it into a use_cassette/2 macro,
  5. I run the tests,
  6. test case Y fails with a message: "cassette from test case X doesn't contain a response matching a request from Y".

This can be seen in this PR: beam-community/stripity-stripe#80
Here's a failing travis build: https://travis-ci.org/robconery/stripity-stripe/builds/153164601

Ignored_hosts option

The original VCR has an ignore_hosts option, any requests made to those hosts will bypass VCR completely. Is there an equivalent option or workaround that could be used to achieve the same?

Suppress first request or mock a couple of requests from single cassette

Hello,

The situation is that prior to performing request under test library must obtain a token from the service provider (do an OAuth authentication), which basically means that there will always be 2 requests.
Is it possible to ignore this first request or mock it within same cassette?

VCR creates downcased folder names

I use the following naming for my cassettes:

use_cassette "Stripe.PlanTest/count" do

The above means I want to keep count.json inside Stripe.PlanTest folder, which is a name for my test module.

However VCR down cases the name of a folder to: stripe.plantest. Can this be changed to not perform downcase on write?

Add a delay to response?

I'm wondering if there's any ability to add a delay before ExVCR sends its response to the HTTP Client?

I'm trying to track down a bug where I'm expecting a timeout to happen, but the timeout is not occurring -- it would be helpful to replicate this situation using an ExVCR cassette where I could have ExVCR not respond for some configurable amount of time.

I glanced through the ExVCR code and didn't see anywhere obvious where this sort of functionality was already in place, so I'm wondering if adding it would be useful for other people? I also understand trying to trigger timeouts really isn't ExVCR's job, so I'm unsure if adding that feature would be a bad idea, but I thought I might take a crack at it and send a PR otherwise.

Mock multiple identical requests with different responses

My app polls a URL which returns "pending" until it returns "done". The URL is exactly the same each time I poll. Is it possible for exvcr to record the fact that the first request and the last request should send different responses?

Unable to use specific query parameters with custom cassettes

There seems to be an issue with the Regex pattern matching in the handler.ex's matching
in which URLs that contain query paramters aren't able to be used (params after the ? will break the match). Here's an example:

test "handles failure" do
  use_cassette "failure", custom: true do
    case HTTPoison.post("https://myurl.com?auth_token=123abc", "", [], []) do
      {:ok, %HTTPoison.Response{status_code: 204}} ->
        Logger.debug("Success")
      {:error, %HTTPoison.Error{id: _, reason: reason}} ->
        Logger.error("Failure")
    end
  end    
end

failure.json:

[
  {
    "request": {
      "body": "",
      "headers": {
        "Content-Type": "application/json"
      },
      "method": "post",
      "options": {
        "connect_timeout": 5000
      },
      "url": "https://myurl.com?auth_token=123abc"
    },
    "response": {
      "body": "",
      "headers": {
      },
      "status_code": 400,
      "type": "error"
    }
  }
]

A work-around is to use regex in the JSON to ignore the query params, or to simply leave the query params off:
failure.json:

[
  {
    "request": {
      "body": "",
      "headers": {
        "Content-Type": "application/json"
      },
      "method": "post",
      "options": {
        "connect_timeout": 5000
      },
      "url": "https://myurl.com.+"
    },
    "response": {
      "body": "",
      "headers": {
      },
      "status_code": 400,
      "type": "error"
    }
  }
]

But, this can be problematic if you need to ensure the query params are set correctly. Adding a Regex.escape (see below) seems to resolve the issue for this case, but looks like it breaks other pattern matching in URLs within the JSON:

pattern = Regex.compile!("^#{Regex.escape(response[:request].url)}.*$")

ExVCR seems to play back responses just recorded during a recording session

I'm working on a test for an API that is stateful and does not always return the same response for the same request. The code I am testing makes the same request multiple times expecting different responses because the API behaves that way, but ExVCR is getting in the way. When the 2nd request happens, it appears to playback the response for the 1st request, even though the cassette does not even exist when I run my test--at least, my test is failing due to getting an identical response as was received on the first request, and the recorded cassette contains only one interaction.

To provide a more concrete example, think about an banking API that provides /current-balance and /send-payment endpoints. One can imagine writing a test that does this sequence:

  1. Calls /current-balance and gets the current balance.
  2. Calls /send-payment with n dollars.
  3. Calls /current-balance and asserts the new balance is original_balance - n.

Can ExVCR be fixed so that no playback happens when the cassette did not exist at the point the test began, no playback occurs? Given that APIs can be stateful it is often necessary to make the same request multiple times as part of a test, expecting different responses.

Error String.Chars not implemented for :multipart in :hackney

I have a somewhat complicated HTTPoision i.e :hackney POST i'm doing:

HTTPoison.post!("/photos", {:multipart, [{:file, "path/to/file", { ["form-data"], [name: "\"photo\"", filename: "\"path/to/file\""]},[]}]}, [], [recv_timeout: 30000])

and this works when I run the test without VCR, but when I run through VCR I get this error:

** (Protocol.UndefinedError) protocol String.Chars not implemented for {:multipart, [{:file, "/path/to/file", {["form-data"], [name: "\"photo\"", filename: "\"/path/to/file\""]}, []}]}
     stacktrace:
       (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
       (elixir) lib/string/chars.ex:17: String.Chars.to_string/1
       lib/exvcr/adapter/hackney/converter.ex:6: ExVCR.Adapter.Hackney.Converter."parse_request_body (overridable 1)"/1
       lib/exvcr/adapter/hackney.ex:40: ExVCR.Adapter.Hackney.generate_keys_for_request/1
       lib/exvcr/handler.ex:22: ExVCR.Handler.get_response_from_cache/2
       lib/exvcr/handler.ex:13: ExVCR.Handler.get_response/2
       (hackney) :hackney.request(:post, ...}, [recv_timeout: 30000])
       (httpoison) lib/httpoison/base.ex:394: HTTPoison.Base.request/9
       ...

Without knowing much of the code, maybe instead of to_string we use IO.inspect here https://github.com/parroty/exvcr/blob/master/lib/exvcr/adapter/hackney/converter.ex#L61

cassettes with multiple requests to URLs that only differ by query params don't seem to match on query params

I have a cassette with two request-response pairs, and the two requests have similar URLs (the first request doesn't contain query params, the second does), however, exvcr seems to always match the first request.

So, something like this in my cassette's json:

{
  "request": {
    "url": "http://test"
  },
  "response": {
    "body": "no url params"
  }
},
{
  "request": {
    "url": "http://test?param=value
  },
  "response": {
    "body": "got url params"
  }
}

When running the test, the response body for two subsequent calls is
http://test -> no url params
http://test?param=value -> no url params

Match requests on query params and request body by default

It seems to me that use_cassette should default to using the options match_requests_on: [:query, :request_body]. If you are submitting different data, it's likely that the server will respond differently. exvcr implicitly hides those different responses, which is both unexpected and likely to lead to false test coverage.

I'm suggesting that the options for match_requests_on: [:query, :request_body] should be opt-out instead of opt-in. At the very least, there should be a global config option to override this behavior, but I'd rather push people in the right direction by default.

I'm happy to write some code for this, but I wanted to get your response before starting anything.

Stubbing POST?

I've been using the use_cassette :stub, [status_code: 500] option for a while and it works great for GET requests. When I moved to using a POST however, the stub doesn't seem to be catching anything.

Things I've tried:

  • Specifying the URL
  • Specifying the post method as "POST" or "post"
  • Upgrading to the newest version of VCR.

I'm using the Hackney adapter and the request is going through HTTPoison. Any ideas would be really helpful. Thanks!

Provide ability to set vcr and custom folders via `config.exs`

I want to set a custom folder for the cassettes and can do this in the setup_all macro in my tests which is fine, but if I want to run the mix tasks I need to supply my custom dirs every time I run it - even though it is something that never changes.

Is it possible to set the directories in my config.exs file? That way I can create a test.exs that gets included from config.exs when in the test env and it is available for both the mix tasks and the test runner.

Thanks.

Inconsistent behavior when using ExVCR to test logic that makes HTTP requests in spawned processes

We've been using ExVCR for a while and for the most part it's working well for us. However, I've spent a few hours using it in a certain situation where it's not working correctly at all and is producing inconsistent results. Here's our setup:

  • We have an umbrella app with multiple child applications including barbosa_client (a simple HTTP client for an internal service using httpoison) and api (a phoenix app).
  • ExVCR works just fine in the tests in barbosa_client
  • In api, we have a phoenix channel that:
    • Receives a get_data event
    • Spawns 4 linked processes that each get part of the data from a different backend data source (although only one of these uses HTTP: the one that uses barbosa_client. The others read data off the file system or from a SQL DB)
    • The 4 linked processes send the fetched data back to the phoenix channel process, which then sends the data over a socket back to the client

I've tried to use ExVCR in the test of the phoenix channel in api and it's not working right at all. Specifically, the results are inconsistent -- I ran it a bunch of times to get it to record the interaction and I got several different results. Eventually, I got it to record, but now it plays back inconsistently. Occasionally (less than 10%) of the time, but the rest of the time it gets one of a few different failures.

Here are the different failures I see (both when recording and when trying to play back). Most common is this:

  1) test sends the expected responses (and only the expected responses) when it receives params (DeloreanAPI.KeywordAnalysisChannelAcceptanceTest)
     apps/api/test/acceptance/keyword_analysis_channel_test.exs:27
     ** (EXIT from #PID<0.14215.0>) killed

Some process exited, but it provides no detail to understand what happen, unfortunately. I occasionally see this:

     ** (EXIT from #PID<0.1154.0>) an exception was raised:
         ** (RuntimeError) {:error, "BarbosaClient.difficulty_for(\"rspec before\", \"google.en-US\") post error (reason: %HTTPoison.Error{id: nil, reason: :req_not_found}); url: http://[REDACTED]/barbosa-internal-api/0.0.1/keyword/difficulty, request_body: %{engine: \"google\", keyword: \"rspec before\", locale: \"en-US\"}, headers: [{\"Content-type\", \"application/json\"}]"}
             (rankings_endpoint_models) lib/keyword_analysis/keyword_difficulty.ex:12: Delorean.RankingsEndpointModels.KeywordAnalysis.KeywordDifficulty.get/1
             (api) web/channels/keyword_analysis_channel.ex:120: anonymous fn/5 in DeloreanAPI.KeywordAnalysisChannel.announce_and_start_with_params/3

Once I get this head-scratcher:

         ** (ExVCR.RequestNotMatchError) Request did not match with any one in the current cassette: /Users/myron/moz/delorean/vcr_cassettes/recorded/link_opportunities_acceptance_test.json.
     Delete the current cassette with [mix vcr.delete] and re-record.

             lib/exvcr/handler.ex:127: ExVCR.Handler.raise_error_if_cassette_already_exists/1
             lib/exvcr/handler.ex:111: ExVCR.Handler.get_response_from_server/2
             (hackney) :hackney.request(:post, "http://staging.roger.dal.moz.com/barbosa-internal-api/0.0.1/keyword/difficulty", [{"Content-type", "application/json"}], "{\"locale\":\"en-US\",\"keyword\":\"rspec before\",\"engine\":\"google\"}", [])
             (httpoison) lib/httpoison/base.ex:396: HTTPoison.Base.request/9
             (rest_client) lib/rest_client.ex:33: anonymous fn/5 in Delorean.RestClient.post/5
             (stdlib) timer.erl:166: :timer.tc/1
             (util) lib/monitor.ex:42: Delorean.Util.Monitor.track/1
             (util) lib/monitor.ex:32: Delorean.Util.Monitor.perform_action_and_log/2
             (barbosa_client) lib/barbosa_client.ex:25: Delorean.BarbosaClient.difficulty_for/3
             (rankings_endpoint_models) lib/keyword_analysis/keyword_difficulty.ex:8: Delorean.RankingsEndpointModels.KeywordAnalysis.KeywordDifficulty.get/1
             (api) web/channels/keyword_analysis_channel.ex:120: anonymous fn/5 in DeloreanAPI.KeywordAnalysisChannel.announce_and_start_with_params/3

What's odd about this is that link_opportunities_acceptance_test.json is not used in this test -- it's used in a completely different test in a completely different file.

I've confirmed that we have async: false in all of our tests that use ExVCR so it can't be async tests at fault (also, adding --max-cases 1 to prevent any async tests resulted in the same inconsistent weirdness).

I've tried isolating this to a simple reproducible example I can provide for you but haven't yet gotten that (sorry), but I could perhaps spend more time on that if you really need it.

I've been assuming the parallel spawned processes in the channel are at fault but (a) only one of them makes an HTTP request so there are no parallel requests happening and (b) when I move the request into a spawned process in other tests where ExVCR is working it keep working just fine...so that may be a red herring.

I did a bit of looking around the ExVCR source and noticed some places where I'd expect there to be race conditions if ExVCR was used for a test of code that makes parallel requests. I'm not sure if these are related or not, but thought I'd mention them all the same:

  • ExVCR.Actor.Responses.pop/1: between the get and the set call the GenServer could respond to another message, right? I think this could be fixed by moving the logic behind a defcall so that it all happens within the GenServer, ensuring it happens synchronously.
  • ExVCR.Checker.add_count/2: Ditto here: the genserver state could change between get and append and I think this logic should happen within the GenServer instead of in the client process.
  • ExVCR.Recorder.update/3: There are multiple get gen sever calls in the map followed by a set at the end and again, the genserver state could change in the meantime. (Also, if the updater or finder functions use any GenServers they could lead to race conditions as well).

To prevent race conditions, I'd expect any operation that needs to be treated atomically to happen within a single GenServer.

Thanks!

Filter request headers

Hi,

I did find an option to filter blacklisted response headers, but I could not find any option to apply the same for request headers. For example when you have api tokens in the request headers, it would be nice to be able to filter those out of the cassette as well.

Gerard.

Interaction with Hound

I'm building a web scraper using Hound, and it works fine, but when trying to test it using exvcr to mock the responses the responses are pretty much empty (at least no interesting data). I'm not sure if this is an exvcr or hound issue, or even if it is an issue with the way I have them set up, but hopefully you can help me.

Here's the code for the files I think are relevant. On the cassette, the important request is on line 68.

Erro with Meck

I have tried to use but I have had this error.

  1) test post events (Keenex.Events.Test)
     test/keenex/events_test.exs:12
     ** (CaseClauseError) no case clause matching: {:error, {:undef, [{:cover, :is_compiled, [:ibrowse], []}, {:meck_proc, :get_cover_state, 1, [file: 'src/meck_proc.erl', line: 392]}, {:meck_proc, :backup_original, 3, [file: 'src/meck_proc.erl', line: 347]}, {:meck_proc, :init, 1, [file: 'src/meck_proc.erl', line: 202]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 328]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 239]}]}}
     stacktrace:
       src/meck_proc.erl:111: :meck_proc.set_expect/2
       src/meck.erl:227: :meck.expect/3
       (elixir) lib/enum.ex:537: Enum."-each/2-lists^foreach/1-0-"/2
       (elixir) lib/enum.ex:537: Enum.each/2
       test/keenex/events_test.exs:13

This project tests also gave the same problem.

no function clause matching in ExVCR.Adapter.Httpc.apply_filters/1

Hi, while trying to use exvcr with my pet project and ExTwitter I get this:

19:53:43.564 [error] Process #PID<0.197.0> raised an exception
** (FunctionClauseError) no function clause matching in ExVCR.Adapter.Httpc.apply_filters/1
    lib/exvcr/adapter/httpc.ex:56: ExVCR.Adapter.Httpc.apply_filters({:ok, #Reference<0.0.2.254>})
    lib/exvcr/handler.ex:136: ExVCR.Handler.get_response_from_server/2
    (inets) :httpc.request(:post, {'https://stream.twitter.com/1.1/statuses/filter.json', [], 'application/x-www-form-urlencoded', 'oauth_signature=...'}, [autoredirect: false], [sync: false, stream: :self])
    (extwitter) lib/extwitter/api/streaming.ex:65: anonymous fn/3 in ExTwitter.API.Streaming.spawn_async_request/1

What am I doing wrong? If you need more details please let me know, thanks!

Matching on request body

Just opening an issue to start discussion of this feature. Is it something you've thought of? Would you be open to a PR?

filter_sensitive_data in query params

First off: great library, thank you.

I find myself wanting to filter out sensitive data in the query params of a url I'm recording AND still use match_requests_on: [:query]. So basically I want to match on URL but substitute false values for real ones. So I could match a recording with url http://example.com?api_key=FAKE_KEY&page=1 when fetching url http://example.com?api_key=abcdefg&page=1

It would not match a cassette with url http://example.com?api_key=abcdefg&page=SOMETHING_ELSE

Is this feature something you'd adopt? Any advice? I can try to put up a PR, but don't want to waste the time if you wouldn't find it valuable.

ExVCR.RequestNotMatchError - Ignore / bypass the request not matching recorded cassette

This happens when we have multiple requests in the test suite

  ** (EXIT from #PID<0.3049.0>) an exception was raised:
         ** (ExVCR.RequestNotMatchError) Request did not match with any one in the current cassette: test/fixture/vcr_cassettes/response.json.
     Delete the current cassette with [mix vcr.delete] and re-record.

     Request: [:get, "https://example.com", [], "", []]

Its clear from the above exception that, there are two requests. My test suite suppose to catch the second request and match the cassette response.

Would it be possible to do following

Ignore the request response if it does not match the cassette name use_cassette "contentful-cache"

In my case, its clear that the first response would not match the my cassette response at all.... in this case, if I could do bypass the request until I find the request that matches my recorded response then we would not have inconsistent behaviour with cassettes..

I have seen multiple issues raised here

#96
#81
#53

When we run the tests independently, they work fine.
Ex: mix test test/model_test.exs

However, when we run the entire test suite
Ex: mix test

we would have this issue with multiple requests not matching the cassettes.

Licensing

Hi,

would it be possible to add an MIT or other license to this project?

Thanks!

Bypass recorded responses

We like VCR, and would like to use it in our test suite. We have a suite, that is full of use_cassette calls

Most of the time we are happy with out test suite hitting recorded response. However, right before deploy, we'd like our requests to hit a live remote service. Unfortunately, it means that we need to remove a folder with all cassettes every time we actually need to hit external service.

What we'd like to have is a configurable ability to bypass cassettes.

Options to implement this include:

  1. make use_cassette include a flag, say skip: true, to skip or not skip the cassette. We then could make something like:

    use_cassette "path/to/my_cassette", skip: System.get_env(:RUN_LIVE_REQUESTS) do
    # ...
    end
  2. same as in item 1, only make this configurable at a higher level, e.g. in config :exvcr, skip_cassettes: true,

  3. have implemented both items 1 and 2, with setting for 1 overriding setting from 2.

Requests to the same endpoint but with different params are treated as the same endpoint

I'm not sure if it's 100% precisely the issue, but most likely the requests to the same endpoint having different query params are considered as the same one and cache from the first response is reused for each of them.

In one of the projects I was recording cassettes for autopagination based on the links returned in headers. So I was starting with http://example.com/bookings url and for the next page http://example.com/bookings?page=2 link was returned. However, another request wasn't performed to /bookings?page=2 but the response for /bookings was reused, causing the infinite recursion (because http://example.com/bookings?page=2 was always returned as the link to the next page).

I'm investigating the issue closer, but I think the problem is in ExVCR.Handler.get_response_from_cache.

[Q] Tests run in iex session

Probably a stupid-simple thing I've missed, but can't get the tests to stop running in the iex -S mix session every time I save a file… :)

Running mix test.watch in another terminal, but this occurs even when just running iex.

Here's the mix.exs file:

defmodule MyApp.Mixfile do
  use Mix.Project

  def project do
    [
      app: :my_app,
      version: "0.1.0",
      elixir: "~> 1.4.0",
      build_embedded: Mix.env == :production,
      start_permanent: Mix.env == :production,
      deps: deps(),
      preferred_cli_env: [
        vcr: :test, "vcr.delete": :test, "vcr.check": :test, "vcr.show": :test
      ],
    ]
  end

  defp deps do
    [
      ...
      {:httpoison, "~> 0.11.0"},
    ]
    ++ devdeps()
    ++ testdeps()
  end

  defp devdeps do
    [
      ...
      {:mix_test_watch, "~> 0.2", only: :dev},
    ]
  end

  defp testdeps do
    [
      ...
      {:exvcr, "~> 0.8", only: [:ci, :test], runtime: false},
    ]
  end
end

Let me know if someone need to see the config stuff as well.

Removing exvcr stops this behaviour.

Add ignore url/host to config

Hi,

We're using ExVCR and we're very happy with it but sometimes we have an issue with some requests that we don't want to be intercepted.

We've looked at the README thoroughly but haven't find any clue about how to do this. We've also looked at the doc and still no clue...

Would you be interested in adding such a functionnality to ExVCR?

I guess something like an attribute that we can add in the config like an array of strings or regexes:

config :exvcr, [
  # ...
  ignore_urls: ["https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz", ~r(^https://\w+.iana.org/.*)]
]

We can of course do a pull request if you're ok with it, we just want to check before we invest some hours of dev. 😉

Cheers!

ExVCR.Adapter.Hackney doesn't work with head requests

It seems that ExVCR.Adapter.Hackney doesn't work with head requests.

If I use HTTPoison.head(url, headers, options) inside of use_cassette block, the relevant test fails with something like:

** (FunctionClauseError) no function clause matching in ExVCR.Adapter.Hackney.apply_filters/1
     stacktrace:
       lib/exvcr/adapter/hackney.ex:54: ExVCR.Adapter.Hackney.apply_filters({:ok, 301, [{"Server", "nginx"}, {"Date", "Thu, 24 Nov 2016 16:19:29 GMT"}, {"Content-Type", "text/html; charset=UTF-8"}, {"Connection", "keep-alive"}, {"Location", "https://dribbble.com/jobs/11990?source=feed"}, {"X-Cache", "MISS"}]})
       lib/exvcr/handler.ex:136: ExVCR.Handler.get_response_from_server/2
       (hackney) :hackney.request(:head, "https://somesite.com/some-path", [], "", [:insecure])
       lib/httpoison/base.ex:422: HTTPoison.Base.request/9

Any ideas how this could be fixed/patched? I am presuming that should be something simple, as long as one knows the internals...

Return value from test in use_cassette macros

Thanks for the great library!

It'd be really useful to be able to return the value from the body of the use_cassette macro, e.g.:

defmacro use_cassette(:stub, options, test) do
  quote do
    stub_fixture = "stub_fixture_#{ExVCR.Util.uniq_id}"
    stub = prepare_stub_record(unquote(options), adapter_method)
    recorder = Recorder.start([fixture: stub_fixture, stub: stub, adapter: adapter_method])

    mock_methods(recorder, adapter_method)

    try do
      value = unquote(test)
      if options_method[:clear_mock] || unquote(options)[:clear_mock] do
        :meck.unload(adapter_method.module_name)
      end
      value
    after
      # do nothing
    end
  end
end

If this seems reasonable, I'll submit a patch.

Argument Error for JSX

1) test fetch lead (ExCloseioTest)
     test/ex_closeio_test.exs:11
     ** (ArgumentError) argument error
     stacktrace:
       lib/jsx.ex:154: JSX.Encoder.Tuple.json/1
       lib/jsx.ex:121: JSX.Encoder.List.unzip/1
       lib/jsx.ex:114: JSX.Encoder.List.json/1
       lib/jsx.ex:105: JSX.Encoder.Map.unpack/2
       lib/jsx.ex:105: JSX.Encoder.Map.unpack/2
       lib/jsx.ex:101: JSX.Encoder.Map.json/1
       lib/jsx.ex:105: JSX.Encoder.Map.unpack/2
       lib/jsx.ex:101: JSX.Encoder.Map.json/1
       lib/jsx.ex:126: JSX.Encoder.List.unhitch/1
       lib/jsx.ex:117: JSX.Encoder.List.json/1
       lib/jsx.ex:4: JSX.encode!/2
       lib/exvcr/json.ex:10: ExVCR.JSON.save/2
       test/ex_closeio_test.exs:15

Finished in 2.9 seconds (0.2s on load, 2.7s on tests)
1 test, 1 failure

I'm not entirely sure what the problem is here. I'm new to Elixir so I haven't really figured out a good way to debug errors that occur in the dependency chain.

I'm use HTTPoison to make the requests, they seems to working fine and returning correct responses, it looks like the error is occuring when ExVCR tries to record something.

request_body doesn't match the body of the request when :stub

I'm having hard time trying to define a stub for a call that has a body payload.

You can see the tests here
https://github.com/weppos/dnsimple-elixir/blob/test-create/test/dnsimple_domains_service_test.exs#L32-L51

It looks like the matcher doesn't properly match the body. Take for example this code fragment

    fixture = ExvcrUtils.response_fixture("createDomain/created.http", [url: "~r/\/v2/$", request_body: ""])
    use_cassette :stub, fixture do
      { :ok, response } = @service.create_domain(@client, "1010", "")
      assert response.__struct__ == Dnsimple.Response

the error message is

  1) test .create_domains returns a Dnsimple.Response (DnsimpleDomainsServiceTest)
     test/dnsimple_domains_service_test.exs:39
     ** (ExVCR.InvalidRequestError) response for [URL:https://api.dnsimple.test/v2/1010/domains, METHOD:post] was not found
     stacktrace:
       lib/exvcr/handler.ex:28: ExVCR.Handler.get_response_from_cache/2
       lib/exvcr/handler.ex:13: ExVCR.Handler.get_response/2
       (hackney) :hackney.request(:post, "https://api.dnsimple.test/v2/1010/domains", [{"Accept", "application/json"}, {"User-Agent", "dnsimple-elixir/0.0.1"}, {"Authorization", "Bearer i-am-a-token"}], "", [])
       (httpoison) lib/httpoison/base.ex:394: HTTPoison.Base.request/9
       (httpoison) lib/httpoison.ex:66: HTTPoison.request!/5
       (dnsimple) lib/dnsimple.ex:123: Dnsimple.Client.execute/6
       (dnsimple) lib/dnsimple/domains_service.ex:32: Dnsimple.DomainsService.create_domain/5
       test/dnsimple_domains_service_test.exs:42

It doesn't make sense at all as the request_body: "" is there. This is actually a simplification of the real test, as I need to check the body matches the JSON-serialized version of the passed payload, but since I could not make it work I'm now trying with the simplest body possible (an empty string).

I also have one more question. Why is the request_body mandatory when I use the stub: stubbing mode? This is very counter-intuitive, as it doesn't work like that with cassettes.

  defp match_by_request_body(response, params, options) do
    if options[:stub] != nil || has_match_requests_on(:request_body, options) do
      (response[:request].body || response[:request].request_body) ==
        params[:request_body] |> to_string
    else
      true
    end
  end

I suggest to remove options[:stub] != nil and just run the match when an explicit request_body parameter is set.

Ignoring not matching urls for cassettes

When using HTTPoison with :hackney, I got the error of response for [URL:#{params[:url]}, METHOD:#{params[:method]}] was not found. The requests are not the same one as recorded in the cassettes so I guess that's the reason it's not working as expected.

So I propose when using ExVCR mockups, we have a switch as bypass. Just like custom, when set to true, it will ignore all the requests that are not specified in the recorded cassettes. Here's an example:

  use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
  ...hooks and other test cases...
  test "it will bypass if the cassette did not match the request" do
    use_cassette "no_match", custom: true, bypass: true do
         HTTPoison.start
         assert HTTPoison.get("http://not_match_cassette.com").status_code == 200
     end
  end

By combining the custom switch and the bypass switch, we now have the request to be passed directly to the server instead of using a pre-recorded one.

See PR #97 for details. Let me know if you have any question or concern regarding to it. Any discussion is welcome.

ibrowse example is not working

Hi I have tried to setup exvcr and use the example to ensure everything is working but I keep getting the following error:

** (ErlangError) erlang error: {:not_mocked, :ibrowse}
     stacktrace:
       src/meck_proc.erl:118: :meck_proc.set_expect/2
       src/meck.erl:234: :meck.expect/3
       (elixir) lib/enum.ex:651: Enum."-each/2-lists^foreach/1-0-"/2
       (elixir) lib/enum.ex:651: Enum.each/2
       test/lib/mail_chimp_test.exs:46: (test)

my code is as follows (the example provided by exvcr):

defmodule ExtraTurn.MailChimpTest do
  use ExUnit.Case, async: false
  use ExVCR.Mock

  setup_all do
    ExVCR.Config.cassette_library_dir("fixture/vcr_cassettes")
    :ok
  end

  test "example single request" do
    use_cassette "example_ibrowse" do
      :ibrowse.start
      {:ok, status_code, _headers, body} = :ibrowse.send_req('http://example.com', [], :get)
      assert status_code == '200'
      assert to_string(body) =~ ~r/Example Domain/
    end
  end

  test "httpotion" do
    use_cassette "example_httpotion" do
      HTTPotion.start
      assert HTTPotion.get("http://example.com", []).body =~ ~r/Example Domain/
    end
  end
end

I am using the latest elixir version, 1.3.4:

Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.3.4

and using Phoenix 1.2.1 (but I don't think that will affect anything).

I have tried using both the latest version on hex.pm and the latest github version.

Any help would be great!

Doesn't mock requests......

defmodule AbrPayments.Api.V1.PaymentsControllerTest do
  use AbrPayments.ConnCase
  use ExUnit.Case, async: false
  use ExVCR.Mock, adapter: ExVCR.Adapter.Httpc

  setup_all do
    AbrPayments.Abr.Http.start
    ExVCR.Config.cassette_library_dir("fixture/vcr_cassettes", "fixture/custom_cassettes")
    :ok
  end

  test "returns 401 if don't send token" do
    conn = conn
            |> get(payments_path(conn, :create))
            |> doc
    assert conn.status == 401
  end

  test "return 422 if accounts in form not user account" do
    use_cassette :stub, [url: "http://localhost:3000/api/v1/accounts/for_payments", body: "Stub Response"] do
      {:ok, body} = HTTPoison.get!("http://localhost:3000/api/v1/accounts/for_payments", [])
      IO.inspect(body)
    end
  end
test "stub request works for HTTPoison" do
  use_cassette :stub, [url: "http://www.example.com", body: "Stub Response"] do
    response = HTTPoison.get!("http://www.example.com")
    assert response.body =~ ~r/Stub Response/
    assert response.headers["Content-Type"] == "text/html"
    assert response.status_code == 200
  end
end
end

Running `mix vcr` without custom cassette folder gives annoying message

If you do not create the custom_cassette folder in the same level as your cassete folder, running mix vcr will print an annoying message like this in the end:

** (ExVCR.PathNotFoundError) Specified path 'fixture/custom_cassettes' for reading cassettes was not found.
    lib/exvcr/task/runner.ex:34: ExVCR.Task.Runner.find_json_files/1
    lib/exvcr/task/runner.ex:23: ExVCR.Task.Runner.read_cassettes/1
    lib/exvcr/task/runner.ex:17: anonymous fn/1 in ExVCR.Task.Runner.show_vcr_cassettes/1
    (elixir) lib/enum.ex:604: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:604: Enum.each/2
    (mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2

My only configuration for exvcr is ExVCR.Config.cassette_library_dir("fixture/vcr_cassettes").

IMHO, if the custom cassette folder is not found it should just be ignored.

Compression problems / problems with gzip

The following test fails

  test "test use_casette with HTTPotion and gzip compression" do
    use_cassette "api_stackexchange_com" do
      HTTPotion.start
      response = :zlib.gunzip(HTTPotion.get("https://api.stackexchange.com/2.2").body)
      assert response =~ "bad_parameter"
    end
  end

with the following error

  1) test test use_casette with HTTPotion and gzip compression (DevQuotes.DataSourceControllerTest)
     test/controllers/data_source_controller_test.exs:36
     ** (ErlangError) erlang error: :data_error
     stacktrace:
       :zlib.call/3
       :zlib.inflate/2
       :zlib.gunzip/1
       lib/exvcr/json.ex:24: ExVCR.JSON.gunzip_recording/1
       (elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
       lib/exvcr/json.ex:11: ExVCR.JSON.save/2
       test/controllers/data_source_controller_test.exs:37: (test)

Any idea on how to fix this?

Annoying logs

Is there a way to turn off these logs?

11:47:30.279 [error] Task #PID<0.2497.0> started from #PID<0.2495.0> terminating
** (ExVCR.RequestNotMatchError) Request did not match with any one in the current cassette: test/fixture/vcr_cassettes/geo_go_to_service.json.
Delete the current cassette with [mix vcr.delete] and re-record.

Request: [:post, "https://hooks.slack.com/services/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", [], {:form, [payload: "{\"text\":\"La tarea con ID 39310 esta en el estado arrived\\n\",\"channel\":\"#test_services\"}"]}, []]

    lib/exvcr/handler.ex:155: ExVCR.Handler.raise_error_if_cassette_already_exists/2
    lib/exvcr/handler.ex:137: ExVCR.Handler.get_response_from_server/2
    (hackney) :hackney.request(:post, "https://hooks.slack.com/services/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", [], {:form, [payload: "{\"text\":\"La tarea con ID 39310 esta en el estado arrived\\n\",\"channel\":\"#test_services\"}"]}, [])
    (httpoison) lib/httpoison/base.ex:402: HTTPoison.Base.request/9
    (elixir) lib/task/supervised.ex:85: Task.Supervised.do_apply/2
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Function: &HTTPoison.post/2
    Args: ["https://hooks.slack.com/services/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", {:form, [payload: "{\"text\":\"La tarea con ID 39310 esta en el estado arrived\\n\",\"channel\":\"#test_services\"}"]}]
11:47:30.280 [error] Task #PID<0.2496.0> started from #PID<0.2495.0> terminating
** (ExVCR.RequestNotMatchError) Request did not match with any one in the current cassette: test/fixture/vcr_cassettes/geo_go_to_service.json.
Delete the current cassette with [mix vcr.delete] and re-record.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.