Fix machinegun format_version field usage (#25)

This commit is contained in:
Andrey Fadeev 2020-06-04 14:13:05 +03:00 committed by GitHub
parent e2d1d3e82f
commit 4e8245860a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 239 additions and 4 deletions

View File

@ -343,16 +343,18 @@ marshal({repair_fail, Schema}, Reason) ->
};
marshal({state_change, Schema}, #{} = V) ->
Version = machinery_mg_schema:get_version(Schema, aux_state),
AuxStateVersion = machinery_mg_schema:get_version(Schema, aux_state),
EventVersion = machinery_mg_schema:get_version(Schema, event),
#mg_stateproc_MachineStateChange{
events = [
#mg_stateproc_Content{data = Event}
|| Event <- marshal({list, {schema, Schema, {event, Version}}}, maps:get(events, V, []))
#mg_stateproc_Content{data = Event, format_version = EventVersion}
|| Event <- marshal({list, {schema, Schema, {event, EventVersion}}}, maps:get(events, V, []))
],
% TODO
% Provide this to logic handlers as well
aux_state = #mg_stateproc_Content{
data = marshal({schema, Schema, {aux_state, Version}}, maps:get(aux_state, V, undefined))
data = marshal({schema, Schema, {aux_state, AuxStateVersion}}, maps:get(aux_state, V, undefined)),
format_version = AuxStateVersion
}
};

View File

