From ec88b7583c0c3a8169edceb95228813fc8246198 Mon Sep 17 00:00:00 2001 From: vk Date: Fri, 17 Dec 2021 15:21:31 +0200 Subject: [PATCH 1/3] Updating * Update cowboy to 2.9.0 * Start use `git` in `vsb` * Start use `gun` instead of `hackney` * Add OTP 21 to CI * Added minimum OTP version * Added GitHub Action badges --- .github/workflows/ci.yaml | 7 +++--- Makefile | 9 ++++++++ README.md | 7 ++++++ rebar.config | 7 +++--- rebar.lock | 45 +++++++++++---------------------------- src/sheep2.app.src | 2 +- src/sheep_try.erl | 35 +++++++++++++++++++++++++----- test/sheep_http_SUITE.erl | 36 ++++++++++++++++++++++++++----- 8 files changed, 98 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c65cf8b..6d38e05 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,8 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - otp: [22.3, 23.3, 24.2] - rebar: [3.17.0] + otp: [21.3, 22.3, 23.3, 24.2] steps: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 @@ -24,4 +23,6 @@ jobs: otp-version: ${{matrix.otp}} rebar3-version: ${{matrix.rebar}} - name: Tests - run: rebar3 test + run: | + make rebar3 + ./rebar3 test diff --git a/Makefile b/Makefile index b437822..0c7c812 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,8 @@ +REBARVER = 3.15.2 +ifeq ($(OTPVER),24.0) + REBARVER = 3.17.0 +endif + compile: rebar3 compile @@ -18,3 +23,7 @@ clean: clean-all: rm -rf _build rm rebar.lock + +rebar3: + wget https://github.com/erlang/rebar3/releases/download/${REBARVER}/rebar3 &&\ + chmod u+x rebar3 diff --git a/README.md b/README.md index 6a1d681..f098759 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Sheep +[![Build Status][gh badge]][gh] +[![Erlang Versions][erlang version badge]][gh] Cowboy protocol and set of utility funs for building JSON/MsgPack APIs @@ -120,3 +122,8 @@ then **GET** and **POST** requests will be processed by list of functions but **PUT** or **DELETE** will get 204 (no content) response. See [pipeline_handler.erl](./test/sheep_http_SUITE_data/pipeline_handler.erl) as example. + + +[gh]: https://github.com/wgnet/sheep2/actions/workflows/ci.yml +[gh badge]: https://img.shields.io/github/workflow/status/wgnet/sheep2/CI?style=flat-square +[erlang version badge]: https://img.shields.io/badge/erlang-21.3%20to%2024.2-blue.svg?style=flat-square diff --git a/rebar.config b/rebar.config index ba95ee3..919cdad 100644 --- a/rebar.config +++ b/rebar.config @@ -1,10 +1,12 @@ +{minimum_otp_vsn, "21"}. + {erl_opts, [warn_missing_spec]}. {deps, [ - {cowboy, "2.7.0"}, + {cowboy, "2.9.0"}, {jiffy, "1.0.8"}, {msgpack, "0.7.0"}, - {hackney, "1.17.4"} + {gun, "2.0.0-rc.2"} ]}. {profiles, [ @@ -15,7 +17,6 @@ ]} ]}. - {project_plugins, [ {rebar3_lint, "~> 1.0.1"} ]}. diff --git a/rebar.lock b/rebar.lock index ea0350a..866af2e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,44 +1,23 @@ {"1.2.0", -[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.6.1">>},1}, - {<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.7.0">>},0}, - {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.8.0">>},1}, - {<<"hackney">>,{pkg,<<"hackney">>,<<"1.17.4">>},0}, - {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},1}, +[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.9.0">>},0}, + {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},1}, + {<<"gun">>,{pkg,<<"gun">>,<<"2.0.0-rc.2">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.0.8">>},0}, - {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1}, - {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},1}, {<<"msgpack">>,{pkg,<<"msgpack">>,<<"0.7.0">>},0}, - {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},1}, - {<<"ranch">>,{pkg,<<"ranch">>,<<"1.7.1">>},1}, - {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1}, - {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}]}. + {<<"ranch">>,{pkg,<<"ranch">>,<<"1.8.0">>},1}]}. [ {pkg_hash,[ - {<<"certifi">>, <<"DBAB8E5E155A0763EEA978C913CA280A6B544BFA115633FA20249C3D396D9493">>}, - {<<"cowboy">>, <<"91ED100138A764355F43316B1D23D7FF6BDB0DE4EA618CB5D8677C93A7A2F115">>}, - {<<"cowlib">>, <<"FD0FF1787DB84AC415B8211573E9A30A3EBE71B5CBFF7F720089972B2319C8A4">>}, - {<<"hackney">>, <<"99DA4674592504D3FB0CFEF0DB84C3BA02B4508BAE2DFF8C0108BAA0D6E0977C">>}, - {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"cowboy">>, <<"865DD8B6607E14CF03282E10E934023A1BD8BE6F6BACF921A7E2A96D800CD452">>}, + {<<"cowlib">>, <<"0B9FF9C346629256C42EBE1EEB769A83C6CB771A6EE5960BD110AB0B9B872063">>}, + {<<"gun">>, <<"7C489A32DEDCCB77B6E82D1F3C5A7DADFBFA004EC14E322CDB5E579C438632D2">>}, {<<"jiffy">>, <<"60E36F00BE35E5AC6E6CF2D4CAF3BDF3103D4460AFF385F543A8D7DF2D6D9613">>}, - {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, - {<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>}, {<<"msgpack">>, <<"128AE0A2227C7E7A2847C0F0F73551C268464F8C1EE96BFFB920BC0A5712B295">>}, - {<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>}, - {<<"ranch">>, <<"6B1FAB51B49196860B733A49C07604465A47BDB78AA10C1C16A3D199F7F8C881">>}, - {<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, + {<<"ranch">>, <<"8C7A100A139FD57F17327B6413E4167AC559FBC04CA7448E9BE9057311597A1D">>}]}, {pkg_hash_ext,[ - {<<"certifi">>, <<"524C97B4991B3849DD5C17A631223896272C6B0AF446778BA4675A1DFF53BB7E">>}, - {<<"cowboy">>, <<"04FD8C6A39EDC6AAA9C26123009200FC61F92A3A94F3178C527B70B767C6E605">>}, - {<<"cowlib">>, <<"79F954A7021B302186A950A32869DBC185523D99D3E44CE430CD1F3289F41ED4">>}, - {<<"hackney">>, <<"DE16FF4996556C8548D512F4DBE22DD58A587BF3332E7FD362430A7EF3986B16">>}, - {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"cowboy">>, <<"2C729F934B4E1AA149AFF882F57C6372C15399A20D54F65C8D67BEF583021BDE">>}, + {<<"cowlib">>, <<"2B3E9DA0B21C4565751A6D4901C20D1B4CC25CBB7FD50D91D2AB6DD287BC86A9">>}, + {<<"gun">>, <<"6B9D1EAE146410D727140DBF8B404B9631302ECC2066D1D12F22097AD7D254FC">>}, {<<"jiffy">>, <<"F9AE986BA5A0854EB48CF6A76192D9367086DA86C20197DA430630BE7C087A4E">>}, - {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, - {<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>}, {<<"msgpack">>, <<"4649353DA003E6F438D105E4B1E0F17757F6F5EC8687A6F30875FF3AC4CE2A51">>}, - {<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>}, - {<<"ranch">>, <<"451D8527787DF716D99DC36162FCA05934915DB0B6141BBDAC2EA8D3C7AFC7D7">>}, - {<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} + {<<"ranch">>, <<"49FBCFD3682FAB1F5D109351B61257676DA1A2FDBE295904176D5E521A2DDFE5">>}]} ]. diff --git a/src/sheep2.app.src b/src/sheep2.app.src index 0c5312b..a00ed89 100644 --- a/src/sheep2.app.src +++ b/src/sheep2.app.src @@ -1,6 +1,6 @@ {application, sheep2, [ {description, "Cowboy protocol and set of utility funs for building JSON/MsgPack APIs"}, - {vsn, "4.3.0"}, + {vsn, git}, {registered, []}, {applications, [ kernel, diff --git a/src/sheep_try.erl b/src/sheep_try.erl index 19d7074..857c89c 100644 --- a/src/sheep_try.erl +++ b/src/sheep_try.erl @@ -10,7 +10,7 @@ run() -> application:set_env(sheep2, log_callback, fun ?MODULE:log/1), application:ensure_all_started(cowboy), - application:ensure_all_started(hackney), + application:ensure_all_started(gun), Routing = cowboy_router:compile([ {"localhost", [ @@ -34,12 +34,37 @@ run() -> -spec query(atom(), string(), list(), binary()) -> {integer(), list(), binary()}. query(Method, Path, Headers, Data) -> - Port = integer_to_list(ranch:get_port(?MODULE)), - FullURL = "http://localhost:" ++ Port ++ Path, - {ok, Status, RHeaders, Ref} = hackney:request(Method, FullURL, Headers, Data), - {ok, Body} = hackney:body(Ref), + Port = ranch:get_port(?MODULE), + {ok, Pid} = gun:open("localhost", Port), + {ok, http} = gun:await_up(Pid), + StreamRef = gun:request(Pid, method(Method), Path, Headers, Data), + Opts = #{ + pid => Pid, + stream_ref => StreamRef, + acc => <<>> + }, + #{status := Status, headers := RHeaders, acc := Body} = get_reponse(Opts), {Status, RHeaders, Body}. +-spec method(atom()) -> binary(). +method(Method) -> + cowboy_bstr:to_upper(erlang:list_to_binary(erlang:atom_to_list(Method))). + +get_reponse(#{pid := Pid, stream_ref := StreamRef, acc := Acc} = Opts) -> + case gun:await(Pid, StreamRef) of + {response, fin, Status, Headers} -> + Opts#{status => Status, headers => Headers}; + {response, nofin, Status, Headers} -> + get_reponse(Opts#{status => Status, headers => Headers}); + {data, nofin, Data} -> + get_reponse(Opts#{acc => <>}); + {data, fin, Data} -> + Opts#{acc := <>}; + {error, timeout} = Response -> + Response; + {error, _Reason} = Response-> + Response + end. -spec log({cowboy_req:req(), sheep_request(), sheep_response()}) -> ok. log({Req, Request, Response}) -> diff --git a/test/sheep_http_SUITE.erl b/test/sheep_http_SUITE.erl index c49b058..e06011a 100644 --- a/test/sheep_http_SUITE.erl +++ b/test/sheep_http_SUITE.erl @@ -40,7 +40,7 @@ all() -> -spec init_per_suite(list()) -> list(). init_per_suite(Config) -> {ok, _} = application:ensure_all_started(cowboy), - {ok, _} = application:ensure_all_started(hackney), + {ok, _} = application:ensure_all_started(gun), Routing = cowboy_router:compile([ {"localhost", [ @@ -318,8 +318,34 @@ query(Method, Path, Headers) -> -spec query(atom(), string(), list(), binary()) -> {http_code(), list(), binary()}. query(Method, Path, Headers, Data) -> - Port = integer_to_list(ranch:get_port(sheep_test_server)), - FullURL = "http://localhost:" ++ Port ++ Path, - {ok, Status, RHeaders, Ref} = hackney:request(Method, FullURL, Headers, Data), - {ok, Body} = hackney:body(Ref), + Port = ranch:get_port(sheep_test_server), + {ok, Pid} = gun:open("localhost", Port), + {ok, http} = gun:await_up(Pid), + StreamRef = gun:request(Pid, method(Method), Path, Headers, Data), + Opts = #{ + pid => Pid, + stream_ref => StreamRef, + acc => <<>> + }, + #{status := Status, headers := RHeaders, acc := Body} = get_reponse(Opts), {Status, RHeaders, Body}. + +-spec method(atom()) -> binary(). +method(Method) -> + cowboy_bstr:to_upper(erlang:list_to_binary(erlang:atom_to_list(Method))). + +get_reponse(#{pid := Pid, stream_ref := StreamRef, acc := Acc} = Opts) -> + case gun:await(Pid, StreamRef) of + {response, fin, Status, Headers} -> + Opts#{status => Status, headers => Headers}; + {response, nofin, Status, Headers} -> + get_reponse(Opts#{status => Status, headers => Headers}); + {data, nofin, Data} -> + get_reponse(Opts#{acc => <>}); + {data, fin, Data} -> + Opts#{acc := <>}; + {error, timeout} = Response -> + Response; + {error, _Reason} = Response-> + Response + end. From 3fe5b613082ce4297dc38375ebcf8bc9748f4a5f Mon Sep 17 00:00:00 2001 From: vk Date: Fri, 17 Dec 2021 16:23:01 +0200 Subject: [PATCH 2/3] Keep query logic in one place --- src/sheep_try.erl | 28 ++++++-- test/sheep_http_SUITE.erl | 142 +++++++++++++------------------------- 2 files changed, 71 insertions(+), 99 deletions(-) diff --git a/src/sheep_try.erl b/src/sheep_try.erl index 857c89c..f111b12 100644 --- a/src/sheep_try.erl +++ b/src/sheep_try.erl @@ -1,10 +1,21 @@ -module(sheep_try). --export([run/0, query/4, log/1]). +-export([run/0, log/1]). -export([init/2, read/2]). +-export([query/1, query/2, query/3, query/4]). -include("sheep.hrl"). +-ifdef(TEST). +-define(PORT, ranch:get_port(sheep_test_server)). %% See sheep_http_SUITE.erl +-else. +-define(PORT, ranch:get_port(?MODULE)). +-endif. + +-define(HEADERS, [ + {<<"content-type">>, <<"application/json">>}, + {<<"accept">>, <<"application/json">>} +]). -spec run() -> {integer(), list(), binary()}. run() -> @@ -31,11 +42,21 @@ run() -> ], query(get, "/hello", H, <<>>). +-spec query(string()) -> {http_code(), list(), binary()}. +query(Path) -> + query(get, Path). + +-spec query(atom(), string()) -> {http_code(), list(), binary()}. +query(Method, Path) -> + query(Method, Path, ?HEADERS). + +-spec query(atom(), string(), list()) -> {http_code(), list(), binary()}. +query(Method, Path, Headers) -> + query(Method, Path, Headers, <<>>). -spec query(atom(), string(), list(), binary()) -> {integer(), list(), binary()}. query(Method, Path, Headers, Data) -> - Port = ranch:get_port(?MODULE), - {ok, Pid} = gun:open("localhost", Port), + {ok, Pid} = gun:open("localhost", ?PORT), {ok, http} = gun:await_up(Pid), StreamRef = gun:request(Pid, method(Method), Path, Headers, Data), Opts = #{ @@ -80,7 +101,6 @@ log({Req, Request, Response}) -> io:format(" Sheep response: ~p~n", [Response]), ok. - %% Sheep handler -spec init(cowboy_req:req(), term()) -> tuple(). diff --git a/test/sheep_http_SUITE.erl b/test/sheep_http_SUITE.erl index e06011a..ff70663 100644 --- a/test/sheep_http_SUITE.erl +++ b/test/sheep_http_SUITE.erl @@ -78,19 +78,19 @@ end_per_suite(_Config) -> -spec ping_test(list()) -> ok. ping_test(_Config) -> - {200, _, <<"pong">>} = query("/ping"), + {200, _, <<"pong">>} = sheep_try:query("/ping"), ok. -spec get_test(list()) -> ok. get_test(_Config) -> - {200, _, Body1} = query("/simple"), + {200, _, Body1} = sheep_try:query("/simple"), #{ <<"path">> := <<"/simple">>, <<"reply_from">> := <<"read">> } = jiffy:decode(Body1, [return_maps]), - {200, _, Body2} = query("/simple?page=25&order=asc"), + {200, _, Body2} = sheep_try:query("/simple?page=25&order=asc"), #{ <<"path">> := <<"/simple">>, <<"got_page">> := <<"25">>, @@ -101,13 +101,13 @@ get_test(_Config) -> -spec post_put_delete_test(list()) -> ok. post_put_delete_test(_Config) -> - {200, _, Body1} = query(post, "/simple"), + {200, _, Body1} = sheep_try:query(post, "/simple"), #{<<"reply_from">> := <<"create">>} = jiffy:decode(Body1, [return_maps]), - {200, _, Body2} = query(put, "/simple"), + {200, _, Body2} = sheep_try:query(put, "/simple"), #{<<"reply_from">> := <<"update">>} = jiffy:decode(Body2, [return_maps]), - {200, _, Body3} = query(delete, "/simple"), + {200, _, Body3} = sheep_try:query(delete, "/simple"), #{<<"reply_from">> := <<"delete">>} = jiffy:decode(Body3, [return_maps]), ok. @@ -116,70 +116,70 @@ post_put_delete_test(_Config) -> pipeline_test(_Config) -> URL = "/pipeline/users", H = [{<<"x-auth-token">>, <<"cft6GLEhLANgstU8sZdL">>} | ?HEADERS], - {200, _, Body1} = query(get, URL, H), + {200, _, Body1} = sheep_try:query(get, URL, H), #{ <<"reply_from">> := <<"read">>, <<"steps">> := [<<"paging">>, <<"auth">>] } = jiffy:decode(Body1, [return_maps]), - {401, _, <<"Auth error">>} = query(URL), + {401, _, <<"Auth error">>} = sheep_try:query(URL), - {200, _, Body3} = query(post, URL, H, <<"{\"user_id\":25}">>), + {200, _, Body3} = sheep_try:query(post, URL, H, <<"{\"user_id\":25}">>), #{ <<"reply_from">> := <<"create">>, <<"user_id">> := 25, <<"steps">> := [<<"stage4">>, <<"stage3">>, <<"validation">>, <<"auth">>] } = jiffy:decode(Body3, [return_maps]), - {400, _, Body4} = query(post, URL, H, <<"{\"id\":25}">>), + {400, _, Body4} = sheep_try:query(post, URL, H, <<"{\"id\":25}">>), #{<<"error">> := <<"User ID not provided">>} = jiffy:decode(Body4, [return_maps]), ok. -spec status_test(list()) -> ok. status_test(_Config) -> - {200, _, Body0} = query("/status/users"), + {200, _, Body0} = sheep_try:query("/status/users"), [ #{<<"id">> := <<"1">>, <<"name">> := <<"Username 1">>}, #{<<"id">> := <<"2">>, <<"name">> := <<"Username 2">>} ] = jiffy:decode(Body0, [return_maps]), - {200, _, Body1} = query("/status/users/1"), + {200, _, Body1} = sheep_try:query("/status/users/1"), #{<<"id">> := <<"1">>, <<"name">> := <<"Username 1">>} = jiffy:decode(Body1, [return_maps]), - {204, Headers2, <<>>} = query("/status/users/2"), + {204, Headers2, <<>>} = sheep_try:query("/status/users/2"), undefined = proplists:get_value(<<"content-length">>, Headers2), - {204, Headers3, <<>>} = query("/status/users/3"), + {204, Headers3, <<>>} = sheep_try:query("/status/users/3"), undefined = proplists:get_value(<<"content-length">>, Headers3), - {404, _, <<"Not found">>} = query("/status/users/4"), + {404, _, <<"Not found">>} = sheep_try:query("/status/users/4"), ok. -spec error_status_test(list()) -> ok. error_status_test(_Config) -> - {200, _, _} = query("/e/status/users"), - {404, _, _} = query("/e/status/users/2"), + {200, _, _} = sheep_try:query("/e/status/users"), + {404, _, _} = sheep_try:query("/e/status/users/2"), - {400, _, Body3} = query("/e/status/users/3"), + {400, _, Body3} = sheep_try:query("/e/status/users/3"), #{<<"error">> := <<"simple_error">>} = jiffy:decode(Body3, [return_maps]), - {500, _, Body4} = query("/e/status/users/4"), + {500, _, Body4} = sheep_try:query("/e/status/users/4"), #{<<"error">> := <<"custom_error">>} = jiffy:decode(Body4, [return_maps]), - {400, _, Body5} = query("/e/status/users/5"), + {400, _, Body5} = sheep_try:query("/e/status/users/5"), #{<<"error">> := <<"Test exception">>} = jiffy:decode(Body5, [return_maps]), - {500, _, <<"Internal server error">>} = query("/status/users/5"), + {500, _, <<"Internal server error">>} = sheep_try:query("/status/users/5"), ok. -spec error_with_stacktrace_test(list()) -> ok. error_with_stacktrace_test(_Config) -> - {200, _, _} = query("/est/status/users"), - {500, _, Body5} = query("/est/status/users/5"), + {200, _, _} = sheep_try:query("/est/status/users"), + {500, _, Body5} = sheep_try:query("/est/status/users/5"), #{ <<"error">> := <<"Test exception">>, <<"stacktrace">> := _ @@ -191,7 +191,7 @@ error_with_stacktrace_test(_Config) -> encode_decode_test(_Config) -> Data = #{<<"answer">> => 42}, JData = jiffy:encode(Data), - {200, _, Body} = query(get, "/encode_decode", ?HEADERS, JData), + {200, _, Body} = sheep_try:query(get, "/encode_decode", ?HEADERS, JData), #{ <<"answer">> := 42, <<"custom_decoder">> := <<"ok">>, @@ -200,25 +200,25 @@ encode_decode_test(_Config) -> } = jiffy:decode(Body, [return_maps]), MData = msgpack:pack(Data), - {415, _, <<"Not supported 'content-type'">>} = query(get, "/encode_decode", ?M_HEADERS, MData), + {415, _, <<"Not supported 'content-type'">>} = sheep_try:query(get, "/encode_decode", ?M_HEADERS, MData), - {204, _, _} = query("/encode_decode/empty"), - {404, _, _} = query("/encode_decode/empty_404"), - {204, _, _} = query("/encode_decode/undefined"), + {204, _, _} = sheep_try:query("/encode_decode/empty"), + {404, _, _} = sheep_try:query("/encode_decode/empty_404"), + {204, _, _} = sheep_try:query("/encode_decode/undefined"), ok. -spec invalid_handler_test(list()) -> ok. invalid_handler_test(_Config) -> Path = "/invalid", - {204, _, <<>>} = query(get, Path), % empty list of callbacks - {405, _, <<"Method not allowed">>} = query(put, Path), % no callbacks in methods_spec - {501, _, <<"Not implemented">>} = query(post, Path), % callback in list but not exported + {204, _, <<>>} = sheep_try:query(get, Path), % empty list of callbacks + {405, _, <<"Method not allowed">>} = sheep_try:query(put, Path), % no callbacks in methods_spec + {501, _, <<"Not implemented">>} = sheep_try:query(post, Path), % callback in list but not exported - {500, _, <<"Internal server error">>} = query("/invalid/init"), - {400, _, Body1} = query("/invalid/init/2"), + {500, _, <<"Internal server error">>} = sheep_try:query("/invalid/init"), + {400, _, Body1} = sheep_try:query("/invalid/init/2"), #{<<"error">> := <<"Test exception">>} = jiffy:decode(Body1, [return_maps]), - {500, _, <<"Internal server error">>} = query("/invalid/init/3"), + {500, _, <<"Internal server error">>} = sheep_try:query("/invalid/init/3"), ok. @@ -226,9 +226,9 @@ invalid_handler_test(_Config) -> invalid_encode_decode_test(_Config) -> Data = <<"{answer\":42}">>, {400, _, <<"Can't decode 'application/json' payload">>} = - query(get, "/encode_decode", ?HEADERS, Data), + sheep_try:query(get, "/encode_decode", ?HEADERS, Data), {500, _, <<"Can't encode 'application/json' payload">>} = - query("/encode_decode/invalid_payload"), + sheep_try:query("/encode_decode/invalid_payload"), ok. @@ -237,18 +237,18 @@ invalid_headers_test(_Config) -> Data = #{<<"answer">> => 42}, JData = jiffy:encode(Data), MData = msgpack:pack(Data), - {415, _, <<"Not supported 'content-type'">>} = query(get, "/simple", [], JData), + {415, _, <<"Not supported 'content-type'">>} = sheep_try:query(get, "/simple", [], JData), {415, _, <<"Not supported 'content-type'">>} = - query(get, "/simple", [{<<"content-type">>, <<"text/html">>}], JData), + sheep_try:query(get, "/simple", [{<<"content-type">>, <<"text/html">>}], JData), {200, _, _} = %% skipping "accept" header is possible - query(get, "/simple", [{<<"content-type">>, <<"application/x-msgpack">>}], MData), + sheep_try:query(get, "/simple", [{<<"content-type">>, <<"application/x-msgpack">>}], MData), {406, _, <<"Not acceptable">>} = - query(get, "/simple", [ + sheep_try:query(get, "/simple", [ {<<"content-type">>, <<"application/x-msgpack">>}, {<<"accept">>, <<"text/html">>} ], MData), {200, _, _} = - query(get, "/simple", [ + sheep_try:query(get, "/simple", [ {<<"content-type">>, <<"application/x-msgpack">>}, {<<"accept">>, <<"application/x-msgpack">>} ], MData), @@ -256,21 +256,21 @@ invalid_headers_test(_Config) -> internal_errors_test(_Config) -> {400, _, ResBody1} = - query(post, "/internal_error/request_decode_error", + sheep_try:query(post, "/internal_error/request_decode_error", [{<<"content-type">>, <<"application/json">>}], <<"{">>), #{<<"type">> := <<"request_decode_error">>, <<"content_type">> := <<"application/json">>} = jiffy:decode(ResBody1, [return_maps]), {415, _, ResBody2} = - query(post, "/internal_error/request_decode_error", + sheep_try:query(post, "/internal_error/request_decode_error", [{<<"content-type">>, <<"text/plain">>}], <<"wasd">>), #{<<"type">> := <<"unsupported_content_type">>, <<"content_type">> := <<"text/plain">>} = jiffy:decode(ResBody2, [return_maps]), {500, _, ResBody3} = - query(post, "/internal_error/response_encode_error", + sheep_try:query(post, "/internal_error/response_encode_error", [{<<"content-type">>, <<"application/json">>}, {<<"accept">>, <<"application/json">>}], <<"{}">>), @@ -278,7 +278,7 @@ internal_errors_test(_Config) -> <<"content_type">> := <<"application/json">>} = jiffy:decode(ResBody3, [return_maps]), {406, _, ResBody4} = - query(post, "/internal_error/unsupported_accept", + sheep_try:query(post, "/internal_error/unsupported_accept", [{<<"content-type">>, <<"application/json">>}, {<<"accept">>, <<"text/plain">>}], <<"{}">>), @@ -286,14 +286,14 @@ internal_errors_test(_Config) -> <<"content_type">> := <<"text/plain">>} = jiffy:decode(ResBody4, [return_maps]), {405, _, ResBody5} = - query(patch, "/internal_error/method_not_allowed", + sheep_try:query(patch, "/internal_error/method_not_allowed", [{<<"content-type">>, <<"application/json">>}, {<<"accept">>, <<"application/json">>}], <<"{}">>), #{<<"type">> := <<"method_not_allowed">>} = jiffy:decode(ResBody5, [return_maps]), {501, _, ResBody6} = - query(get, "/internal_error/handler_callback_missing", + sheep_try:query(get, "/internal_error/handler_callback_missing", [{<<"content-type">>, <<"application/json">>}, {<<"accept">>, <<"application/json">>}], <<"{}">>), @@ -301,51 +301,3 @@ internal_errors_test(_Config) -> <<"module">> := <<"internal_error_handler">>, <<"handler">> := <<"read">>} = jiffy:decode(ResBody6, [return_maps]), ok. - -%%% Utils - --spec query(string()) -> {http_code(), list(), binary()}. -query(Path) -> - query(get, Path). - --spec query(atom(), string()) -> {http_code(), list(), binary()}. -query(Method, Path) -> - query(Method, Path, ?HEADERS). - --spec query(atom(), string(), list()) -> {http_code(), list(), binary()}. -query(Method, Path, Headers) -> - query(Method, Path, Headers, <<>>). - --spec query(atom(), string(), list(), binary()) -> {http_code(), list(), binary()}. -query(Method, Path, Headers, Data) -> - Port = ranch:get_port(sheep_test_server), - {ok, Pid} = gun:open("localhost", Port), - {ok, http} = gun:await_up(Pid), - StreamRef = gun:request(Pid, method(Method), Path, Headers, Data), - Opts = #{ - pid => Pid, - stream_ref => StreamRef, - acc => <<>> - }, - #{status := Status, headers := RHeaders, acc := Body} = get_reponse(Opts), - {Status, RHeaders, Body}. - --spec method(atom()) -> binary(). -method(Method) -> - cowboy_bstr:to_upper(erlang:list_to_binary(erlang:atom_to_list(Method))). - -get_reponse(#{pid := Pid, stream_ref := StreamRef, acc := Acc} = Opts) -> - case gun:await(Pid, StreamRef) of - {response, fin, Status, Headers} -> - Opts#{status => Status, headers => Headers}; - {response, nofin, Status, Headers} -> - get_reponse(Opts#{status => Status, headers => Headers}); - {data, nofin, Data} -> - get_reponse(Opts#{acc => <>}); - {data, fin, Data} -> - Opts#{acc := <>}; - {error, timeout} = Response -> - Response; - {error, _Reason} = Response-> - Response - end. From b19d69f31f0cced75f9d147fe4878f5732c8a01c Mon Sep 17 00:00:00 2001 From: vk Date: Fri, 17 Dec 2021 16:26:06 +0200 Subject: [PATCH 3/3] Fix lint --- test/sheep_http_SUITE.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/sheep_http_SUITE.erl b/test/sheep_http_SUITE.erl index ff70663..184a01c 100644 --- a/test/sheep_http_SUITE.erl +++ b/test/sheep_http_SUITE.erl @@ -200,7 +200,8 @@ encode_decode_test(_Config) -> } = jiffy:decode(Body, [return_maps]), MData = msgpack:pack(Data), - {415, _, <<"Not supported 'content-type'">>} = sheep_try:query(get, "/encode_decode", ?M_HEADERS, MData), + {415, _, <<"Not supported 'content-type'">>} = + sheep_try:query(get, "/encode_decode", ?M_HEADERS, MData), {204, _, _} = sheep_try:query("/encode_decode/empty"), {404, _, _} = sheep_try:query("/encode_decode/empty_404"), @@ -213,7 +214,8 @@ invalid_handler_test(_Config) -> Path = "/invalid", {204, _, <<>>} = sheep_try:query(get, Path), % empty list of callbacks {405, _, <<"Method not allowed">>} = sheep_try:query(put, Path), % no callbacks in methods_spec - {501, _, <<"Not implemented">>} = sheep_try:query(post, Path), % callback in list but not exported + {501, _, <<"Not implemented">>} = + sheep_try:query(post, Path), % callback in list but not exported {500, _, <<"Internal server error">>} = sheep_try:query("/invalid/init"), {400, _, Body1} = sheep_try:query("/invalid/init/2"),