Mock test on WAPI (#65)

This commit is contained in:
Артем 2019-02-21 17:34:37 +03:00 committed by GitHub
parent c6bc6368ab
commit 0f506bd4e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 851 additions and 76 deletions

View File

@ -108,7 +108,11 @@ start_app(wapi = AppName) ->
wapi => {pem_file, "/opt/wapi/config/private.pem"}
}
}
}},
}}
]), #{}};
start_app(wapi_woody_client = AppName) ->
{start_app_with(AppName, [
{service_urls, #{
cds_storage => "http://cds:8022/v1/storage",
identdoc_storage => "http://cds:8022/v1/identity_document_storage",

View File

@ -16,7 +16,8 @@
default_termset => dmsl_domain_thrift:'TermSet'(),
company_termset => dmsl_domain_thrift:'TermSet'(),
payment_inst_identity_id => id(),
provider_identity_id => id()
provider_identity_id => id(),
optional_apps => list()
}.
-opaque system() :: #{
started_apps := [atom()],
@ -93,8 +94,7 @@ start_processing_apps(Options) ->
{withdrawal,
#{provider => withdrawal_provider_config(Options)}
}
]},
wapi
]}
]),
SuiteSup = ct_sup:start(),
BeOpts = machinery_backend_options(Options),
@ -133,11 +133,17 @@ start_processing_apps(Options) ->
}
)),
Processing = #{
started_apps => StartedApps,
started_apps => StartedApps ++ start_optional_apps(Options),
suite_sup => SuiteSup
},
{ok, Processing}.
start_optional_apps(#{optional_apps := Apps})->
{StartedApps, _StartupCtx} = ct_helper:start_apps(Apps),
StartedApps;
start_optional_apps(_)->
[].
setup_dominant(Options, C) ->
ok = ct_domain_config:upsert(domain_config(Options, C)).

View File

@ -9,6 +9,7 @@
public_key,
genlib,
woody,
wapi_woody_client,
erl_health,
lager,
dmsl,
@ -16,6 +17,7 @@
fistful_reporter_proto,
file_storage_proto,
swag_server_wallet,
scoper,
jose,
cowboy_cors,
cowboy_access_log,

View File

@ -104,18 +104,9 @@ create_woody_context(Tag, #{'X-Request-ID' := RequestID}, AuthContext, Opts) ->
_ = lager:debug("Created TraceID for the request"),
woody_user_identity:put(
collect_user_identity(AuthContext, Opts),
woody_context:new(RpcID, undefined, get_deadline(Tag))
woody_context:new(RpcID, undefined, wapi_woody_client:get_service_deadline(Tag))
).
get_deadline(Tag) ->
ApiDeadlines = genlib_app:env(wapi, api_deadlines, #{}),
case maps:get(Tag, ApiDeadlines, undefined) of
Timeout when is_integer(Timeout) andalso Timeout >= 0 ->
woody_deadline:from_timeout(Timeout);
undefined ->
undefined
end.
attach_deadline(#{'X-Request-Deadline' := undefined}, Context) ->
Context;
attach_deadline(#{'X-Request-Deadline' := Header}, Context) ->

View File

@ -391,62 +391,87 @@ get_currency(CurrencyId, _Context) ->
%% Reports
-spec create_report(params(), ctx()) ->
result(map(), invalid_request | invalid_contract).
-spec create_report(params(), ctx()) -> result(map(),
{identity, unauthorized} |
{identity, notfound} |
invalid_request |
invalid_contract
).
create_report(#{
identityID := IdentityID,
'ReportParams' := ReportParams
}, Context) ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
Req = create_report_request(#{
party_id => wapi_handler_utils:get_owner(Context),
contract_id => ContractID,
from_time => get_time(<<"fromTime">>, ReportParams),
to_time => get_time(<<"toTime">>, ReportParams)
}),
Call = {fistful_report, 'GenerateReport', [Req, maps:get(<<"reportType">>, ReportParams)]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, ReportID} ->
get_report(ReportID, ContractID, Context);
{exception, #'InvalidRequest'{}} ->
{error, invalid_request};
{exception, #ff_reports_ContractNotFound{}} ->
{error, invalid_contract}
end.
do(fun () ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
Req = create_report_request(#{
party_id => wapi_handler_utils:get_owner(Context),
contract_id => ContractID,
from_time => get_time(<<"fromTime">>, ReportParams),
to_time => get_time(<<"toTime">>, ReportParams)
}),
Call = {fistful_report, 'GenerateReport', [Req, maps:get(<<"reportType">>, ReportParams)]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, ReportID} ->
unwrap(get_report(contractID, ReportID, ContractID, Context));
{exception, #'InvalidRequest'{}} ->
throw(invalid_request);
{exception, #ff_reports_ContractNotFound{}} ->
throw(invalid_contract)
end
end).
-spec get_report(integer(), binary(), ctx()) -> result().
-spec get_report(integer(), binary(), ctx()) -> result(map(),
{identity, unauthorized} |
{identity, notfound} |
notfound
).
get_report(ReportID, IdentityID, Context) ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
PartyID = wapi_handler_utils:get_owner(Context),
Call = {fistful_report, 'GetReport', [PartyID, ContractID, ReportID]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, Report} ->
do(fun () -> to_swag(report_object, Report) end);
{exception, #ff_reports_ReportNotFound{}} ->
{error, notfound}
end.
get_report(identityID, ReportID, IdentityID, Context).
-spec get_reports(params(), ctx()) ->
result(map(), invalid_request | {dataset_too_big, integer()}).
get_report(identityID, ReportID, IdentityID, Context) ->
do(fun () ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
unwrap(get_report(contractID, ReportID, ContractID, Context))
end);
get_report(contractID, ReportID, ContractID, Context) ->
do(fun () ->
PartyID = wapi_handler_utils:get_owner(Context),
Call = {fistful_report, 'GetReport', [PartyID, ContractID, ReportID]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, Report} ->
to_swag(report_object, Report);
{exception, #ff_reports_ReportNotFound{}} ->
throw(notfound)
end
end).
-spec get_reports(params(), ctx()) -> result(map(),
{identity, unauthorized} |
{identity, notfound} |
invalid_request |
{dataset_too_big, integer()}
).
get_reports(#{
identityID := IdentityID
} = Params, Context) ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
Req = create_report_request(#{
party_id => wapi_handler_utils:get_owner(Context),
contract_id => ContractID,
from_time => get_time(fromTime, Params),
to_time => get_time(toTime, Params)
}),
Call = {fistful_report, 'GetReports', [Req, [genlib:to_binary(maps:get(type, Params))]]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, ReportList} ->
do(fun () -> to_swag({list, report_object}, ReportList) end);
{exception, #'InvalidRequest'{}} ->
{error, invalid_request};
{exception, #ff_reports_DatasetTooBig{limit = Limit}} ->
{error, {dataset_too_big, Limit}}
end.
do(fun () ->
ContractID = get_contract_id_from_identity(IdentityID, Context),
Req = create_report_request(#{
party_id => wapi_handler_utils:get_owner(Context),
contract_id => ContractID,
from_time => get_time(fromTime, Params),
to_time => get_time(toTime, Params)
}),
Call = {fistful_report, 'GetReports', [Req, [genlib:to_binary(maps:get(type, Params))]]},
case wapi_handler_utils:service_call(Call, Context) of
{ok, ReportList} ->
to_swag({list, report_object}, ReportList);
{exception, #'InvalidRequest'{}} ->
throw(invalid_request);
{exception, #ff_reports_DatasetTooBig{limit = Limit}} ->
throw({dataset_too_big, Limit})
end
end).
-spec download_file(binary(), binary(), ctx()) -> result().
download_file(FileID, ExpiresAt, Context) ->
@ -1147,7 +1172,7 @@ to_swag(report_object, #ff_reports_Report{
<<"createdAt">> => to_swag(timestamp, CreatedAt),
<<"status">> => to_swag(report_status, Status),
<<"type">> => Type,
<<"files">> => to_swag(report_files, Files)
<<"files">> => to_swag(report_files, {files, Files})
});
to_swag(report_status, pending) ->
<<"pending">>;
@ -1155,10 +1180,12 @@ to_swag(report_status, created) ->
<<"created">>;
to_swag(report_status, canceled) ->
<<"canceled">>;
to_swag(report_files, Files) ->
to_swag(report_files, {files, undefined}) ->
[];
to_swag(report_files, {files, Files}) ->
to_swag({list, report_file}, Files);
to_swag(report_file, File) ->
File;
#{<<"id">> => File};
to_swag({list, Type}, List) ->
lists:map(fun(V) -> to_swag(Type, V) end, List);

View File

@ -349,6 +349,16 @@ process_request('GetCurrency', #{'currencyID' := CurrencyId}, Context, _Opts) ->
process_request('CreateReport', Params, Context, _Opts) ->
case wapi_wallet_ff_backend:create_report(Params, Context) of
{ok, Report} -> wapi_handler_utils:reply_ok(201, Report);
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NoMatch">>,
<<"name">> => <<"timestamps">>,
@ -361,16 +371,36 @@ process_request('CreateReport', Params, Context, _Opts) ->
})
end;
process_request('GetReport', #{
contractID := ContractId,
identityID := IdentityID,
reportID := ReportId
}, Context, _Opts) ->
case wapi_wallet_ff_backend:get_report(ReportId, ContractId, Context) of
case wapi_wallet_ff_backend:get_report(ReportId, IdentityID, Context) of
{ok, Report} -> wapi_handler_utils:reply_ok(200, Report);
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, notfound} -> wapi_handler_utils:reply_ok(404)
end;
process_request('GetReports', Params, Context, _Opts) ->
case wapi_wallet_ff_backend:get_reports(Params, Context) of
{ok, ReportList} -> wapi_handler_utils:reply_ok(200, ReportList);
{error, {identity, notfound}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, {identity, unauthorized}} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NotFound">>,
<<"name">> => <<"identity">>,
<<"description">> => <<"identity not found">>
});
{error, invalid_request} -> wapi_handler_utils:reply_ok(400, #{
<<"errorType">> => <<"NoMatch">>,
<<"name">> => <<"timestamps">>,

View File

@ -64,7 +64,11 @@ init_per_suite(C) ->
ct_helper:makeup_cfg([
ct_helper:test_case_name(init),
ct_payment_system:setup(#{
default_termset => get_default_termset()
default_termset => get_default_termset(),
optional_apps => [
wapi,
wapi_woody_client
]
})
], C).
@ -334,9 +338,9 @@ get_withdrawal(C) ->
{save_config, Cfg}.
woody_retry_test(C) ->
Urls = application:get_env(wapi, service_urls, #{}),
Urls = application:get_env(wapi_woody_client, service_urls, #{}),
ok = application:set_env(
wapi,
wapi_woody_client,
service_urls,
Urls#{fistful_stat => "http://spanish.inquision/fistful_stat"}
),
@ -358,7 +362,7 @@ woody_retry_test(C) ->
T2 = erlang:monotonic_time(),
Time = erlang:convert_time_unit(T2 - T1, native, micro_seconds),
true = (Time > 3000000) and (Time < 6000000),
ok = application:set_env(wapi, service_urls, Urls).
ok = application:set_env(wapi_woody_client, service_urls, Urls).
%%

View File

@ -0,0 +1,242 @@
-module(wapi_ct_helper).
-include_lib("common_test/include/ct.hrl").
-include_lib("wapi_wallet_dummy_data.hrl").
-include_lib("dmsl/include/dmsl_domain_config_thrift.hrl").
-export([init_suite/2]).
-export([start_app/1]).
-export([start_app/2]).
-export([start_wapi/1]).
-export([issue_token/2]).
-export([issue_token/3]).
-export([get_context/1]).
-export([get_keysource/2]).
-export([start_mocked_service_sup/1]).
-export([stop_mocked_service_sup/1]).
-export([mock_services/2]).
-export([mock_services_/2]).
-export([get_lifetime/0]).
-define(WAPI_IP, "::").
-define(WAPI_PORT, 8080).
-define(WAPI_HOST_NAME, "localhost").
-define(WAPI_URL, ?WAPI_HOST_NAME ++ ":" ++ integer_to_list(?WAPI_PORT)).
%%
-type config() :: [{atom(), any()}].
-type app_name() :: atom().
-spec init_suite(module(), config()) ->
config().
init_suite(Module, Config) ->
SupPid = start_mocked_service_sup(Module),
Apps1 =
start_app(lager) ++
start_app(scoper) ++
start_app(woody),
ServiceURLs = mock_services_([
{
'Repository',
{dmsl_domain_config_thrift, 'Repository'},
fun('Checkout', _) -> {ok, ?SNAPSHOT} end
}
], SupPid),
Apps2 =
start_app(dmt_client, [{max_cache_size, #{}}, {service_urls, ServiceURLs}, {cache_update_interval, 50000}]) ++
start_wapi(Config),
[{apps, lists:reverse(Apps2 ++ Apps1)}, {suite_test_sup, SupPid} | Config].
-spec start_app(app_name()) ->
[app_name()].
start_app(lager = AppName) ->
start_app(AppName, [
{async_threshold, 1},
{async_threshold_window, 0},
{error_logger_hwm, 600},
{suppress_application_start_stop, false},
{suppress_supervisor_start_stop, false},
{handlers, [
{lager_common_test_backend, [debug, {lager_logstash_formatter, []}]}
]}
]);
start_app(scoper = AppName) ->
start_app(AppName, [
{storage, scoper_storage_lager}
]);
start_app(woody = AppName) ->
start_app(AppName, [
{acceptors_pool_size, 4}
]);
start_app(wapi_woody_client = AppName) ->
start_app(AppName, [
{service_urls, #{
cds_storage => "http://cds:8022/v1/storage",
identdoc_storage => "http://cds:8022/v1/identity_document_storage",
fistful_stat => "http://fistful-magista:8022/stat"
}},
{service_retries, #{
fistful_stat => #{
'GetWallets' => {linear, 3, 1000},
'_' => finish
}
}}
]);
start_app(AppName) ->
genlib_app:start_application(AppName).
-spec start_app(app_name(), list()) ->
[app_name()].
start_app(AppName, Env) ->
genlib_app:start_application_with(AppName, Env).
-spec start_wapi(config()) ->
[app_name()].
start_wapi(Config) ->
start_app(wapi, [
{ip, ?WAPI_IP},
{port, ?WAPI_PORT},
{realm, <<"external">>},
{public_endpoint, <<"localhost:8080">>},
{authorizers, #{
jwt => #{
signee => wapi,
keyset => #{
wapi => {pem_file, get_keysource("keys/local/private.pem", Config)}
}
}
}}
]).
-spec get_keysource(_, config()) ->
_.
get_keysource(Key, Config) ->
filename:join(?config(data_dir, Config), Key).
-spec issue_token(_, _) ->
{ok, binary()} |
{error,
nonexistent_signee
}.
issue_token(ACL, LifeTime) ->
issue_token(?STRING, ACL, LifeTime).
-spec issue_token(_, _, _) ->
{ok, binary()} |
{error,
nonexistent_signee
}.
issue_token(PartyID, ACL, LifeTime) ->
Claims = #{?STRING => ?STRING},
wapi_authorizer_jwt:issue({{PartyID, wapi_acl:from_list(ACL)}, Claims}, LifeTime).
-spec get_context(binary()) ->
wapi_client_lib:context().
get_context(Token) ->
wapi_client_lib:get_context(?WAPI_URL, Token, 10000, ipv4).
% TODO move it to `wapi_dummy_service`, looks more appropriate
-spec start_mocked_service_sup(module()) ->
pid().
start_mocked_service_sup(Module) ->
{ok, SupPid} = supervisor:start_link(Module, []),
_ = unlink(SupPid),
SupPid.
-spec stop_mocked_service_sup(pid()) ->
_.
stop_mocked_service_sup(SupPid) ->
exit(SupPid, shutdown).
-spec mock_services(_, _) ->
_.
mock_services(Services, SupOrConfig) ->
start_woody_client(mock_services_(Services, SupOrConfig)).
start_woody_client(ServiceURLs) ->
ok = application:set_env(
wapi_woody_client,
service_urls,
ServiceURLs
),
start_app(wapi_woody_client, []).
-spec mock_services_(_, _) ->
_.
% TODO need a better name
mock_services_(Services, Config) when is_list(Config) ->
mock_services_(Services, ?config(test_sup, Config));
mock_services_(Services, SupPid) when is_pid(SupPid) ->
Name = lists:map(fun get_service_name/1, Services),
Port = get_random_port(),
{ok, IP} = inet:parse_address(?WAPI_IP),
ChildSpec = woody_server:child_spec(
{dummy, Name},
#{
ip => IP,
port => Port,
event_handler => scoper_woody_event_handler,
handlers => lists:map(fun mock_service_handler/1, Services)
}
),
{ok, _} = supervisor:start_child(SupPid, ChildSpec),
lists:foldl(
fun (Service, Acc) ->
ServiceName = get_service_name(Service),
Acc#{ServiceName => make_url(ServiceName, Port)}
end,
#{},
Services
).
get_service_name({ServiceName, _Fun}) ->
ServiceName;
get_service_name({ServiceName, _WoodyService, _Fun}) ->
ServiceName.
mock_service_handler({ServiceName, Fun}) ->
mock_service_handler(ServiceName, wapi_woody_client:get_service_modname(ServiceName), Fun);
mock_service_handler({ServiceName, WoodyService, Fun}) ->
mock_service_handler(ServiceName, WoodyService, Fun).
mock_service_handler(ServiceName, WoodyService, Fun) ->
{make_path(ServiceName), {WoodyService, {wapi_dummy_service, #{function => Fun}}}}.
% TODO not so failproof, ideally we need to bind socket first and then give to a ranch listener
get_random_port() ->
rand:uniform(32768) + 32767.
make_url(ServiceName, Port) ->
iolist_to_binary(["http://", ?WAPI_HOST_NAME, ":", integer_to_list(Port), make_path(ServiceName)]).
make_path(ServiceName) ->
"/" ++ atom_to_list(ServiceName).
-spec get_lifetime() ->
map().
get_lifetime() ->
get_lifetime(0, 0, 7).
get_lifetime(YY, MM, DD) ->
#{
<<"years">> => YY,
<<"months">> => MM,
<<"days">> => DD
}.

View File

@ -0,0 +1,10 @@
-module(wapi_dummy_service).
-behaviour(woody_server_thrift_handler).
-export([handle_function/4]).
-spec handle_function(woody:func(), woody:args(), woody_context:ctx(), #{}) ->
{ok, term()}.
handle_function(FunName, Args, _, #{function := Fun}) ->
Fun(FunName, Args).

View File

@ -0,0 +1,144 @@
-define(STRING, <<"TEST">>).
-define(RUB, <<"RUB">>).
-define(USD, <<"USD">>).
-define(BANKID_RU, <<"PUTIN">>).
-define(BANKID_US, <<"TRAMP">>).
-define(WALLET_TOOL, <<"TOOL">>).
-define(JSON, <<"{}">>).
-define(INTEGER, 10000).
-define(INTEGER_BINARY, <<"10000">>).
-define(TIMESTAMP, <<"2016-03-22T06:12:27Z">>).
-define(MD5, <<"033BD94B1168D7E4F0D644C3C95E35BF">>).
-define(SHA256, <<"94EE059335E587E501CC4BF90613E0814F00A7B08BC7C648FD865A2AF6A22CC2">>).
-define(CASH, #'Cash'{
amount = ?INTEGER,
currency = #'CurrencyRef'{
symbolic_code = ?RUB
}
}).
-define(BLOCKING, unblocked).
-define(ACCOUNT, #account_Account{
id = ?STRING,
identity = ?STRING,
currency = #'CurrencyRef'{
symbolic_code = ?RUB
},
accounter_account_id = ?INTEGER
}).
-define(WALLET_STATE, #wlt_WalletState{
id = ?STRING,
name = ?STRING,
blocking = ?BLOCKING,
account = ?ACCOUNT
}).
-define(REPORT_ID, ?INTEGER).
-define(REPORT_EXT(Status, FilesList), #ff_reports_Report{
report_id = ?INTEGER,
time_range = #ff_reports_ReportTimeRange{
from_time = ?TIMESTAMP,
to_time = ?TIMESTAMP
},
created_at = ?TIMESTAMP,
report_type = <<"withdrawalRegistry">>,
status = Status,
file_data_ids = FilesList
}).
-define(REPORT_WITH_STATUS(Status), ?REPORT_EXT(Status, [?STRING, ?STRING,?STRING])).
-define(REPORT, ?REPORT_WITH_STATUS(created)).
-define(SNAPSHOT, #'Snapshot'{
version = ?INTEGER,
domain = #{
{category, #domain_CategoryRef{id = ?INTEGER}} =>
{category, #domain_CategoryObject{
ref = #domain_CategoryRef{id = ?INTEGER},
data = #domain_Category{
name = ?STRING,
description = ?STRING
}
}},
{business_schedule, #domain_BusinessScheduleRef{id = ?INTEGER}} =>
{business_schedule, #domain_BusinessScheduleObject{
ref = #domain_BusinessScheduleRef{id = ?INTEGER},
data = #domain_BusinessSchedule{
name = ?STRING,
description = ?STRING,
schedule = #'Schedule'{
year = {every, #'ScheduleEvery'{}},
month = {every, #'ScheduleEvery'{}},
day_of_month = {every, #'ScheduleEvery'{}},
day_of_week = {every, #'ScheduleEvery'{}},
hour = {every, #'ScheduleEvery'{}},
minute = {every, #'ScheduleEvery'{}},
second = {every, #'ScheduleEvery'{}}
},
delay = #'TimeSpan'{},
policy = #domain_PayoutCompilationPolicy{
assets_freeze_for = #'TimeSpan'{}
}
}
}},
{globals, #domain_GlobalsRef{}} =>
{globals, #domain_GlobalsObject{
ref = #domain_GlobalsRef{},
data = #domain_Globals{
external_account_set = {value, #domain_ExternalAccountSetRef{id = ?INTEGER}},
payment_institutions = [#domain_PaymentInstitutionRef{id = ?INTEGER}]
}
}},
{payment_institution, #domain_PaymentInstitutionRef{id = ?INTEGER}} =>
{payment_institution, #domain_PaymentInstitutionObject{
ref = #domain_PaymentInstitutionRef{id = ?INTEGER},
data = #domain_PaymentInstitution{
name = ?STRING,
description = ?STRING,
system_account_set = {value, #domain_SystemAccountSetRef{id = ?INTEGER}},
default_contract_template = {value, #domain_ContractTemplateRef{id = ?INTEGER}},
providers = {value, []},
inspector = {value, #domain_InspectorRef{id = ?INTEGER}},
realm = test,
residences = [rus]
}
}}
}
}).
-define(TERM_SET, #domain_TermSet{
payouts = ?PAYOUTS_SERVICE_TERMS,
payments = ?PAYMENTS_SERVICE_TERMS
}).
-define(PAYOUTS_SERVICE_TERMS, #domain_PayoutsServiceTerms{}).
-define(PAYMENTS_SERVICE_TERMS, #domain_PaymentsServiceTerms{
payment_methods = {value,
[
#domain_PaymentMethodRef{
id = {bank_card, mastercard}
},
#domain_PaymentMethodRef{
id = {bank_card, visa}
},
#domain_PaymentMethodRef{
id = {tokenized_bank_card, #domain_TokenizedBankCard{
payment_system = mastercard,
token_provider = applepay
}}
},
#domain_PaymentMethodRef{
id = {tokenized_bank_card, #domain_TokenizedBankCard{
payment_system = visa,
token_provider = applepay
}}
}
]
}
}).

