parroty / exvcr

HTTP request/response recording library for elixir, inspired by VCR.
MIT License
722 stars 132 forks source link

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

Open kpanic opened 8 years ago

kpanic commented 8 years ago

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!

deepakmohanakrishnan07 commented 7 years ago

I am getting the similar error:

    ** (exit) exited in: :gen_server.call(#PID<0.695.0>, :close)
         ** (EXIT) an exception was raised:
             ** (FunctionClauseError) no function clause matching in ExVCR.Adapter.Hackney.apply_filters/1
                 lib/exvcr/adapter/hackney.ex:54: ExVCR.Adapter.Hackney.apply_filters({:ok, #Reference<0.0.7.51>})
                 lib/exvcr/handler.ex:136: ExVCR.Handler.get_response_from_server/2
                 (hackney) :hackney.request(:get, "http://myappserver/service/events", [{"Content-Type", "application/json"}, {"Accept", "text/event-stream"}], "", [:async, {:stream_to, #PID<0.696.0>}, {:recv_timeout, :infinity}])
                 (httpoison) lib/httpoison/base.ex:402: HTTPoison.Base.request/9
                 (myapp_streamer) lib/myapp_streamer/conn.ex:49: MyAppStreamer.Connection.connect/2
                 lib/connection.ex:622: Connection.enter_connect/5
                 (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
     stacktrace:
       (stdlib) gen_server.erl:204: :gen_server.call/2```
deepakmohanakrishnan07 commented 7 years ago

Tried with both ExVCR 0.7 and 0.8.4 and issue still persists..

kpanic commented 7 years ago

I investigated a little bit more and I found: https://github.com/erlang/otp/blob/maint/lib/inets/src/http_client/httpc.erl#L139

%% Description: Sends a HTTP-request. The function can be both
%% syncronus and asynchronous in the later case the function will
%% return {ok, RequestId} and later on a message will be sent to the
%% calling process on the format {http, {RequestId, {StatusLine,
%% Headers, Body}}} or {http, {RequestId, {error, Reason}}}
kpanic commented 7 years ago

I was able to narrow the use case, this fails:

defmodule TwitterReqTest do
  use ExUnit.Case, async: false
  use ExVCR.Mock, adapter: ExVCR.Adapter.Httpc
  doctest TwitterReq

  test "example test" do
    use_cassette "example httpc" do
      :httpc.request(:get, {'http://www.twitter.com', []}, [], [{:sync, false}, {:stream, :self}])

      # NOTE: This will never be reached currently
      receive do
        everything -> IO.inspect everything
      after
        10_000 -> IO.inspect "timeout!"
      end
    end
  end
end

And the exception is:

$ MIX_ENV=test mix vcr.delete --all; mix test
Deleted example_httpc.json.

  1) test example test (TwitterReqTest)
     test/twitter_req_test.exs:6
     ** (FunctionClauseError) no function clause matching in ExVCR.Adapter.Httpc.apply_filters/1
     stacktrace:
       lib/exvcr/adapter/httpc.ex:56: ExVCR.Adapter.Httpc.apply_filters({:ok, #Reference<0.0.3.49>})
       lib/exvcr/handler.ex:136: ExVCR.Handler.get_response_from_server/2
       (inets) :httpc.request(:get, {'http://www.twitter.com', []}, [], [sync: false, stream: :self])
       test/twitter_req_test.exs:8: (test)

Finished in 0.2 seconds
1 test, 1 failure

Then I did a dumb change on exvcr to consume in apply_filters the stream and it records correctly, however it does not replay. Will investigate more. Hope that @parroty could help us! thanks :)

parroty commented 7 years ago

Thanks for the report and investigation (sorry not being responding).

The current ExVCR.Adapter.Httpc hasn't been able to parse the stream response. When [sync: false, stream: :self] is specified for :httpc.request, it returns the id reference to the stream, instead of response body, then ExVCR is failing to parse the result. The id reference is as recorded in the error message like {:ok, #Reference<0.0.3.49>}.

It's one of the reason that tests for ExTwitter stream API (which uses httpc.request) is not using exvcr and doing some tricky mocking as follows.

I'm wondering what would be the good way to mock this type of request response. It may require special treatment and also it becomes different for each adapters.

One quick approach might be to do simple mock response like as in the above extwitter_stream_test.exs though.

rupurt commented 5 years ago

@parroty do you have any plans to implement this or is there some way I can help? I'm running into the same problem.

kpanic commented 5 years ago

@rupurt if it's useful you could grab some initial prototyping work from here https://github.com/parroty/exvcr/pull/85 Unluckily, I have no time in these days and I don't have that use case to cover anymore. Feel free!

rupurt commented 5 years ago

@kpanic no worries. Thanks for the initial library. It's been very useful :)