From 1ad31582776b1ea70ef759f521d67ba2764238d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Wed, 4 Feb 2026 20:41:41 +0300 Subject: [PATCH 01/11] wip --- src/wapi_wallet_handler.erl | 19 +++ src/wapi_wallet_limits.erl | 284 +++++++++++++++++++++++++++++++ test/wapi_ct_helper.erl | 14 +- test/wapi_wallet_tests_SUITE.erl | 196 ++++++++++++++++++++- 4 files changed, 510 insertions(+), 3 deletions(-) create mode 100644 src/wapi_wallet_limits.erl diff --git a/src/wapi_wallet_handler.erl b/src/wapi_wallet_handler.erl index f15037e..1ad8844 100644 --- a/src/wapi_wallet_handler.erl +++ b/src/wapi_wallet_handler.erl @@ -108,6 +108,25 @@ prepare('GetWalletAccount' = OperationID, #{'walletID' := WalletID}, Context, _O end end, {ok, #{authorize => Authorize, process => Process}}; +prepare('GetWalletCashLimits' = OperationID, #{'partyID' := PartyID, 'walletID' := WalletID}, Context, _Opts) -> + AuthContext = build_auth_context([{wallet, WalletID}], [], Context), + Authorize = fun() -> + Prototypes = [ + {operation, build_prototype_for(operation, #{id => OperationID}, AuthContext)}, + {wallet, build_prototype_for(wallet, [], AuthContext)} + ], + Resolution = wapi_auth:authorize_operation(Prototypes, Context), + {ok, Resolution} + end, + Process = fun() -> + case wapi_wallet_limits:get_wallet_limits(PartyID, WalletID, Context) of + {ok, Limits} -> + wapi_handler_utils:reply_ok(200, Limits); + {error, {wallet, notfound}} -> + wapi_handler_utils:reply_ok(404) + end + end, + {ok, #{authorize => Authorize, process => Process}}; %% Destinations prepare('ListDestinations' = OperationID, Req0, Context, _Opts) -> AuthContext = build_auth_context( diff --git a/src/wapi_wallet_limits.erl b/src/wapi_wallet_limits.erl new file mode 100644 index 0000000..ff3eb41 --- /dev/null +++ b/src/wapi_wallet_limits.erl @@ -0,0 +1,284 @@ +-module(wapi_wallet_limits). +% +% IMPORTANT: calculation is approximate and does NOT cover some cases: +% - selectors {decisions, _} for terminals are not handled +% - exclusive bounds are treated as inclusive (strictness lost) +% - terminals with cash_limit=decisions are ignored (no provider fallback) +% - when methods are missing, response is empty even if limits are computed +% + +-include_lib("damsel/include/dmsl_domain_thrift.hrl"). + +-export([get_wallet_limits/3]). + +-type handler_context() :: wapi_wallet_backend:handler_context(). + +-spec get_wallet_limits(binary(), binary(), handler_context()) -> {ok, [map()]} | {error, {wallet, notfound}}. +get_wallet_limits(PartyID, WalletID, _Context) -> + case get_wallet_config(PartyID, WalletID) of + {error, notfound} -> + {error, {wallet, notfound}}; + {ok, WalletConfig} -> + Currency = WalletConfig#domain_WalletConfig.account#domain_WalletAccount.currency, + Revision = wapi_domain_backend:head(), + WalletTerms = get_wallet_terms(WalletConfig#domain_WalletConfig.terms, Revision), + Methods = extract_withdrawal_methods(WalletTerms), + WithdrawalLimit = extract_withdrawal_limit(WalletTerms, Currency), + TerminalRefs = get_withdrawal_terminal_refs(WalletConfig#domain_WalletConfig.payment_institution, Revision), + TermLimit = aggregate_terminal_limits(TerminalRefs, Currency, Revision), + EffectiveLimit = intersect_optional(WithdrawalLimit, TermLimit), + Limits = lists:flatmap( + fun(Method) -> + encode_limits(Currency, Method, EffectiveLimit) + end, + Methods + ), + {ok, Limits} + end. + +get_wallet_config(PartyID, WalletID) -> + ObjectRef = {wallet_config, #domain_WalletConfigRef{id = WalletID}}, + case wapi_domain_backend:get_object(ObjectRef) of + {ok, #domain_WalletConfig{party_ref = #domain_PartyConfigRef{id = PartyID}} = WalletConfig} -> + {ok, WalletConfig}; + _ -> + {error, notfound} + end. + +get_wallet_terms(TermsRef, Revision) -> + case wapi_domain_backend:get_object(Revision, {term_set_hierarchy, TermsRef}) of + {ok, #domain_TermSetHierarchy{term_set = TermSet}} -> + TermSet; + _ -> + undefined + end. + +extract_withdrawal_methods(#domain_TermSet{ + wallets = #domain_WalletServiceTerms{ + withdrawals = #domain_WithdrawalServiceTerms{methods = {value, MethodRefs}} + } +}) -> + Methods = [normalize_withdrawal_method(Type) || #domain_PaymentMethodRef{id = {Type, _ID}} <- MethodRefs], + lists:usort(Methods); +extract_withdrawal_methods(_) -> + []. + +extract_withdrawal_limit(undefined, _Currency) -> + undefined; +extract_withdrawal_limit(#domain_TermSet{wallets = undefined}, _Currency) -> + undefined; +extract_withdrawal_limit(#domain_TermSet{wallets = Wallets}, Currency) -> + case Wallets#domain_WalletServiceTerms.withdrawals of + undefined -> + undefined; + #domain_WithdrawalServiceTerms{cash_limit = CashLimitSelector} -> + range_from_selector(CashLimitSelector, Currency) + end. + +get_withdrawal_terminal_refs(PiRef, Revision) -> + case wapi_domain_backend:get_object(Revision, {payment_institution, PiRef}) of + {ok, #domain_PaymentInstitution{withdrawal_routing_rules = Rules}} -> + lists:usort(collect_ruleset_terminals(Rules, Revision)); + _ -> + [] + end. + +collect_ruleset_terminals(undefined, _Revision) -> + []; +collect_ruleset_terminals(#domain_RoutingRules{policies = PoliciesRef}, Revision) -> + collect_ruleset_terminals(PoliciesRef, Revision, sets:new()). + +collect_ruleset_terminals(#domain_RoutingRulesetRef{} = Ref, Revision, Seen) -> + case sets:is_element(Ref, Seen) of + true -> + []; + false -> + Seen1 = sets:add_element(Ref, Seen), + case wapi_domain_backend:get_object(Revision, {routing_rules, Ref}) of + {ok, #domain_RoutingRuleset{} = Ruleset} -> + collect_ruleset_terminals(Ruleset, Revision, Seen1); + _ -> + [] + end + end; +collect_ruleset_terminals(#domain_RoutingRuleset{decisions = Decisions}, Revision, Seen) -> + collect_terminals_from_decisions(Decisions, Revision, Seen). + +collect_terminals_from_decisions({candidates, Candidates}, _Revision, _Seen) -> + [C#domain_RoutingCandidate.terminal || C <- Candidates]; +collect_terminals_from_decisions({delegates, Delegates}, Revision, Seen) -> + lists:flatmap( + fun(#domain_RoutingDelegate{ruleset = Ref}) -> + collect_ruleset_terminals(Ref, Revision, Seen) + end, + Delegates + ). + +aggregate_terminal_limits([], _Currency, _Revision) -> + undefined; +aggregate_terminal_limits([TerminalRef | TerminalRefs], Currency, Revision) -> + Limit0 = get_terminal_limit(TerminalRef, Currency, Revision), + log_terminal_terms(TerminalRef, Limit0), + lists:foldl( + fun(TerminalRef1, LimitAcc) -> + Limit = get_terminal_limit(TerminalRef1, Currency, Revision), + log_terminal_terms(TerminalRef1, Limit), + union_optional(LimitAcc, Limit) + end, + Limit0, + TerminalRefs + ). + +log_terminal_terms(TerminalRef, Limit) -> + logger:debug( + "Wallet cash limits for terminal ~p: limit=~p", + [TerminalRef, Limit] + ). + +get_terminal_limit(TerminalRef, Currency, Revision) -> + case get_and_check_terminal(TerminalRef, Revision) of + {ok, #domain_Terminal{provider_ref = ProviderRef, terms = TerminalTerms}} -> + TerminalLimit = extract_provider_limit(TerminalTerms, Currency), + case TerminalLimit of + undefined -> + ProviderTerms = get_provider_terms(ProviderRef, Revision), + extract_provider_limit(ProviderTerms, Currency); + _ -> + TerminalLimit + end; + _ -> + undefined + end. + +get_and_check_terminal(TerminalRef, Revision) -> + case wapi_domain_backend:get_object(Revision, {terminal, TerminalRef}) of + {ok, #domain_Terminal{terms = Terms} = Terminal} -> + case extract_terminal_cash_limit(Terms) of + {decisions, _} -> + undefined; + _ -> + {ok, Terminal} + end; + _ -> + undefined + end. + +extract_terminal_cash_limit(#domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{cash_limit = CashLimit} + } +}) -> + CashLimit; +extract_terminal_cash_limit(_) -> + undefined. + +get_provider_terms(ProviderRef, Revision) -> + case wapi_domain_backend:get_object(Revision, {provider, ProviderRef}) of + {ok, #domain_Provider{terms = Terms}} -> + Terms; + _ -> + undefined + end. + +extract_provider_limit(undefined, _Currency) -> + undefined; +extract_provider_limit(#domain_ProvisionTermSet{wallet = undefined}, _Currency) -> + undefined; +extract_provider_limit(#domain_ProvisionTermSet{wallet = WalletTerms}, Currency) -> + case WalletTerms#domain_WalletProvisionTerms.withdrawals of + undefined -> + undefined; + #domain_WithdrawalProvisionTerms{cash_limit = CashLimitSelector} -> + range_from_selector(CashLimitSelector, Currency) + end. + +range_from_selector({value, #domain_CashRange{} = Range}, Currency) -> + normalize_range(Range, Currency); +range_from_selector(_, _Currency) -> + undefined. + +normalize_range(#domain_CashRange{lower = Lower, upper = Upper}, #domain_CurrencyRef{symbolic_code = CurrencyCode}) -> + {LowerAmount, LowerCode} = extract_bound(Lower), + {UpperAmount, UpperCode} = extract_bound(Upper), + case {LowerCode, UpperCode} of + {CurrencyCode, CurrencyCode} -> + #{ + currency => CurrencyCode, + lower => LowerAmount, + upper => UpperAmount + }; + _ -> + undefined + end. + +extract_bound({inclusive, #domain_Cash{amount = Amount, currency = #domain_CurrencyRef{symbolic_code = Code}}}) -> + {Amount, Code}; +extract_bound({exclusive, #domain_Cash{amount = Amount, currency = #domain_CurrencyRef{symbolic_code = Code}}}) -> + {Amount, Code}. + +intersect_optional(undefined, Range) -> + Range; +intersect_optional(Range, undefined) -> + Range; +intersect_optional(#{currency := Currency} = R1, #{currency := Currency} = R2) -> + intersect_ranges(R1, R2). + +intersect_ranges(#{lower := Lower1, upper := Upper1} = R1, #{lower := Lower2, upper := Upper2}) -> + Lower = max(Lower1, Lower2), + Upper = min(Upper1, Upper2), + case valid_range(Lower, Upper) of + true -> + R1#{lower => Lower, upper => Upper}; + false -> + undefined + end. + +union_optional(undefined, Range) -> + Range; +union_optional(Range, undefined) -> + Range; +union_optional(#{currency := Currency} = R1, #{currency := Currency} = R2) -> + union_ranges(R1, R2). + +union_ranges(#{lower := Lower1, upper := Upper1} = R1, #{lower := Lower2, upper := Upper2}) -> + Lower = min(Lower1, Lower2), + Upper = max(Upper1, Upper2), + R1#{lower => Lower, upper => Upper}. + +valid_range(LowerAmount, UpperAmount) when LowerAmount < UpperAmount -> + true; +valid_range(_, _) -> + false. + +encode_limits(_Currency, _Method, undefined) -> + []; +encode_limits(_Currency, Method, #{currency := CurrencyCode} = Range) -> + Encoded = encode_range(CurrencyCode, Range), + [Encoded#{<<"withdrawalMethod">> => encode_withdrawal_method(Method)}]. + +encode_range(CurrencyCode, #{lower := Lower, upper := Upper}) -> + #{ + <<"currency">> => CurrencyCode, + <<"lowerBound">> => encode_bound(Lower), + <<"upperBound">> => encode_bound(Upper) + }. + +encode_bound(Amount) -> + #{ + <<"amount">> => Amount, + <<"inclusive">> => true + }. + +normalize_withdrawal_method(bank_card) -> + bank_card; +normalize_withdrawal_method(digital_wallet) -> + digital_wallet; +normalize_withdrawal_method(_) -> + generic. + +encode_withdrawal_method(bank_card) -> + #{<<"method">> => <<"WithdrawalMethodBankCard">>}; +encode_withdrawal_method(digital_wallet) -> + #{<<"method">> => <<"WithdrawalMethodDigitalWallet">>}; +encode_withdrawal_method(generic) -> + #{<<"method">> => <<"WithdrawalMethodGeneric">>}. diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 2da2e97..24d7f02 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -306,7 +306,19 @@ start_woody_client(wapi_lib, Urls) -> wapi_lib, service_urls, Urls - ). + ); +start_woody_client(domain_config, Url) -> + update_dmt_service_url('Repository', Url); +start_woody_client(domain_config_client, Url) -> + update_dmt_service_url('RepositoryClient', Url). + +update_dmt_service_url(Key, Url) -> + ServiceUrls = + case application:get_env(dmt_client, service_urls) of + {ok, Urls} -> Urls; + undefined -> #{} + end, + ok = application:set_env(dmt_client, service_urls, ServiceUrls#{Key => Url}). -spec mock_services_(_, _) -> _. % TODO need a better name diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index e36d82e..78ccc08 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -25,7 +25,8 @@ get_fail_wallet_notfound/1, get_account_ok/1, get_account_fail_wallet_notfound/1, - get_account_fail_account_notfound/1 + get_account_fail_account_notfound/1, + get_cash_limits_ok/1 ]). -define(EMPTY_RESP(Code), {error, {Code, #{}}}). @@ -54,7 +55,8 @@ groups() -> get_fail_wallet_notfound, get_account_ok, get_account_fail_wallet_notfound, - get_account_fail_account_notfound + get_account_fail_account_notfound, + get_cash_limits_ok ]} ]. @@ -129,6 +131,19 @@ get_account_fail_account_notfound(C) -> ok = mock_account_with_balance(424242, C), ?assertEqual({error, {404, #{}}}, get_account_call_api(?STRING, C)). +-spec get_cash_limits_ok(config()) -> _. +get_cash_limits_ok(C) -> + PartyID = ?config(party, C), + WalletID = ?STRING, + OldServiceUrls = get_dmt_service_urls(), + try + ok = mock_wallet_limits_domain(PartyID, C), + {ok, Limits} = wapi_wallet_limits:get_wallet_limits(PartyID, WalletID, #{}), + ?assertEqual(expected_wallet_limits(), Limits) + after + ok = application:set_env(dmt_client, service_urls, OldServiceUrls) + end. + %% -spec call_api(function(), map(), wapi_client_lib:context()) -> {ok, term()} | {error, term()}. @@ -159,6 +174,183 @@ get_account_call_api(WalletID, C) -> wapi_ct_helper:cfg(context, C) ). +expected_wallet_limits() -> + [ + #{ + <<"currency">> => <<"RUB">>, + <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, + <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, + <<"withdrawalMethod">> => #{<<"method">> => <<"WithdrawalMethodBankCard">>} + }, + #{ + <<"currency">> => <<"RUB">>, + <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, + <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, + <<"withdrawalMethod">> => #{<<"method">> => <<"WithdrawalMethodDigitalWallet">>} + } + ]. + +get_dmt_service_urls() -> + case application:get_env(dmt_client, service_urls) of + {ok, Urls} -> Urls; + undefined -> #{} + end. + +mock_wallet_limits_domain(PartyID, C) -> + CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + WalletLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 100, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 1000, currency = CurrencyRef}} + }, + WithdrawalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} + }, + TerminalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} + }, + PaymentMethods = [ + #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, + #domain_PaymentMethodRef{id = {digital_wallet, #domain_PaymentServiceRef{id = <<"DW">>}}} + ], + TermSetHierarchyObject = + #domain_TermSetHierarchyObject{ + ref = #domain_TermSetHierarchyRef{id = 1}, + data = #domain_TermSetHierarchy{ + term_set = #domain_TermSet{ + wallets = #domain_WalletServiceTerms{ + wallet_limit = {value, WalletLimitRange}, + withdrawals = #domain_WithdrawalServiceTerms{ + methods = {value, PaymentMethods}, + cash_limit = {value, WithdrawalLimitRange} + } + } + } + } + }, + RoutingRulesObject = + #domain_RoutingRulesObject{ + ref = #domain_RoutingRulesetRef{id = 100}, + data = #domain_RoutingRuleset{ + name = <<"test">>, + decisions = {candidates, [ + #domain_RoutingCandidate{ + allowed = {constant, true}, + terminal = #domain_TerminalRef{id = 10} + } + ]} + } + }, + TerminalTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + cash_limit = {value, TerminalLimitRange} + } + } + }, + TerminalObject = + #domain_TerminalObject{ + ref = #domain_TerminalRef{id = 10}, + data = #domain_Terminal{ + name = <<"test">>, + description = <<"test">>, + provider_ref = #domain_ProviderRef{id = 11}, + terms = TerminalTerms + } + }, + ProviderObject = + #domain_ProviderObject{ + ref = #domain_ProviderRef{id = 11}, + data = #domain_Provider{ + name = <<"test">>, + description = <<"test">>, + proxy = #domain_Proxy{ + ref = #domain_ProxyRef{id = 1}, + additional = #{} + }, + realm = test, + terms = TerminalTerms + } + }, + PaymentInstitutionObject = + #domain_PaymentInstitutionObject{ + ref = #domain_PaymentInstitutionRef{id = 1}, + data = #domain_PaymentInstitution{ + name = <<"test">>, + system_account_set = {value, #domain_SystemAccountSetRef{id = 1}}, + inspector = {value, #domain_InspectorRef{id = 1}}, + realm = test, + residences = [rus], + withdrawal_routing_rules = #domain_RoutingRules{ + policies = #domain_RoutingRulesetRef{id = 100}, + prohibitions = #domain_RoutingRulesetRef{id = 101} + } + } + }, + WalletConfigObject = + #domain_WalletConfigObject{ + ref = #domain_WalletConfigRef{id = ?STRING}, + data = #domain_WalletConfig{ + name = ?STRING, + block = + {unblocked, #domain_Unblocked{ + reason = <<"">>, + since = wapi_time:rfc3339() + }}, + suspension = + {active, #domain_Active{ + since = wapi_time:rfc3339() + }}, + payment_institution = #domain_PaymentInstitutionRef{id = 1}, + terms = #domain_TermSetHierarchyRef{id = 1}, + account = #domain_WalletAccount{ + currency = CurrencyRef, + settlement = ?INTEGER + }, + party_ref = #domain_PartyConfigRef{id = PartyID} + } + }, + DomainConfigClient = fun + ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> + {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> + {ok, mk_versioned_object(payment_institution, PaymentInstitutionObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> + {ok, mk_versioned_object(routing_rules, RoutingRulesObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> + {ok, mk_versioned_object(terminal, TerminalObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> + {ok, mk_versioned_object(provider, ProviderObject)}; + ('CheckoutObject', _) -> + woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) + end, + DomainConfig = fun('GetLatestVersion', _) -> {ok, ?INTEGER} end, + _ = wapi_ct_helper:mock_services( + [ + {domain_config, DomainConfig}, + {domain_config_client, DomainConfigClient} + ], + C + ), + ok. + +mk_versioned_object(Type, Object) -> + #domain_conf_v2_VersionedObject{ + info = #domain_conf_v2_VersionedObjectInfo{ + version = ?INTEGER, + changed_at = genlib_rfc3339:format(genlib_time:unow(), second), + changed_by = #domain_conf_v2_Author{ + id = ?STRING, + name = ?STRING, + email = ?STRING + } + }, + object = {Type, Object} + }. + mock_account_with_balance(ExistingAccountID, C) -> _ = wapi_ct_helper:mock_services( [ From 683ef058346851b3e4529c758f62833f5b27f28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Mon, 2 Mar 2026 16:10:33 +0300 Subject: [PATCH 02/11] wip 2 --- rebar.config | 4 +- src/wapi_wallet_limits.erl | 10 +- test/wapi_ct_helper.erl | 152 ++++++++++++++++++++++++ test/wapi_wallet_tests_SUITE.erl | 197 ++++--------------------------- 4 files changed, 188 insertions(+), 175 deletions(-) diff --git a/rebar.config b/rebar.config index 2232f4e..7778d1c 100644 --- a/rebar.config +++ b/rebar.config @@ -77,8 +77,8 @@ {test, [ {deps, [ %% Libraries generated with swagger-codegen-erlang from valitydev/swag-wallets - {swag_server_wallet, {git, "https://github.com/valitydev/swag-wallets", {ref, "96add0f"}}}, - {swag_client_wallet, {git, "https://github.com/valitydev/swag-wallets", {ref, "5fd60dd"}}}, + {swag_server_wallet, {git, "https://github.com/valitydev/swag-wallets", {ref, "6ded7f2"}}}, + {swag_client_wallet, {git, "https://github.com/valitydev/swag-wallets", {ref, "1686026"}}}, {meck, "0.9.2"} ]}, {cover_enabled, true}, diff --git a/src/wapi_wallet_limits.erl b/src/wapi_wallet_limits.erl index ff3eb41..302ad37 100644 --- a/src/wapi_wallet_limits.erl +++ b/src/wapi_wallet_limits.erl @@ -277,8 +277,14 @@ normalize_withdrawal_method(_) -> generic. encode_withdrawal_method(bank_card) -> - #{<<"method">> => <<"WithdrawalMethodBankCard">>}; + #{ + <<"method">> => <<"WithdrawalMethodBankCard">>, + <<"paymentSystems">> => [] + }; encode_withdrawal_method(digital_wallet) -> - #{<<"method">> => <<"WithdrawalMethodDigitalWallet">>}; + #{ + <<"method">> => <<"WithdrawalMethodDigitalWallet">>, + <<"providers">> => [] + }; encode_withdrawal_method(generic) -> #{<<"method">> => <<"WithdrawalMethodGeneric">>}. diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 24d7f02..fe6d835 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -24,6 +24,7 @@ -export([stop_mocked_service_sup/1]). -export([mock_services/2]). -export([mock_services_/2]). +-export([mock_wallet_limits_domain/2]). -export([get_lifetime/0]). -export([create_auth_ctx/1]). @@ -411,3 +412,154 @@ create_auth_ctx(PartyID) -> #{ swagger_context => #{auth_context => {?STRING, PartyID, #{}}} }. + +-spec mock_wallet_limits_domain(binary(), sup_or_config()) -> ok. +mock_wallet_limits_domain(PartyID, SupOrConfig) -> + CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + WithdrawalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} + }, + TerminalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} + }, + PaymentMethods = [ + #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, + #domain_PaymentMethodRef{id = {digital_wallet, #domain_PaymentServiceRef{id = <<"DW">>}}} + ], + TermSetHierarchyObject = + #domain_TermSetHierarchyObject{ + ref = #domain_TermSetHierarchyRef{id = 1}, + data = #domain_TermSetHierarchy{ + term_set = #domain_TermSet{ + wallets = #domain_WalletServiceTerms{ + withdrawals = #domain_WithdrawalServiceTerms{ + methods = {value, PaymentMethods}, + cash_limit = {value, WithdrawalLimitRange} + } + } + } + } + }, + RoutingRulesObject = + #domain_RoutingRulesObject{ + ref = #domain_RoutingRulesetRef{id = 100}, + data = #domain_RoutingRuleset{ + name = <<"test">>, + decisions = {candidates, [ + #domain_RoutingCandidate{ + allowed = {constant, true}, + terminal = #domain_TerminalRef{id = 10} + } + ]} + } + }, + TerminalTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + cash_limit = {value, TerminalLimitRange} + } + } + }, + TerminalObject = + #domain_TerminalObject{ + ref = #domain_TerminalRef{id = 10}, + data = #domain_Terminal{ + name = <<"test">>, + description = <<"test">>, + provider_ref = #domain_ProviderRef{id = 11}, + terms = TerminalTerms + } + }, + ProviderObject = + #domain_ProviderObject{ + ref = #domain_ProviderRef{id = 11}, + data = #domain_Provider{ + name = <<"test">>, + description = <<"test">>, + proxy = #domain_Proxy{ + ref = #domain_ProxyRef{id = 1}, + additional = #{} + }, + realm = test, + terms = TerminalTerms + } + }, + PaymentInstitutionObject = + #domain_PaymentInstitutionObject{ + ref = #domain_PaymentInstitutionRef{id = 1}, + data = #domain_PaymentInstitution{ + name = <<"test">>, + system_account_set = {value, #domain_SystemAccountSetRef{id = 1}}, + inspector = {value, #domain_InspectorRef{id = 1}}, + realm = test, + residences = [rus], + withdrawal_routing_rules = #domain_RoutingRules{ + policies = #domain_RoutingRulesetRef{id = 100}, + prohibitions = #domain_RoutingRulesetRef{id = 101} + } + } + }, + WalletConfigObject = + #domain_WalletConfigObject{ + ref = #domain_WalletConfigRef{id = ?STRING}, + data = #domain_WalletConfig{ + name = ?STRING, + block = + {unblocked, #domain_Unblocked{ + reason = <<"">>, + since = wapi_time:rfc3339() + }}, + suspension = + {active, #domain_Active{ + since = wapi_time:rfc3339() + }}, + payment_institution = #domain_PaymentInstitutionRef{id = 1}, + terms = #domain_TermSetHierarchyRef{id = 1}, + account = #domain_WalletAccount{ + currency = CurrencyRef, + settlement = ?INTEGER + }, + party_ref = #domain_PartyConfigRef{id = PartyID} + } + }, + DomainConfigClient = fun + ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> + {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> + {ok, mk_versioned_object(payment_institution, PaymentInstitutionObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> + {ok, mk_versioned_object(routing_rules, RoutingRulesObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> + {ok, mk_versioned_object(terminal, TerminalObject)}; + ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> + {ok, mk_versioned_object(provider, ProviderObject)}; + ('CheckoutObject', _) -> + woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) + end, + DomainConfig = fun('GetLatestVersion', _) -> {ok, ?INTEGER} end, + _ = mock_services( + [ + {domain_config, DomainConfig}, + {domain_config_client, DomainConfigClient} + ], + SupOrConfig + ), + ok. + +mk_versioned_object(Type, Object) -> + #domain_conf_v2_VersionedObject{ + info = #domain_conf_v2_VersionedObjectInfo{ + version = ?INTEGER, + changed_at = genlib_rfc3339:format(genlib_time:unow(), second), + changed_by = #domain_conf_v2_Author{ + id = ?STRING, + name = ?STRING, + email = ?STRING + } + }, + object = {Type, Object} + }. diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index 78ccc08..00e65fe 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -135,14 +135,10 @@ get_account_fail_account_notfound(C) -> get_cash_limits_ok(C) -> PartyID = ?config(party, C), WalletID = ?STRING, - OldServiceUrls = get_dmt_service_urls(), - try - ok = mock_wallet_limits_domain(PartyID, C), - {ok, Limits} = wapi_wallet_limits:get_wallet_limits(PartyID, WalletID, #{}), - ?assertEqual(expected_wallet_limits(), Limits) - after - ok = application:set_env(dmt_client, service_urls, OldServiceUrls) - end. + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + ok = wapi_ct_helper:mock_wallet_limits_domain(PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + ?assertEqual(expected_wallet_limits(), Limits). %% @@ -174,182 +170,41 @@ get_account_call_api(WalletID, C) -> wapi_ct_helper:cfg(context, C) ). +get_cash_limits_call_api(WalletID, PartyID, C) -> + call_api( + fun swag_client_wallet_wallets_api:get_wallet_cash_limits/3, + #{ + binding => #{ + <<"walletID">> => WalletID + }, + qs_val => #{ + <<"partyID">> => PartyID + } + }, + wapi_ct_helper:cfg(context, C) + ). + expected_wallet_limits() -> [ #{ <<"currency">> => <<"RUB">>, <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, - <<"withdrawalMethod">> => #{<<"method">> => <<"WithdrawalMethodBankCard">>} + <<"withdrawalMethod">> => #{ + <<"method">> => <<"WithdrawalMethodBankCard">>, + <<"paymentSystems">> => [] + } }, #{ <<"currency">> => <<"RUB">>, <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, - <<"withdrawalMethod">> => #{<<"method">> => <<"WithdrawalMethodDigitalWallet">>} - } - ]. - -get_dmt_service_urls() -> - case application:get_env(dmt_client, service_urls) of - {ok, Urls} -> Urls; - undefined -> #{} - end. - -mock_wallet_limits_domain(PartyID, C) -> - CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, - WalletLimitRange = #domain_CashRange{ - lower = {inclusive, #domain_Cash{amount = 100, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 1000, currency = CurrencyRef}} - }, - WithdrawalLimitRange = #domain_CashRange{ - lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} - }, - TerminalLimitRange = #domain_CashRange{ - lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} - }, - PaymentMethods = [ - #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, - #domain_PaymentMethodRef{id = {digital_wallet, #domain_PaymentServiceRef{id = <<"DW">>}}} - ], - TermSetHierarchyObject = - #domain_TermSetHierarchyObject{ - ref = #domain_TermSetHierarchyRef{id = 1}, - data = #domain_TermSetHierarchy{ - term_set = #domain_TermSet{ - wallets = #domain_WalletServiceTerms{ - wallet_limit = {value, WalletLimitRange}, - withdrawals = #domain_WithdrawalServiceTerms{ - methods = {value, PaymentMethods}, - cash_limit = {value, WithdrawalLimitRange} - } - } - } - } - }, - RoutingRulesObject = - #domain_RoutingRulesObject{ - ref = #domain_RoutingRulesetRef{id = 100}, - data = #domain_RoutingRuleset{ - name = <<"test">>, - decisions = {candidates, [ - #domain_RoutingCandidate{ - allowed = {constant, true}, - terminal = #domain_TerminalRef{id = 10} - } - ]} - } - }, - TerminalTerms = #domain_ProvisionTermSet{ - wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{ - cash_limit = {value, TerminalLimitRange} + <<"withdrawalMethod">> => #{ + <<"method">> => <<"WithdrawalMethodDigitalWallet">>, + <<"providers">> => [] } } - }, - TerminalObject = - #domain_TerminalObject{ - ref = #domain_TerminalRef{id = 10}, - data = #domain_Terminal{ - name = <<"test">>, - description = <<"test">>, - provider_ref = #domain_ProviderRef{id = 11}, - terms = TerminalTerms - } - }, - ProviderObject = - #domain_ProviderObject{ - ref = #domain_ProviderRef{id = 11}, - data = #domain_Provider{ - name = <<"test">>, - description = <<"test">>, - proxy = #domain_Proxy{ - ref = #domain_ProxyRef{id = 1}, - additional = #{} - }, - realm = test, - terms = TerminalTerms - } - }, - PaymentInstitutionObject = - #domain_PaymentInstitutionObject{ - ref = #domain_PaymentInstitutionRef{id = 1}, - data = #domain_PaymentInstitution{ - name = <<"test">>, - system_account_set = {value, #domain_SystemAccountSetRef{id = 1}}, - inspector = {value, #domain_InspectorRef{id = 1}}, - realm = test, - residences = [rus], - withdrawal_routing_rules = #domain_RoutingRules{ - policies = #domain_RoutingRulesetRef{id = 100}, - prohibitions = #domain_RoutingRulesetRef{id = 101} - } - } - }, - WalletConfigObject = - #domain_WalletConfigObject{ - ref = #domain_WalletConfigRef{id = ?STRING}, - data = #domain_WalletConfig{ - name = ?STRING, - block = - {unblocked, #domain_Unblocked{ - reason = <<"">>, - since = wapi_time:rfc3339() - }}, - suspension = - {active, #domain_Active{ - since = wapi_time:rfc3339() - }}, - payment_institution = #domain_PaymentInstitutionRef{id = 1}, - terms = #domain_TermSetHierarchyRef{id = 1}, - account = #domain_WalletAccount{ - currency = CurrencyRef, - settlement = ?INTEGER - }, - party_ref = #domain_PartyConfigRef{id = PartyID} - } - }, - DomainConfigClient = fun - ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> - {ok, mk_versioned_object(wallet_config, WalletConfigObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> - {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> - {ok, mk_versioned_object(payment_institution, PaymentInstitutionObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> - {ok, mk_versioned_object(routing_rules, RoutingRulesObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> - {ok, mk_versioned_object(terminal, TerminalObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> - {ok, mk_versioned_object(provider, ProviderObject)}; - ('CheckoutObject', _) -> - woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) - end, - DomainConfig = fun('GetLatestVersion', _) -> {ok, ?INTEGER} end, - _ = wapi_ct_helper:mock_services( - [ - {domain_config, DomainConfig}, - {domain_config_client, DomainConfigClient} - ], - C - ), - ok. - -mk_versioned_object(Type, Object) -> - #domain_conf_v2_VersionedObject{ - info = #domain_conf_v2_VersionedObjectInfo{ - version = ?INTEGER, - changed_at = genlib_rfc3339:format(genlib_time:unow(), second), - changed_by = #domain_conf_v2_Author{ - id = ?STRING, - name = ?STRING, - email = ?STRING - } - }, - object = {Type, Object} - }. + ]. mock_account_with_balance(ExistingAccountID, C) -> _ = wapi_ct_helper:mock_services( From 356b4bb75d8f3f20efe5b45654f1837979f8da3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 16:33:06 +0300 Subject: [PATCH 03/11] wip 3 --- src/wapi_wallet_limits.erl | 343 +++++++++++++---- test/wapi_ct_helper.erl | 610 +++++++++++++++++++++---------- test/wapi_wallet_dummy_data.hrl | 9 + test/wapi_wallet_tests_SUITE.erl | 110 ++++-- 4 files changed, 793 insertions(+), 279 deletions(-) diff --git a/src/wapi_wallet_limits.erl b/src/wapi_wallet_limits.erl index 302ad37..038379a 100644 --- a/src/wapi_wallet_limits.erl +++ b/src/wapi_wallet_limits.erl @@ -3,18 +3,19 @@ % IMPORTANT: calculation is approximate and does NOT cover some cases: % - selectors {decisions, _} for terminals are not handled % - exclusive bounds are treated as inclusive (strictness lost) -% - terminals with cash_limit=decisions are ignored (no provider fallback) -% - when methods are missing, response is empty even if limits are computed +% - terminals with withdrawal cash_limit=decisions are ignored (no provider fallback) +% - candidate terminals with allowed=false are ignored % -include_lib("damsel/include/dmsl_domain_thrift.hrl"). +-include_lib("damsel/include/dmsl_payproc_thrift.hrl"). -export([get_wallet_limits/3]). --type handler_context() :: wapi_wallet_backend:handler_context(). +-type handler_context() :: wapi_handler_utils:handler_context(). -spec get_wallet_limits(binary(), binary(), handler_context()) -> {ok, [map()]} | {error, {wallet, notfound}}. -get_wallet_limits(PartyID, WalletID, _Context) -> +get_wallet_limits(PartyID, WalletID, Context) -> case get_wallet_config(PartyID, WalletID) of {error, notfound} -> {error, {wallet, notfound}}; @@ -24,7 +25,13 @@ get_wallet_limits(PartyID, WalletID, _Context) -> WalletTerms = get_wallet_terms(WalletConfig#domain_WalletConfig.terms, Revision), Methods = extract_withdrawal_methods(WalletTerms), WithdrawalLimit = extract_withdrawal_limit(WalletTerms, Currency), - TerminalRefs = get_withdrawal_terminal_refs(WalletConfig#domain_WalletConfig.payment_institution, Revision), + TerminalRefs = get_withdrawal_terminal_refs( + WalletConfig#domain_WalletConfig.payment_institution, + PartyID, + WalletID, + Revision, + Context + ), TermLimit = aggregate_terminal_limits(TerminalRefs, Currency, Revision), EffectiveLimit = intersect_optional(WithdrawalLimit, TermLimit), Limits = lists:flatmap( @@ -75,44 +82,48 @@ extract_withdrawal_limit(#domain_TermSet{wallets = Wallets}, Currency) -> range_from_selector(CashLimitSelector, Currency) end. -get_withdrawal_terminal_refs(PiRef, Revision) -> +get_withdrawal_terminal_refs(PiRef, PartyID, WalletID, Revision, Context) -> case wapi_domain_backend:get_object(Revision, {payment_institution, PiRef}) of - {ok, #domain_PaymentInstitution{withdrawal_routing_rules = Rules}} -> - lists:usort(collect_ruleset_terminals(Rules, Revision)); + {ok, #domain_PaymentInstitution{withdrawal_routing_rules = RulesetRef}} -> + case compute_routing_ruleset(RulesetRef, PartyID, WalletID, Revision, Context) of + {ok, #domain_RoutingRuleset{decisions = {candidates, Candidates}}} -> + AllowedCandidates = [ + C#domain_RoutingCandidate.terminal + || C <- Candidates, + predicate_allowed(C#domain_RoutingCandidate.allowed) + ], + lists:usort(AllowedCandidates); + _ -> + [] + end; _ -> [] end. -collect_ruleset_terminals(undefined, _Revision) -> - []; -collect_ruleset_terminals(#domain_RoutingRules{policies = PoliciesRef}, Revision) -> - collect_ruleset_terminals(PoliciesRef, Revision, sets:new()). - -collect_ruleset_terminals(#domain_RoutingRulesetRef{} = Ref, Revision, Seen) -> - case sets:is_element(Ref, Seen) of - true -> - []; - false -> - Seen1 = sets:add_element(Ref, Seen), - case wapi_domain_backend:get_object(Revision, {routing_rules, Ref}) of - {ok, #domain_RoutingRuleset{} = Ruleset} -> - collect_ruleset_terminals(Ruleset, Revision, Seen1); - _ -> - [] - end - end; -collect_ruleset_terminals(#domain_RoutingRuleset{decisions = Decisions}, Revision, Seen) -> - collect_terminals_from_decisions(Decisions, Revision, Seen). - -collect_terminals_from_decisions({candidates, Candidates}, _Revision, _Seen) -> - [C#domain_RoutingCandidate.terminal || C <- Candidates]; -collect_terminals_from_decisions({delegates, Delegates}, Revision, Seen) -> - lists:flatmap( - fun(#domain_RoutingDelegate{ruleset = Ref}) -> - collect_ruleset_terminals(Ref, Revision, Seen) - end, - Delegates - ). +compute_routing_ruleset(undefined, _PartyID, _WalletID, _Revision, _Context) -> + undefined; +compute_routing_ruleset( + #domain_RoutingRules{policies = RulesetRef}, + PartyID, + WalletID, + Revision, + Context +) -> + Varset = #payproc_Varset{ + party_ref = #domain_PartyConfigRef{id = PartyID}, + wallet_id = WalletID + }, + case + wapi_handler_utils:service_call( + {party_management, 'ComputeRoutingRuleset', {RulesetRef, Revision, Varset}}, + Context + ) + of + {ok, #domain_RoutingRuleset{} = Ruleset} -> + {ok, Ruleset}; + _ -> + undefined + end. aggregate_terminal_limits([], _Currency, _Revision) -> undefined; @@ -136,41 +147,60 @@ log_terminal_terms(TerminalRef, Limit) -> ). get_terminal_limit(TerminalRef, Currency, Revision) -> - case get_and_check_terminal(TerminalRef, Revision) of + case wapi_domain_backend:get_object(Revision, {terminal, TerminalRef}) of {ok, #domain_Terminal{provider_ref = ProviderRef, terms = TerminalTerms}} -> - TerminalLimit = extract_provider_limit(TerminalTerms, Currency), - case TerminalLimit of - undefined -> - ProviderTerms = get_provider_terms(ProviderRef, Revision), - extract_provider_limit(ProviderTerms, Currency); - _ -> - TerminalLimit - end; + ProviderTerms = get_provider_terms(ProviderRef, Revision), + compute_terminal_limit(TerminalTerms, ProviderTerms, Currency); _ -> undefined end. -get_and_check_terminal(TerminalRef, Revision) -> - case wapi_domain_backend:get_object(Revision, {terminal, TerminalRef}) of - {ok, #domain_Terminal{terms = Terms} = Terminal} -> - case extract_terminal_cash_limit(Terms) of - {decisions, _} -> - undefined; - _ -> - {ok, Terminal} +compute_terminal_limit(TerminalTerms, ProviderTerms, Currency) -> + TerminalWithdrawalTerms = extract_withdrawal_terms(TerminalTerms), + case terminal_and_provider_allowed(TerminalWithdrawalTerms, ProviderTerms) of + true -> + case extract_provider_limit(TerminalTerms, Currency) of + undefined -> + extract_provider_limit(ProviderTerms, Currency); + TerminalLimit -> + TerminalLimit end; - _ -> + false -> undefined end. -extract_terminal_cash_limit(#domain_ProvisionTermSet{ +extract_withdrawal_terms(#domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{withdrawals = WithdrawalTerms} +}) -> + WithdrawalTerms; +extract_withdrawal_terms(_) -> + undefined. + +predicate_allowed({constant, false}) -> + false; +predicate_allowed({all_of, List}) when is_list(List) -> + lists:all(fun predicate_allowed/1, List); +predicate_allowed(_) -> + true. + +terminal_and_provider_allowed(undefined, ProviderTerms) -> + provider_withdrawal_allowed(ProviderTerms); +terminal_and_provider_allowed(#domain_WithdrawalProvisionTerms{} = TerminalTerms, ProviderTerms) -> + TerminalAllowed = predicate_allowed(TerminalTerms#domain_WithdrawalProvisionTerms.allow), + TerminalGlobalAllowed = predicate_allowed(TerminalTerms#domain_WithdrawalProvisionTerms.global_allow), + TerminalAllowed andalso TerminalGlobalAllowed andalso provider_withdrawal_allowed(ProviderTerms). + +provider_withdrawal_allowed(undefined) -> + true; +provider_withdrawal_allowed(#domain_ProvisionTermSet{ wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{cash_limit = CashLimit} + withdrawals = #domain_WithdrawalProvisionTerms{} = ProviderWithdrawalTerms } }) -> - CashLimit; -extract_terminal_cash_limit(_) -> - undefined. + predicate_allowed(ProviderWithdrawalTerms#domain_WithdrawalProvisionTerms.allow) andalso + predicate_allowed(ProviderWithdrawalTerms#domain_WithdrawalProvisionTerms.global_allow); +provider_withdrawal_allowed(_) -> + true. get_provider_terms(ProviderRef, Revision) -> case wapi_domain_backend:get_object(Revision, {provider, ProviderRef}) of @@ -288,3 +318,194 @@ encode_withdrawal_method(digital_wallet) -> }; encode_withdrawal_method(generic) -> #{<<"method">> => <<"WithdrawalMethodGeneric">>}. + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-spec predicate_allowed_undefined_test() -> _. +predicate_allowed_undefined_test() -> + ?assertEqual(true, predicate_allowed(undefined)). + +-spec predicate_allowed_constant_true_test() -> _. +predicate_allowed_constant_true_test() -> + ?assertEqual(true, predicate_allowed({constant, true})). + +-spec predicate_allowed_constant_false_test() -> _. +predicate_allowed_constant_false_test() -> + ?assertEqual(false, predicate_allowed({constant, false})). + +-spec predicate_allowed_other_predicates_test() -> _. +predicate_allowed_other_predicates_test() -> + ?assertEqual(true, predicate_allowed({all_of, []})), + ?assertEqual(true, predicate_allowed({all_of, [{constant, true}, {constant, true}]})), + ?assertEqual(false, predicate_allowed({all_of, [{constant, true}, {constant, false}]})), + ?assertEqual(false, predicate_allowed({all_of, [{all_of, [{constant, true}, {constant, false}]}]})), + ?assertEqual(true, predicate_allowed({any_of, []})), + ?assertEqual(true, predicate_allowed({condition, []})). + +-spec terminal_and_provider_allowed_all_true_test() -> _. +terminal_and_provider_allowed_all_true_test() -> + TerminalTerms = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + } + } + }, + ?assertEqual(true, terminal_and_provider_allowed(TerminalTerms, ProviderTerms)). + +-spec terminal_and_provider_allowed_terminal_allow_false_test() -> _. +terminal_and_provider_allowed_terminal_allow_false_test() -> + TerminalTerms = #domain_WithdrawalProvisionTerms{ + allow = {constant, false}, + global_allow = {constant, true} + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + } + } + }, + ?assertEqual(false, terminal_and_provider_allowed(TerminalTerms, ProviderTerms)). + +-spec terminal_and_provider_allowed_provider_global_allow_false_test() -> _. +terminal_and_provider_allowed_provider_global_allow_false_test() -> + TerminalTerms = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, false} + } + } + }, + ?assertEqual(false, terminal_and_provider_allowed(TerminalTerms, ProviderTerms)). + +-spec terminal_and_provider_allowed_provider_allow_false_test() -> _. +terminal_and_provider_allowed_provider_allow_false_test() -> + TerminalTerms = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, false}, + global_allow = {constant, true} + } + } + }, + ?assertEqual(false, terminal_and_provider_allowed(TerminalTerms, ProviderTerms)). + +-spec terminal_and_provider_allowed_provider_undefined_test() -> _. +terminal_and_provider_allowed_provider_undefined_test() -> + TerminalTerms = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + }, + ?assertEqual(true, terminal_and_provider_allowed(TerminalTerms, undefined)). + +-spec intersect_ranges_non_overlapping_test() -> _. +intersect_ranges_non_overlapping_test() -> + R1 = #{currency => <<"RUB">>, lower => 200, upper => 400}, + R2 = #{currency => <<"RUB">>, lower => 500, upper => 800}, + ?assertEqual(undefined, intersect_ranges(R1, R2)). + +-spec compute_terminal_limit_provider_disallowed_returns_undefined_test() -> _. +compute_terminal_limit_provider_disallowed_returns_undefined_test() -> + Rub = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + TerminalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 300, currency = Rub}}, + upper = {inclusive, #domain_Cash{amount = 900, currency = Rub}} + }, + TerminalTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true}, + cash_limit = {value, TerminalLimitRange} + } + } + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, false} + } + } + }, + ?assertEqual(undefined, compute_terminal_limit(TerminalTerms, ProviderTerms, Rub)). + +-spec compute_terminal_limit_allowed_returns_terminal_limit_test() -> _. +compute_terminal_limit_allowed_returns_terminal_limit_test() -> + Rub = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + TerminalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 300, currency = Rub}}, + upper = {inclusive, #domain_Cash{amount = 900, currency = Rub}} + }, + TerminalTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true}, + cash_limit = {value, TerminalLimitRange} + } + } + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true} + } + } + }, + ?assertEqual( + #{currency => <<"RUB">>, lower => 300, upper => 900}, + compute_terminal_limit(TerminalTerms, ProviderTerms, Rub) + ). + +-spec compute_terminal_limit_allowed_fallback_to_provider_limit_test() -> _. +compute_terminal_limit_allowed_fallback_to_provider_limit_test() -> + Rub = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + ProviderLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 100, currency = Rub}}, + upper = {inclusive, #domain_Cash{amount = 500, currency = Rub}} + }, + TerminalTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true}, + cash_limit = undefined + } + } + }, + ProviderTerms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = {constant, true}, + global_allow = {constant, true}, + cash_limit = {value, ProviderLimitRange} + } + } + }, + ?assertEqual( + #{currency => <<"RUB">>, lower => 100, upper => 500}, + compute_terminal_limit(TerminalTerms, ProviderTerms, Rub) + ). + +-endif. diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index fe6d835..644eb83 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -3,6 +3,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("damsel/include/dmsl_domain_conf_v2_thrift.hrl"). -include_lib("damsel/include/dmsl_domain_thrift.hrl"). +-include_lib("damsel/include/dmsl_payproc_thrift.hrl"). -include_lib("wapi_wallet_dummy_data.hrl"). -include_lib("wapi_token_keeper_data.hrl"). @@ -24,7 +25,9 @@ -export([stop_mocked_service_sup/1]). -export([mock_services/2]). -export([mock_services_/2]). --export([mock_wallet_limits_domain/2]). +-export([default_party_management_routing/0]). +-export([set_party_management_account/1]). +-export([init_party_management_mock/1]). -export([get_lifetime/0]). -export([create_auth_ctx/1]). @@ -101,6 +104,7 @@ init_suite(Module, Config) -> start_app(woody) ++ start_app({dmt_client, SupPid}) ++ start_app({wapi_lib, Config}), + ok = init_party_management_mock(SupPid), {ok, _} = supervisor:start_child( SupPid, wapi_ct_helper_swagger_server:child_spec(#{wallet => {wapi_ct_helper_handler, #{}}}) ), @@ -134,28 +138,18 @@ start_app(woody = AppName) -> {acceptors_pool_size, 4} ]); start_app({dmt_client = AppName, SupPid}) -> - WalletConfigObject = #domain_WalletConfigObject{ - ref = #domain_WalletConfigRef{id = ?STRING}, - data = #domain_WalletConfig{ - name = ?STRING, - block = - {unblocked, #domain_Unblocked{ - reason = <<"">>, - since = wapi_time:rfc3339() - }}, - suspension = - {active, #domain_Active{ - since = wapi_time:rfc3339() - }}, - payment_institution = #domain_PaymentInstitutionRef{id = 1}, - terms = #domain_TermSetHierarchyRef{id = 1}, - account = #domain_WalletAccount{ - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, - settlement = ?INTEGER - }, - party_ref = #domain_PartyConfigRef{id = ?STRING} - } - }, + CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + Version = ?INTEGER, + %% Base wallet config (get_ok, get_account_ok) + WalletConfigObject = mk_wallet_config(?STRING, 1), + %% Wallet configs for cash limits scenarios (each -> different PI) + WalletConfigLimitsOk = mk_wallet_config(?WALLET_ID_OK, 1), + WalletConfigCandidateDisabled = mk_wallet_config(?WALLET_ID_CANDIDATE_DISABLED, 2), + WalletConfigProviderGlobalDisallow = mk_wallet_config(?WALLET_ID_PROVIDER_GLOBAL_DISALLOW, 10), + WalletConfigTerminal2Disabled = mk_wallet_config(?WALLET_ID_TERMINAL_2_DISABLED, 4), + WalletConfigTerminal1Disabled = mk_wallet_config(?WALLET_ID_TERMINAL_1_DISABLED, 5), + WalletConfigProvider2Disabled = mk_wallet_config(?WALLET_ID_PROVIDER_2_DISABLED, 6), + WalletConfigProvider1Disabled = mk_wallet_config(?WALLET_ID_PROVIDER_1_DISABLED, 9), PartyConfigObject = #domain_PartyConfigObject{ ref = #domain_PartyConfigRef{id = ?STRING}, data = #domain_PartyConfig{ @@ -174,41 +168,223 @@ start_app({dmt_client = AppName, SupPid}) -> } } }, + %% Term set hierarchy (shared) + WithdrawalLimitRange = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 100, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 500, currency = CurrencyRef}} + }, + PaymentMethods = [ + #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, + #domain_PaymentMethodRef{id = {digital_wallet, #domain_PaymentServiceRef{id = <<"DW">>}}} + ], + TermSetHierarchyObject = #domain_TermSetHierarchyObject{ + ref = #domain_TermSetHierarchyRef{id = 1}, + data = #domain_TermSetHierarchy{ + term_set = #domain_TermSet{ + wallets = #domain_WalletServiceTerms{ + withdrawals = #domain_WithdrawalServiceTerms{ + methods = {value, PaymentMethods}, + cash_limit = {value, WithdrawalLimitRange} + } + } + } + } + }, + Term10Limit = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} + }, + Term20Limit = #domain_CashRange{ + lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, + upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} + }, + Allowed = {constant, true}, + Disallowed = {constant, false}, + Terminal10 = mk_terminal_object(10, 11, Term10Limit, Allowed, Allowed), + Terminal20 = mk_terminal_object(20, 21, Term20Limit, Allowed, Allowed), + Terminal12 = mk_terminal_object(12, 12, Term10Limit, Allowed, Allowed), + Terminal22 = mk_terminal_object(22, 22, Term20Limit, Disallowed, Allowed), + Provider11 = mk_provider_object(11, Allowed, Allowed), + Provider21 = mk_provider_object(21, Allowed, Allowed), + Provider12 = mk_provider_object(12, Allowed, Disallowed), + Provider22 = mk_provider_object(22, Allowed, Allowed), + %% Routing rulesets: 100 both, 101 none, 103 term20 only, 104 term10 only, + %% 105 term10+22 (prov2 off), 106 term12+20 (prov1 off), 107 term12+22 (both prov off) + Routing100 = #domain_RoutingRuleset{ + name = <<"both">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + Routing101 = #domain_RoutingRuleset{ + name = <<"none">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + Routing103 = #domain_RoutingRuleset{ + name = <<"term20">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + Routing104 = #domain_RoutingRuleset{ + name = <<"term10">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + Routing105 = #domain_RoutingRuleset{ + name = <<"prov2_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} + ]} + }, + Routing106 = #domain_RoutingRuleset{ + name = <<"prov1_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + Routing107 = #domain_RoutingRuleset{ + name = <<"both_prov_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} + ]} + }, + %% Routing 108: empty (for provider_global_disallow - both term 12,22 have disallowed providers) + Routing108 = #domain_RoutingRuleset{ + name = <<"empty">>, + decisions = {candidates, []} + }, + RoutingRules = #{ + 100 => Routing100, + 101 => Routing101, + 103 => Routing103, + 104 => Routing104, + 105 => Routing105, + 106 => Routing106, + 107 => Routing107, + 108 => Routing108 + }, + RoutingRulesObjects = [ + #domain_RoutingRulesObject{ref = #domain_RoutingRulesetRef{id = Id}, data = Data} + || {Id, Data} <- maps:to_list(RoutingRules) + ], + %% PIs: 1=100, 2=101, 4=104, 5=103, 6=105, 7=107, 8=107, 9=106, 10=108 + ProhibitionsId = 101, + PiObjects = [ + mk_pi_object(1, 100, ProhibitionsId), + mk_pi_object(2, 101, ProhibitionsId), + mk_pi_object(4, 104, ProhibitionsId), + mk_pi_object(5, 103, ProhibitionsId), + mk_pi_object(6, 105, ProhibitionsId), + mk_pi_object(7, 107, ProhibitionsId), + mk_pi_object(8, 107, ProhibitionsId), + mk_pi_object(9, 106, ProhibitionsId), + mk_pi_object(10, 108, ProhibitionsId) + ], + PiMap = maps:from_list([ + {(P#domain_PaymentInstitutionObject.ref)#domain_PaymentInstitutionRef.id, P} + || P <- PiObjects + ]), + RoutingMap = maps:from_list([ + {(R#domain_RoutingRulesObject.ref)#domain_RoutingRulesetRef.id, R} + || R <- RoutingRulesObjects + ]), + DomainConfigClient = fun + ('CheckoutObject', {{version, V}, {wallet_config, #domain_WalletConfigRef{id = Id}}}) when + V =:= Version + -> + Wc = + case Id of + ?STRING -> WalletConfigObject; + ?WALLET_ID_OK -> WalletConfigLimitsOk; + ?WALLET_ID_CANDIDATE_DISABLED -> WalletConfigCandidateDisabled; + ?WALLET_ID_PROVIDER_GLOBAL_DISALLOW -> WalletConfigProviderGlobalDisallow; + ?WALLET_ID_TERMINAL_2_DISABLED -> WalletConfigTerminal2Disabled; + ?WALLET_ID_TERMINAL_1_DISABLED -> WalletConfigTerminal1Disabled; + ?WALLET_ID_PROVIDER_2_DISABLED -> WalletConfigProvider2Disabled; + ?WALLET_ID_PROVIDER_1_DISABLED -> WalletConfigProvider1Disabled; + _ -> undefined + end, + case Wc of + undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); + _ -> {ok, mk_versioned_object(wallet_config, Wc, Version)} + end; + ('CheckoutObject', {{version, V}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) when + V =:= Version + -> + {ok, mk_versioned_object(party_config, PartyConfigObject, Version)}; + ('CheckoutObject', {{version, V}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) when + V =:= Version + -> + {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject, Version)}; + ('CheckoutObject', {{version, V}, {payment_institution, #domain_PaymentInstitutionRef{id = PiId}}}) when + V =:= Version + -> + case maps:get(PiId, PiMap, undefined) of + undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); + PiObj -> {ok, mk_versioned_object(payment_institution, PiObj, Version)} + end; + ('CheckoutObject', {{version, V}, {routing_rules, #domain_RoutingRulesetRef{id = Id}}}) when + V =:= Version + -> + case maps:get(Id, RoutingMap, undefined) of + undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); + RrObj -> {ok, mk_versioned_object(routing_rules, RrObj, Version)} + end; + ('CheckoutObject', {{version, V}, {terminal, #domain_TerminalRef{id = Id}}}) when + V =:= Version + -> + T = + case Id of + 10 -> Terminal10; + 20 -> Terminal20; + 12 -> Terminal12; + 22 -> Terminal22; + _ -> undefined + end, + case T of + undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); + _ -> {ok, mk_versioned_object(terminal, T, Version)} + end; + ('CheckoutObject', {{version, V}, {provider, #domain_ProviderRef{id = Id}}}) when + V =:= Version + -> + P = + case Id of + 11 -> Provider11; + 21 -> Provider21; + 12 -> Provider12; + 22 -> Provider22; + _ -> undefined + end, + case P of + undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); + _ -> {ok, mk_versioned_object(provider, P, Version)} + end; + ('CheckoutObject', _) -> + woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) + end, Urls = mock_services_( [ - {domain_config_client, fun - ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> - {ok, #domain_conf_v2_VersionedObject{ - info = #domain_conf_v2_VersionedObjectInfo{ - version = ?INTEGER, - changed_at = genlib_rfc3339:format(genlib_time:unow(), second), - changed_by = #domain_conf_v2_Author{ - id = ?STRING, - name = ?STRING, - email = ?STRING - } - }, - object = {wallet_config, WalletConfigObject} - }}; - ('CheckoutObject', {{version, ?INTEGER}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) -> - {ok, #domain_conf_v2_VersionedObject{ - info = #domain_conf_v2_VersionedObjectInfo{ - version = ?INTEGER, - changed_at = genlib_rfc3339:format(genlib_time:unow(), second), - changed_by = #domain_conf_v2_Author{ - id = ?STRING, - name = ?STRING, - email = ?STRING - } - }, - object = {party_config, PartyConfigObject} - }}; - ('CheckoutObject', _) -> - woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) - end}, - {domain_config, fun('GetLatestVersion', _) -> - {ok, ?INTEGER} - end} + {domain_config_client, DomainConfigClient}, + {domain_config, fun('GetLatestVersion', _) -> {ok, Version} end} ], SupPid ), @@ -303,10 +479,15 @@ start_woody_client(bender, Urls) -> ), start_app(bender_client, []); start_woody_client(wapi_lib, Urls) -> + Existing = + case application:get_env(wapi_lib, service_urls) of + {ok, M} when is_map(M) -> M; + _ -> #{} + end, ok = application:set_env( wapi_lib, service_urls, - Urls + maps:merge(Existing, Urls) ); start_woody_client(domain_config, Url) -> update_dmt_service_url('Repository', Url); @@ -413,147 +594,117 @@ create_auth_ctx(PartyID) -> swagger_context => #{auth_context => {?STRING, PartyID, #{}}} }. --spec mock_wallet_limits_domain(binary(), sup_or_config()) -> ok. -mock_wallet_limits_domain(PartyID, SupOrConfig) -> - CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, - WithdrawalLimitRange = #domain_CashRange{ - lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} - }, - TerminalLimitRange = #domain_CashRange{ - lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} - }, - PaymentMethods = [ - #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, - #domain_PaymentMethodRef{id = {digital_wallet, #domain_PaymentServiceRef{id = <<"DW">>}}} - ], - TermSetHierarchyObject = - #domain_TermSetHierarchyObject{ - ref = #domain_TermSetHierarchyRef{id = 1}, - data = #domain_TermSetHierarchy{ - term_set = #domain_TermSet{ - wallets = #domain_WalletServiceTerms{ - withdrawals = #domain_WithdrawalServiceTerms{ - methods = {value, PaymentMethods}, - cash_limit = {value, WithdrawalLimitRange} +%% Sets which account ID exists for GetAccountState (used by mock_account_with_balance) +-spec set_party_management_account(integer() | undefined) -> ok. +set_party_management_account(AccountID) -> + application:set_env(wapi_lib, test_account_id, AccountID). + +%% Starts party_management mock (separate from dmt_client) +-spec init_party_management_mock(pid()) -> ok. +init_party_management_mock(SupPid) -> + RoutingFun = default_party_management_routing(), + PartyManagement = fun + ('ComputeRoutingRuleset', X) -> + RoutingFun('ComputeRoutingRuleset', X); + ('GetAccountState', {_PartyRef, AccountID, ?INTEGER}) -> + case application:get_env(wapi_lib, test_account_id, undefined) of + AccountID -> + {ok, #payproc_AccountState{ + account_id = AccountID, + own_amount = ?INTEGER, + available_amount = ?INTEGER, + currency = #domain_Currency{ + name = ?STRING, + symbolic_code = ?RUB, + numeric_code = ?INTEGER, + exponent = ?INTEGER } - } - } - } + }}; + _ -> + throw(#payproc_AccountNotFound{}) + end + end, + Urls = mock_services_([{party_management, PartyManagement}], SupPid), + case maps:get(wapi_lib, Urls, undefined) of + undefined -> ok; + WapiUrls -> start_woody_client(wapi_lib, WapiUrls) + end. + +%% Returns ComputeRoutingRuleset handler for wallet limits domain +-spec default_party_management_routing() -> fun(). +default_party_management_routing() -> + Allowed = {constant, true}, + Disallowed = {constant, false}, + RoutingRules = #{ + 100 => #domain_RoutingRuleset{ + name = <<"both">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} }, - RoutingRulesObject = - #domain_RoutingRulesObject{ - ref = #domain_RoutingRulesetRef{id = 100}, - data = #domain_RoutingRuleset{ - name = <<"test">>, - decisions = {candidates, [ - #domain_RoutingCandidate{ - allowed = {constant, true}, - terminal = #domain_TerminalRef{id = 10} - } + 101 => #domain_RoutingRuleset{ + name = <<"none">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} ]} - } }, - TerminalTerms = #domain_ProvisionTermSet{ - wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{ - cash_limit = {value, TerminalLimitRange} - } - } - }, - TerminalObject = - #domain_TerminalObject{ - ref = #domain_TerminalRef{id = 10}, - data = #domain_Terminal{ - name = <<"test">>, - description = <<"test">>, - provider_ref = #domain_ProviderRef{id = 11}, - terms = TerminalTerms - } + 103 => #domain_RoutingRuleset{ + name = <<"term20">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} }, - ProviderObject = - #domain_ProviderObject{ - ref = #domain_ProviderRef{id = 11}, - data = #domain_Provider{ - name = <<"test">>, - description = <<"test">>, - proxy = #domain_Proxy{ - ref = #domain_ProxyRef{id = 1}, - additional = #{} - }, - realm = test, - terms = TerminalTerms - } + 104 => #domain_RoutingRuleset{ + name = <<"term10">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} + ]} }, - PaymentInstitutionObject = - #domain_PaymentInstitutionObject{ - ref = #domain_PaymentInstitutionRef{id = 1}, - data = #domain_PaymentInstitution{ - name = <<"test">>, - system_account_set = {value, #domain_SystemAccountSetRef{id = 1}}, - inspector = {value, #domain_InspectorRef{id = 1}}, - realm = test, - residences = [rus], - withdrawal_routing_rules = #domain_RoutingRules{ - policies = #domain_RoutingRulesetRef{id = 100}, - prohibitions = #domain_RoutingRulesetRef{id = 101} - } - } + 105 => #domain_RoutingRuleset{ + name = <<"prov2_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} + ]} }, - WalletConfigObject = - #domain_WalletConfigObject{ - ref = #domain_WalletConfigRef{id = ?STRING}, - data = #domain_WalletConfig{ - name = ?STRING, - block = - {unblocked, #domain_Unblocked{ - reason = <<"">>, - since = wapi_time:rfc3339() - }}, - suspension = - {active, #domain_Active{ - since = wapi_time:rfc3339() - }}, - payment_institution = #domain_PaymentInstitutionRef{id = 1}, - terms = #domain_TermSetHierarchyRef{id = 1}, - account = #domain_WalletAccount{ - currency = CurrencyRef, - settlement = ?INTEGER - }, - party_ref = #domain_PartyConfigRef{id = PartyID} - } + 106 => #domain_RoutingRuleset{ + name = <<"prov1_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} }, - DomainConfigClient = fun - ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> - {ok, mk_versioned_object(wallet_config, WalletConfigObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> - {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> - {ok, mk_versioned_object(payment_institution, PaymentInstitutionObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> - {ok, mk_versioned_object(routing_rules, RoutingRulesObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> - {ok, mk_versioned_object(terminal, TerminalObject)}; - ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> - {ok, mk_versioned_object(provider, ProviderObject)}; - ('CheckoutObject', _) -> - woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) - end, - DomainConfig = fun('GetLatestVersion', _) -> {ok, ?INTEGER} end, - _ = mock_services( - [ - {domain_config, DomainConfig}, - {domain_config_client, DomainConfigClient} - ], - SupOrConfig - ), - ok. + 107 => #domain_RoutingRuleset{ + name = <<"both_prov_off">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} + ]} + }, + 108 => #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}} + }, + fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> + case maps:get(Id, RoutingRules, undefined) of + undefined -> {ok, #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}}}; + Ruleset -> {ok, Ruleset} + end + end. -mk_versioned_object(Type, Object) -> +mk_versioned_object(Type, Object, Version) -> #domain_conf_v2_VersionedObject{ info = #domain_conf_v2_VersionedObjectInfo{ - version = ?INTEGER, + version = Version, changed_at = genlib_rfc3339:format(genlib_time:unow(), second), changed_by = #domain_conf_v2_Author{ id = ?STRING, @@ -563,3 +714,88 @@ mk_versioned_object(Type, Object) -> }, object = {Type, Object} }. + +%% Terminal helper: TerminalRefId, ProviderRefId, CashLimitRange, Allow, GlobalAllow +mk_terminal_object(TermId, ProvId, LimitRange, Allow, GlobalAllow) -> + #domain_TerminalObject{ + ref = #domain_TerminalRef{id = TermId}, + data = #domain_Terminal{ + name = <<"term">>, + description = <<"test">>, + provider_ref = #domain_ProviderRef{id = ProvId}, + terms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + cash_limit = {value, LimitRange}, + allow = Allow, + global_allow = GlobalAllow + } + } + } + } + }. + +%% Payment institution helper: PiRefId, PoliciesRulesetId, ProhibitionsRulesetId +mk_pi_object(PiId, PoliciesId, ProhibitionsId) -> + #domain_PaymentInstitutionObject{ + ref = #domain_PaymentInstitutionRef{id = PiId}, + data = #domain_PaymentInstitution{ + name = <<"test">>, + system_account_set = {value, #domain_SystemAccountSetRef{id = 1}}, + inspector = {value, #domain_InspectorRef{id = 1}}, + realm = test, + residences = [rus], + withdrawal_routing_rules = #domain_RoutingRules{ + policies = #domain_RoutingRulesetRef{id = PoliciesId}, + prohibitions = #domain_RoutingRulesetRef{id = ProhibitionsId} + } + } + }. + +%% Wallet config helper: WalletConfigRefId, PaymentInstitutionId +mk_wallet_config(WalletRefId, PiId) -> + #domain_WalletConfigObject{ + ref = #domain_WalletConfigRef{id = WalletRefId}, + data = #domain_WalletConfig{ + name = ?STRING, + block = + {unblocked, #domain_Unblocked{ + reason = <<"">>, + since = wapi_time:rfc3339() + }}, + suspension = + {active, #domain_Active{ + since = wapi_time:rfc3339() + }}, + payment_institution = #domain_PaymentInstitutionRef{id = PiId}, + terms = #domain_TermSetHierarchyRef{id = 1}, + account = #domain_WalletAccount{ + currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, + settlement = ?INTEGER + }, + party_ref = #domain_PartyConfigRef{id = ?STRING} + } + }. + +%% Provider helper: ProviderRefId, Allow, GlobalAllow +mk_provider_object(ProvId, Allow, GlobalAllow) -> + #domain_ProviderObject{ + ref = #domain_ProviderRef{id = ProvId}, + data = #domain_Provider{ + name = <<"provider">>, + description = <<"test">>, + proxy = #domain_Proxy{ + ref = #domain_ProxyRef{id = 1}, + additional = #{} + }, + realm = test, + terms = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + allow = Allow, + global_allow = GlobalAllow + } + } + } + } + }. diff --git a/test/wapi_wallet_dummy_data.hrl b/test/wapi_wallet_dummy_data.hrl index 817d0c7..d540439 100644 --- a/test/wapi_wallet_dummy_data.hrl +++ b/test/wapi_wallet_dummy_data.hrl @@ -1,5 +1,14 @@ -define(STRING, <<"TEST">>). -define(STRING2, <<"TEST2">>). + +%% Wallet config IDs for cash limits test scenarios (each maps to different PI/routing) +-define(WALLET_ID_OK, <<"wallet_limits_ok">>). +-define(WALLET_ID_CANDIDATE_DISABLED, <<"wallet_limits_candidate_disabled">>). +-define(WALLET_ID_PROVIDER_GLOBAL_DISALLOW, <<"wallet_limits_provider_global_disallow">>). +-define(WALLET_ID_TERMINAL_2_DISABLED, <<"wallet_limits_terminal_2_disabled">>). +-define(WALLET_ID_TERMINAL_1_DISABLED, <<"wallet_limits_terminal_1_disabled">>). +-define(WALLET_ID_PROVIDER_2_DISABLED, <<"wallet_limits_provider_2_disabled">>). +-define(WALLET_ID_PROVIDER_1_DISABLED, <<"wallet_limits_provider_1_disabled">>). -define(RUB, <<"RUB">>). -define(USD, <<"USD">>). -define(BANKID_RU, <<"PUTIN">>). diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index 00e65fe..5615e97 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -26,7 +26,13 @@ get_account_ok/1, get_account_fail_wallet_notfound/1, get_account_fail_account_notfound/1, - get_cash_limits_ok/1 + get_cash_limits_ok/1, + get_cash_limits_with_candidate_disabled/1, + get_cash_limits_with_provider_global_disallow/1, + get_cash_limits_with_terminal_2_disabled/1, + get_cash_limits_with_terminal_1_disabled/1, + get_cash_limits_with_provider_2_disabled/1, + get_cash_limits_with_provider_1_disabled/1 ]). -define(EMPTY_RESP(Code), {error, {Code, #{}}}). @@ -56,7 +62,13 @@ groups() -> get_account_ok, get_account_fail_wallet_notfound, get_account_fail_account_notfound, - get_cash_limits_ok + get_cash_limits_ok, + get_cash_limits_with_candidate_disabled, + get_cash_limits_with_provider_global_disallow, + get_cash_limits_with_terminal_2_disabled, + get_cash_limits_with_terminal_1_disabled, + get_cash_limits_with_provider_2_disabled, + get_cash_limits_with_provider_1_disabled ]} ]. @@ -134,12 +146,66 @@ get_account_fail_account_notfound(C) -> -spec get_cash_limits_ok(config()) -> _. get_cash_limits_ok(C) -> PartyID = ?config(party, C), - WalletID = ?STRING, + WalletID = ?WALLET_ID_OK, _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - ok = wapi_ct_helper:mock_wallet_limits_domain(PartyID, C), {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Both terminals: lower 200 from routing (term1), upper 500 from wallet ?assertEqual(expected_wallet_limits(), Limits). +-spec get_cash_limits_with_candidate_disabled(config()) -> _. +get_cash_limits_with_candidate_disabled(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_CANDIDATE_DISABLED, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% No terminals: only wallet limits (lower 100, upper 500) + ?assertEqual(expected_wallet_limits(100, 500), Limits). + +-spec get_cash_limits_with_provider_global_disallow(config()) -> _. +get_cash_limits_with_provider_global_disallow(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_PROVIDER_GLOBAL_DISALLOW, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Both providers disabled: no terminals, only wallet limits + ?assertEqual(expected_wallet_limits(100, 500), Limits). + +-spec get_cash_limits_with_terminal_2_disabled(config()) -> _. +get_cash_limits_with_terminal_2_disabled(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_TERMINAL_2_DISABLED, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Only terminal 1 (lower 200): lower from routing, upper from wallet + ?assertEqual(expected_wallet_limits(200, 500), Limits). + +-spec get_cash_limits_with_terminal_1_disabled(config()) -> _. +get_cash_limits_with_terminal_1_disabled(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_TERMINAL_1_DISABLED, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Only terminal 2 (lower 300): lower from routing, upper from wallet + ?assertEqual(expected_wallet_limits(300, 500), Limits). + +-spec get_cash_limits_with_provider_2_disabled(config()) -> _. +get_cash_limits_with_provider_2_disabled(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_PROVIDER_2_DISABLED, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Terminal 2 excluded (uses provider 2): only terminal 1, lower 200 + ?assertEqual(expected_wallet_limits(200, 500), Limits). + +-spec get_cash_limits_with_provider_1_disabled(config()) -> _. +get_cash_limits_with_provider_1_disabled(C) -> + PartyID = ?config(party, C), + WalletID = ?WALLET_ID_PROVIDER_1_DISABLED, + _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), + %% Terminal 1 excluded (uses provider 1): only terminal 2, lower 300 + ?assertEqual(expected_wallet_limits(300, 500), Limits). + %% -spec call_api(function(), map(), wapi_client_lib:context()) -> {ok, term()} | {error, term()}. @@ -185,11 +251,14 @@ get_cash_limits_call_api(WalletID, PartyID, C) -> ). expected_wallet_limits() -> + expected_wallet_limits(200, 500). + +expected_wallet_limits(LowerBound, UpperBound) -> [ #{ <<"currency">> => <<"RUB">>, - <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, - <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, + <<"lowerBound">> => #{<<"amount">> => LowerBound, <<"inclusive">> => true}, + <<"upperBound">> => #{<<"amount">> => UpperBound, <<"inclusive">> => true}, <<"withdrawalMethod">> => #{ <<"method">> => <<"WithdrawalMethodBankCard">>, <<"paymentSystems">> => [] @@ -197,8 +266,8 @@ expected_wallet_limits() -> }, #{ <<"currency">> => <<"RUB">>, - <<"lowerBound">> => #{<<"amount">> => 300, <<"inclusive">> => true}, - <<"upperBound">> => #{<<"amount">> => 800, <<"inclusive">> => true}, + <<"lowerBound">> => #{<<"amount">> => LowerBound, <<"inclusive">> => true}, + <<"upperBound">> => #{<<"amount">> => UpperBound, <<"inclusive">> => true}, <<"withdrawalMethod">> => #{ <<"method">> => <<"WithdrawalMethodDigitalWallet">>, <<"providers">> => [] @@ -206,26 +275,5 @@ expected_wallet_limits() -> } ]. -mock_account_with_balance(ExistingAccountID, C) -> - _ = wapi_ct_helper:mock_services( - [ - {party_management, fun - ('GetAccountState', {_, AccountID, ?INTEGER}) when AccountID =:= ExistingAccountID -> - {ok, #payproc_AccountState{ - account_id = AccountID, - own_amount = ?INTEGER, - available_amount = ?INTEGER, - currency = #domain_Currency{ - name = ?STRING, - symbolic_code = ?RUB, - numeric_code = ?INTEGER, - exponent = ?INTEGER - } - }}; - ('GetAccountState', {_PartyID, _AccountID, _DomainRevision}) -> - throw(#payproc_AccountNotFound{}) - end} - ], - C - ), - ok. +mock_account_with_balance(ExistingAccountID, _C) -> + wapi_ct_helper:set_party_management_account(ExistingAccountID). From 4af09696df2808aafc212474c4d013cad8c4ae51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 19:04:53 +0300 Subject: [PATCH 04/11] fixed --- test/wapi_ct_helper.erl | 10 ++---- test/wapi_wallet_dummy_data.hrl | 8 ++--- test/wapi_wallet_tests_SUITE.erl | 46 +++++----------------------- test/wapi_withdrawal_tests_SUITE.erl | 4 +-- 4 files changed, 14 insertions(+), 54 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 644eb83..92c588a 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -146,10 +146,7 @@ start_app({dmt_client = AppName, SupPid}) -> WalletConfigLimitsOk = mk_wallet_config(?WALLET_ID_OK, 1), WalletConfigCandidateDisabled = mk_wallet_config(?WALLET_ID_CANDIDATE_DISABLED, 2), WalletConfigProviderGlobalDisallow = mk_wallet_config(?WALLET_ID_PROVIDER_GLOBAL_DISALLOW, 10), - WalletConfigTerminal2Disabled = mk_wallet_config(?WALLET_ID_TERMINAL_2_DISABLED, 4), - WalletConfigTerminal1Disabled = mk_wallet_config(?WALLET_ID_TERMINAL_1_DISABLED, 5), - WalletConfigProvider2Disabled = mk_wallet_config(?WALLET_ID_PROVIDER_2_DISABLED, 6), - WalletConfigProvider1Disabled = mk_wallet_config(?WALLET_ID_PROVIDER_1_DISABLED, 9), + WalletConfigPrimaryDisabled = mk_wallet_config(?WALLET_ID_PRIMARY_DISABLED, 5), PartyConfigObject = #domain_PartyConfigObject{ ref = #domain_PartyConfigRef{id = ?STRING}, data = #domain_PartyConfig{ @@ -316,10 +313,7 @@ start_app({dmt_client = AppName, SupPid}) -> ?WALLET_ID_OK -> WalletConfigLimitsOk; ?WALLET_ID_CANDIDATE_DISABLED -> WalletConfigCandidateDisabled; ?WALLET_ID_PROVIDER_GLOBAL_DISALLOW -> WalletConfigProviderGlobalDisallow; - ?WALLET_ID_TERMINAL_2_DISABLED -> WalletConfigTerminal2Disabled; - ?WALLET_ID_TERMINAL_1_DISABLED -> WalletConfigTerminal1Disabled; - ?WALLET_ID_PROVIDER_2_DISABLED -> WalletConfigProvider2Disabled; - ?WALLET_ID_PROVIDER_1_DISABLED -> WalletConfigProvider1Disabled; + ?WALLET_ID_PRIMARY_DISABLED -> WalletConfigPrimaryDisabled; _ -> undefined end, case Wc of diff --git a/test/wapi_wallet_dummy_data.hrl b/test/wapi_wallet_dummy_data.hrl index d540439..43ec068 100644 --- a/test/wapi_wallet_dummy_data.hrl +++ b/test/wapi_wallet_dummy_data.hrl @@ -1,14 +1,12 @@ -define(STRING, <<"TEST">>). -define(STRING2, <<"TEST2">>). +-define(VALID_EMAIL, <<"test@test.ru">>). %% Wallet config IDs for cash limits test scenarios (each maps to different PI/routing) -define(WALLET_ID_OK, <<"wallet_limits_ok">>). -define(WALLET_ID_CANDIDATE_DISABLED, <<"wallet_limits_candidate_disabled">>). -define(WALLET_ID_PROVIDER_GLOBAL_DISALLOW, <<"wallet_limits_provider_global_disallow">>). --define(WALLET_ID_TERMINAL_2_DISABLED, <<"wallet_limits_terminal_2_disabled">>). --define(WALLET_ID_TERMINAL_1_DISABLED, <<"wallet_limits_terminal_1_disabled">>). --define(WALLET_ID_PROVIDER_2_DISABLED, <<"wallet_limits_provider_2_disabled">>). --define(WALLET_ID_PROVIDER_1_DISABLED, <<"wallet_limits_provider_1_disabled">>). +-define(WALLET_ID_PRIMARY_DISABLED, <<"wallet_limits_primary_disabled">>). -define(RUB, <<"RUB">>). -define(USD, <<"USD">>). -define(BANKID_RU, <<"PUTIN">>). @@ -112,7 +110,7 @@ domain_revision = 123, contact_info = #fistful_base_ContactInfo{ phone_number = ?STRING, - email = ?STRING + email = ?VALID_EMAIL } }). diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index 5615e97..39e98cd 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -29,10 +29,7 @@ get_cash_limits_ok/1, get_cash_limits_with_candidate_disabled/1, get_cash_limits_with_provider_global_disallow/1, - get_cash_limits_with_terminal_2_disabled/1, - get_cash_limits_with_terminal_1_disabled/1, - get_cash_limits_with_provider_2_disabled/1, - get_cash_limits_with_provider_1_disabled/1 + get_cash_limits_with_primary_disabled/1 ]). -define(EMPTY_RESP(Code), {error, {Code, #{}}}). @@ -65,10 +62,7 @@ groups() -> get_cash_limits_ok, get_cash_limits_with_candidate_disabled, get_cash_limits_with_provider_global_disallow, - get_cash_limits_with_terminal_2_disabled, - get_cash_limits_with_terminal_1_disabled, - get_cash_limits_with_provider_2_disabled, - get_cash_limits_with_provider_1_disabled + get_cash_limits_with_primary_disabled ]} ]. @@ -170,40 +164,14 @@ get_cash_limits_with_provider_global_disallow(C) -> %% Both providers disabled: no terminals, only wallet limits ?assertEqual(expected_wallet_limits(100, 500), Limits). --spec get_cash_limits_with_terminal_2_disabled(config()) -> _. -get_cash_limits_with_terminal_2_disabled(C) -> +%% Default: both terminals, lower 200 (from primary). When primary disabled: fallback terminal, lower 300. +-spec get_cash_limits_with_primary_disabled(config()) -> _. +get_cash_limits_with_primary_disabled(C) -> PartyID = ?config(party, C), - WalletID = ?WALLET_ID_TERMINAL_2_DISABLED, + WalletID = ?WALLET_ID_PRIMARY_DISABLED, _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Only terminal 1 (lower 200): lower from routing, upper from wallet - ?assertEqual(expected_wallet_limits(200, 500), Limits). - --spec get_cash_limits_with_terminal_1_disabled(config()) -> _. -get_cash_limits_with_terminal_1_disabled(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_TERMINAL_1_DISABLED, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Only terminal 2 (lower 300): lower from routing, upper from wallet - ?assertEqual(expected_wallet_limits(300, 500), Limits). - --spec get_cash_limits_with_provider_2_disabled(config()) -> _. -get_cash_limits_with_provider_2_disabled(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_PROVIDER_2_DISABLED, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Terminal 2 excluded (uses provider 2): only terminal 1, lower 200 - ?assertEqual(expected_wallet_limits(200, 500), Limits). - --spec get_cash_limits_with_provider_1_disabled(config()) -> _. -get_cash_limits_with_provider_1_disabled(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_PROVIDER_1_DISABLED, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Terminal 1 excluded (uses provider 1): only terminal 2, lower 300 + %% Primary terminal disabled -> fallback terminal kicks in, lower changes 200 -> 300 ?assertEqual(expected_wallet_limits(300, 500), Limits). %% diff --git a/test/wapi_withdrawal_tests_SUITE.erl b/test/wapi_withdrawal_tests_SUITE.erl index c02d6f1..7c43da6 100644 --- a/test/wapi_withdrawal_tests_SUITE.erl +++ b/test/wapi_withdrawal_tests_SUITE.erl @@ -152,7 +152,7 @@ create_ok(C) -> {ok, #{ <<"contactInfo">> := #{ <<"phoneNumber">> := ?STRING, - <<"email">> := ?STRING + <<"email">> := ?VALID_EMAIL } }} = create_withdrawal_call_api(C). @@ -647,7 +647,7 @@ create_withdrawal_call_api(C) -> <<"currency">> => ?RUB }, <<"contactInfo">> => #{ - <<"email">> => ?STRING, + <<"email">> => ?VALID_EMAIL, <<"phoneNumber">> => ?STRING } }) From 1fe6514f7ecc5f7e32419638b4af6194cf50e847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 19:28:55 +0300 Subject: [PATCH 05/11] removed cfg --- test/wapi_ct_helper.erl | 56 +++-------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 92c588a..0d04ab9 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -199,14 +199,9 @@ start_app({dmt_client = AppName, SupPid}) -> Disallowed = {constant, false}, Terminal10 = mk_terminal_object(10, 11, Term10Limit, Allowed, Allowed), Terminal20 = mk_terminal_object(20, 21, Term20Limit, Allowed, Allowed), - Terminal12 = mk_terminal_object(12, 12, Term10Limit, Allowed, Allowed), - Terminal22 = mk_terminal_object(22, 22, Term20Limit, Disallowed, Allowed), Provider11 = mk_provider_object(11, Allowed, Allowed), Provider21 = mk_provider_object(21, Allowed, Allowed), - Provider12 = mk_provider_object(12, Allowed, Disallowed), - Provider22 = mk_provider_object(22, Allowed, Allowed), - %% Routing rulesets: 100 both, 101 none, 103 term20 only, 104 term10 only, - %% 105 term10+22 (prov2 off), 106 term12+20 (prov1 off), 107 term12+22 (both prov off) + %% Routing rulesets: 100 both, 101 none, 103 term20 only, 108 empty Routing100 = #domain_RoutingRuleset{ name = <<"both">>, decisions = @@ -231,39 +226,7 @@ start_app({dmt_client = AppName, SupPid}) -> #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} ]} }, - Routing104 = #domain_RoutingRuleset{ - name = <<"term10">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - Routing105 = #domain_RoutingRuleset{ - name = <<"prov2_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} - ]} - }, - Routing106 = #domain_RoutingRuleset{ - name = <<"prov1_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - Routing107 = #domain_RoutingRuleset{ - name = <<"both_prov_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} - ]} - }, - %% Routing 108: empty (for provider_global_disallow - both term 12,22 have disallowed providers) + %% Routing 108: empty (for provider_global_disallow) Routing108 = #domain_RoutingRuleset{ name = <<"empty">>, decisions = {candidates, []} @@ -272,27 +235,18 @@ start_app({dmt_client = AppName, SupPid}) -> 100 => Routing100, 101 => Routing101, 103 => Routing103, - 104 => Routing104, - 105 => Routing105, - 106 => Routing106, - 107 => Routing107, 108 => Routing108 }, RoutingRulesObjects = [ #domain_RoutingRulesObject{ref = #domain_RoutingRulesetRef{id = Id}, data = Data} || {Id, Data} <- maps:to_list(RoutingRules) ], - %% PIs: 1=100, 2=101, 4=104, 5=103, 6=105, 7=107, 8=107, 9=106, 10=108 + %% PIs: 1=100, 2=101, 5=103, 10=108 ProhibitionsId = 101, PiObjects = [ mk_pi_object(1, 100, ProhibitionsId), mk_pi_object(2, 101, ProhibitionsId), - mk_pi_object(4, 104, ProhibitionsId), mk_pi_object(5, 103, ProhibitionsId), - mk_pi_object(6, 105, ProhibitionsId), - mk_pi_object(7, 107, ProhibitionsId), - mk_pi_object(8, 107, ProhibitionsId), - mk_pi_object(9, 106, ProhibitionsId), mk_pi_object(10, 108, ProhibitionsId) ], PiMap = maps:from_list([ @@ -349,8 +303,6 @@ start_app({dmt_client = AppName, SupPid}) -> case Id of 10 -> Terminal10; 20 -> Terminal20; - 12 -> Terminal12; - 22 -> Terminal22; _ -> undefined end, case T of @@ -364,8 +316,6 @@ start_app({dmt_client = AppName, SupPid}) -> case Id of 11 -> Provider11; 21 -> Provider21; - 12 -> Provider12; - 22 -> Provider22; _ -> undefined end, case P of From dab2c2e9ad6b7fb7647c0e939a9b39e5aae0ac87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 19:36:02 +0300 Subject: [PATCH 06/11] removed --- test/wapi_ct_helper.erl | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 0d04ab9..781a431 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -604,38 +604,6 @@ default_party_management_routing() -> #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} ]} }, - 104 => #domain_RoutingRuleset{ - name = <<"term10">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 105 => #domain_RoutingRuleset{ - name = <<"prov2_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} - ]} - }, - 106 => #domain_RoutingRuleset{ - name = <<"prov1_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 107 => #domain_RoutingRuleset{ - name = <<"both_prov_off">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 12}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 22}} - ]} - }, 108 => #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}} }, fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> From aafd1ed6e559066b117aedbbf45a99799c3b2228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 19:41:41 +0300 Subject: [PATCH 07/11] moved --- test/wapi_ct_helper.erl | 80 -------------------------------- test/wapi_wallet_tests_SUITE.erl | 77 +++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 82 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 781a431..211159b 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -3,7 +3,6 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("damsel/include/dmsl_domain_conf_v2_thrift.hrl"). -include_lib("damsel/include/dmsl_domain_thrift.hrl"). --include_lib("damsel/include/dmsl_payproc_thrift.hrl"). -include_lib("wapi_wallet_dummy_data.hrl"). -include_lib("wapi_token_keeper_data.hrl"). @@ -25,9 +24,6 @@ -export([stop_mocked_service_sup/1]). -export([mock_services/2]). -export([mock_services_/2]). --export([default_party_management_routing/0]). --export([set_party_management_account/1]). --export([init_party_management_mock/1]). -export([get_lifetime/0]). -export([create_auth_ctx/1]). @@ -104,7 +100,6 @@ init_suite(Module, Config) -> start_app(woody) ++ start_app({dmt_client, SupPid}) ++ start_app({wapi_lib, Config}), - ok = init_party_management_mock(SupPid), {ok, _} = supervisor:start_child( SupPid, wapi_ct_helper_swagger_server:child_spec(#{wallet => {wapi_ct_helper_handler, #{}}}) ), @@ -538,81 +533,6 @@ create_auth_ctx(PartyID) -> swagger_context => #{auth_context => {?STRING, PartyID, #{}}} }. -%% Sets which account ID exists for GetAccountState (used by mock_account_with_balance) --spec set_party_management_account(integer() | undefined) -> ok. -set_party_management_account(AccountID) -> - application:set_env(wapi_lib, test_account_id, AccountID). - -%% Starts party_management mock (separate from dmt_client) --spec init_party_management_mock(pid()) -> ok. -init_party_management_mock(SupPid) -> - RoutingFun = default_party_management_routing(), - PartyManagement = fun - ('ComputeRoutingRuleset', X) -> - RoutingFun('ComputeRoutingRuleset', X); - ('GetAccountState', {_PartyRef, AccountID, ?INTEGER}) -> - case application:get_env(wapi_lib, test_account_id, undefined) of - AccountID -> - {ok, #payproc_AccountState{ - account_id = AccountID, - own_amount = ?INTEGER, - available_amount = ?INTEGER, - currency = #domain_Currency{ - name = ?STRING, - symbolic_code = ?RUB, - numeric_code = ?INTEGER, - exponent = ?INTEGER - } - }}; - _ -> - throw(#payproc_AccountNotFound{}) - end - end, - Urls = mock_services_([{party_management, PartyManagement}], SupPid), - case maps:get(wapi_lib, Urls, undefined) of - undefined -> ok; - WapiUrls -> start_woody_client(wapi_lib, WapiUrls) - end. - -%% Returns ComputeRoutingRuleset handler for wallet limits domain --spec default_party_management_routing() -> fun(). -default_party_management_routing() -> - Allowed = {constant, true}, - Disallowed = {constant, false}, - RoutingRules = #{ - 100 => #domain_RoutingRuleset{ - name = <<"both">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 101 => #domain_RoutingRuleset{ - name = <<"none">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 103 => #domain_RoutingRuleset{ - name = <<"term20">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 108 => #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}} - }, - fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> - case maps:get(Id, RoutingRules, undefined) of - undefined -> {ok, #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}}}; - Ruleset -> {ok, Ruleset} - end - end. - mk_versioned_object(Type, Object, Version) -> #domain_conf_v2_VersionedObject{ info = #domain_conf_v2_VersionedObjectInfo{ diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index 39e98cd..cd5bbd6 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -71,7 +71,10 @@ groups() -> %% -spec init_per_suite(config()) -> config(). init_per_suite(C) -> - wapi_ct_helper:init_suite(?MODULE, C). + Config = wapi_ct_helper:init_suite(?MODULE, C), + SupPid = ?config(suite_test_sup, Config), + ok = init_party_management_mock(SupPid), + Config. -spec end_per_suite(config()) -> _. end_per_suite(C) -> @@ -244,4 +247,74 @@ expected_wallet_limits(LowerBound, UpperBound) -> ]. mock_account_with_balance(ExistingAccountID, _C) -> - wapi_ct_helper:set_party_management_account(ExistingAccountID). + set_party_management_account(ExistingAccountID). + +%% Party management mock (GetAccountState, ComputeRoutingRuleset for wallet limits) +-spec init_party_management_mock(pid()) -> ok. +init_party_management_mock(SupPid) -> + RoutingFun = default_party_management_routing(), + PartyManagement = fun + ('ComputeRoutingRuleset', X) -> + RoutingFun('ComputeRoutingRuleset', X); + ('GetAccountState', {_PartyRef, AccountID, ?INTEGER}) -> + case application:get_env(wapi_lib, test_account_id, undefined) of + AccountID -> + {ok, #payproc_AccountState{ + account_id = AccountID, + own_amount = ?INTEGER, + available_amount = ?INTEGER, + currency = #domain_Currency{ + name = ?STRING, + symbolic_code = ?RUB, + numeric_code = ?INTEGER, + exponent = ?INTEGER + } + }}; + _ -> + throw(#payproc_AccountNotFound{}) + end + end, + _ = wapi_ct_helper:mock_services([{party_management, PartyManagement}], SupPid), + ok. + +-spec set_party_management_account(integer() | undefined) -> ok. +set_party_management_account(AccountID) -> + application:set_env(wapi_lib, test_account_id, AccountID). + +-spec default_party_management_routing() -> fun(). +default_party_management_routing() -> + Allowed = {constant, true}, + Disallowed = {constant, false}, + RoutingRules = #{ + 100 => #domain_RoutingRuleset{ + name = <<"both">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + 101 => #domain_RoutingRuleset{ + name = <<"none">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + 103 => #domain_RoutingRuleset{ + name = <<"term20">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }, + 108 => #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}} + }, + fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> + case maps:get(Id, RoutingRules, undefined) of + undefined -> {ok, #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}}}; + Ruleset -> {ok, Ruleset} + end + end. From 4b3b6433c24086dac71db5fa49cd9386b2bab233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 19:57:32 +0300 Subject: [PATCH 08/11] refactored --- test/wapi_ct_helper.erl | 115 +++++++++++-------------------- test/wapi_wallet_dummy_data.hrl | 1 - test/wapi_wallet_tests_SUITE.erl | 14 +--- 3 files changed, 41 insertions(+), 89 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 211159b..981a410 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -135,12 +135,10 @@ start_app(woody = AppName) -> start_app({dmt_client = AppName, SupPid}) -> CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, Version = ?INTEGER, - %% Base wallet config (get_ok, get_account_ok) WalletConfigObject = mk_wallet_config(?STRING, 1), %% Wallet configs for cash limits scenarios (each -> different PI) WalletConfigLimitsOk = mk_wallet_config(?WALLET_ID_OK, 1), WalletConfigCandidateDisabled = mk_wallet_config(?WALLET_ID_CANDIDATE_DISABLED, 2), - WalletConfigProviderGlobalDisallow = mk_wallet_config(?WALLET_ID_PROVIDER_GLOBAL_DISALLOW, 10), WalletConfigPrimaryDisabled = mk_wallet_config(?WALLET_ID_PRIMARY_DISABLED, 5), PartyConfigObject = #domain_PartyConfigObject{ ref = #domain_PartyConfigRef{id = ?STRING}, @@ -196,7 +194,7 @@ start_app({dmt_client = AppName, SupPid}) -> Terminal20 = mk_terminal_object(20, 21, Term20Limit, Allowed, Allowed), Provider11 = mk_provider_object(11, Allowed, Allowed), Provider21 = mk_provider_object(21, Allowed, Allowed), - %% Routing rulesets: 100 both, 101 none, 103 term20 only, 108 empty + Routing100 = #domain_RoutingRuleset{ name = <<"both">>, decisions = @@ -221,28 +219,21 @@ start_app({dmt_client = AppName, SupPid}) -> #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} ]} }, - %% Routing 108: empty (for provider_global_disallow) - Routing108 = #domain_RoutingRuleset{ - name = <<"empty">>, - decisions = {candidates, []} - }, RoutingRules = #{ 100 => Routing100, 101 => Routing101, - 103 => Routing103, - 108 => Routing108 + 103 => Routing103 }, RoutingRulesObjects = [ #domain_RoutingRulesObject{ref = #domain_RoutingRulesetRef{id = Id}, data = Data} || {Id, Data} <- maps:to_list(RoutingRules) ], - %% PIs: 1=100, 2=101, 5=103, 10=108 + ProhibitionsId = 101, PiObjects = [ mk_pi_object(1, 100, ProhibitionsId), mk_pi_object(2, 101, ProhibitionsId), - mk_pi_object(5, 103, ProhibitionsId), - mk_pi_object(10, 108, ProhibitionsId) + mk_pi_object(5, 103, ProhibitionsId) ], PiMap = maps:from_list([ {(P#domain_PaymentInstitutionObject.ref)#domain_PaymentInstitutionRef.id, P} @@ -253,70 +244,44 @@ start_app({dmt_client = AppName, SupPid}) -> || R <- RoutingRulesObjects ]), DomainConfigClient = fun - ('CheckoutObject', {{version, V}, {wallet_config, #domain_WalletConfigRef{id = Id}}}) when - V =:= Version - -> - Wc = - case Id of - ?STRING -> WalletConfigObject; - ?WALLET_ID_OK -> WalletConfigLimitsOk; - ?WALLET_ID_CANDIDATE_DISABLED -> WalletConfigCandidateDisabled; - ?WALLET_ID_PROVIDER_GLOBAL_DISALLOW -> WalletConfigProviderGlobalDisallow; - ?WALLET_ID_PRIMARY_DISABLED -> WalletConfigPrimaryDisabled; - _ -> undefined - end, - case Wc of - undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); - _ -> {ok, mk_versioned_object(wallet_config, Wc, Version)} - end; - ('CheckoutObject', {{version, V}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) when - V =:= Version - -> + ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigObject, Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_OK}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigLimitsOk, Version)}; + ( + 'CheckoutObject', + {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_CANDIDATE_DISABLED}}} + ) -> + {ok, mk_versioned_object(wallet_config, WalletConfigCandidateDisabled, Version)}; + ( + 'CheckoutObject', + {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_PRIMARY_DISABLED}}} + ) -> + {ok, mk_versioned_object(wallet_config, WalletConfigPrimaryDisabled, Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) -> {ok, mk_versioned_object(party_config, PartyConfigObject, Version)}; - ('CheckoutObject', {{version, V}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) when - V =:= Version - -> + ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject, Version)}; - ('CheckoutObject', {{version, V}, {payment_institution, #domain_PaymentInstitutionRef{id = PiId}}}) when - V =:= Version - -> - case maps:get(PiId, PiMap, undefined) of - undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); - PiObj -> {ok, mk_versioned_object(payment_institution, PiObj, Version)} - end; - ('CheckoutObject', {{version, V}, {routing_rules, #domain_RoutingRulesetRef{id = Id}}}) when - V =:= Version - -> - case maps:get(Id, RoutingMap, undefined) of - undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); - RrObj -> {ok, mk_versioned_object(routing_rules, RrObj, Version)} - end; - ('CheckoutObject', {{version, V}, {terminal, #domain_TerminalRef{id = Id}}}) when - V =:= Version - -> - T = - case Id of - 10 -> Terminal10; - 20 -> Terminal20; - _ -> undefined - end, - case T of - undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); - _ -> {ok, mk_versioned_object(terminal, T, Version)} - end; - ('CheckoutObject', {{version, V}, {provider, #domain_ProviderRef{id = Id}}}) when - V =:= Version - -> - P = - case Id of - 11 -> Provider11; - 21 -> Provider21; - _ -> undefined - end, - case P of - undefined -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}); - _ -> {ok, mk_versioned_object(provider, P, Version)} - end; + ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> + {ok, mk_versioned_object(payment_institution, maps:get(1, PiMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 2}}}) -> + {ok, mk_versioned_object(payment_institution, maps:get(2, PiMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 5}}}) -> + {ok, mk_versioned_object(payment_institution, maps:get(5, PiMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> + {ok, mk_versioned_object(routing_rules, maps:get(100, RoutingMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 101}}}) -> + {ok, mk_versioned_object(routing_rules, maps:get(101, RoutingMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 103}}}) -> + {ok, mk_versioned_object(routing_rules, maps:get(103, RoutingMap), Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> + {ok, mk_versioned_object(terminal, Terminal10, Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 20}}}) -> + {ok, mk_versioned_object(terminal, Terminal20, Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> + {ok, mk_versioned_object(provider, Provider11, Version)}; + ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 21}}}) -> + {ok, mk_versioned_object(provider, Provider21, Version)}; ('CheckoutObject', _) -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) end, diff --git a/test/wapi_wallet_dummy_data.hrl b/test/wapi_wallet_dummy_data.hrl index 43ec068..d1b6ed9 100644 --- a/test/wapi_wallet_dummy_data.hrl +++ b/test/wapi_wallet_dummy_data.hrl @@ -5,7 +5,6 @@ %% Wallet config IDs for cash limits test scenarios (each maps to different PI/routing) -define(WALLET_ID_OK, <<"wallet_limits_ok">>). -define(WALLET_ID_CANDIDATE_DISABLED, <<"wallet_limits_candidate_disabled">>). --define(WALLET_ID_PROVIDER_GLOBAL_DISALLOW, <<"wallet_limits_provider_global_disallow">>). -define(WALLET_ID_PRIMARY_DISABLED, <<"wallet_limits_primary_disabled">>). -define(RUB, <<"RUB">>). -define(USD, <<"USD">>). diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index cd5bbd6..6d87c04 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -28,7 +28,6 @@ get_account_fail_account_notfound/1, get_cash_limits_ok/1, get_cash_limits_with_candidate_disabled/1, - get_cash_limits_with_provider_global_disallow/1, get_cash_limits_with_primary_disabled/1 ]). @@ -61,7 +60,6 @@ groups() -> get_account_fail_account_notfound, get_cash_limits_ok, get_cash_limits_with_candidate_disabled, - get_cash_limits_with_provider_global_disallow, get_cash_limits_with_primary_disabled ]} ]. @@ -158,15 +156,6 @@ get_cash_limits_with_candidate_disabled(C) -> %% No terminals: only wallet limits (lower 100, upper 500) ?assertEqual(expected_wallet_limits(100, 500), Limits). --spec get_cash_limits_with_provider_global_disallow(config()) -> _. -get_cash_limits_with_provider_global_disallow(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_PROVIDER_GLOBAL_DISALLOW, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Both providers disabled: no terminals, only wallet limits - ?assertEqual(expected_wallet_limits(100, 500), Limits). - %% Default: both terminals, lower 200 (from primary). When primary disabled: fallback terminal, lower 300. -spec get_cash_limits_with_primary_disabled(config()) -> _. get_cash_limits_with_primary_disabled(C) -> @@ -309,8 +298,7 @@ default_party_management_routing() -> #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} ]} - }, - 108 => #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}} + } }, fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> case maps:get(Id, RoutingRules, undefined) of From 7d044da3cda1adcc941f4538e1b1e4bca6c55108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 21:09:06 +0300 Subject: [PATCH 09/11] removed --- test/wapi_ct_helper.erl | 133 +++++-------------------------- test/wapi_wallet_dummy_data.hrl | 3 - test/wapi_wallet_tests_SUITE.erl | 56 ++----------- 3 files changed, 27 insertions(+), 165 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 981a410..2c75f7b 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -134,12 +134,8 @@ start_app(woody = AppName) -> ]); start_app({dmt_client = AppName, SupPid}) -> CurrencyRef = #domain_CurrencyRef{symbolic_code = <<"RUB">>}, - Version = ?INTEGER, WalletConfigObject = mk_wallet_config(?STRING, 1), - %% Wallet configs for cash limits scenarios (each -> different PI) WalletConfigLimitsOk = mk_wallet_config(?WALLET_ID_OK, 1), - WalletConfigCandidateDisabled = mk_wallet_config(?WALLET_ID_CANDIDATE_DISABLED, 2), - WalletConfigPrimaryDisabled = mk_wallet_config(?WALLET_ID_PRIMARY_DISABLED, 5), PartyConfigObject = #domain_PartyConfigObject{ ref = #domain_PartyConfigRef{id = ?STRING}, data = #domain_PartyConfig{ @@ -189,106 +185,38 @@ start_app({dmt_client = AppName, SupPid}) -> upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} }, Allowed = {constant, true}, - Disallowed = {constant, false}, Terminal10 = mk_terminal_object(10, 11, Term10Limit, Allowed, Allowed), Terminal20 = mk_terminal_object(20, 21, Term20Limit, Allowed, Allowed), Provider11 = mk_provider_object(11, Allowed, Allowed), Provider21 = mk_provider_object(21, Allowed, Allowed), - Routing100 = #domain_RoutingRuleset{ - name = <<"both">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - Routing101 = #domain_RoutingRuleset{ - name = <<"none">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - Routing103 = #domain_RoutingRuleset{ - name = <<"term20">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - RoutingRules = #{ - 100 => Routing100, - 101 => Routing101, - 103 => Routing103 - }, - RoutingRulesObjects = [ - #domain_RoutingRulesObject{ref = #domain_RoutingRulesetRef{id = Id}, data = Data} - || {Id, Data} <- maps:to_list(RoutingRules) - ], - - ProhibitionsId = 101, - PiObjects = [ - mk_pi_object(1, 100, ProhibitionsId), - mk_pi_object(2, 101, ProhibitionsId), - mk_pi_object(5, 103, ProhibitionsId) - ], - PiMap = maps:from_list([ - {(P#domain_PaymentInstitutionObject.ref)#domain_PaymentInstitutionRef.id, P} - || P <- PiObjects - ]), - RoutingMap = maps:from_list([ - {(R#domain_RoutingRulesObject.ref)#domain_RoutingRulesetRef.id, R} - || R <- RoutingRulesObjects - ]), + PiObject = mk_pi_object(1, 100, 101), DomainConfigClient = fun - ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> - {ok, mk_versioned_object(wallet_config, WalletConfigObject, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_OK}}}) -> - {ok, mk_versioned_object(wallet_config, WalletConfigLimitsOk, Version)}; - ( - 'CheckoutObject', - {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_CANDIDATE_DISABLED}}} - ) -> - {ok, mk_versioned_object(wallet_config, WalletConfigCandidateDisabled, Version)}; - ( - 'CheckoutObject', - {{version, ?INTEGER}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_PRIMARY_DISABLED}}} - ) -> - {ok, mk_versioned_object(wallet_config, WalletConfigPrimaryDisabled, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) -> - {ok, mk_versioned_object(party_config, PartyConfigObject, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> - {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> - {ok, mk_versioned_object(payment_institution, maps:get(1, PiMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 2}}}) -> - {ok, mk_versioned_object(payment_institution, maps:get(2, PiMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {payment_institution, #domain_PaymentInstitutionRef{id = 5}}}) -> - {ok, mk_versioned_object(payment_institution, maps:get(5, PiMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 100}}}) -> - {ok, mk_versioned_object(routing_rules, maps:get(100, RoutingMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 101}}}) -> - {ok, mk_versioned_object(routing_rules, maps:get(101, RoutingMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {routing_rules, #domain_RoutingRulesetRef{id = 103}}}) -> - {ok, mk_versioned_object(routing_rules, maps:get(103, RoutingMap), Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 10}}}) -> - {ok, mk_versioned_object(terminal, Terminal10, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {terminal, #domain_TerminalRef{id = 20}}}) -> - {ok, mk_versioned_object(terminal, Terminal20, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 11}}}) -> - {ok, mk_versioned_object(provider, Provider11, Version)}; - ('CheckoutObject', {{version, ?INTEGER}, {provider, #domain_ProviderRef{id = 21}}}) -> - {ok, mk_versioned_object(provider, Provider21, Version)}; + ('CheckoutObject', {{version, V}, {wallet_config, #domain_WalletConfigRef{id = ?STRING}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigObject, V)}; + ('CheckoutObject', {{version, V}, {wallet_config, #domain_WalletConfigRef{id = ?WALLET_ID_OK}}}) -> + {ok, mk_versioned_object(wallet_config, WalletConfigLimitsOk, V)}; + ('CheckoutObject', {{version, V}, {party_config, #domain_PartyConfigRef{id = ?STRING}}}) -> + {ok, mk_versioned_object(party_config, PartyConfigObject, V)}; + ('CheckoutObject', {{version, V}, {term_set_hierarchy, #domain_TermSetHierarchyRef{id = 1}}}) -> + {ok, mk_versioned_object(term_set_hierarchy, TermSetHierarchyObject, V)}; + ('CheckoutObject', {{version, V}, {payment_institution, #domain_PaymentInstitutionRef{id = 1}}}) -> + {ok, mk_versioned_object(payment_institution, PiObject, V)}; + ('CheckoutObject', {{version, V}, {terminal, #domain_TerminalRef{id = 10}}}) -> + {ok, mk_versioned_object(terminal, Terminal10, V)}; + ('CheckoutObject', {{version, V}, {terminal, #domain_TerminalRef{id = 20}}}) -> + {ok, mk_versioned_object(terminal, Terminal20, V)}; + ('CheckoutObject', {{version, V}, {provider, #domain_ProviderRef{id = 11}}}) -> + {ok, mk_versioned_object(provider, Provider11, V)}; + ('CheckoutObject', {{version, V}, {provider, #domain_ProviderRef{id = 21}}}) -> + {ok, mk_versioned_object(provider, Provider21, V)}; ('CheckoutObject', _) -> woody_error:raise(business, #domain_conf_v2_ObjectNotFound{}) end, Urls = mock_services_( [ {domain_config_client, DomainConfigClient}, - {domain_config, fun('GetLatestVersion', _) -> {ok, Version} end} + {domain_config, fun('GetLatestVersion', _) -> {ok, ?INTEGER} end} ], SupPid ), @@ -383,28 +311,11 @@ start_woody_client(bender, Urls) -> ), start_app(bender_client, []); start_woody_client(wapi_lib, Urls) -> - Existing = - case application:get_env(wapi_lib, service_urls) of - {ok, M} when is_map(M) -> M; - _ -> #{} - end, ok = application:set_env( wapi_lib, service_urls, - maps:merge(Existing, Urls) - ); -start_woody_client(domain_config, Url) -> - update_dmt_service_url('Repository', Url); -start_woody_client(domain_config_client, Url) -> - update_dmt_service_url('RepositoryClient', Url). - -update_dmt_service_url(Key, Url) -> - ServiceUrls = - case application:get_env(dmt_client, service_urls) of - {ok, Urls} -> Urls; - undefined -> #{} - end, - ok = application:set_env(dmt_client, service_urls, ServiceUrls#{Key => Url}). + Urls + ). -spec mock_services_(_, _) -> _. % TODO need a better name diff --git a/test/wapi_wallet_dummy_data.hrl b/test/wapi_wallet_dummy_data.hrl index d1b6ed9..dad30fd 100644 --- a/test/wapi_wallet_dummy_data.hrl +++ b/test/wapi_wallet_dummy_data.hrl @@ -2,10 +2,7 @@ -define(STRING2, <<"TEST2">>). -define(VALID_EMAIL, <<"test@test.ru">>). -%% Wallet config IDs for cash limits test scenarios (each maps to different PI/routing) -define(WALLET_ID_OK, <<"wallet_limits_ok">>). --define(WALLET_ID_CANDIDATE_DISABLED, <<"wallet_limits_candidate_disabled">>). --define(WALLET_ID_PRIMARY_DISABLED, <<"wallet_limits_primary_disabled">>). -define(RUB, <<"RUB">>). -define(USD, <<"USD">>). -define(BANKID_RU, <<"PUTIN">>). diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index 6d87c04..ef10f83 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -26,9 +26,7 @@ get_account_ok/1, get_account_fail_wallet_notfound/1, get_account_fail_account_notfound/1, - get_cash_limits_ok/1, - get_cash_limits_with_candidate_disabled/1, - get_cash_limits_with_primary_disabled/1 + get_cash_limits_ok/1 ]). -define(EMPTY_RESP(Code), {error, {Code, #{}}}). @@ -58,9 +56,7 @@ groups() -> get_account_ok, get_account_fail_wallet_notfound, get_account_fail_account_notfound, - get_cash_limits_ok, - get_cash_limits_with_candidate_disabled, - get_cash_limits_with_primary_disabled + get_cash_limits_ok ]} ]. @@ -147,25 +143,6 @@ get_cash_limits_ok(C) -> %% Both terminals: lower 200 from routing (term1), upper 500 from wallet ?assertEqual(expected_wallet_limits(), Limits). --spec get_cash_limits_with_candidate_disabled(config()) -> _. -get_cash_limits_with_candidate_disabled(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_CANDIDATE_DISABLED, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% No terminals: only wallet limits (lower 100, upper 500) - ?assertEqual(expected_wallet_limits(100, 500), Limits). - -%% Default: both terminals, lower 200 (from primary). When primary disabled: fallback terminal, lower 300. --spec get_cash_limits_with_primary_disabled(config()) -> _. -get_cash_limits_with_primary_disabled(C) -> - PartyID = ?config(party, C), - WalletID = ?WALLET_ID_PRIMARY_DISABLED, - _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), - {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Primary terminal disabled -> fallback terminal kicks in, lower changes 200 -> 300 - ?assertEqual(expected_wallet_limits(300, 500), Limits). - %% -spec call_api(function(), map(), wapi_client_lib:context()) -> {ok, term()} | {error, term()}. @@ -273,36 +250,13 @@ set_party_management_account(AccountID) -> -spec default_party_management_routing() -> fun(). default_party_management_routing() -> Allowed = {constant, true}, - Disallowed = {constant, false}, - RoutingRules = #{ - 100 => #domain_RoutingRuleset{ + fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = 100}, _V, _Varset}) -> + {ok, #domain_RoutingRuleset{ name = <<"both">>, decisions = {candidates, [ #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} ]} - }, - 101 => #domain_RoutingRuleset{ - name = <<"none">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }, - 103 => #domain_RoutingRuleset{ - name = <<"term20">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Disallowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - } - }, - fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = Id}, _V, _Varset}) -> - case maps:get(Id, RoutingRules, undefined) of - undefined -> {ok, #domain_RoutingRuleset{name = <<"empty">>, decisions = {candidates, []}}}; - Ruleset -> {ok, Ruleset} - end + }} end. From 037cf0313064e8df29ccaac9a1370ded88992ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Fri, 6 Mar 2026 21:52:35 +0300 Subject: [PATCH 10/11] changed amount in test --- test/wapi_ct_helper.erl | 8 +++++--- test/wapi_wallet_tests_SUITE.erl | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/wapi_ct_helper.erl b/test/wapi_ct_helper.erl index 2c75f7b..a54781e 100644 --- a/test/wapi_ct_helper.erl +++ b/test/wapi_ct_helper.erl @@ -155,9 +155,10 @@ start_app({dmt_client = AppName, SupPid}) -> } }, %% Term set hierarchy (shared) + %% Wallet limit 100-1000 (wide) - terminal limits will constrain WithdrawalLimitRange = #domain_CashRange{ lower = {inclusive, #domain_Cash{amount = 100, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 500, currency = CurrencyRef}} + upper = {inclusive, #domain_Cash{amount = 1000, currency = CurrencyRef}} }, PaymentMethods = [ #domain_PaymentMethodRef{id = {bank_card, #domain_BankCardPaymentMethod{}}}, @@ -176,13 +177,14 @@ start_app({dmt_client = AppName, SupPid}) -> } } }, + %% Term limits 200-400 and 300-500, union = 200-500 (terminal limits constrain) Term10Limit = #domain_CashRange{ lower = {inclusive, #domain_Cash{amount = 200, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 900, currency = CurrencyRef}} + upper = {inclusive, #domain_Cash{amount = 400, currency = CurrencyRef}} }, Term20Limit = #domain_CashRange{ lower = {inclusive, #domain_Cash{amount = 300, currency = CurrencyRef}}, - upper = {inclusive, #domain_Cash{amount = 800, currency = CurrencyRef}} + upper = {inclusive, #domain_Cash{amount = 500, currency = CurrencyRef}} }, Allowed = {constant, true}, Terminal10 = mk_terminal_object(10, 11, Term10Limit, Allowed, Allowed), diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index ef10f83..c09f7f8 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -140,7 +140,7 @@ get_cash_limits_ok(C) -> WalletID = ?WALLET_ID_OK, _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), - %% Both terminals: lower 200 from routing (term1), upper 500 from wallet + %% Term union 200-500 (term1: 200-400, term2: 300-500), wallet 100-1000; terminal limits constrain ?assertEqual(expected_wallet_limits(), Limits). %% @@ -188,6 +188,7 @@ get_cash_limits_call_api(WalletID, PartyID, C) -> ). expected_wallet_limits() -> + %% 200-500 from terminal union (term1: 200-400, term2: 300-500) expected_wallet_limits(200, 500). expected_wallet_limits(LowerBound, UpperBound) -> From abc547eaccffe091ff50844195818a1558dd0e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC?= Date: Sat, 7 Mar 2026 01:48:40 +0300 Subject: [PATCH 11/11] fixed --- test/wapi_wallet_tests_SUITE.erl | 68 ++++++++++++-------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/test/wapi_wallet_tests_SUITE.erl b/test/wapi_wallet_tests_SUITE.erl index c09f7f8..699f8b8 100644 --- a/test/wapi_wallet_tests_SUITE.erl +++ b/test/wapi_wallet_tests_SUITE.erl @@ -65,10 +65,7 @@ groups() -> %% -spec init_per_suite(config()) -> config(). init_per_suite(C) -> - Config = wapi_ct_helper:init_suite(?MODULE, C), - SupPid = ?config(suite_test_sup, Config), - ok = init_party_management_mock(SupPid), - Config. + wapi_ct_helper:init_suite(?MODULE, C). -spec end_per_suite(config()) -> _. end_per_suite(C) -> @@ -118,20 +115,20 @@ get_fail_wallet_notfound(C) -> get_account_ok(C) -> PartyID = ?config(party, C), _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletAccount">>, ?STRING, PartyID, C), - ok = mock_account_with_balance(?INTEGER, C), + ok = mock_party_management(?INTEGER, C), {ok, _} = get_account_call_api(?STRING, C). -spec get_account_fail_wallet_notfound(config()) -> _. get_account_fail_wallet_notfound(C) -> _ = wapi_ct_helper_bouncer:mock_arbiter(wapi_ct_helper_bouncer:judge_always_forbidden(), C), - ok = mock_account_with_balance(?INTEGER, C), + ok = mock_party_management(?INTEGER, C), ?assertEqual(?EMPTY_RESP(401), get_account_call_api(<<"non existant wallet id">>, C)). -spec get_account_fail_account_notfound(config()) -> _. get_account_fail_account_notfound(C) -> PartyID = ?config(party, C), _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletAccount">>, ?STRING, PartyID, C), - ok = mock_account_with_balance(424242, C), + ok = mock_party_management(424242, C), ?assertEqual({error, {404, #{}}}, get_account_call_api(?STRING, C)). -spec get_cash_limits_ok(config()) -> _. @@ -139,6 +136,7 @@ get_cash_limits_ok(C) -> PartyID = ?config(party, C), WalletID = ?WALLET_ID_OK, _ = wapi_ct_helper_bouncer:mock_assert_wallet_op_ctx(<<"GetWalletCashLimits">>, WalletID, PartyID, C), + ok = mock_party_management(?INTEGER, C), {ok, Limits} = get_cash_limits_call_api(WalletID, PartyID, C), %% Term union 200-500 (term1: 200-400, term2: 300-500), wallet 100-1000; terminal limits constrain ?assertEqual(expected_wallet_limits(), Limits). @@ -213,19 +211,22 @@ expected_wallet_limits(LowerBound, UpperBound) -> } ]. -mock_account_with_balance(ExistingAccountID, _C) -> - set_party_management_account(ExistingAccountID). - -%% Party management mock (GetAccountState, ComputeRoutingRuleset for wallet limits) --spec init_party_management_mock(pid()) -> ok. -init_party_management_mock(SupPid) -> - RoutingFun = default_party_management_routing(), - PartyManagement = fun - ('ComputeRoutingRuleset', X) -> - RoutingFun('ComputeRoutingRuleset', X); - ('GetAccountState', {_PartyRef, AccountID, ?INTEGER}) -> - case application:get_env(wapi_lib, test_account_id, undefined) of - AccountID -> +-spec mock_party_management(integer() | undefined, config()) -> ok. +mock_party_management(ExistingAccountID, C) -> + _ = wapi_ct_helper:mock_services( + [ + {party_management, fun + ('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = 100}, _V, _Varset}) -> + Allowed = {constant, true}, + {ok, #domain_RoutingRuleset{ + name = <<"both">>, + decisions = + {candidates, [ + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, + #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} + ]} + }}; + ('GetAccountState', {_, AccountID, ?INTEGER}) when AccountID =:= ExistingAccountID -> {ok, #payproc_AccountState{ account_id = AccountID, own_amount = ?INTEGER, @@ -237,27 +238,10 @@ init_party_management_mock(SupPid) -> exponent = ?INTEGER } }}; - _ -> + ('GetAccountState', {_PartyID, _AccountID, _DomainRevision}) -> throw(#payproc_AccountNotFound{}) - end - end, - _ = wapi_ct_helper:mock_services([{party_management, PartyManagement}], SupPid), + end} + ], + C + ), ok. - --spec set_party_management_account(integer() | undefined) -> ok. -set_party_management_account(AccountID) -> - application:set_env(wapi_lib, test_account_id, AccountID). - --spec default_party_management_routing() -> fun(). -default_party_management_routing() -> - Allowed = {constant, true}, - fun('ComputeRoutingRuleset', {#domain_RoutingRulesetRef{id = 100}, _V, _Varset}) -> - {ok, #domain_RoutingRuleset{ - name = <<"both">>, - decisions = - {candidates, [ - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 10}}, - #domain_RoutingCandidate{allowed = Allowed, terminal = #domain_TerminalRef{id = 20}} - ]} - }} - end.