@ -0,0 +1,233 @@
-module(machinery_mg_schema_versions_SUITE).
-behaviour(machinery).
-include_lib("stdlib/include/assert.hrl").
-include_lib("common_test/include/ct.hrl").
%% Common Tests callbacks
-export([all/0]).
-export([groups/0]).
-export([init_per_suite/1]).
-export([end_per_suite/1]).
-export([init_per_testcase/2]).
%% Tests
-export([schema_versions_used_properly_test/1]).
%% Machinery callbacks
-export([init/4]).
-export([process_timeout/3]).
-export([process_repair/4]).
-export([process_call/4]).
%% machinery_mg_schema callbacks
-export([marshal/2]).
-export([unmarshal/2]).
-export([get_version/1]).
%% Internal types
-type config() :: ct_helper:config().
-type test_case_name() :: ct_helper:test_case_name().
-type group_name() :: ct_helper:group_name().
-type test_return() :: _ | no_return().
-spec all() -> [test_case_name() | {group, group_name()}].
all() ->
[
{group, all}
].
-spec groups() ->
[{group_name(), list(), test_case_name()}].
groups() ->
[
{all, [parallel], [
schema_versions_used_properly_test
]}
].
-spec init_per_suite(config()) -> config().
init_per_suite(C0) ->
{StartedApps, _StartupCtx} = ct_helper:start_apps([machinery]),
C1 = [{backend, machinery_mg_backend}, {group_sup, ct_sup:start()} | C0],
{ok, _Pid} = start_backend(C1),
[{started_apps, StartedApps}| C1].
-spec end_per_suite(config()) -> _.
end_per_suite(C) ->
ok = ct_helper:stop_apps(?config(started_apps, C)),
ok = ct_sup:stop(?config(group_sup, C)).
-spec init_per_testcase(test_case_name(), config()) -> config().
init_per_testcase(TestCaseName, C) ->
ct_helper:makeup_cfg([ct_helper:test_case_name(TestCaseName), ct_helper:woody_ctx()], C).
%% Tests
-spec schema_versions_used_properly_test(config()) -> test_return().
schema_versions_used_properly_test(C) ->
ID = unique(),
?assertEqual(ok, start(ID, init_something, C)),
?assertEqual({ok, done}, call(ID, do_something, C)),
timer:sleep(timer:seconds(5)),
?assertEqual({ok, done}, call(ID, do_something, C)),
?assertError({failed, general, ID}, call(ID, fail, C)),
?assertEqual({ok, done}, repair(ID, repair_something, {20, 10, forward}, C)),
?assertEqual({ok, done}, call(ID, do_something, C)).
%% Machinery handler
-type event() :: any().
-type aux_st() :: any().
-type machine() :: machinery:machine(event(), aux_st()).
-type handler_opts() :: machinery:handler_opts(_).
-type result() :: machinery:result(event(), aux_st()).
-type response() :: machinery:response(_).
-spec init(_Args, machine(), undefined, handler_opts()) ->
result().
init(init_something, _Machine, _, _Opts) ->
#{
events => [init_event],
action => continue,
aux_state => #{some => <<"complex">>, aux_state => 1}
}.
-spec process_timeout(machine(), undefined, handler_opts()) ->
result().
process_timeout(_Machine, _, _Opts) ->
#{
events => [{timeout_event, 1}],
action => unset_timer, % why not
aux_state => #{some => <<"other complex">>, aux_state => 1}
}.
-spec process_call(_Args, machine(), undefined, handler_opts()) ->
{response(), result()}.
process_call(do_something, _Machine, _, _Opts) ->
{done, #{
events => [call_event],
aux_state => undefined
}};
process_call(fail, _Machine, _, _Opts) ->
erlang:error(fail).
-spec process_repair(_Args, machine(), undefined, handler_opts()) ->
no_return().
process_repair(repair_something, #{history := History}, _, _Opts) ->
{ok, {done, #{events => [{count_events, erlang:length(History)}]}}}.
%% machinery_mg_schema callbacks
-spec marshal(machinery_mg_schema:t(), any()) ->
machinery_msgpack:t().
marshal(T, V) when
T =:= {aux_state, 1} orelse
T =:= {event, 2} orelse
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {response, call} orelse
T =:= {response, {repair, success}} orelse
T =:= {response, {repair, failure}}
->
{bin, erlang:term_to_binary(V)}.
-spec unmarshal(machinery_mg_schema:t(), machinery_msgpack:t()) ->
any().
unmarshal(T, V) when
T =:= {aux_state, 1} orelse
T =:= {event, 2} orelse
T =:= {args, init} orelse
T =:= {args, call} orelse
T =:= {args, repair} orelse
T =:= {response, call} orelse
T =:= {response, {repair, failure}}
->
{bin, EncodedV} = V,
erlang:binary_to_term(EncodedV);
unmarshal({aux_state, undefined}, {bin, <<>>}) ->
% initial aux_state
undefined;
unmarshal({response, {repair, success}}, {bin, <<"ok">>}) ->
% mg repair migration artefact
done.
-spec get_version(machinery_mg_schema:vt()) ->
machinery_mg_schema:version().
get_version(aux_state) ->
1;
get_version(event) ->
2.
%% Helpers
start(ID, Args, C) ->
machinery:start(namespace(), ID, Args, get_backend(C)).
call(Ref, Args, C) ->
machinery:call(namespace(), Ref, Args, get_backend(C)).
repair(Ref, Args, Range, C) ->
machinery:repair(namespace(), Ref, Range, Args, get_backend(C)).
namespace() ->
general.
unique() ->
genlib:unique().
start_backend(C) ->
{ok, _PID} = supervisor:start_child(
?config(group_sup, C),
child_spec(C)
).
-spec child_spec(config()) ->
supervisor:child_spec().
child_spec(C) ->
child_spec(?config(backend, C), C).
-spec child_spec(atom(), config()) ->
supervisor:child_spec().
child_spec(machinery_mg_backend, _C) ->
BackendConfig = #{
path => <<"/v1/stateproc">>,
backend_config => #{
schema => ?MODULE
}
},
Handler = {?MODULE, BackendConfig},
Routes = machinery_mg_backend:get_routes(
[Handler],
#{event_handler => woody_event_handler_default}
),
ServerConfig = #{
ip => {0, 0, 0, 0},
port => 8022
},
machinery_utils:woody_child_spec(machinery_mg_backend, Routes, ServerConfig).
-spec get_backend(config()) ->
machinery_mg_backend:backend().
get_backend(C) ->
get_backend(?config(backend, C), C).
-spec get_backend(atom(), config()) ->
machinery_mg_backend:backend().
get_backend(machinery_mg_backend, C) ->
machinery_mg_backend:new(
ct_helper:get_woody_ctx(C),
#{
client => #{
url => <<"http://machinegun:8022/v1/automaton">>,
event_handler => woody_event_handler_default
},
schema => ?MODULE
}
).