Skip to content

Commit e3f5931

Browse files
authored
Makes batch limit rollback to respect operation flags (#172)
* Makes batch limit rollback to respect operation flags * Makes batch limit rollback to respect operation flags In case of pre-processing repair it allows to rollback non-existent hold operations. * Moves try/catch deeper into turnover-limits iteration function * Adds non-existent limit rollback testcase * Fixes expected error term * Fixes `repair_fail_routing_not_existent_operation` testcase * Cleans up and adds assertion for limit-configs to exist in domain * Updates testcase with exception assertion * Allows non-existent limits rollback when repairing pre-processing * Fixes missing args
1 parent 9b931ed commit e3f5931

6 files changed

Lines changed: 145 additions & 49 deletions

File tree

apps/hellgate/src/hg_invoice_payment.erl

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,7 +2512,7 @@ get_limit_overflow_routes(Routes, VS, Iter, St) ->
25122512
fun(Route, {RoutesNoOverflowIn, RejectedIn, LimitsIn}) ->
25132513
PaymentRoute = hg_route:to_payment_route(Route),
25142514
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
2515-
TurnoverLimits = get_turnover_limits(ProviderTerms),
2515+
TurnoverLimits = get_turnover_limits(ProviderTerms, strict),
25162516
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment, PaymentRoute, Iter) of
25172517
{ok, Limits} ->
25182518
{[Route | RoutesNoOverflowIn], RejectedIn, LimitsIn#{PaymentRoute => Limits}};
@@ -2570,10 +2570,8 @@ rollback_shop_limits(Opts, St, Flags) ->
25702570
Flags
25712571
).
25722572