View File

@ -0,0 +1,290 @@
-module(wapi_wallet_tests_SUITE).
-include_lib("common_test/include/ct.hrl").
-include_lib("dmsl/include/dmsl_domain_config_thrift.hrl").
-include_lib("fistful_reporter_proto/include/ff_reporter_reports_thrift.hrl").
-include_lib("file_storage_proto/include/fs_file_storage_thrift.hrl").
-include_lib("fistful_proto/include/ff_proto_base_thrift.hrl").
-include_lib("jose/include/jose_jwk.hrl").
-include_lib("wapi_wallet_dummy_data.hrl").
-export([all/0]).
-export([groups/0]).
-export([init_per_suite/1]).
-export([end_per_suite/1]).
-export([init_per_group/2]).
-export([end_per_group/2]).
-export([init_per_testcase/2]).
-export([end_per_testcase/2]).
-export([init/1]).
-export([
create_report_ok_test/1,
get_report_ok_test/1,
get_reports_ok_test/1,
reports_with_wrong_identity_ok_test/1,
download_file_ok_test/1
]).
-define(badresp(Code), {error, {invalid_response_code, Code}}).
-define(emptyresp(Code), {error, {Code, #{}}}).
-type test_case_name() :: atom().
-type config() :: [{atom(), any()}].
-type group_name() :: atom().
-behaviour(supervisor).
-spec init([]) ->
{ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
{ok, {#{strategy => one_for_all, intensity => 1, period => 1}, []}}.
-spec all() ->
[test_case_name()].
all() ->
[
{group, base}
].
-spec groups() ->
[{group_name(), list(), [test_case_name()]}].
groups() ->
[
{base, [],
[
create_report_ok_test,
get_report_ok_test,
get_reports_ok_test,
reports_with_wrong_identity_ok_test,
download_file_ok_test
]
}
].
%%
%% starting/stopping
%%
-spec init_per_suite(config()) ->
config().
init_per_suite(Config) ->
%% TODO remove this after cut off wapi
ct_helper:makeup_cfg([
ct_helper:test_case_name(init),
ct_payment_system:setup(#{
optional_apps => [
wapi,
wapi_woody_client
]
})
], Config).
-spec end_per_suite(config()) ->
_.
end_per_suite(C) ->
%% TODO remove this after cut off wapi
ok = ct_payment_system:shutdown(C).
-spec init_per_group(group_name(), config()) ->
config().
init_per_group(Group, Config) when Group =:= base ->
ok = ff_woody_ctx:set(woody_context:new(<<"init_per_group/", (atom_to_binary(Group, utf8))/binary>>)),
Party = create_party(Config),
Token = issue_token(Party, [{[party], write}], unlimited),
Config1 = [{party, Party} | Config],
[{context, wapi_ct_helper:get_context(Token)} | Config1];
init_per_group(_, Config) ->
Config.
-spec end_per_group(group_name(), config()) ->
_.
end_per_group(_Group, _C) ->
ok.
-spec init_per_testcase(test_case_name(), config()) ->
config().
init_per_testcase(Name, C) ->
C1 = ct_helper:makeup_cfg([ct_helper:test_case_name(Name), ct_helper:woody_ctx()], C),
ok = ff_woody_ctx:set(ct_helper:get_woody_ctx(C1)),
[{test_sup, wapi_ct_helper:start_mocked_service_sup(?MODULE)} | C1].
-spec end_per_testcase(test_case_name(), config()) ->
config().
end_per_testcase(_Name, C) ->
ok = ff_woody_ctx:unset(),
wapi_ct_helper:stop_mocked_service_sup(?config(test_sup, C)),
ok.
%%% Tests
-spec create_report_ok_test(config()) ->
_.
create_report_ok_test(C) ->
{ok, Identity} = create_identity(C),
IdentityID = maps:get(<<"id">>, Identity),
wapi_ct_helper:mock_services([{fistful_report, fun
('GenerateReport', _) -> {ok, ?REPORT_ID};
('GetReport', _) -> {ok, ?REPORT}
end}], C),
{ok, _} = call_api(
fun swag_client_wallet_reports_api:create_report/3,
#{
binding => #{
<<"identityID">> => IdentityID
},
body => #{
<<"reportType">> => <<"withdrawalRegistry">>,
<<"fromTime">> => ?TIMESTAMP,
<<"toTime">> => ?TIMESTAMP
}
},
ct_helper:cfg(context, C)
).
-spec get_report_ok_test(config()) ->
_.
get_report_ok_test(C) ->
{ok, Identity} = create_identity(C),
IdentityID = maps:get(<<"id">>, Identity),
wapi_ct_helper:mock_services([{fistful_report, fun
('GetReport', _) -> {ok, ?REPORT}
end}], C),
{ok, _} = call_api(
fun swag_client_wallet_reports_api:get_report/3,
#{
binding => #{
<<"identityID">> => IdentityID,
<<"reportID">> => ?INTEGER
}
},
ct_helper:cfg(context, C)
).
-spec get_reports_ok_test(config()) ->
_.
get_reports_ok_test(C) ->
{ok, Identity} = create_identity(C),
IdentityID = maps:get(<<"id">>, Identity),
wapi_ct_helper:mock_services([{fistful_report, fun
('GetReports', _) -> {ok, [
?REPORT_EXT(pending, []),
?REPORT_EXT(created, undefined),
?REPORT_WITH_STATUS(canceled)]}
end}], C),
{ok, _} = call_api(
fun swag_client_wallet_reports_api:get_reports/3,
#{
binding => #{
<<"identityID">> => IdentityID
},
qs_val => #{
<<"fromTime">> => ?TIMESTAMP,
<<"toTime">> => ?TIMESTAMP,
<<"type">> => <<"withdrawalRegistry">>
}
},
ct_helper:cfg(context, C)
).
-spec reports_with_wrong_identity_ok_test(config()) ->
_.
reports_with_wrong_identity_ok_test(C) ->
IdentityID = <<"WrongIdentity">>,
wapi_ct_helper:mock_services([{fistful_report, fun
('GenerateReport', _) -> {ok, ?REPORT_ID};
('GetReport', _) -> {ok, ?REPORT};
('GetReports', _) -> {ok, [?REPORT, ?REPORT, ?REPORT]}
end}], C),
?emptyresp(400) = call_api(
fun swag_client_wallet_reports_api:create_report/3,
#{
binding => #{
<<"identityID">> => IdentityID
},
body => #{
<<"reportType">> => <<"withdrawalRegistry">>,
<<"fromTime">> => ?TIMESTAMP,
<<"toTime">> => ?TIMESTAMP
}
},
ct_helper:cfg(context, C)
),
?emptyresp(400) = call_api(
fun swag_client_wallet_reports_api:get_report/3,
#{
binding => #{
<<"identityID">> => IdentityID,
<<"reportID">> => ?INTEGER
}
},
ct_helper:cfg(context, C)
),
?emptyresp(400) = call_api(
fun swag_client_wallet_reports_api:get_reports/3,
#{
binding => #{
<<"identityID">> => IdentityID
},
qs_val => #{
<<"fromTime">> => ?TIMESTAMP,
<<"toTime">> => ?TIMESTAMP,
<<"type">> => <<"withdrawalRegistry">>
}
},
ct_helper:cfg(context, C)
).
-spec download_file_ok_test(config()) ->
_.
download_file_ok_test(C) ->
wapi_ct_helper:mock_services([{file_storage, fun
('GenerateDownloadUrl', _) -> {ok, ?STRING}
end}], C),
{ok, _} = call_api(
fun swag_client_wallet_downloads_api:download_file/3,
#{
binding => #{
<<"fileID">> => ?STRING
},
qs_val => #{
<<"expiresAt">> => ?TIMESTAMP
}
},
ct_helper:cfg(context, C)
).
%%
-spec call_api(function(), map(), wapi_client_lib:context()) ->
{ok, term()} | {error, term()}.
call_api(F, Params, Context) ->
{Url, PreparedParams, Opts} = wapi_client_lib:make_request(Context, Params),
Response = F(Url, PreparedParams, Opts),
wapi_client_lib:handle_response(Response).
create_party(_C) ->
ID = genlib:bsuuid(),
_ = ff_party:create(ID),
ID.
create_identity(C) ->
PartyID = ?config(party, C),
Params = #{
<<"provider">> => <<"good-one">>,
<<"class">> => <<"person">>,
<<"name">> => <<"HAHA NO2">>
},
wapi_wallet_ff_backend:create_identity(Params, create_auth_ctx(PartyID)).
create_auth_ctx(PartyID) ->
#{
swagger_context => #{auth_context => {{PartyID, empty}, empty}}
}.
issue_token(PartyID, ACL, LifeTime) ->
Claims = #{?STRING => ?STRING},
{ok, Token} = wapi_authorizer_jwt:issue({{PartyID, wapi_acl:from_list(ACL)}, Claims}, LifeTime),
Token.

