Packages

Testing helpers for the Nova web framework

Current section

Files

Jump to
nova_test src nova_test.erl
Raw

src/nova_test.erl

-module(nova_test).
-export([
start/1,
start/2,
stop/1,
get/2,
get/3,
post/2,
post/3,
put/3,
patch/3,
delete/2,
delete/3,
request/4,
status/1,
body/1,
json/1,
headers/1,
header/2
]).
-type response() :: #{
status := integer(),
headers := [{string(), string()}],
body := binary()
}.
-type opts() :: #{
headers => [{binary(), binary()}],
json => map(),
body => binary(),
content_type => string()
}.
%% Lifecycle
-spec start(App :: atom()) -> [{atom(), term()}].
start(App) ->
start(App, []).
-spec start(App :: atom(), Config :: [{atom(), term()}]) -> [{atom(), term()}].
start(App, Config) ->
application:ensure_all_started(inets),
{ok, _} = application:ensure_all_started(App),
Port = get_port(),
[{nova_test_port, Port}, {nova_test_app, App} | Config].
-spec stop(Config :: [{atom(), term()}]) -> ok.
stop(Config) ->
App = proplists:get_value(nova_test_app, Config),
application:stop(App),
ok.
%% HTTP methods
-spec get(Path :: string(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
get(Path, Config) ->
request(get, Path, #{}, Config).
-spec get(Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
get(Path, Opts, Config) ->
request(get, Path, Opts, Config).
-spec post(Path :: string(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
post(Path, Config) ->
request(post, Path, #{}, Config).
-spec post(Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
post(Path, Opts, Config) ->
request(post, Path, Opts, Config).
-spec put(Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
put(Path, Opts, Config) ->
request(put, Path, Opts, Config).
-spec patch(Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
patch(Path, Opts, Config) ->
request(patch, Path, Opts, Config).
-spec delete(Path :: string(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
delete(Path, Config) ->
request(delete, Path, #{}, Config).
-spec delete(Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) -> {ok, response()} | {error, term()}.
delete(Path, Opts, Config) ->
request(delete, Path, Opts, Config).
-spec request(Method :: atom(), Path :: string(), Opts :: opts(), Config :: [{atom(), term()}]) ->
{ok, response()} | {error, term()}.
request(Method, Path, Opts, Config) ->
Port = proplists:get_value(nova_test_port, Config),
URL = "http://localhost:" ++ integer_to_list(Port) ++ Path,
Headers = build_headers(Opts),
HttpMethod = Method,
Request = build_request(URL, Headers, Opts),
HttpOpts = [{autoredirect, false}],
ReqOpts = [{body_format, binary}],
case httpc:request(HttpMethod, Request, HttpOpts, ReqOpts) of
{ok, {{_, Status, _}, RespHeaders, Body}} ->
{ok, #{
status => Status,
headers => RespHeaders,
body => Body
}};
{error, Reason} ->
{error, Reason}
end.
%% Response accessors
-spec status(response()) -> integer().
status(#{status := Status}) -> Status.
-spec body(response()) -> binary().
body(#{body := Body}) -> Body.
-spec json(response()) -> map().
json(#{body := Body}) ->
JsonLib = json_lib(),
{ok, Decoded} = JsonLib:decode(Body),
Decoded.
-spec headers(response()) -> [{string(), string()}].
headers(#{headers := Headers}) -> Headers.
-spec header(Name :: string(), response()) -> string() | undefined.
header(Name, #{headers := Headers}) ->
LowerName = string:lowercase(Name),
case lists:keyfind(LowerName, 1, [{string:lowercase(K), V} || {K, V} <- Headers]) of
{_, Value} -> Value;
false -> undefined
end.
%% Internal
get_port() ->
Config = application:get_env(nova, cowboy_configuration, #{}),
maps:get(port, Config, 8080).
build_headers(#{headers := Headers}) ->
[{binary_to_list(K), binary_to_list(V)} || {K, V} <- Headers];
build_headers(_) ->
[].
build_request(URL, Headers, #{json := Json}) ->
JsonLib = json_lib(),
Body = JsonLib:encode(Json),
BodyStr = case Body of
B when is_binary(B) -> binary_to_list(B);
B when is_list(B) -> B
end,
{URL, Headers, "application/json", BodyStr};
build_request(URL, Headers, #{body := Body, content_type := CT}) ->
BodyStr = binary_to_list(Body),
{URL, Headers, CT, BodyStr};
build_request(URL, Headers, #{body := Body}) ->
BodyStr = binary_to_list(Body),
{URL, Headers, "text/plain", BodyStr};
build_request(URL, Headers, _) ->
{URL, Headers}.
json_lib() ->
try nova:get_env(json_lib, thoas)
catch _:_ -> thoas
end.