Coder Social home page Coder Social logo

bookish_spork's People

Contributors

gaynetdinov avatar kianmeng avatar nixxquality avatar rutaka-n avatar tank-bohr avatar vtm9 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

bookish_spork's Issues

По поводу работы

Здравствуйте, Алексей.
Заранее извиняюсь, что пишу в github.
Я Дмитрий. 27 августа на вашу почту отправлял вакансии Ruby on Rails dev.
Хочу узнать вы готовы рассмотреть предложение о работе?

stub_request (and response in turn) should support iodata as a body

With OTP 27 now released I'm going around my codebase and trying to replace jsone with the new stdlib json library.
One difference is that the stdlib json library returns an iodata from json:encode.

Unfortunately, bookish_spork currently doesn't support iodata bodies, leading to uncomfortable use of iolist_to_binary in each response.
It would be nice if the response body supported iodata. Looking at the code, it should be easy, and I'm probably going to submit a PR in a day or two.

Wait response if request is async

:bookish_spork.start_server()
:bookish_spork.stub_request()
Task.async(fn -> some_http_req_to_bookish_spork() end)
:bookish_spork.capture_request() # => %{:error, :no_request}

Need to add ability for capture_request to wait for async requests to complete

Fix closing connection when an asynchronous requests

What I've done

I reproduced this error by adding such a test (examples/chuck_ex/test/chuck_norris_api_test.exs):

   test "retrieves two random jokes in different processes" do
     :bookish_spork.stub_request([200, %{}, "{\"value\": \"First joke.\"}"])
     :bookish_spork.stub_request([200, %{}, "{\"value\": \"Second joke.\"}"])

     Task.start(fn -> ChuckNorrisApi.random() end)
     Task.start(fn -> ChuckNorrisApi.random() end)

     Process.sleep(500)

     {:ok, _request} = :bookish_spork.capture_request()
     {:ok, _request} = :bookish_spork.capture_request()
     {:error, _request} = :bookish_spork.capture_request()
   end

Actual result

I get this error when checking the second capture_request:

  1) test retrieves two random jokes in different processes (ChuckNorrisApiTest)
     test/chuck_norris_api_test.exs:30
     ** (MatchError) no match of right hand side value: {:error, :no_request}
     code: {:ok, _request} = :bookish_spork.capture_request()
     stacktrace:
       test/chuck_norris_api_test.exs:40: (test)

Expected result

If I forcibly add an http header {"Connection", "close"} to an http request, then the test passes.

examples/chuck_ex/lib/chuck_norris_api.ex

   defp request(url) do
     HTTPoison.get!(url, [{"Connection", "close"}]).body |> Poison.decode! |> Map.fetch!("value")
   end

crash

What I've done

bookish_spork:start_server([ssl]),
bookish_spork:stub_request(),
hackney:get("https://localhost:32002", [], <<>>, [{ssl_options, [{verify, verify_none}]}])
bookish_spork:capture_request()

Actual result