2573-
get_shop_turnover_limits(#domain_ShopConfig{turnover_limits = undefined}) ->
2574-
[];
2575-
get_shop_turnover_limits(#domain_ShopConfig{turnover_limits = T}) ->
2576-
ordsets:to_list(T).
2573+
get_shop_turnover_limits(ShopConfig) ->
2574+
hg_limiter:get_turnover_limits(ShopConfig, strict).
25772575

25782576
%%
25792577

@@ -2588,7 +2586,7 @@ hold_limit_routes(Routes0, VS, Iter, St) ->
25882586
fun(Route, {LimitHeldRoutes, RejectedRoutes} = Acc) ->
25892587
PaymentRoute = hg_route:to_payment_route(Route),
25902588
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
2591-
TurnoverLimits = get_turnover_limits(ProviderTerms),
2589+
TurnoverLimits = get_turnover_limits(ProviderTerms, strict),
25922590
try
25932591
ok = hg_limiter:hold_payment_limits(TurnoverLimits, Invoice, Payment, PaymentRoute, Iter),
25942592
{[Route | LimitHeldRoutes], RejectedRoutes}
@@ -2622,7 +2620,7 @@ rollback_payment_limits(Routes, Iter, St, Flags) ->
26222620
lists:foreach(
26232621
fun(Route) ->
26242622
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
2625-
TurnoverLimits = get_turnover_limits(ProviderTerms),
2623+
TurnoverLimits = get_turnover_limits(ProviderTerms, strict),
26262624
ok = hg_limiter:rollback_payment_limits(TurnoverLimits, Invoice, Payment, Route, Iter, Flags)
26272625
end,
26282626
Routes
@@ -2632,7 +2630,7 @@ rollback_broken_payment_limits(St) ->
26322630
Opts = get_opts(St),
26332631
Payment = get_payment(St),
26342632
Invoice = get_invoice(Opts),
2635-
LimitValues = get_limit_values(St),
2633+
LimitValues = get_limit_values_(St, lenient),
26362634
Iter = maps:size(LimitValues),
26372635
maps:fold(
26382636
fun
@@ -2661,9 +2659,8 @@ rollback_unused_payment_limits(St) ->
26612659
UnUsedRoutes = Routes -- [Route],
26622660
rollback_payment_limits(UnUsedRoutes, get_iter(St), St, [ignore_business_error, ignore_not_found]).
26632661

2664-
get_turnover_limits(ProviderTerms) ->
2665-
TurnoverLimitSelector = ProviderTerms#domain_PaymentsProvisionTerms.turnover_limits,
2666-
hg_limiter:get_turnover_limits(TurnoverLimitSelector).
2662+
get_turnover_limits(ProviderTerms, Mode) ->
2663+
hg_limiter:get_turnover_limits(ProviderTerms, Mode).
26672664

26682665
commit_payment_limits(#st{capture_data = CaptureData} = St) ->
26692666
Opts = get_opts(St),
@@ -2673,7 +2670,7 @@ commit_payment_limits(#st{capture_data = CaptureData} = St) ->
26732670
Invoice = get_invoice(Opts),
26742671
Route = get_route(St),
26752672
ProviderTerms = get_provider_terms(St, Revision),
2676-
TurnoverLimits = get_turnover_limits(ProviderTerms),
2673+
TurnoverLimits = get_turnover_limits(ProviderTerms, strict),
26772674
Iter = get_iter(St),
26782675
hg_limiter:commit_payment_limits(TurnoverLimits, Invoice, Payment, Route, Iter, CapturedCash).
26792676

@@ -3450,8 +3447,11 @@ accrue_status_timing(Name, Opts, #st{timings = Timings}) ->
34503447
EventTime = define_event_timestamp(Opts),
34513448
hg_timings:mark(Name, EventTime, hg_timings:accrue(Name, started, EventTime, Timings)).
34523449

3453-
-spec get_limit_values(st()) -> route_limit_context().
3454-
get_limit_values(St) ->
3450+
-spec get_limit_values(st(), opts()) -> route_limit_context().
3451+
get_limit_values(St, Opts) ->
3452+
get_limit_values_(St#st{opts = Opts}, strict).
3453+
3454+
get_limit_values_(St, Mode) ->
34553455
{PaymentInstitution, VS, Revision} = route_args(St),
34563456
Ctx = build_routing_context(PaymentInstitution, VS, Revision, St),
34573457
Payment = get_payment(St),
@@ -3468,20 +3468,15 @@ get_limit_values(St) ->
34683468
fun(Route, Acc) ->
34693469
PaymentRoute = hg_route:to_payment_route(Route),
34703470
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
3471-
TurnoverLimits = get_turnover_limits(ProviderTerms),
3472-
TurnoverLimitValues = hg_limiter:get_limit_values(
3473-
TurnoverLimits, Invoice, Payment, PaymentRoute, Iter
3474-
),
3471+
TurnoverLimits = get_turnover_limits(ProviderTerms, Mode),
3472+
TurnoverLimitValues =
3473+
hg_limiter:get_limit_values(TurnoverLimits, Invoice, Payment, PaymentRoute, Iter),
34753474
Acc#{PaymentRoute => TurnoverLimitValues}
34763475
end,
34773476
#{},
34783477
hg_routing_ctx:considered_candidates(Ctx)
34793478
).
34803479

3481-
-spec get_limit_values(st(), opts()) -> route_limit_context().
3482-
get_limit_values(St, Opts) ->
3483-
get_limit_values(St#st{opts = Opts}).
3484-
34853480
try_accrue_waiting_timing(Opts, #st{payment = Payment, timings = Timings}) ->
34863481
case get_payment_flow(Payment) of
34873482
?invoice_payment_flow_instant() ->

apps/hellgate/src/hg_invoice_payment_refund.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,7 @@ get_resource_payment_tool(#domain_DisposablePaymentResource{payment_tool = Payme
422422
PaymentTool.
423423

424424
get_turnover_limits(ProviderTerms) ->
425-
TurnoverLimitSelector = ProviderTerms#domain_PaymentsProvisionTerms.turnover_limits,
426-
hg_limiter:get_turnover_limits(TurnoverLimitSelector).
425+
hg_limiter:get_turnover_limits(ProviderTerms, strict).
427426

428427
prepare_refund_cashflow(Refund) ->
429428
hg_accounting:hold(construct_refund_plan_id(Refund), make_batch(Refund)).

apps/hellgate/src/hg_invoice_registered_payment.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,7 @@ get_turnover_limits(Payment, Route, St) ->
215215
RiskScore = hg_invoice_payment:get_risk_score(St),
216216
VS = collect_validation_varset(PartyConfigRef, ShopObj, Cost, PaymentTool, RiskScore),
217217
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
218-
TurnoverLimitSelector = ProviderTerms#domain_PaymentsProvisionTerms.turnover_limits,
219-
hg_limiter:get_turnover_limits(TurnoverLimitSelector).
218+
hg_limiter:get_turnover_limits(ProviderTerms, strict).
220219

221220
construct_payment(
222221
PaymentID,

apps/hellgate/src/hg_limiter.erl

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
88
-include_lib("limiter_proto/include/limproto_context_payproc_thrift.hrl").
99

10-
-type turnover_selector() :: dmsl_domain_thrift:'TurnoverLimitSelector'().
10+
-type turnover_terms_container() :: dmsl_domain_thrift:'PaymentsProvisionTerms'() | dmsl_domain_thrift:'ShopConfig'().
1111
-type turnover_limit() :: dmsl_domain_thrift:'TurnoverLimit'().
1212
-type invoice() :: dmsl_domain_thrift:'Invoice'().
1313
-type payment() :: dmsl_domain_thrift:'InvoicePayment'().
@@ -23,7 +23,7 @@
2323

2424
-export_type([turnover_limit_value/0]).
2525

26-
-export([get_turnover_limits/1]).
26+
-export([get_turnover_limits/2]).
2727
-export([check_limits/5]).
2828
-export([check_shop_limits/5]).
2929
-export([hold_payment_limits/5]).
@@ -42,14 +42,56 @@
4242
terminal = TerminalRef
4343
}).
4444

45-
-spec get_turnover_limits(turnover_selector() | undefined) -> [turnover_limit()].
46-
get_turnover_limits(undefined) ->
45+
-define(party(PartyID), #domain_Party{
46+
id = PartyID
47+
}).
48+
49+
-define(shop(ShopID), #domain_Shop{
50+
id = ShopID
51+
}).
52+
53+
%% Very specific errors to crutch around
54+
-define(POSTING_PLAN_NOT_FOUND(ID), #base_InvalidRequest{errors = [<<"Posting plan not found: ", ID/binary>>]}).
55+
-define(OPERATION_NOT_FOUND, {invalid_request, [<<"OperationNotFound">>]}).
56+
57+
-spec get_turnover_limits(turnover_terms_container(), strict | lenient) -> [turnover_limit()].
58+
59+
get_turnover_limits(#domain_ShopConfig{turnover_limits = undefined}, _Mode) ->
60+
[];
61+
get_turnover_limits(#domain_ShopConfig{turnover_limits = Limits}, Mode) ->
62+
ordsets:to_list(filter_existing_turnover_limits(Limits, Mode));
63+
get_turnover_limits(#domain_PaymentsProvisionTerms{turnover_limits = undefined}, _Mode) ->
4764
[];
48-
get_turnover_limits({value, Limits}) ->
49-
Limits;
50-
get_turnover_limits(Ambiguous) ->
65+
get_turnover_limits(#domain_PaymentsProvisionTerms{turnover_limits = {value, Limits}}, Mode) ->
66+
filter_existing_turnover_limits(Limits, Mode);
67+
get_turnover_limits(#domain_PaymentsProvisionTerms{turnover_limits = Ambiguous}, _Mode) ->
5168
error({misconfiguration, {'Could not reduce selector to a value', Ambiguous}}).
5269

70+
-define(LIMIT_NOT_FOUND(Revision, LimitID),
71+
{object_not_found, {Revision, {limit_config, #domain_LimitConfigRef{id = LimitID}}}}
72+
).
73+
filter_existing_turnover_limits(Limits, Mode) ->
74+
%% When mode is strict and limit-config does not exist it raises a
75+
%% misconfiguration error.
76+
%% Otherwise it filters out non existent one.
77+
lists:filter(
78+
fun
79+
(#domain_TurnoverLimit{domain_revision = undefined}) ->
80+
true;
81+
(#domain_TurnoverLimit{id = ID, domain_revision = Ver}) ->
82+
try
83+
_ = hg_domain:get(Ver, {limit_config, #domain_LimitConfigRef{id = ID}}),
84+
true
85+
catch
86+
error:?LIMIT_NOT_FOUND(_Revision, _LimitID) when Mode =:= lenient ->
87+
false;
88+
error:?LIMIT_NOT_FOUND(Revision, LimitID) when Mode =:= strict ->
89+
error({misconfiguration, {'Limit config not found', {Revision, LimitID}}})
90+
end
91+
end,
92+
Limits
93+
).
94+
5395
-spec get_limit_values([turnover_limit()], invoice(), payment(), route(), pos_integer()) -> [turnover_limit_value()].
5496
get_limit_values(TurnoverLimits, Invoice, Payment, Route, Iter) ->
5597
Context = gen_limit_context(Invoice, Payment, Route),
@@ -277,13 +319,19 @@ rollback_payment_limits(TurnoverLimits, Invoice, Payment, Route, Iter, Flags) ->
277319
{LegacyTurnoverLimits, BatchTurnoverLimits} = split_turnover_limits_by_available_limiter_api(TurnoverLimits),
278320
ok = legacy_rollback_payment_limits(Context, LegacyTurnoverLimits, Invoice, Payment, Route, Iter, Flags),
279321
OperationIdSegments = make_route_operation_segments(Invoice, Payment, Route, Iter),
280-
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments).
322+
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, Flags).
281323

282-
batch_rollback_limits(_Context, [], _OperationIdSegments) ->
324+
batch_rollback_limits(_Context, [], _OperationIdSegments, _Flags) ->
283325
ok;
284-
batch_rollback_limits(Context, TurnoverLimits, OperationIdSegments) ->
326+
batch_rollback_limits(Context, TurnoverLimits, OperationIdSegments, Flags) ->
285327
{LimitRequest, _} = prepare_limit_request(TurnoverLimits, OperationIdSegments),
286-
hg_limiter_client:rollback_batch(LimitRequest, Context).
328+
IgnoreError = lists:member(ignore_not_found, Flags) orelse lists:member(ignore_business_error, Flags),
329+
try
330+
ok = hg_limiter_client:rollback_batch(LimitRequest, Context)
331+
catch
332+
error:(?OPERATION_NOT_FOUND) when IgnoreError =:= true ->
333+
ok
334+
end.
287335

288336
legacy_rollback_payment_limits(Context, TurnoverLimits, Invoice, Payment, Route, Iter, Flags) ->
289337
ChangeIDs = [
@@ -308,7 +356,7 @@ rollback_shop_limits(TurnoverLimits, PartyConfigRef, ShopConfigRef, Invoice, Pay
308356
Context, LegacyTurnoverLimits, PartyConfigRef, ShopConfigRef, Invoice, Payment, Flags
309357
),
310358
OperationIdSegments = make_shop_operation_segments(PartyConfigRef, ShopConfigRef, Invoice, Payment),
311-
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments).
359+
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, Flags).
312360

313361
legacy_rollback_shop_limits(Context, TurnoverLimits, PartyConfigRef, ShopConfigRef, Invoice, Payment, Flags) ->
314362
ChangeIDs = [construct_shop_change_id(PartyConfigRef, ShopConfigRef, Invoice, Payment)],
@@ -321,7 +369,7 @@ rollback_refund_limits(TurnoverLimits, Invoice, Payment, Refund, Route) ->
321369
{LegacyTurnoverLimits, BatchTurnoverLimits} = split_turnover_limits_by_available_limiter_api(TurnoverLimits),
322370
ok = legacy_rollback_refund_limits(Context, LegacyTurnoverLimits, Invoice, Payment, Refund),
323371
OperationIdSegments = make_refund_operation_segments(Invoice, Payment, Refund),
324-
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments).
372+
ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, []).
325373

326374
legacy_rollback_refund_limits(Context, TurnoverLimits, Invoice, Payment, Refund) ->
327375
ChangeIDs = [construct_refund_change_id(Invoice, Payment, Refund)],
@@ -348,9 +396,6 @@ process_changes(LimitChangesQueues, WithFun, Clock, Context, Flags) ->
348396
LimitChangesQueues
349397
).
350398

351-
%% Very specific error to crutch around
352-
-define(POSTING_PLAN_NOT_FOUND(ID), #base_InvalidRequest{errors = [<<"Posting plan not found: ", ID/binary>>]}).
353-
354399
process_changes_try_wrap([LimitChange], WithFun, Clock, Context, Flags) ->
355400
IgnoreNotFound = lists:member(ignore_not_found, Flags),
356401
#limiter_LimitChange{change_id = ChangeID} = LimitChange,

apps/hellgate/src/hg_limiter_client.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
-spec get(limit_id(), limit_version() | undefined, clock(), context()) -> limit() | no_return().
3232
get(LimitID, Version, Clock, Context) ->
33-
Args = {LimitID, Version, Clock, Context},
3433
Opts = hg_woody_wrapper:get_service_options(limiter),
34+
Args = {LimitID, Version, Clock, Context},
3535
case hg_woody_wrapper:call(limiter, 'GetVersioned', Args, Opts) of
3636
{ok, Limit} ->
3737
Limit;

apps/hellgate/test/hg_invoice_tests_SUITE.erl

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@
181181
-export([repair_fulfill_session_on_captured_succeeded/1]).
182182

183183
-export([repair_fail_routing_succeeded/1]).
184+
-export([repair_fail_routing_not_existent_operation/1]).
184185
-export([repair_fail_cash_flow_building_succeeded/1]).
185186

186187
-export([consistent_account_balances/1]).
@@ -472,6 +473,7 @@ groups() ->
472473
]},
473474
{repair_preproc_w_limits, [], [
474475
repair_fail_routing_succeeded,
476+
repair_fail_routing_not_existent_operation,
475477
repair_fail_cash_flow_building_succeeded
476478
]},
477479
{route_cascading, [parallel], [
@@ -515,7 +517,6 @@ init_per_suite(C) ->
515517
]),
516518

517519
BaseLimitsRevision = hg_limiter_helper:init_per_suite(C),
518-
_ = logger:error("BaseLimitsRevision: ~p", [BaseLimitsRevision]),
519520

520521
RootUrl = maps:get(hellgate_root_url, Ret),
521522

@@ -724,6 +725,8 @@ init_per_testcase(Name = repair_fail_routing_succeeded, C) ->
724725
fun override_check_limits/5
725726
),
726727
init_per_testcase_(Name, C);
728+
init_per_testcase(Name = repair_fail_routing_not_existent_operation, C) ->
729+
init_per_testcase_(Name, override_terms_limit_reference(?prv(5), C));
727730
init_per_testcase(Name = repair_fail_cash_flow_building_succeeded, C) ->
728731
meck:expect(
729732
hg_cashflow_utils,
@@ -756,6 +759,27 @@ override_domain_fixture(Fixture, C) ->
756759
override_domain_fixture(Fixture, Name, C) ->
757760
init_per_testcase_(Name, override_domain_fixture(Fixture, C)).
758761

762+
override_terms_limit_reference(ProviderRef, C) ->
763+
override_domain_fixture(
764+
fun(Revision, _C) ->
765+
[
766+
change_provider_payments_provision_terms(ProviderRef, Revision, fun(PaymentsProvisionTerms) ->
767+
PaymentsProvisionTerms#domain_PaymentsProvisionTerms{
768+
turnover_limits =
769+
{value, [
770+
#domain_TurnoverLimit{
771+
id = <<"NOT_EXISTENT_LIMIT_ID">>,
772+
upper_boundary = ?LIMIT_UPPER_BOUNDARY,
773+
domain_revision = Revision
774+
}
775+
]}
776+
}
777+
end)
778+
]
779+
end,
780+
C
781+
).
782+
759783
init_per_testcase_(Name, C) ->
760784
ApiClient = hg_ct_helper:create_client(cfg(root_url, C)),
761785
Client = hg_client_invoicing:start_link(ApiClient),
@@ -774,6 +798,8 @@ trace_testcase(Name, C) ->
774798
end_per_testcase(repair_fail_routing_succeeded, C) ->
775799
meck:unload(hg_limiter),
776800
end_per_testcase(default, C);
801+
end_per_testcase(repair_fail_routing_not_existent_operation, C) ->
802+
end_per_testcase(default, C);
777803
end_per_testcase(repair_fail_cash_flow_building_succeeded, C) ->
778804
meck:unload(hg_cashflow_utils),
779805
end_per_testcase(default, C);
@@ -1349,11 +1375,6 @@ payment_limit_overflow(C) ->
13491375
) = create_payment(PartyConfigRef, ShopConfigRef, PaymentAmount, Client, PmtSys),
13501376

13511377
Failure = create_payment_limit_overflow(PartyConfigRef, ShopConfigRef, 1000, Client, PmtSys),
1352-
_ = logger:error("configured_limit_version(?LIMIT_ID, C): ~p", [configured_limit_version(?LIMIT_ID, C)]),
1353-
Res = dmt_client:checkout_object(
1354-
configured_limit_version(?LIMIT_ID, C), {limit_config, #domain_LimitConfigRef{id = ?LIMIT_ID}}
1355-
),
1356-
_ = logger:error("dmt_client:checkout_object({limit_config, #domain_LimitConfigRef{id = ?LIMIT_ID}}: ~p", [Res]),
13571378
ok = hg_limiter_helper:assert_payment_limit_amount(
13581379
?LIMIT_ID, configured_limit_version(?LIMIT_ID, C), PaymentAmount, Payment, Invoice
13591380
),
@@ -5715,6 +5736,43 @@ repair_fail_routing_succeeded(C) ->
57155736
InvoiceID, fail_pre_processing, Client
57165737
).
57175738

5739+
-spec repair_fail_routing_not_existent_operation(config()) -> test_return().
5740+
repair_fail_routing_not_existent_operation(C) ->
5741+
RootUrl = cfg(root_url, C),
5742+
Client = hg_client_invoicing:start_link(hg_ct_helper:create_client(RootUrl)),
5743+
PartyClient = cfg(party_client, C),
5744+
#{party_config_ref := PartyConfigRef} = cfg(limits, C),
5745+
ShopConfigRef = hg_ct_helper:create_shop(PartyConfigRef, ?cat(8), <<"RUB">>, ?trms(1), ?pinst(1), PartyClient),
5746+
5747+
%% Invoice
5748+
InvoiceParams =
5749+
make_invoice_params(PartyConfigRef, ShopConfigRef, <<"rubberduck">>, make_due_date(10), make_cash(10000)),
5750+
InvoiceID = create_invoice(InvoiceParams, Client),
5751+
?invoice_created(?invoice_w_status(?invoice_unpaid())) = next_change(InvoiceID, Client),
5752+
5753+
%% Payment
5754+
PaymentParams = make_payment_params(?pmt_sys(<<"visa-ref">>)),
5755+
?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client),
5756+
[
5757+
?payment_ev(PaymentID, ?payment_started(?payment_w_status(?pending()))),
5758+
?payment_ev(PaymentID, ?shop_limit_initiated()),
5759+
?payment_ev(PaymentID, ?shop_limit_applied()),
5760+
?payment_ev(PaymentID, ?risk_score_changed(_))
5761+
] = next_changes(InvoiceID, 4, Client),
5762+
%% Routing broken: limit holds fail with misconfiguration error
5763+
timeout = next_change(InvoiceID, 2000, Client),
5764+
5765+
%% Repair with rollback limits
5766+
ok = repair_invoice_with_scenario(InvoiceID, fail_pre_processing, Client),
5767+
5768+
%% Check final status
5769+
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, _Failure}))) = next_change(InvoiceID, Client),
5770+
5771+
%% Check duplicate repair
5772+
{exception, {base_InvalidRequest, [<<"No need to repair">>]}} = repair_invoice_with_scenario(
5773+
InvoiceID, fail_pre_processing, Client
5774+
).
5775+
57185776
%% fail cash_flow_building before accounting hold
57195777
-spec repair_fail_cash_flow_building_succeeded(config()) -> test_return().
57205778
repair_fail_cash_flow_building_succeeded(C) ->

0 commit comments

Comments
 (0)