Skip to content

Commit

Permalink
add runtime_value/runtime_validation validation type re:#29
Browse files Browse the repository at this point in the history
  • Loading branch information
andreineculau committed Mar 1, 2016
1 parent 7f7592a commit add566f
Show file tree
Hide file tree
Showing 5 changed files with 423 additions and 8 deletions.
72 changes: 69 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ default, one can do `{..., "{{_}}": "{{unexpected}}"}` or
are expected beyond the ones defined.

For more complex validations, KATT supports extensible validation types.
An example is the built-in "set" validation type for JSON,
which will ignore the order of an array's items, and just check for existence:
Built-in validation types: `set`, `runtime_value`, `runtime_validation`.

`set` will ignore the order of an array's items, and just check for existence:

```
{
Expand All @@ -53,10 +54,75 @@ which will ignore the order of an array's items, and just check for existence:
}
```

The above example would validate against JSON instances such as
So the above would validate against JSON instances such as
`{"some_array": [1, 3, 2]}`, or `{"some_array": [3, 2, 1]}`,
or even `{"some_array": [4, 3, 2, 1]}` unless we add `{{unexpected}}`.

`runtime_value` would just run code (only Erlang supported for now),
while having access to `ParentKey`, `Actual`, `Unexpected` and `Callbacks`,
and return a JSON string that is treated as the expected value
and matched against the actual one.

```
{
"rfc1123": {
"{{type}}": "runtime_validation",
"erlang": "list_to_binary(httpd_util:rfc1123_date(calendar:now_to_datetime(erlang:now())))"
}
}
```

or in array format

```
{
"rfc1123": {
"{{type}}": "runtime_validation",
"erlang": ["list_to_binary(",
" httpd_util:rfc1123_date(",
" calendar:now_to_datetime(",
" erlang:now()",
")))"
]
}
}
```

`runtime_validation` would just run code (only Erlang supported for now),
while having access to `ParentKey`, `Actual`, `Unexpected` and `Callbacks`,
and return

* `{pass, [{"Key", "Value"}]}` i.e. validation passed, store new param "Key" with value "Value"
* `{not_equal, {Key, Expected, Actual}}`
* `{not_equal, {Key, Expected, Actual, [{"more", "info"}]}}`

```
{
"rfc1123": {
"{{type}}": "runtime_validation",
"erlang": "Expected = httpd_util:rfc1123_date(calendar:now_to_datetime(erlang:now())), case Actual =:= Expected of true -> {pass, []}; false -> {not_equal, {ParentKey, Expected, Actual}} end"
}
}
```

or in array format

```
{
"rfc1123": {
"{{type}}": "runtime_validation",
"erlang": ["Expected = httpd_util:rfc1123_date(calendar:now_to_datetime(erlang:now())),",
"case Actual =:= Expected of",
" true ->",
" {pass, []};",
" false ->",
" {not_equal, {ParentKey, Expected, Actual}}",
"end."
]
}
}
```

## Examples

A simple example that will make requests to a third party server:
Expand Down
42 changes: 39 additions & 3 deletions src/katt_callbacks_json.erl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
, parse/5
, validate_body/4
, validate_type/7
, parse_json/1
]).

%%%_* Includes =================================================================
Expand Down Expand Up @@ -96,13 +97,15 @@ validate_body( false = _Justcheck


validate_type( true = _JustCheck
, "set"
, Type
, _ParentKey
, _Options
, _Actual
, _Unexpected
, _Callbacks
) ->
) when Type =:= "set" orelse
Type =:= "runtime_value" orelse
Type =:= "runtime_validation" ->
true;
validate_type( true = _JustCheck
, _Type
Expand All @@ -127,6 +130,34 @@ validate_type( false = _JustCheck
, Unexpected
, Callbacks
);
validate_type( false = _JustCheck
, "runtime_value"
, ParentKey
, Options
, Actual
, Unexpected
, Callbacks
) ->
katt_validate_type:validate_type_runtime_value( ParentKey
, Options
, Actual
, Unexpected
, Callbacks
);
validate_type( false = _JustCheck
, "runtime_validation"
, ParentKey
, Options
, Actual
, Unexpected
, Callbacks
) ->
katt_validate_type:validate_type_runtime_validation( ParentKey
, Options
, Actual
, Unexpected
, Callbacks
);
validate_type( false = _JustCheck
, _Type
, _ParentKey
Expand Down Expand Up @@ -160,7 +191,12 @@ normalize_mochijson3({struct, Items0}) ->
|| {Key, Value} <- Items0
]),
Type = proplists:get_value(?TYPE, Items1, struct),
Items = proplists:delete(?TYPE, Items1),
Items = case Type of
struct ->
Items1;
_ ->
proplists:delete(?TYPE, Items1)
end,
{Type, Items};
normalize_mochijson3(Items0) when is_list(Items0) ->
Items1 = [ normalize_mochijson3(Item)
Expand Down
8 changes: 6 additions & 2 deletions src/katt_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
, validate/5
, enumerate/1
, external_http_request/6
, erl_to_list/1
]).

%%%_* Includes =================================================================
Expand Down Expand Up @@ -97,7 +98,7 @@ insert_escape_quotes(Str) when is_list(Str) ->
run_result_to_mochijson3({error, Reason, Details}) ->
{struct, [ {error, true}
, {reason, Reason}
, {details, list_to_binary(io_lib:format("~p", [Details]))}
, {details, list_to_binary(erl_to_list(Details))}
]};
run_result_to_mochijson3({ PassOrFail
, ScenarioFilename
Expand Down Expand Up @@ -139,6 +140,9 @@ external_http_request(Url, Method, Hdrs, Body, Timeout, []) ->
Error
end.

erl_to_list(Term) ->
io_lib:format("~p", [Term]).

%%%_* Internal =================================================================

my_float_to_list(X) when is_float(X) ->
Expand Down Expand Up @@ -294,7 +298,7 @@ value_to_mochijson3(List) when is_list(List) ->
)
end;
value_to_mochijson3(Value) ->
list_to_binary(io_lib:format("~p", [Value])).
list_to_binary(erl_to_list(Value)).

is_valid(ParentKey, E, A) ->
case validate(ParentKey, E, A) of
Expand Down
84 changes: 84 additions & 0 deletions src/katt_validate_type.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
%%%_* Exports ==================================================================
%% API
-export([ validate_type_set/5
, validate_type_runtime_value/5
, validate_type_runtime_validation/5
]).

%%%_* Includes =================================================================
Expand Down Expand Up @@ -54,6 +56,88 @@ validate_type_set( ParentKey
validate_type_set(ParentKey, Options, Actual, _Unexpected, _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
, Options
, Actual
, Unexpected
, Callbacks
) ->
Erlang0 = proplists:get_value("erlang", Options),
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),
{value, Expected0, _} = erl_eval:exprs( Exprs
, [ {'ParentKey', ParentKey}
, {'Actual', Actual}
, {'Unexpected', Unexpected}
, {'Callbacks', Callbacks}]
),
{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, Unexpected, Callbacks);
_ ->
{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
, Options
, Actual
, Unexpected
, Callbacks
) ->
Erlang0 = proplists:get_value("erlang", Options),
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),
{value, Result, _} = erl_eval:exprs( Exprs
, [ {'ParentKey', ParentKey}
, {'Actual', Actual}
, {'Unexpected', Unexpected}
, {'Callbacks', Callbacks}]
),
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.

%%%_* Internal =================================================================

-spec validate_set( string()
Expand Down
Loading

0 comments on commit add566f

Please sign in to comment.