View File

@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAK9fx7qOJT7Aoseu7KKgaLagBh3wvDzg7F/ZMtGbPFikJnnvRWvF
B5oEGbMPblvtF0/fjqfu+eqjP3Z1tUSn7TkCAwEAAQJABUY5KIgr4JZEjwLYxQ9T
9uIbLP1Xe/E7yqoqmBk2GGhSrPY0OeRkYnUVLcP96UPQhF63iuG8VF6uZ7oAPsq+
gQIhANZy3jSCzPjXYHRU1kRqQzpt2S+OqoEiqQ6YG1HrC/VxAiEA0Vq6JlQK2tOX
37SS00dK0Qog4Qi8dN73GliFQNP18EkCIQC4epSA48zkfJMzQBAbRraSuxDNApPX
BzQbo+pMrEDbYQIgY4AncQgIkLB4Qk5kah48JNYXglzQlQtTjiX8Ty9ueGECIQCM
GD3UbQKiA0gf5plBA24I4wFVKxxa4wXbW/7SfP6XmQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,10 @@
{application, wapi_woody_client , [
{description, "Woody client for calling backend"},
{vsn, "0.1.0"},
{applications, [
kernel,
stdlib,
woody,
genlib
]}
]}.

View File

@ -4,9 +4,10 @@
-export([call_service/5]).
-export([get_service_modname/1]).
-export([get_service_deadline/1]).
%%
-define(APP, wapi).
-define(APP, wapi_woody_client).
-type service_name() :: atom().
@ -84,6 +85,8 @@ get_service_modname(file_storage) ->
%% get_service_modname(webhook_manager) ->
%% {dmsl_webhooker_thrift, 'WebhookManager'}.
-spec get_service_deadline(service_name()) -> undefined | woody_deadline:deadline().
get_service_deadline(ServiceName) ->
ServiceDeadlines = genlib_app:env(?APP, api_deadlines, #{}),
case maps:get(ServiceName, ServiceDeadlines, undefined) of

View File

@ -131,6 +131,13 @@
}
}
}},
{health_checkers, [
{erl_health, service , [<<"wapi">>]}
]},
{max_deadline, 60000} % milliseconds
]},
{wapi_woody_client, [
{service_urls, #{
webhook_manager => "http://hooker:8022/hook",
cds_storage => "http://cds:8022/v1/storage",
@ -140,9 +147,6 @@
{api_deadlines, #{
wallet => 5000 % millisec
}},
{health_checkers, [
{erl_health, service , [<<"wapi">>]}
]},
{service_retries, #{
party_management => #{
% function => retry strategy
@ -153,8 +157,7 @@
'Get' => {linear, 3, 1000},
'_' => finish
}
}},
{max_deadline, 60000} % milliseconds
}}
]},
{ff_server, [