Packages

Erlang wrapper for Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.

Current section

Files

Jump to
pact_erlang src pact_consumer_http.erl
Raw

src/pact_consumer_http.erl

-module(pact_consumer_http).
-export([
v4/2,
interaction/2,
cleanup_interaction/1
]).
-type pact_ref() :: integer().
-type pact_interaction_ref() :: integer().
-type consumer() :: binary().
-type provider() :: binary().
-type pact_pid() :: pid().
-type pact_interaction_details() :: map().
-type pact_mock_server_port() :: integer().
-type request_details() :: map().
-type response_details() :: map().
-spec v4(consumer(), provider()) -> pact_pid().
v4(Consumer, Provider) ->
{ok, PactPid} = pact_ref_server:start(Consumer, Provider),
PactPid.
-spec interaction(pact_pid(), pact_interaction_details()) ->
{ok, pact_mock_server_port()}.
interaction(PactPid, Interaction) ->
{PactRef, InteractionRef} = init_interaction(PactPid, Interaction),
RequestDetails = maps:get(with_request, Interaction, #{}),
ok = insert_request_details(InteractionRef, RequestDetails),
ResponseDetails = maps:get(will_respond_with, Interaction, #{}),
ok = insert_response_details(InteractionRef, ResponseDetails),
start_mock_server(PactPid, PactRef, <<"127.0.0.1">>, 0, <<"http">>).
-spec cleanup_interaction(pact_pid()) -> ok.
cleanup_interaction(PactPid) ->
PactRef = pact_ref_server:get_pact_ref(PactPid),
MockServerPort = pact_ref_server:get_mock_server_port(PactPid),
ok = pactffi_nif:cleanup_mock_server(MockServerPort),
pactffi_nif:free_pact_handle(PactRef).
%% Internal functions
-spec init_interaction(pact_pid(), pact_interaction_details()) ->
{pact_ref(), pact_interaction_ref()}.
init_interaction(PactPid, Interaction) ->
{Consumer, Producer} = pact_ref_server:get_consumer_producer(PactPid),
PactRef = pactffi_nif:new_pact(Consumer, Producer),
ok = pact_ref_server:set_pact_ref(PactPid, PactRef),
InteractionDesc = maps:get(upon_receiving, Interaction, <<"">>),
InteractionRef = pactffi_nif:new_interaction(PactRef, InteractionDesc),
case maps:get(given, Interaction, undefined) of
undefined -> ok;
GivenState -> pactffi_nif:given(InteractionRef, GivenState)
end,
ok = pact_ref_server:create_interaction(PactPid, InteractionRef, Interaction),
{PactRef, InteractionRef}.
-spec start_mock_server(pact_pid(), pact_ref(), binary(), integer(), binary()) ->
{ok, pact_mock_server_port()}.
start_mock_server(PactPid, PactRef, Host, Port, InteractionPart) ->
MockServerPort = pactffi_nif:create_mock_server_for_transport(
PactRef, Host, Port, InteractionPart
),
ok = pact_ref_server:set_mock_server_port(PactPid, MockServerPort),
{ok, MockServerPort}.
%% Internal functions
-spec insert_request_details(pact_interaction_ref(), request_details()) -> ok.
insert_request_details(InteractionRef, RequestDetails) ->
ReqMethod = maps:get(method, RequestDetails),
ReqPath = maps:get(path, RequestDetails),
pactffi_nif:with_request(InteractionRef, ReqMethod, ReqPath),
ReqHeaders = maps:get(headers, RequestDetails, #{}),
ContentType = get_content_type(ReqHeaders),
maps:fold(
fun(Key, Value, _Acc) ->
%% FIXME: 4th parameter is Index.. need to increment
pactffi_nif:with_header_v2(InteractionRef, 0, Key, 0, Value)
end,
ok,
ReqHeaders
),
ReqBody = maps:get(body, RequestDetails, undefined),
case ReqBody of
undefined -> ok;
_ -> pactffi_nif:with_body(InteractionRef, 0, ContentType, ReqBody)
end,
ReqQueryParams = maps:get(query_params, RequestDetails, undefined),
case ReqQueryParams of
undefined ->
ok;
_ ->
maps:fold(
fun(Key, Value, _Acc) ->
%% FIXME: 3rd parameter is Index.. need to increment
pactffi_nif:with_query_parameter_v2(InteractionRef, Key, 0, Value)
end,
ok,
ReqQueryParams
)
end,
ok.
-spec insert_response_details(pact_interaction_ref(), response_details()) -> ok.
insert_response_details(InteractionRef, ResponseDetails) ->
ResponseStatusCode = maps:get(status, ResponseDetails, undefined),
case ResponseStatusCode of
undefined -> ok;
_ -> pactffi_nif:response_status(InteractionRef, ResponseStatusCode)
end,
ResHeaders = maps:get(headers, ResponseDetails, #{}),
ContentType = get_content_type(ResHeaders),
maps:fold(
fun(Key, Value, _Acc) ->
%% FIXME: 4th parameter is Index.. need to increment
pactffi_nif:with_header_v2(InteractionRef, 1, Key, 0, Value)
end,
ok,
ResHeaders
),
ResBody = maps:get(body, ResponseDetails, undefined),
case ResBody of
undefined -> ok;
_ -> pactffi_nif:with_body(InteractionRef, 1, ContentType, ResBody)
end,
ok.
-spec get_content_type(map()) -> binary().
get_content_type(Headers) ->
maps:get(<<"Content-Type">>, Headers, <<"application/json">>).