2019-08-23T09:06:06.714141+03:00 error: ** State machine <0.954.0> terminating, ** Last event = {{timeout,recv},timeout}, ** When server state  = [{data,[{"State",{connection,{state,{static_env,client,gen_tcp,tls_connection,tcp,tcp_closed,tcp_error,"localhost",32002,#Port<0.100>,#Ref<0.3453212124.1051066371.98471>,#Ref<0.3453212124.1051066371.98479>,ssl_session_cache,{ssl_crl_cache,{{#Ref<0.3453212124.1051066371.98474>,#Ref<0.3453212124.1051066371.98475>},[]}},{#Ref<0.3453212124.1051066371.98472>,#Ref<0.3453212124.1051066371.98473>},#Ref<0.3453212124.1050935299.103344>,undefined},"***",{ssl_options,tls,[{3,3},{3,2},{3,1},{3,0}],verify_none,{#Fun<ssl.8.129595827>,[]},#Fun<ssl.9.129595827>,false,false,undefined,1,<<>>,"***",<<>>,"***","***","***",<<>>,"***",undefined,undefined,"***","***",[<<"À,">>,<<"À0">>,<<"À$">>,<<"À(">>,<<"À\b">>,<<"À.">>,<<"À2">>,<<"À&">>,<<"À*">>,<<0,163>>,<<0,106>>,<<0,157>>,<<0,61>>,<<"À+">>,<<"À/">>,<<"À#">>,<<"À'">>,<<"À-">>,<<"À1">>,<<"À%">>,<<"À)">>,<<0,162>>,<<0,64>>,<<0,156>>,<<0,60>>,<<"À\n">>,<<192,20>>,<<0,56>>,<<192,5>>,<<192,15>>,<<0,53>>,<<"À\t">>,<<192,19>>,<<0,50>>,<<192,4>>,<<192,14>>,<<0,47>>],undefined,true,268435456,true,undefined,infinity,false,undefined,undefined,undefined,undefined,true,"localhost",[],undefined,undefined,true,one_n_minus_one,false,false,{ssl_crl_cache,{internal,[]}},[{sha512,ecdsa},{sha512,rsa},{sha384,ecdsa},{sha384,rsa},{sha256,ecdsa},{sha256,rsa},{sha224,ecdsa},{sha224,rsa},{sha,ecdsa},{sha,rsa},{sha,dsa}],{elliptic_curves,[{1,3,132,0,39},{1,3,132,0,38},{1,3,132,0,35},{1,3,36,3,3,2,8,1,1,13},{1,3,132,0,36},{1,3,132,0,37},{1,3,36,3,3,2,8,1,1,11},{1,3,132,0,34},{1,3,132,0,16},{1,3,132,0,17},{1,3,36,3,3,2,8,1,1,7},{1,3,132,0,10},{1,2,840,10045,3,1,7},{1,3,132,0,3},{1,3,132,0,26},{1,3,132,0,27},{1,3,132,0,32},{1,3,132,0,33},{1,3,132,0,24},{1,3,132,0,25},{1,3,132,0,31},{1,2,840,10045,3,1,1},{1,3,132,0,1},{1,3,132,0,2},{1,3,132,0,15},{1,3,132,0,9},{1,3,132,0,8},{1,3,132,0,30}]},undefined,262144,full,[]},{socket_options,binary,raw,0,0,false},"***","***",false,#{active_n => 100,active_n_toggle => false,sender => <0.953.0>},"***","***","***","***",undefined,undefined}}}]}], ** Reason for termination = error:{bad_action_from_state_function,{reply,undefined,{error,timeout}}}, ** Callback mode = state_functions, ** Stacktrace =, **  [{gen_statem,parse_actions_reply,7,[{file,"gen_statem.erl"},{line,1290}]},{gen_statem,loop_event_actions_list,10,[{file,"gen_statem.erl"},{line,1206}]},{tls_connection,init,1,[{file,"tls_connection.erl"},{line,133}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,249}]}]
2019-08-23T09:06:06.719806+03:00 notice: TLS server: In state connection received CLIENT ALERT: Fatal - Internal Error
2019-08-23T09:06:06.720099+03:00 error: Error in process <0.814.0> with exit value:, {{case_clause,{error,{tls_alert,{internal_error,"received SERVER ALERT: Fatal - Internal Error"}}}},[{bookish_spork_server,read_from_socket,3,[{file,"/Users/xoma/repos/basin/_checkouts/bookish_spork/src/bookish_spork_server.erl"},{line,167}]},{bookish_spork_server,handle_connection,3,[{file,"/Users/xoma/repos/basin/_checkouts/bookish_spork/src/bookish_spork_server.erl"},{line,137}]},{bookish_spork_server,'-accept/2-AcceptorFun/0-0-',2,[{file,"/Users/xoma/repos/basin/_checkouts/bookish_spork/src/bookish_spork_server.erl"},{line,130}]}]}
2019-08-23T09:06:06.728731+03:00 error: crasher: initial call: tls_connection:init/1, pid: <0.954.0>, registered_name: [], error: {{bad_action_from_state_function,{reply,undefined,{error,timeout}}},[{gen_statem,parse_actions_reply,7,[{file,"gen_statem.erl"},{line,1290}]},{gen_statem,loop_event_actions_list,10,[{file,"gen_statem.erl"},{line,1206}]},{tls_connection,init,1,[{file,"tls_connection.erl"},{line,133}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,249}]}]}, ancestors: [tls_connection_sup,ssl_connection_sup,ssl_sup,<0.80.0>], message_queue_len: 0, messages: [], links: [<0.953.0>,<0.86.0>], dictionary: [{ssl_pem_cache,ssl_pem_cache},{ssl_manager,ssl_manager}], trap_exit: true, status: running, heap_size: 10958, stack_size: 27, reductions: 31716; neighbours: neighbour: pid: <0.953.0>, registered_name: [], initial call: tls_sender:init/1, current_function: {gen_statem,loop_receive,3}, ancestors: [<0.949.0>], message_queue_len: 0, links: [<0.954.0>], trap_exit: false, status: waiting, heap_size: 376, stack_size: 12, reductions: 374, current_stacktrace: [{gen_statem,loop_receive,3,[{file,"gen_statem.erl"},{line,880}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,249}]}]
2019-08-23T09:06:06.729595+03:00 error: supervisor: {local,tls_connection_sup}, errorContext: child_terminated, reason: {{bad_action_from_state_function,{reply,undefined,{error,timeout}}},[{gen_statem,parse_actions_reply,7,[{file,"gen_statem.erl"},{line,1290}]},{gen_statem,loop_event_actions_list,10,[{file,"gen_statem.erl"},{line,1206}]},{tls_connection,init,1,[{file,"tls_connection.erl"},{line,133}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,249}]}]}, offender: [{pid,<0.954.0>},{id,undefined},{mfargs,{tls_connection,start_link,undefined}},{restart_type,temporary},{shutdown,4000},{child_type,worker}]

Expected result

  • No error

Environment

bookish_spork 7a384d8 #45

Replace triple nested case with foldl

handle_connection(#state{transport = Transport, socket = Socket, server = Server} = State) ->
case receive_request(State) of
{ok, Request} ->
ok = bookish_spork_server:store_request(Server, Request),
case bookish_spork_server:response(Server) of
{ok, Response} ->
reply(Transport, Socket, Response, Request),
complete_connection(State, Request);
{error, no_response} ->
Transport:close(Socket),
{stop, normal, State}
end;
socket_closed ->
{stop, normal, State}
end.
-spec complete_connection(State, Request) -> {noreply, State} | {stop, normal, State} when
State :: state(),
Request :: bookish_spork_request:t().
%% @private
complete_connection(#state{transport = Transport, socket = Socket} = State, Request) ->
case bookish_spork_request:is_keepalive(Request) of
true ->
handle_connection(),
{noreply, State};
false ->
Transport:shutdown(Socket, read_write),
{stop, normal, State}
end.

See

SSL requests fail if a request body is present

Here's a failing test case:

ssl_post_test(_Config) ->
    ok = bookish_spork:stub_request(),
    RequestBody = <<"{\"name\": \"John Doe\", \"email\": \"[email protected]\"}">>,
    {ok, {{"HTTP/1.1", 204, "No Content"}, _, _}} = httpc:request(post, {
        "https://localhost:32002/secure",
        [{"Connection", "close"}],
        "application/json",
        RequestBody
    }, [], []),
    {ok, _Request} = bookish_spork:capture_request().

Here's the relevant stacktrace:

      current_stacktrace: [{prim_inet,accept0,3,[]},
                  {inet_tcp,accept,1,[{file,"inet_tcp.erl"},{line,173}]},
                  {bookish_spork_transport,accept,1,
                      [{file,
                           "/Users/linus/Code/bookish_spork/src/bookish_spork_transport.erl"},
                       {line,85}]},
                  {bookish_spork_acceptor,accept,2,
                      [{file,
                           "/Users/linus/Code/bookish_spork/src/bookish_spork_acceptor.erl"},
                       {line,64}]},
                  {bookish_spork_acceptor,handle_cast,2,
                      [{file,
                           "/Users/linus/Code/bookish_spork/src/bookish_spork_acceptor.erl"},
                       {line,48}]},

Example of stubbing multiple requests

Hi.

I see from the code that it seems to be possible to stub several requests at once, i.e. when I need to tests multiple pages response. This would require stubbing request according to the request query I think. For example response contains some next marker or url for the next page.
So I'd need to stub it like that

:bookish_spork.stub_request("/", 200, Poison.encode!(first_page_data))
:bookish_spork.stub_request("/?next_page=2", 200, Poison.encode!(second_page_data))

Would it be possible?
If yes, could you please add an example to your example apps how one could use it?

Testing timeouts

Hi, I really like this library and I've been using it for testing my things.

However, I recently ran into a rare issue I'd like to write a test for but I'm not sure how.

I need to test requests that time out. Do you have an idea on how I would do that with this library?

Problems with gun

Hello.

I recently started to use bookish_spork instead of bypass, because bookish_spork does not have cowboy dependency and I wanted to use gun. However I got some problems testing HTTP requests with gun. The problem is that when I stub multiple requests, gun fails to get second response or gets it after ~5 seconds timeout. In order to demonstrate it I've created a test repo for you https://github.com/gaynetdinov/gun_with_spork.

There is HttpClient module which is a simple wrapper around gun (because gun is quite verbose) and there is simple test file with several test cases there.
gun works ok when I create a separate connection per request, but if I want to reuse already established connection (see https://github.com/gaynetdinov/gun_with_spork/blob/master/test/gun_with_spork_test.exs#L28) I either get second response after 4-5 seconds or get the following error

$ mix test

[error] Process #PID<0.210.0> raised an exception
** (CaseClauseError) no case clause matching: {:error, :closed}
    (bookish_spork) /Users/damir/projects/gun_with_spork/deps/bookish_spork/src/bookish_spork_server.erl:95: :bookish_spork_server.receive_request/2
    (bookish_spork) /Users/damir/projects/gun_with_spork/deps/bookish_spork/src/bookish_spork_server.erl:82: anonymous fn/3 in :bookish_spork_server.accept/3

Do you know why this is happening? Or maybe you could suggest some workarounds?
Thank you for your time!

Error evaluating Rebar config script

Hello.

Sorry, it's me again. I just upgraded to 0.2.4 and got this error

Upgraded:
  bookish_spork 0.1.1 => 0.2.4
* Updating bookish_spork (Hex package)
Error evaluating Rebar config script ./rebar.config.script:15: evaluation failed with reason error:undef and stacktrace [{rebar_git_resource,make_vsn,[[46]],[]},{erl_eval,do_apply,6,[{file,[101,114,108,95,101,118,97,108,46,101,114,108]},{line,670}]},{erl_eval,expr,5,[{file,[101,114,108,95,101,118,97,108,46,101,114,108]},{line,438}]},{erl_eval,exprs,5,[{file,[101,114,108,95,101,118,97,108,46,101,114,108]},{line,122}]},{file,eval_stream2,6,[{file,[102,105,108,101,46,101,114,108]},{line,1397}]},{file,script,2,[{file,[102,105,108,101,46,101,114,108]},{line,1097}]},{'Elixir.File','cd!',2,[{file,[108,105,98,47,102,105,108,101,46,101,120]},{line,1277}]},{'Elixir.Mix.Rebar',eval_script,2,[{file,[108,105,98,47,109,105,120,47,114,101,98,97,114,46,101,120]},{line,190}]}]
Any dependencies defined in the script won't be available unless you add them to your Mix project

I believe that this commit 6a83e53 introduced it, because there is no such error with version 0.2.3.

{:error, :no_request} as result

What I've done

setup_all do
    {:ok, []} = HTTPoison.start()
    {:ok, _} = :bookish_spork.start_server()

{:ok, %{}}
end

test "just test" do
	:bookish_spork.stub_request([200, %{}, ""])


	api_endpoint = "http://localhost:32002/api"
	payload = "{\"destinations\":[],\"whatsApp\":{\"text\":\"Do Androids Dream of Electric Sheep?\"}}"
	get_headers = [
	  {"Authorization", "Basic Q1wewewewewewewewewewg=="},
	  {"accept", "application/json"},
	  {"Content-Type", "application/json"}
	]

	http_options = []

	result = HTTPoison.post(api_endpoint(), payload, get_headers(), http_options())

	{:ok, request} = :bookish_spork.capture_request()
end

Actual result

** (MatchError) no match of right hand side value: {:error, :no_request}
     code: {:ok, request} = :bookish_spork.capture_request()

Expected result

No error

Environment

Erlang/OTP 20
elixir (1.8.1)
"bookish_spork": {:hex, :bookish_spork, "0.3.3", "975c93911dfef1e581b477348739ab388eff0e2edf329bc7668e7ecd10568af8", [:rebar3], [], "hexpm"},

Drop OTP-20 support

It will allow to

  • get rid of ugly macros -ifdef(OTP_RELEASE).
  • get rid of unused gen_server callbacks: code_change/3
  • use fancy handle_continue in bookish_spork_acceptor

mix/rebar3 error

What I've done

  • mix deps.update bookish_spork

Actual result

Upgraded:
  bookish_spork 0.3.0 => 0.3.1
* Updating bookish_spork (Hex package)
Error evaluating Rebar config script ./rebar.config.script:30: evaluation failed with reason error:{badmatch,{error,enoent}} and stacktrace [{erl_eval,expr,5,[{file,[101,114,108,95,101,118,97,108,46,101,114,108]},{line,453}]},{erl_eval,exprs,5,[{file,[101,114,108,95,101,118,97,108,46,101,114,108]},{line,126}]},{file,eval_stream2,6,[{file,[102,105,108,101,46,101,114,108]},{line,1393}]},{file,script,2,[{file,[102,105,108,101,46,101,114,108]},{line,1090}]},{'Elixir.File','cd!',2,[{file,[108,105,98,47,102,105,108,101,46,101,120]},{line,1443}]},{'Elixir.Mix.Rebar',eval_script,2,[{file,[108,105,98,47,109,105,120,47,114,101,98,97,114,46,101,120]},{line,195}]},{'Elixir.File','cd!',2,[{file,[108,105,98,47,102,105,108,101,46,101,120]},{line,1443}]}]
Any dependencies defined in the script won't be available unless you add them to your Mix project

with rebar3

===> Error evaluating configuration script at "/bookish_spork/examples/chuck/_build/test/lib/bookish_spork/rebar.config.script":
{error,{30,file,
        {error,{badmatch,{error,enoent}},
               [{erl_eval,expr,5,[{file,"erl_eval.erl"},{line,453}]},
                {erl_eval,exprs,5,[{file,"erl_eval.erl"},{line,126}]},
                {file,eval_stream2,6,[{file,"file.erl"},{line,1393}]},
                {file,script,2,[{file,"file.erl"},{line,1090}]},
                {rebar_config,consult_and_eval,2,
                              [{file,"/opt/rebar3-3.7.5/src/rebar_config.erl"},
                               {line,287}]},
                {rebar_config,consult_file_,1,
                              [{file,"/opt/rebar3-3.7.5/src/rebar_config.erl"},
                               {line,230}]},
                {rebar_config,consult_file,1,
                              [{file,"/opt/rebar3-3.7.5/src/rebar_config.erl"},
                               {line,212}]}]}}}

===> Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace or consult rebar3.crashdump

Expected result

  • No error

Environment

Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

IEx 1.7.4 (compiled with Erlang/OTP 21)
Rebar3 report
 version 3.7.5
 generated at 2019-01-27T09:25:26+00:00
=================
Please submit this along with your issue at https://github.com/erlang/rebar3/issues (and feel free to edit out private information, if any)
-----------------
Task:
Entered as:

-----------------
Operating System: x86_64-apple-darwin17.7.0
ERTS: Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Root Directory: /Users/xoma/kerl/21.2
Library directory: /Users/xoma/kerl/21.2/lib
-----------------
Loaded Applications:
bbmustache: 1.6.0
certifi: 2.3.1
cf: 0.2.2
common_test: 1.16.1
compiler: 7.3
crypto: 4.4
cth_readable: 1.4.2
dialyzer: 3.3.1
edoc: 0.9.4
erlware_commons: 1.3.0
eunit: 2.3.7
eunit_formatters: 0.5.0
getopt: 1.0.1
hex_core: 0.2.0
hipe: 3.18.2
inets: 7.0.3
kernel: 6.2
providers: 1.7.0
public_key: 1.6.4
relx: 3.27.0
sasl: 3.3
snmp: 5.2.12
ssl_verify_fun: 1.1.3
stdlib: 3.7
syntax_tools: 2.1.6
tools: 3.0.2

-----------------
Escript path: /bin/rebar3
Providers:
  app_discovery as auto clean compile compile config cover ct cut deps dialyzer do docs edoc escriptize eunit get-deps help info install install_deps key list lock new owner path pkgs publish release relup report repos search shell state tar tree unlock update upgrade upgrade upgrade user version xref

Allow to return primitive data structures in fun

What I've done

bookish_spork:stub_request(fun(Request) ->
    case bookish_spork_request:uri(Request) of
        "/bookish/spork" ->
            [200, [], <<"Hello">>];
        "/admin/sporks" ->
            [403, [], <<"It is not possible here">>]
    end
end)

Actual result

  • It doesn't work
  • We have to use clumsy syntax
  • too many bookish_spork

Expected result

  • Neat concise clojure-like syntax is allowed

Environment

  • bookish_spork version is 0.2.5

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.