diff --git a/apps/hellgate/src/hg_limiter.erl b/apps/hellgate/src/hg_limiter.erl index 2ed16f12..d0b8d2b6 100644 --- a/apps/hellgate/src/hg_limiter.erl +++ b/apps/hellgate/src/hg_limiter.erl @@ -50,6 +50,10 @@ id = ShopID }). +%% Very specific errors to crutch around +-define(POSTING_PLAN_NOT_FOUND(ID), #base_InvalidRequest{errors = [<<"Posting plan not found: ", ID/binary>>]}). +-define(OPERATION_NOT_FOUND, #base_InvalidRequest{errors = [<<"OperationNotFound">>]}). + -spec get_turnover_limits(turnover_selector() | undefined) -> [turnover_limit()]. get_turnover_limits(undefined) -> []; @@ -279,13 +283,19 @@ rollback_payment_limits(TurnoverLimits, Invoice, Payment, Route, Iter, Flags) -> {LegacyTurnoverLimits, BatchTurnoverLimits} = split_turnover_limits_by_available_limiter_api(TurnoverLimits), ok = legacy_rollback_payment_limits(Context, LegacyTurnoverLimits, Invoice, Payment, Route, Iter, Flags), OperationIdSegments = make_route_operation_segments(Invoice, Payment, Route, Iter), - ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments). + ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, Flags). -batch_rollback_limits(_Context, [], _OperationIdSegments) -> +batch_rollback_limits(_Context, [], _OperationIdSegments, _Flags) -> ok; -batch_rollback_limits(Context, TurnoverLimits, OperationIdSegments) -> +batch_rollback_limits(Context, TurnoverLimits, OperationIdSegments, Flags) -> {LimitRequest, _} = prepare_limit_request(TurnoverLimits, OperationIdSegments), - hg_limiter_client:rollback_batch(LimitRequest, Context). + IgnoreError = lists:member(ignore_not_found, Flags) orelse lists:member(ignore_business_error, Flags), + try + ok = hg_limiter_client:rollback_batch(LimitRequest, Context) + catch + error:(?OPERATION_NOT_FOUND) when IgnoreError =:= true -> + ok + end. legacy_rollback_payment_limits(Context, TurnoverLimits, Invoice, Payment, Route, Iter, Flags) -> ChangeIDs = [ @@ -302,7 +312,7 @@ rollback_shop_limits(TurnoverLimits, Party, Shop, Invoice, Payment, Flags) -> {LegacyTurnoverLimits, BatchTurnoverLimits} = split_turnover_limits_by_available_limiter_api(TurnoverLimits), ok = legacy_rollback_shop_limits(Context, LegacyTurnoverLimits, Party, Shop, Invoice, Payment, Flags), OperationIdSegments = make_shop_operation_segments(Party, Shop, Invoice, Payment), - ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments). + ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, Flags). legacy_rollback_shop_limits(Context, TurnoverLimits, Party, Shop, Invoice, Payment, Flags) -> ChangeIDs = [construct_shop_change_id(Party, Shop, Invoice, Payment)], @@ -315,7 +325,7 @@ rollback_refund_limits(TurnoverLimits, Invoice, Payment, Refund, Route) -> {LegacyTurnoverLimits, BatchTurnoverLimits} = split_turnover_limits_by_available_limiter_api(TurnoverLimits), ok = legacy_rollback_refund_limits(Context, LegacyTurnoverLimits, Invoice, Payment, Refund), OperationIdSegments = make_refund_operation_segments(Invoice, Payment, Refund), - ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments). + ok = batch_rollback_limits(Context, BatchTurnoverLimits, OperationIdSegments, []). legacy_rollback_refund_limits(Context, TurnoverLimits, Invoice, Payment, Refund) -> ChangeIDs = [construct_refund_change_id(Invoice, Payment, Refund)], @@ -342,9 +352,6 @@ process_changes(LimitChangesQueues, WithFun, Clock, Context, Flags) -> LimitChangesQueues ). -%% Very specific error to crutch around --define(POSTING_PLAN_NOT_FOUND(ID), #base_InvalidRequest{errors = [<<"Posting plan not found: ", ID/binary>>]}). - process_changes_try_wrap([LimitChange], WithFun, Clock, Context, Flags) -> IgnoreNotFound = lists:member(ignore_not_found, Flags), #limiter_LimitChange{change_id = ChangeID} = LimitChange, diff --git a/apps/hellgate/test/hg_invoice_tests_SUITE.erl b/apps/hellgate/test/hg_invoice_tests_SUITE.erl index db5ffbc0..79f952a0 100644 --- a/apps/hellgate/test/hg_invoice_tests_SUITE.erl +++ b/apps/hellgate/test/hg_invoice_tests_SUITE.erl @@ -189,6 +189,7 @@ -export([repair_fulfill_session_on_captured_succeeded/1]). -export([repair_fail_routing_succeeded/1]). +-export([repair_fail_routing_not_existent_operation/1]). -export([repair_fail_cash_flow_building_succeeded/1]). -export([consistent_account_balances/1]). @@ -489,6 +490,7 @@ groups() -> ]}, {repair_preproc_w_limits, [], [ repair_fail_routing_succeeded, + repair_fail_routing_not_existent_operation, repair_fail_cash_flow_building_succeeded ]}, {allocation, [parallel], [ @@ -728,6 +730,8 @@ init_per_testcase(Name = repair_fail_routing_succeeded, C) -> fun override_check_limits/5 ), init_per_testcase_(Name, C); +init_per_testcase(Name = repair_fail_routing_not_existent_operation, C) -> + init_per_testcase_(Name, override_terms_limit_reference(?prv(5), C)); init_per_testcase(Name = repair_fail_cash_flow_building_succeeded, C) -> meck:expect( hg_cashflow_utils, @@ -760,6 +764,27 @@ override_domain_fixture(Fixture, C) -> override_domain_fixture(Fixture, Name, C) -> init_per_testcase_(Name, override_domain_fixture(Fixture, C)). +override_terms_limit_reference(ProviderRef, C) -> + override_domain_fixture( + fun(Revision, _C) -> + [ + change_provider_payments_provision_terms(ProviderRef, Revision, fun(PaymentsProvisionTerms) -> + PaymentsProvisionTerms#domain_PaymentsProvisionTerms{ + turnover_limits = + {value, [ + #domain_TurnoverLimit{ + id = <<"NOT_EXISTENT_LIMIT_ID">>, + upper_boundary = ?LIMIT_UPPER_BOUNDARY, + domain_revision = Revision + } + ]} + } + end) + ] + end, + C + ). + init_per_testcase_(Name, C) -> ApiClient = hg_ct_helper:create_client(cfg(root_url, C)), Client = hg_client_invoicing:start_link(ApiClient), @@ -778,6 +803,8 @@ trace_testcase(Name, C) -> end_per_testcase(repair_fail_routing_succeeded, C) -> meck:unload(hg_limiter), end_per_testcase(default, C); +end_per_testcase(repair_fail_routing_not_existent_operation, C) -> + end_per_testcase(default, C); end_per_testcase(repair_fail_cash_flow_building_succeeded, C) -> meck:unload(hg_cashflow_utils), end_per_testcase(default, C); @@ -5798,6 +5825,42 @@ repair_fail_routing_succeeded(C) -> InvoiceID, fail_pre_processing, Client ). +-spec repair_fail_routing_not_existent_operation(config()) -> test_return(). +repair_fail_routing_not_existent_operation(C) -> + RootUrl = cfg(root_url, C), + Client = hg_client_invoicing:start_link(hg_ct_helper:create_client(RootUrl)), + PartyClient = cfg(party_client, C), + #{party_id := PartyID} = cfg(limits, C), + ShopID = hg_ct_helper:create_shop(PartyID, ?cat(8), <<"RUB">>, ?tmpl(1), ?pinst(1), PartyClient), + + %% Invoice + InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(10000)), + InvoiceID = create_invoice(InvoiceParams, Client), + ?invoice_created(?invoice_w_status(?invoice_unpaid())) = next_change(InvoiceID, Client), + + %% Payment + PaymentParams = make_payment_params(?pmt_sys(<<"visa-ref">>)), + ?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client), + [ + ?payment_ev(PaymentID, ?payment_started(?payment_w_status(?pending()))), + ?payment_ev(PaymentID, ?shop_limit_initiated()), + ?payment_ev(PaymentID, ?shop_limit_applied()), + ?payment_ev(PaymentID, ?risk_score_changed(_)) + ] = next_changes(InvoiceID, 4, Client), + %% routing broken + timeout = next_change(InvoiceID, 2000, Client), + + %% Repair with rollback limits + ok = repair_invoice_with_scenario(InvoiceID, fail_pre_processing, Client), + + %% Check final status + ?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, _Failure}))) = next_change(InvoiceID, Client), + + %% Check duplicate repair + {exception, {base_InvalidRequest, [<<"No need to repair">>]}} = repair_invoice_with_scenario( + InvoiceID, fail_pre_processing, Client + ). + %% fail cash_flow_building before accounting hold -spec repair_fail_cash_flow_building_succeeded(config()) -> test_return(). repair_fail_cash_flow_building_succeeded(C) -> diff --git a/compose.yaml b/compose.yaml index c407ce86..0c97a35a 100644 --- a/compose.yaml +++ b/compose.yaml @@ -63,7 +63,7 @@ services: retries: 10 limiter: - image: ghcr.io/valitydev/limiter:sha-2271094 + image: ghcr.io/valitydev/limiter:sha-36eb612 command: /opt/limiter/bin/limiter foreground depends_on: machinegun: