Packages

KATT (Klarna API Testing Tool) is an HTTP-based API testing tool for Erlang.

Current section

Files

Jump to
katt src katt_validate_type.erl
Raw

src/katt_validate_type.erl

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Copyright 2014- AUTHORS
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%% http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%
%%% @copyright 2014- AUTHORS
%%%
%%% @doc Klarna API Testing Tool Validate Type
%%% @end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%_* Module declaration =======================================================
%% @private
-module(katt_validate_type).
%%%_* Exports ==================================================================
%% API
-export([ validate_type_set/5
, validate_type_runtime_value/5
, validate_type_runtime_validation/5
]).
%%%_* Includes =================================================================
-include("katt.hrl").
%%%_* API ======================================================================
-spec validate_type_set( string()
, proplists:proplist()
, proplists:proplist()
, term()
, callbacks()
) -> pass | [validation_failure()].
validate_type_set( ParentKey
, Options
, {array, AItems0} = _Actual
, _ItemsMode
, Callbacks
) ->
{array, EItems0} = proplists:get_value("value", Options),
ItemsMode = proplists:get_value(?MATCH_ANY, EItems0, ?MATCH_ANY),
EItems1 = proplists:delete(?MATCH_ANY, EItems0),
EItems = lists:keysort(2, EItems1),
AItems = lists:keysort(2, AItems0),
validate_set(ParentKey, EItems, AItems, ItemsMode, Callbacks, []);
validate_type_set(ParentKey, Options, Actual, _ItemsMode, _Callbacks) ->
[{not_equal, {ParentKey, Options, Actual}}].
-spec validate_type_runtime_value( string()
, proplists:proplist()
, proplists:proplist()
, term()
, callbacks()
) -> pass | [validation_failure()].
validate_type_runtime_value( ParentKey
, [{"erlang", Erlang0} | _Options]
, Actual
, ItemsMode
, Callbacks
) ->
Erlang = case Erlang0 of
{array, ErlangLines} ->
string:join(lists:map(fun({_, V}) -> V end, ErlangLines), "\n");
_ ->
Erlang0
end ++ ".",
{Error, Expected} =
try
{ok, Tokens, _} = erl_scan:string(Erlang),
{ok, Exprs} = erl_parse:parse_exprs(Tokens),
Bindings = [ {'ParentKey', ParentKey}
, {'Actual', Actual}
, {'ItemsMode', ItemsMode}
, {'Callbacks', Callbacks}
],
BindingsDict = orddict:from_list(Bindings),
{value, Expected0, _} = erl_eval:exprs(Exprs, BindingsDict),
{undefined, Expected0}
catch
C:E ->
{ Erlang ++ "~n"
++ katt_util:erl_to_list(C) ++ ":" ++ katt_util:erl_to_list(E)
, undefined
}
end,
case Error of
undefined ->
katt_util:validate(ParentKey, Expected, Actual, ItemsMode, Callbacks);
_ ->
{not_equal, {ParentKey, Error, Actual}}
end;
validate_type_runtime_value( ParentKey
, [{"shell", Shell0} | _Options]
, Actual
, ItemsMode
, Callbacks
) ->
Shell = case Shell0 of
{array, ShellLines} ->
string:join(lists:map(fun({_, V}) -> V end, ShellLines), "\n");
_ ->
Shell0
end,
try
{0, Erlang} = katt_util:os_cmd( Shell
, [ {"KATT_PARENT_KEY", ParentKey}
, { "KATT_ACTUAL"
, io_lib:format("~p", [Actual])
}
, { "KATT_ITEMS_MODE"
, io_lib:format("~p", [ItemsMode])
}
]),
validate_type_runtime_value( ParentKey
, [{"erlang", Erlang}]
, Actual
, ItemsMode
, Callbacks
)
catch
C:E ->
Error = Shell ++ "~n"
++ katt_util:erl_to_list(C) ++ ":" ++ katt_util:erl_to_list(E),
{not_equal, {ParentKey, Error, Actual}}
end.
-spec validate_type_runtime_validation( string()
, proplists:proplist()
, proplists:proplist()
, term()
, callbacks()
) -> pass | [validation_failure()].
validate_type_runtime_validation( ParentKey
, [{"erlang", Erlang0} | _Options]
, Actual
, ItemsMode
, Callbacks
) ->
Erlang = case Erlang0 of
{array, ErlangLines} ->
string:join(lists:map(fun({_, V}) -> V end, ErlangLines), "\n");
_ ->
Erlang0
end ++ ".",
try
{ok, Tokens, _} = erl_scan:string(Erlang),
{ok, Exprs} = erl_parse:parse_exprs(Tokens),
Bindings = [ {'ParentKey', ParentKey}
, {'Actual', Actual}
, {'ItemsMode', ItemsMode}
, {'Callbacks', Callbacks}
],
{value, Result, _} = erl_eval:exprs(Exprs, orddict:from_list(Bindings)),
Result
catch
C:E ->
Error = { Erlang ++ "~n"
++ katt_util:erl_to_list(C) ++ ":" ++ katt_util:erl_to_list(E)
, undefined
},
{not_equal, {ParentKey, Error, Actual}}
end;
validate_type_runtime_validation( ParentKey
, [{"shell", Shell0} | _Options]
, Actual
, ItemsMode
, Callbacks
) ->
Shell = case Shell0 of
{array, ShellLines} ->
string:join(lists:map(fun({_, V}) -> V end, ShellLines), "\n");
_ ->
Shell0
end,
try
{0, Erlang} = katt_util:os_cmd( Shell
, [ {"KATT_PARENT_KEY", ParentKey}
, { "KATT_ACTUAL"
, io_lib:format("~p", [Actual])
}
, { "KATT_ITEMS_MODE"
, io_lib:format("~p", [ItemsMode])
}
]),
validate_type_runtime_validation( ParentKey
, [{"erlang", Erlang}]
, Actual
, ItemsMode
, Callbacks
)
catch
C:E ->
Error = { Shell ++ "~n"
++ katt_util:erl_to_list(C) ++ ":" ++ katt_util:erl_to_list(E)
, undefined
},
{not_equal, {ParentKey, Error, Actual}}
end.
%%%_* Internal =================================================================
-spec validate_set( string()
, proplists:proplist()
, proplists:proplist()
, term()
, callbacks()
, list()
) -> pass | [validation_failure()].
%% No more items in any of the expected / actual arrays
validate_set( _ParentKey
, [] = _Expected
, [] = _Actual
, _ItemsMode
, _Callbacks
, Errors
) ->
lists:flatten(Errors);
%% All expected elements have been consumed,
%% and we allow extra actual elements
validate_set( _ParentKey
, [] = _Expected
, _Actual
, ?MATCH_ANY
, _Callbacks
, Errors
) ->
lists:flatten(Errors);
%% All expected elements have been consumed,
%% and we do not allow extra actual elements
validate_set( ParentKey
, [] = _Expected
, Actual
, ?UNEXPECTED
, _Callbacks
, Errors
) ->
[ {unexpected, {ParentKey ++ "/" ++ AKey, undefined, AValue}}
|| {AKey, AValue} <- Actual
] ++ Errors;
%% All actual elements have been consumed,
%% and there are still expected elements
validate_set( ParentKey
, Expected
, []
, _ItemsMode
, _Callbacks
, Errors
) ->
[ {not_contains, {ParentKey ++ "/" ++ EKey, EValue, undefined}}
|| {EKey, EValue} <- Expected
] ++ Errors;
%% There is at least one expected and one actual element
validate_set( ParentKey
, [{EKey, EValue} | ERest] = _Expected
, Actual
, ItemsMode
, Callbacks
, Errors
) ->
Split =
lists:splitwith( fun({_Key, Value}) ->
not katt_util:is_valid( _ParentKey = ""
, EValue
, Value
, ItemsMode
, Callbacks
)
end
, Actual),
case Split of
{Actual, []} ->
Errors1 =
[{not_contains, {ParentKey ++ "/" ++ EKey, EValue, undefined}}] ++
Errors,
validate_set(ParentKey, ERest, Actual, ItemsMode, Callbacks, Errors1);
{ARest1, [{_AKey, AValue}|ARest2]} ->
ARest = ARest1 ++ ARest2,
Errors1 = [katt_util:validate( _ParentKey = ""
, EValue
, AValue
, ItemsMode
, Callbacks
)] ++ Errors,
validate_set(ParentKey, ERest, ARest, ItemsMode, Callbacks, Errors1)
end.