package com.braintree.transaction.service.impl;

import com.braintree.command.request.BrainTreeAuthorizationRequest;
import com.braintree.command.request.BrainTreeCreatePaymentMethodRequest;
import com.braintree.command.request.BrainTreeDeletePaymentMethodRequest;
import com.braintree.command.request.BrainTreeFindMerchantAccountRequest;
import com.braintree.command.request.BrainTreeFindTransactionRequest;
import com.braintree.command.result.BrainTreeAbstractTransactionResult;
import com.braintree.command.result.BrainTreeAuthorizationResult;
import com.braintree.command.result.BrainTreeCreatePaymentMethodResult;
import com.braintree.command.result.BrainTreeFindMerchantAccountResult;
import com.braintree.command.result.BrainTreeFindTransactionResult;
import com.braintree.command.result.BrainTreePaymentDetailsResult;
import com.braintree.command.result.BrainTreePaymentMethodResult;
import com.braintree.command.result.BrainTreeRefundTransactionResult;
import com.braintree.command.result.BrainTreeVoidResult;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.constants.BraintreeConstants;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.customfield.service.BraintreeCustomFieldsService;
import com.braintree.enums.BrainTreePaymentMethod;
import com.braintree.enums.BraintreePaymentTransactionStatus;
import com.braintree.enums.BraintreeThreeDSecureFlow;
import com.braintree.exceptions.BraintreePaymentUnAvailableForReplenishmentException;
import com.braintree.helpers.SubmitForSettlementHelper;
import com.braintree.hybris.data.BrainTreeSubscriptionInfoData;
import com.braintree.hybris.data.BraintreeRefundData;
import com.braintree.hybris.data.BraintreeStatusEventData;
import com.braintree.hybris.data.BraintreeTransactionData;
import com.braintree.hybris.data.BraintreeTransactionEntryData;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeExchangeLegacyIdRequest;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodDetails;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeThreeDSecure;
import com.braintree.hybris.data.graphql.commands.response.BraintreeAuthenticationInsight;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.model.PaymentTransactionLogEntryModel;
import com.braintree.order.service.BraintreeOrderBackofficeUtilService;
import com.braintree.paypal.converters.impl.BraintreeBillingAddressConverter;
import com.braintree.service.BrainTreeFraudCheckToolService;
import com.braintree.transaction.dao.BraintreeTransactionDAO;
import com.braintree.transaction.service.BrainTreePaymentTransactionService;
import com.google.common.collect.Lists;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.commerceservices.enums.CustomerType;
import de.hybris.platform.commerceservices.service.data.CommerceOrderParameter;
import de.hybris.platform.commerceservices.strategies.CheckoutCustomerStrategy;
import de.hybris.platform.core.enums.CreditCardType;
import de.hybris.platform.core.enums.OrderStatus;
import de.hybris.platform.core.model.BraintreeGeneralConfigurationModel;
import de.hybris.platform.core.model.BraintreePluginConfigurationModel;
import de.hybris.platform.core.model.c2l.CurrencyModel;
import de.hybris.platform.core.model.order.AbstractOrderEntryModel;
import de.hybris.platform.core.model.order.CartModel;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.core.model.product.UnitModel;
import de.hybris.platform.core.model.user.AddressModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.core.servicelayer.data.SearchPageData;
import de.hybris.platform.jalo.order.price.PriceInformation;
import de.hybris.platform.order.CartService;
import de.hybris.platform.payment.PaymentService;
import de.hybris.platform.payment.dto.BillingInfo;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;
import de.hybris.platform.payment.enums.PaymentTransactionType;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.product.PriceService;
import de.hybris.platform.servicelayer.exceptions.ModelNotFoundException;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.UserService;
import de.hybris.platform.util.DiscountValue;
import de.hybris.platform.util.PriceValue;
import de.hybris.platform.util.TaxValue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.braintree.constants.BraintreeConstants.BRAINTREE_NOT_SUCCESS;
import static com.braintree.constants.BraintreeConstants.BRAINTREE_PROVIDER_NAME;
import static com.braintreegateway.Transaction.Status.SETTLEMENT_DECLINED;
import static com.braintreegateway.Transaction.Status.SETTLING;
import static com.braintreegateway.Transaction.Status.VOIDED;
import static com.braintreegateway.Transaction.Status.SETTLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class DefaultBrainTreeTransactionServiceTest {

    private static final String BT_CUSTOMER_ID = "BT_CUSTOMER_ID";
    private static final String NONCE = "NONCE";
    private static final String DEVICE_DATA = "DEVICE_DATA";
    private static final String PAYMENT_PROVIDER = "PAYMENT_PROVIDER";
    private static final String CODE = "12341234";
    private static final String ID = "11111111";
    private static final String EMAIL = "testEmail@test.com";
    private static final Boolean USE_PAYMENT_METHOD_TOKEN = Boolean.TRUE;
    private static final String PAYMNET_METHOD_TOKEN = "PAYMNET_METHOD_TOKEN";
    private static final Boolean LIABILITY_SHIFTED = Boolean.TRUE;
    private static final Boolean THREE_D_SECURE_CONFIGURATION = Boolean.TRUE;
    private static final String CREDIT_CARD_STATEMENT_NAME = "CREDIT_CARD_STATEMENT_NAME";
    private static final String MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE = "MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE";
    private static final String BRAINTREE_CHANNEL = "BRAINTREE_CHANNEL";
    private static final String PAYMENT_CODE = "PAYMENT_CODE";
    private static final String SHIPS_FROM_POSTAL_CODE = "SHIPS_FROM_POSTAL_CODE";
    private static final String CURRENCY_CODE_USD = "USD";
    private static final Double TOTAL_PRICE = Double.valueOf(100);
    private static final Double TOTAL_TAX = Double.valueOf(20);
    private static final Double DELIVERY_COST = Double.valueOf(10);
    private static final String REQUEST_ID = "REQUEST_ID";
    private static final TransactionStatusDetails TRANSACTION_STATUS_DETAILS = TransactionStatusDetails.SUCCESFULL;
    private static final TransactionStatus TRANSACTION_STATUS = TransactionStatus.ACCEPTED;
    private static final String NEW_ENTRY_CODE = "NEW_ENTRY_CODE";
    private static final String CART_CODE = "CART_CODE";
    private static final String ORDER_INTENT = "sale";
    private static final Boolean STORE_IN_VAULT = Boolean.TRUE;
    private static final String BRAINTREE_ADDRESS_ID = "BRAINTREE_ADDRESS_ID";
    private static final String POSTAL_CODE = "POSTAL_CODE";
    private static final String TRANSACTION_ID = "transaction id";
    private static final String NOT_EXISTING_TRANSACTION_ID = "not existing transaction id";
    private static final String CAPTURE_ID = "captureId";
    private static final String STATUS = "status";
    private static final String RESPONSE_BODY = "{data: {refundTransaction: {refund: {legacyId: 1111}}}}";
    private static final String RESPONSE_BODY_WITH_EMPTY_DATA= "{data: {}}";
    private static final String LEGACY_ID = "1111";
    private static final String TEST_DATE = "01/01/01 1:01 PM";

    @Mock
    private BraintreeTransactionDAO braintreeTransactionDAO;
    @Mock
    private CartService cartService;
    @Mock
    private ModelService modelService;
    @Mock
    private BrainTreePaymentService brainTreePaymentService;
    @Mock
    private PaymentService paymentService;
    @Mock
    private BraintreeBillingAddressConverter billingAddressConverter;
    @Mock
    private SubmitForSettlementHelper submitForSettlementHelper;
    @Mock
    private BrainTreeFraudCheckToolService btFraudCheckToolService;
    @Mock
    private CommonI18NService commonI18NService;
    @Mock
    private BrainTreeConfigService brainTreeConfigService;
    @Mock
    private BraintreeCustomFieldsService customFieldsService;
    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;
    @Mock
    private BraintreeOrderBackofficeUtilService backofficeUtilService;
    @Mock
    private BrainTreePaymentTransactionService brainTreePaymentTransactionService;
    @Mock
    private PriceService priceService;
    @Mock
    private UserService userService;
    @Mock
    private CheckoutCustomerStrategy checkoutCustomerStrategy;

    @Spy
    @InjectMocks
    private DefaultBrainTreeTransactionService unit;

    @Mock
    private CartModel cart;
    @Mock
    private OrderModel order;
    @Mock
    private BrainTreePaymentInfoModel paymentInfoModel;
    @Mock
    private Map<String, String> customFields;
    @Mock
    private PaymentTransactionModel transactionModel;
    @Mock
    private PaymentTransactionEntryModel entryModel;
    @Mock
    private PaymentTransactionEntryModel captureTransactionEntry;
    @Mock
    private PaymentTransactionEntryModel authorizationTransactionEntry;
    @Mock
    private PaymentTransactionLogEntryModel logEntryModel;
    @Mock
    private BigDecimal amount;
    @Mock
    private CustomerModel customerModel;
    @Mock
    private BraintreePluginConfigurationModel pluginConfigurationModel;
    @Mock
    private BraintreeGeneralConfigurationModel generalConfigurationModel;
    @Mock
    private BraintreeTransactionData transactionData;
    @Mock
    private BraintreeTransactionEntryData entryData;
    @Mock
    private BrainTreeFindTransactionResult findTransactionResult;
    @Mock
    private CommerceOrderParameter parameter;

    @Before
    public void init() {
        when(brainTreeConfigService.getBrainTreePluginConfiguration()).thenReturn(pluginConfigurationModel);
        when(pluginConfigurationModel.getBraintreeGeneralConfiguration()).thenReturn(generalConfigurationModel);
        when(commonI18NService.getCurrentCurrency()).thenReturn(mock(CurrencyModel.class));

        when(modelService.create(PaymentTransactionModel.class)).thenReturn(transactionModel);
        when(modelService.create(PaymentTransactionEntryModel.class)).thenReturn(entryModel);
        when(modelService.create(PaymentTransactionLogEntryModel.class)).thenReturn(logEntryModel);
    }

    @Test
    public void shouldCreateAuthTransactionSuccess() {
        when(cart.getEntries()).thenReturn(Collections.emptyList());
        when(cart.getCode()).thenReturn(CART_CODE);
        when(cart.getTotalTax()).thenReturn(TOTAL_TAX);
        when(cart.getDiscounts()).thenReturn(Collections.emptyList());
        when(cart.getDeliveryCost()).thenReturn(DELIVERY_COST);
        when(cart.getUser()).thenReturn(customerModel);
        when(customerModel.getBraintreeCustomerId()).thenReturn(BT_CUSTOMER_ID);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        final CurrencyModel currencyModel = mock(CurrencyModel.class);
        when(currencyModel.getIsocode()).thenReturn(CURRENCY_CODE_USD);
        when(cart.getCurrency()).thenReturn(currencyModel);
        when(paymentInfoModel.getNonce()).thenReturn(NONCE);
        when(paymentInfoModel.getDeviceData()).thenReturn(DEVICE_DATA);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(PAYMENT_PROVIDER);
        when(paymentInfoModel.isUsePaymentMethodToken()).thenReturn(USE_PAYMENT_METHOD_TOKEN);
        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMNET_METHOD_TOKEN);
        when(paymentInfoModel.isLiabilityShifted()).thenReturn(LIABILITY_SHIFTED);
        when(paymentInfoModel.isThreeDSecureConfiguration()).thenReturn(THREE_D_SECURE_CONFIGURATION);
        when(paymentInfoModel.getMerchantAccountIdForCurrentSite()).thenReturn(MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE);
        when(paymentInfoModel.getBrainTreeChannel()).thenReturn(BRAINTREE_CHANNEL);
        when(paymentInfoModel.getShipsFromPostalCode()).thenReturn(SHIPS_FROM_POSTAL_CODE);

        final AddressModel shippingAddress = mock(AddressModel.class);
        final BillingInfo shippingInfo = mock(BillingInfo.class);
        when(cart.getDeliveryAddress()).thenReturn(shippingAddress);
        when(shippingAddress.getBrainTreeAddressId()).thenReturn(BRAINTREE_ADDRESS_ID);
        when(shippingAddress.getPostalcode()).thenReturn(POSTAL_CODE);
        when(billingAddressConverter.convert(shippingAddress)).thenReturn(shippingInfo);
        when(cart.getTotalPrice()).thenReturn(TOTAL_PRICE);

        final AddressModel billingAddress = mock(AddressModel.class);
        final BillingInfo billingInfo = mock(BillingInfo.class);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getBillingAddress()).thenReturn(billingAddress);
        when(billingAddressConverter.convert(billingAddress)).thenReturn(billingInfo);

        final BrainTreeFindMerchantAccountResult brainTreeFindMerchantAccountResult = mock(
                BrainTreeFindMerchantAccountResult.class);
        when(Boolean.valueOf(brainTreeFindMerchantAccountResult.isMerchantAccountExist())).thenReturn(Boolean.TRUE);
        when(brainTreePaymentService.findMerchantAccount(any(BrainTreeFindMerchantAccountRequest.class)))
                .thenReturn(brainTreeFindMerchantAccountResult);

        when(paymentInfoModel.getCode()).thenReturn(PAYMENT_CODE);

        final BrainTreePaymentDetailsResult paymentDetailsResult = new BrainTreePaymentDetailsResult();
        when(entryModel.getRequestId()).thenReturn(REQUEST_ID);

        final BrainTreeAuthorizationResult authorizationResult = mock(BrainTreeAuthorizationResult.class);
        when(authorizationResult.getTransactionStatus()).thenReturn(TRANSACTION_STATUS);
        when(authorizationResult.getTransactionStatusDetails()).thenReturn(TRANSACTION_STATUS_DETAILS);
        when(authorizationResult.getRequestId()).thenReturn(REQUEST_ID);
        when(authorizationResult.getPaymentDetails()).thenReturn(paymentDetailsResult);
        when(authorizationResult.isSuccess()).thenReturn(true);
        when(entryModel.getAmount()).thenReturn(BigDecimal.valueOf(TOTAL_PRICE));
        when(authorizationResult.getStatus()).thenReturn(STATUS);
        when(authorizationResult.getCaptureId()).thenReturn(CAPTURE_ID);

        when(brainTreePaymentService.authorize(any(BrainTreeAuthorizationRequest.class), any(CustomerModel.class)))
                .thenReturn(authorizationResult);

        final List<PaymentTransactionEntryModel> paymentTransactionEntries = Lists.newArrayList();
        paymentTransactionEntries.add(entryModel);
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);

        when(brainTreeConfigService.getIntent()).thenReturn(ORDER_INTENT);
        when(brainTreeConfigService.isStoreInVault()).thenReturn(STORE_IN_VAULT);

        final AbstractOrderEntryModel abstractOrderEntryModel = mock(AbstractOrderEntryModel.class);
        final ProductModel productModel = mock(ProductModel.class);
        final PriceInformation priceInformation = mock(PriceInformation.class);
        final PriceValue priceValue = mock(PriceValue.class);
        when(cart.getEntries()).thenReturn(Arrays.asList(abstractOrderEntryModel));
        when(abstractOrderEntryModel.getProduct()).thenReturn(productModel);
        when(priceService.getPriceInformationsForProduct(productModel)).thenReturn(Arrays.asList(priceInformation));
        when(priceInformation.getPriceValue()).thenReturn(priceValue);
        when(priceValue.getCurrencyIso()).thenReturn(CURRENCY_CODE_USD);

        final UnitModel unitModel = mock(UnitModel.class);
        when(abstractOrderEntryModel.getUnit()).thenReturn(unitModel);

        final TaxValue taxValue = mock(TaxValue.class);
        when(abstractOrderEntryModel.getTaxValues()).thenReturn(Arrays.asList(taxValue));

        final DiscountValue discountValue = mock(DiscountValue.class);
        when(abstractOrderEntryModel.getDiscountValues()).thenReturn(Arrays.asList(discountValue));

        doNothing().when(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);
        doNothing().when(btFraudCheckToolService).updateBtFraudToolReviewStatus(transactionModel, authorizationResult);

        CommerceOrderParameter parameter = new CommerceOrderParameter();
        parameter.setOrder(cart);
        parameter.setCustomFields(customFields);

        PaymentTransactionEntryModel result = unit.createAuthorizationTransaction(parameter);

        assertEquals(entryModel, result);

        verify(entryModel).setType(PaymentTransactionType.AUTHORIZATION);
        verify(entryModel).setTransactionStatus(TRANSACTION_STATUS.toString());
        verify(entryModel).setTransactionStatusDetails(TRANSACTION_STATUS_DETAILS.toString());
        verify(entryModel).setRequestId(REQUEST_ID);
        verify(entryModel).setCurrency(currencyModel);
        verify(entryModel).setRequestToken(NONCE);
        verify(entryModel).setCode(anyString());
        verify(entryModel).setAmount(Mockito.<BigDecimal>anyObject());
        verify(entryModel).setTime(Mockito.<Date>anyObject());

        verify(transactionModel).setRequestId(REQUEST_ID);
        verify(transactionModel).setEntries(paymentTransactionEntries);
        verify(transactionModel).setRequestToken(NONCE);
        verify(transactionModel).setPaymentProvider(BRAINTREE_PROVIDER_NAME);
        final List<PaymentTransactionModel> paymentTransactions = Lists.newArrayList();
        paymentTransactions.add(transactionModel);
        verify(cart).setPaymentTransactions(paymentTransactions);

        verify(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);
        verify(btFraudCheckToolService).updateBtFraudToolReviewStatus(transactionModel, authorizationResult);
        verify(logEntryModel).setCaptureId(CAPTURE_ID);
    }

    @Test
    public void shouldCreateCancelTransaction() {
        final BrainTreeVoidResult brainTreeVoidResult = mock(BrainTreeVoidResult.class);
        final CurrencyModel currencyModel = mock(CurrencyModel.class);
        when(paymentService.getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.CANCEL))
                .thenReturn(NEW_ENTRY_CODE);
        when(brainTreeVoidResult.getTransactionStatus()).thenReturn(TRANSACTION_STATUS);
        final BigDecimal amount = BigDecimal.ONE;
        when(brainTreeVoidResult.getAmount()).thenReturn(amount);
        when(brainTreeVoidResult.getTransactionStatusDetails()).thenReturn(TRANSACTION_STATUS_DETAILS);

        final PaymentTransactionEntryModel result = unit
                .createCancelTransaction(transactionModel, brainTreeVoidResult);

        assertEquals(entryModel, result);

        verify(modelService).saveAll(entryModel, transactionModel);
    }

    @Test
    public void shouldCreateAuthorizationTransactionWithUnSuccessResult() {
        final BrainTreeAuthorizationResult authorizationResult = mock(BrainTreeAuthorizationResult.class);
        final BrainTreePaymentMethodResult paymentMethodResult = mock(BrainTreePaymentMethodResult.class);
        when(authorizationResult.getTransactionStatus()).thenReturn(TransactionStatus.REJECTED);
        when(authorizationResult.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.PROCESSOR_DECLINE);

        when(cartService.getSessionCart()).thenReturn(cart);

        doReturn(amount).when(unit).calculateTotalAmount(cart);
        doReturn(authorizationResult).when(unit).brainTreeAuthorize(any(CommerceOrderParameter.class));

        when(authorizationResult.isSuccess()).thenReturn(false);

        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);

        when(paymentInfoModel.isSaved()).thenReturn(false);
        when(customerModel.getType()).thenReturn(CustomerType.REGISTERED);
        doReturn(false).when(unit).isCustomerHaveNotCapturedOrdersForPaymentDetails(customerModel, paymentInfoModel);

        doNothing().when(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customerModel, paymentInfoModel);

        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMNET_METHOD_TOKEN);
        when(customerModel.getBraintreeCustomerId()).thenReturn(BT_CUSTOMER_ID);
        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMNET_METHOD_TOKEN);
        when(brainTreePaymentService.deletePaymentMethod(any(BrainTreeDeletePaymentMethodRequest.class))).thenReturn(paymentMethodResult);
        doNothing().when(modelService).save(paymentInfoModel);
        doNothing().when(modelService).save(cart);

        CommerceOrderParameter parameter = new CommerceOrderParameter();
        parameter.setOrder(cart);
        parameter.setCustomFields(customFields);

        PaymentTransactionEntryModel result = unit.createAuthorizationTransaction(parameter);

        assertEquals(entryModel, result);

        verify(userService).getCurrentUser();
        verify(paymentInfoModel).isSaved();
        verify(customerModel, times(2)).getType();
        verify(unit).isCustomerHaveNotCapturedOrdersForPaymentDetails(customerModel, paymentInfoModel);
        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customerModel, paymentInfoModel);
        verify(paymentInfoModel, times(2)).getPaymentMethodToken();
        verify(customerModel).getBraintreeCustomerId();
        verify(brainTreePaymentService).deletePaymentMethod(any(BrainTreeDeletePaymentMethodRequest.class));
        verify(paymentInfoModel).setIsDeletedOnBraintree(true);
        verify(modelService).save(paymentInfoModel);
        verify(cart).setPaymentInfo(null);
        verify(modelService).save(cart);
    }

    @Test
    public void shouldCheckIsCustomerHaveNotCapturedOrdersForPaymentDetails() {
        final BrainTreePaymentInfoModel paymentInfoModel2 = mock(BrainTreePaymentInfoModel.class);

        final List<OrderModel> orders = new ArrayList<>();
        orders.add(order);

        when(customerModel.getOrders()).thenReturn(orders);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel2);
        when(paymentInfoModel2.getPaymentMethodToken()).thenReturn(PAYMNET_METHOD_TOKEN);
        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMNET_METHOD_TOKEN);

        when(order.getStatus()).thenReturn(OrderStatus.APPROVED);
        when(backofficeUtilService.isOrderVoided(order)).thenReturn(false);

        final boolean result = unit.isCustomerHaveNotCapturedOrdersForPaymentDetails(customerModel, paymentInfoModel);

        assertTrue(result);

        verify(customerModel).getOrders();
        verify(order).getPaymentInfo();
        verify(paymentInfoModel2).getPaymentMethodToken();
        verify(paymentInfoModel).getPaymentMethodToken();

        verify(order).getStatus();
        verify(backofficeUtilService).isOrderVoided(order);
    }

    @Test
    public void shouldCreatePaymentMethodTokenForOrderReplenishment() {
        final BrainTreeCreatePaymentMethodResult result = mock(BrainTreeCreatePaymentMethodResult.class);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customerModel);
        when(cartService.getSessionCart()).thenReturn(cart);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.isUsePaymentMethodToken()).thenReturn(false);
        when(brainTreePaymentService.createPaymentMethod(any(BrainTreeCreatePaymentMethodRequest.class))).thenReturn(result);
        when(result.getPaymentMethodToken()).thenReturn(CODE);
        when(result.getPaymentMethodGraphQLToken()).thenReturn(REQUEST_ID);
        doNothing().when(modelService).save(paymentInfoModel);

        unit.createPaymentMethodTokenForOrderReplenishment();

        verify(checkoutCustomerStrategy).getCurrentUserForCheckout();
        verify(cartService).getSessionCart();
        verify(cart).getPaymentInfo();
        verify(paymentInfoModel).isUsePaymentMethodToken();
        verify(brainTreePaymentService).createPaymentMethod(any(BrainTreeCreatePaymentMethodRequest.class));
        verify(paymentInfoModel).setPaymentMethodToken(CODE);
        verify(paymentInfoModel).setPaymentMethodGraphQLToken(REQUEST_ID);
        verify(paymentInfoModel).setUsePaymentMethodToken(Boolean.TRUE);
        verify(modelService).save(paymentInfoModel);
    }

    @Test(expected = BraintreePaymentUnAvailableForReplenishmentException.class)
    public void shouldThrowExceptionCreatePaymentMethodTokenForOrderReplenishment() {
        final BrainTreeCreatePaymentMethodResult result = mock(BrainTreeCreatePaymentMethodResult.class);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customerModel);
        when(cartService.getSessionCart()).thenReturn(cart);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.isUsePaymentMethodToken()).thenReturn(false);
        when(brainTreePaymentService.createPaymentMethod(any(BrainTreeCreatePaymentMethodRequest.class))).thenThrow(new BraintreePaymentUnAvailableForReplenishmentException());

        unit.createPaymentMethodTokenForOrderReplenishment();
    }

    @Test
    public void shouldCreateCaptureTransaction() {
        final BrainTreeAuthorizationResult authorizationResult = mock(BrainTreeAuthorizationResult.class);
        final Calendar calendar = mock(Calendar.class);
        final BrainTreePaymentDetailsResult paymentDetailsResult = mock(BrainTreePaymentDetailsResult.class);

        doReturn(authorizationResult).when(unit).brainTreeAuthorizeCapture(parameter);
        when(parameter.getOrder()).thenReturn(order);
        when(parameter.getTransactionEntry()).thenReturn(authorizationTransactionEntry);

        when(authorizationResult.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(authorizationResult.getAuthorizationExpiresAt()).thenReturn(calendar);
        when(authorizationResult.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);

        when(authorizationResult.getStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        doNothing().when(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(authorizationResult.getPaymentDetails()).thenReturn(paymentDetailsResult);
        when(authorizationTransactionEntry.getPaymentTransaction()).thenReturn(transactionModel);

        final PaymentTransactionEntryModel result = unit.createAuthorizationTransactionForCapture(parameter, false);

        assertEquals(entryModel, result);

        verify(authorizationResult, times(2)).isSuccess();
        verify(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
    }

    @Test
    public void shouldCreateAuthorizationTransaction() {
        final BrainTreeAuthorizationResult authorizationResult = mock(BrainTreeAuthorizationResult.class);
        final Calendar calendar = mock(Calendar.class);
        final BrainTreePaymentDetailsResult paymentDetailsResult = mock(BrainTreePaymentDetailsResult.class);

        doReturn(authorizationResult).when(unit).brainTreeAuthorize(parameter);
        when(parameter.getOrder()).thenReturn(cart);

        when(authorizationResult.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(authorizationResult.getAuthorizationExpiresAt()).thenReturn(calendar);
        when(authorizationResult.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);
        when(authorizationResult.isSuccess()).thenReturn(true);

        when(entryModel.getAmount()).thenReturn(amount);

        when(authorizationResult.getStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        doNothing().when(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
        doNothing().when(btFraudCheckToolService).updateBtFraudToolReviewStatus(transactionModel, authorizationResult);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(authorizationResult.getPaymentDetails()).thenReturn(paymentDetailsResult);
        doNothing().when(brainTreePaymentService).updatePaymentInfo(paymentInfoModel, paymentDetailsResult);

        final PaymentTransactionEntryModel result = unit.createAuthorizationTransaction(parameter);

        assertEquals(entryModel, result);

        verify(unit).brainTreeAuthorize(parameter);
        verify(authorizationResult, times(2)).isSuccess();
        verify(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
        verify(btFraudCheckToolService).updateBtFraudToolReviewStatus(transactionModel, authorizationResult);
        verify(brainTreePaymentService).updatePaymentInfo(paymentInfoModel, paymentDetailsResult);
    }

    @Test
    public void shouldCreateAuthorizationTransactionOrderAndAmount() {
        doReturn(entryModel).when(unit).createAuthorizationTransaction(parameter);

        final PaymentTransactionEntryModel result = unit.createAuthorizationTransaction(parameter);

        assertEquals(entryModel, result);

        verify(unit).createAuthorizationTransaction(parameter);
    }

    @Test
    public void shouldCreateAuthorizationTransactionForCapture() {
        final BrainTreeAuthorizationResult result = mock(BrainTreeAuthorizationResult.class);
        final Calendar calendar = mock(Calendar.class);

        when(customFieldsService.getDefaultCustomFieldsMap()).thenReturn(customFields);
        doReturn(result).when(unit).brainTreeAuthorizeCapture(parameter);
        when(parameter.getOrder()).thenReturn(order);
        when(parameter.getTransactionEntry()).thenReturn(authorizationTransactionEntry);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(result.getAuthorizationExpiresAt()).thenReturn(calendar);
        when(result.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);
        when(brainTreePaymentTransactionService.isOrderFullyCaptured(authorizationTransactionEntry)).thenReturn(true);

        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(authorizationTransactionEntry.getPaymentTransaction()).thenReturn(transactionModel);

        doNothing().when(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        doNothing().when(brainTreePaymentTransactionService).continueOrderProcess(order);

        when(result.getStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        doNothing().when(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createAuthorizationTransactionForCapture(parameter, false);

        assertEquals(entryModel, testResult);

        verify(unit).brainTreeAuthorizeCapture(parameter);
        verify(result, times(2)).isSuccess();

        verify(brainTreePaymentTransactionService).isOrderFullyCaptured(authorizationTransactionEntry);
        verify(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        verify(brainTreePaymentTransactionService).continueOrderProcess(order);
        verify(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
    }

    @Test
    public void shouldCreateAuthorizationTransactionForCaptureWhenForceCompleteTrue() {
        final BrainTreeAuthorizationResult result = mock(BrainTreeAuthorizationResult.class);
        final Calendar calendar = mock(Calendar.class);

        when(customFieldsService.getDefaultCustomFieldsMap()).thenReturn(customFields);
        doReturn(result).when(unit).brainTreeAuthorizeCapture(parameter);
        when(parameter.getOrder()).thenReturn(order);
        when(parameter.getTransactionEntry()).thenReturn(authorizationTransactionEntry);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(result.getAuthorizationExpiresAt()).thenReturn(calendar);
        when(result.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);

        when(brainTreePaymentTransactionService.isOrderFullyCaptured(authorizationTransactionEntry)).thenReturn(false);

        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(authorizationTransactionEntry.getPaymentTransaction()).thenReturn(transactionModel);

        doNothing().when(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        doNothing().when(brainTreePaymentTransactionService).continueOrderProcess(order);

        when(result.getStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        doNothing().when(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createAuthorizationTransactionForCapture(parameter, true);

        assertEquals(entryModel, testResult);

        verify(unit).brainTreeAuthorizeCapture(parameter);
        verify(result, times(2)).isSuccess();

        verify(brainTreePaymentTransactionService).isOrderFullyCaptured(authorizationTransactionEntry);
        verify(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        verify(brainTreePaymentTransactionService).continueOrderProcess(order);
        verify(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
    }

    @Test
    public void shouldCreateAuthorizationTransactionForCaptureWithFakeRequestId() {
        final BrainTreeAuthorizationResult result = mock(BrainTreeAuthorizationResult.class);
        final Calendar calendar = mock(Calendar.class);

        when(customFieldsService.getDefaultCustomFieldsMap()).thenReturn(customFields);
        doReturn(result).when(unit).brainTreeAuthorizeCapture(parameter);
        when(parameter.getOrder()).thenReturn(order);
        when(parameter.getTransactionEntry()).thenReturn(authorizationTransactionEntry);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(result.getAuthorizationExpiresAt()).thenReturn(calendar);
        when(result.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);

        when(brainTreePaymentTransactionService.isOrderFullyCaptured(authorizationTransactionEntry)).thenReturn(true);

        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(authorizationTransactionEntry.getPaymentTransaction()).thenReturn(transactionModel);

        when(order.getPaymentTransactions()).thenReturn(Arrays.asList(transactionModel));
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);

        doNothing().when(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        doNothing().when(brainTreePaymentTransactionService).continueOrderProcess(order);

        when(result.getStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        doNothing().when(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createAuthorizationTransactionForCapture(parameter, false);

        assertEquals(entryModel, testResult);

        verify(unit).brainTreeAuthorizeCapture(parameter);
        verify(result, times(2)).isSuccess();

        verify(brainTreePaymentTransactionService).isOrderFullyCaptured(authorizationTransactionEntry);
        verify(brainTreePaymentTransactionService).setOrderStatus(order, OrderStatus.PAYMENT_CAPTURED);
        verify(brainTreePaymentTransactionService).continueOrderProcess(order);
        verify(unit).saveBraintreeTransactionStatus(TransactionStatus.ACCEPTED.name(), transactionModel);
    }

    @Test
    public void shouldCreateRefundTransaction() {
        final BrainTreeRefundTransactionResult result = mock(BrainTreeRefundTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.REFUND_STANDALONE;

        when(transactionModel.getPlannedAmount()).thenReturn(amount);
        doReturn(entryModel).when(unit).createPaymentTransaction(transactionModel, result, amount, transactionType);

        final PaymentTransactionEntryModel testResult = unit.createRefundTransaction(transactionModel, result);

        assertEquals(entryModel, testResult);

        verify(unit).createPaymentTransaction(transactionModel, result, amount, transactionType);
    }

    @Test
    public void shouldCreatePartialRefundTransaction() {
        final BrainTreeRefundTransactionResult result = mock(BrainTreeRefundTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.REFUND_PARTIAL;

        when(captureTransactionEntry.getPaymentTransaction()).thenReturn(transactionModel);
        when(captureTransactionEntry.getRequestId()).thenReturn(REQUEST_ID);
        doReturn(entryModel).when(unit).createPaymentTransaction(transactionModel, result, amount, transactionType);

        final PaymentTransactionEntryModel testResult = unit.createPartialRefundTransaction(captureTransactionEntry, result, amount);

        assertEquals(entryModel, testResult);

        verify(unit).createPaymentTransaction(transactionModel, result, amount, transactionType);
        verify(captureTransactionEntry).getPaymentTransaction();
        verify(entryModel).setSubscriptionID(REQUEST_ID);
        verify(modelService).save(entryModel);
    }

    @Test
    public void shouldCreatePaymentTransaction() {
        final BrainTreeAbstractTransactionResult result = mock(BrainTreeAbstractTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.AUTHORIZATION;

        doReturn(entryModel).when(unit).createPaymentTransaction(
                transactionModel, result, amount, transactionType);
        doNothing().when(modelService).save(entryModel);
        when(result.getStatus()).thenReturn(STATUS);
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        doNothing().when(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createPaymentTransaction(
                transactionModel, result, amount, transactionType, TRANSACTION_ID);

        assertEquals(entryModel, testResult);

        verify(unit).createPaymentTransaction(transactionModel, result, amount, transactionType, TRANSACTION_ID);
        verify(entryModel).setSubscriptionID(TRANSACTION_ID);
        verify(modelService).save(entryModel);
        verify(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);
    }

    @Test
    public void shouldCreatePaymentTransactionIfResultTrue() {
        final BrainTreeAbstractTransactionResult result = mock(BrainTreeAbstractTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.AUTHORIZATION;

        when(paymentService.getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.AUTHORIZATION)).thenReturn(NEW_ENTRY_CODE);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(result.isSuccess()).thenReturn(true);
        when(result.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL);

        doNothing().when(modelService).saveAll(entryModel, logEntryModel, transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createPaymentTransaction(transactionModel, result, amount, transactionType);

        assertEquals(entryModel, testResult);

        verify(paymentService).getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.AUTHORIZATION);
        verify(modelService).create(PaymentTransactionEntryModel.class);
        verify(modelService).create(PaymentTransactionLogEntryModel.class);
        verify(result).getTransactionStatus();
        verify(result, times(2)).isSuccess();
        verify(result).getTransactionStatusDetails();
        verify(modelService).saveAll(entryModel, logEntryModel, transactionModel);
    }

    @Test
    public void shouldCreatePaymentTransactionIfResultFalse() {
        final BrainTreeAbstractTransactionResult result = mock(BrainTreeAbstractTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.REFUND_STANDALONE;

        when(paymentService.getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.REFUND_STANDALONE)).thenReturn(NEW_ENTRY_CODE);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.REJECTED);
        when(result.isSuccess()).thenReturn(false);
        when(result.getResponseBody()).thenReturn(RESPONSE_BODY);
        when(transactionModel.getOrder()).thenReturn(order);

        doNothing().when(modelService).saveAll(entryModel, logEntryModel, transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createPaymentTransaction(transactionModel, result, amount, transactionType);

        assertEquals(entryModel, testResult);

        verify(paymentService).getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.REFUND_STANDALONE);
        verify(modelService).create(PaymentTransactionEntryModel.class);
        verify(modelService).create(PaymentTransactionLogEntryModel.class);
        verify(result).getTransactionStatus();
        verify(result, times(2)).isSuccess();
        verify(result, times(2)).getResponseBody();
        verify(transactionModel).getOrder();
        verify(modelService).saveAll(entryModel, logEntryModel, transactionModel);
        verify(testResult).setRequestId(LEGACY_ID);
    }

    @Test
    public void shouldNotSetRequestIdIfRefundTransactionDataNotPresentWhenCreatePaymentTransactionWhenResultFalse() {
        final BrainTreeAbstractTransactionResult result = mock(BrainTreeAbstractTransactionResult.class);
        final PaymentTransactionType transactionType = PaymentTransactionType.REFUND_STANDALONE;

        when(paymentService.getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.REFUND_STANDALONE)).thenReturn(NEW_ENTRY_CODE);

        when(result.getTransactionStatus()).thenReturn(TransactionStatus.REJECTED);
        when(result.isSuccess()).thenReturn(false);
        when(result.getResponseBody()).thenReturn(RESPONSE_BODY_WITH_EMPTY_DATA);
        when(transactionModel.getOrder()).thenReturn(order);

        doNothing().when(modelService).saveAll(entryModel, logEntryModel, transactionModel);

        final PaymentTransactionEntryModel testResult = unit.createPaymentTransaction(transactionModel, result, amount, transactionType);

        assertEquals(entryModel, testResult);
        verify(testResult, times(0)).setRequestId(anyString());

    }

    @Test
    public void shouldAddTransactionLog() {
        final BrainTreeAbstractTransactionResult result = mock(BrainTreeAbstractTransactionResult.class);

        doNothing().when(modelService).saveAll(logEntryModel, transactionModel);

        unit.addTransactionLog(transactionModel, result, PAYMENT_PROVIDER);

        verify(modelService).create(PaymentTransactionLogEntryModel.class);
        verify(modelService).saveAll(logEntryModel, transactionModel);
    }

    @Test
    public void shouldGetGraphQLIdFromTransactionEntryIsNotNull() {
        when(brainTreePaymentTransactionService.getTransactionEntryByRequestId(CODE)).thenReturn(entryModel);
        when(entryModel.getRequestIdGraphQL()).thenReturn(null);
        when(brainTreePaymentService.exchangeLegacyIdToGraphQLId(any(BrainTreeExchangeLegacyIdRequest.class))).thenReturn(ID);
        doNothing().when(modelService).save(entryModel);

        final String result = unit.getGraphQLIdFromTransaction(CODE);

        assertEquals(ID, result);

        verify(brainTreePaymentTransactionService).getTransactionEntryByRequestId(CODE);
        verify(entryModel).getRequestIdGraphQL();
        verify(brainTreePaymentService).exchangeLegacyIdToGraphQLId(any(BrainTreeExchangeLegacyIdRequest.class));
        verify(entryModel).setRequestIdGraphQL(ID);
        verify(modelService).save(entryModel);
    }

    @Test
    public void shouldGetGraphQLIdFromTransactionEntryIsNull() {
        when(brainTreePaymentTransactionService.getTransactionEntryByRequestId(CODE)).thenReturn(null);
        when(brainTreePaymentService.exchangeLegacyIdToGraphQLId(any(BrainTreeExchangeLegacyIdRequest.class))).thenReturn(ID);

        final String result = unit.getGraphQLIdFromTransaction(CODE);

        assertEquals(ID, result);

        verify(brainTreePaymentTransactionService).getTransactionEntryByRequestId(CODE);
        verify(brainTreePaymentService).exchangeLegacyIdToGraphQLId(any(BrainTreeExchangeLegacyIdRequest.class));
    }

    @Test
    public void shouldCreateTransactionLogEntry() {
        final Date date = mock(Date.class);

        final PaymentTransactionLogEntryModel result = unit.createTransactionLogEntry(
                PAYMENT_PROVIDER, STATUS, CREDIT_CARD_STATEMENT_NAME, CODE, date);

        assertEquals(logEntryModel, result);

        verify(modelService).create(PaymentTransactionLogEntryModel.class);
        verify(logEntryModel).setInteractionType(PAYMENT_PROVIDER);
        verify(logEntryModel).setStatus(STATUS);
        verify(logEntryModel).setPaymentType(CREDIT_CARD_STATEMENT_NAME);
        verify(logEntryModel).setAchReturnCode(CODE);
        verify(logEntryModel).setGatewayDate(date);
        verify(logEntryModel).setGatewayResultCode(CODE);
    }

    @Test
    public void shouldCreatePaymentMethodInfo() {
        final AddressModel billingAddress = mock(AddressModel.class);
        final BrainTreeSubscriptionInfoData braintreeInfo = mock(BrainTreeSubscriptionInfoData.class);

        when(customerModel.getType()).thenReturn(CustomerType.GUEST);
        when(modelService.create(BrainTreePaymentInfoModel.class)).thenReturn(paymentInfoModel);

        when(braintreeInfo.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPAL.getCode());
        when(billingAddress.getEmail()).thenReturn(EMAIL);
        when(customerModel.getUid()).thenReturn(EMAIL);

        when(userService.isAnonymousUser(customerModel)).thenReturn(true);
        when(customerModel.getBraintreeCustomerId()).thenReturn(BT_CUSTOMER_ID);
        when(braintreeInfo.getLiabilityShifted()).thenReturn(false);
        when(braintreeInfo.getCardType()).thenReturn(CreditCardType.VISA.getCode());
        when(braintreeInfo.getLast4()).thenReturn("1111");
        when(braintreeInfo.getRoutingNumber()).thenReturn(CODE);

        doNothing().when(modelService).save(billingAddress);
        doNothing().when(modelService).refresh(customerModel);
        doNothing().when(modelService).save(paymentInfoModel);
        doNothing().when(brainTreeCustomerAccountService).setDefaultPaymentInfo(customerModel, paymentInfoModel);

        final BrainTreePaymentInfoModel result = unit.createPaymentMethodInfo(billingAddress, customerModel, braintreeInfo);

        assertEquals(paymentInfoModel, result);

        verify(billingAddress, times(2)).setOwner(paymentInfoModel);
        verify(modelService).save(billingAddress);
        verify(modelService).refresh(customerModel);
        verify(modelService, times(2)).save(paymentInfoModel);
        verify(paymentInfoModel).setIsDefault(Boolean.TRUE);
        verify(brainTreeCustomerAccountService).setDefaultPaymentInfo(customerModel, paymentInfoModel);
    }

    @Test
    public void shouldCreateTransactionEntryForLocalPaymentReversed() {
        final CurrencyModel currency = mock(CurrencyModel.class);

        when(cart.getCurrency()).thenReturn(currency);
        when(cart.getCode()).thenReturn(CODE);
        doNothing().when(modelService).save(entryModel);

        unit.createTransactionEntryForLocalPaymentReversed(cart, PaymentTransactionType.REFUND_FOLLOW_ON);

        verify(modelService).create(PaymentTransactionEntryModel.class);
        verify(cart).getCurrency();
        verify(cart).getCode();
        verify(modelService).save(entryModel);
    }

    @Test
    public void shouldSaveBraintreeTransactionStatus() {
        when(transactionModel.getOrder()).thenReturn(order);
        doNothing().when(modelService).saveAll(order, transactionModel);

        unit.saveBraintreeTransactionStatus(
                BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT.toString(), transactionModel);

        verify(transactionModel).getOrder();
        verify(order).setBraintreePaymentTransactionStatus(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        verify(transactionModel).setBraintreePaymentTransactionStatus(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        verify(modelService).saveAll(order, transactionModel);
    }

    @Test
    public void shouldNotSaveBraintreeTransactionStatusIfNull() {
        //when
        unit.saveBraintreeTransactionStatus(null, transactionModel);

        //then
        verify(transactionModel, times(0)).getOrder();
        verify(order, times(0)).setBraintreePaymentTransactionStatus(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        verify(transactionModel, times(0)).setBraintreePaymentTransactionStatus(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        verify(modelService, times(0)).saveAll(order, transactionModel);
    }

    @Test
    public void shouldGetPaymentTransactionsByStatuses() {
        final List<BraintreePaymentTransactionStatus> statuses = mock(ArrayList.class);
        final SearchPageData<PaymentTransactionModel> searchPage = mock(SearchPageData.class);

        when(braintreeTransactionDAO.getPaymentTransactionsByStatuses(statuses, searchPage)).thenReturn(searchPage);

        final SearchPageData<PaymentTransactionModel> result = unit.getPaymentTransactionsByStatuses(statuses, searchPage);

        assertEquals(searchPage, result);

        verify(braintreeTransactionDAO).getPaymentTransactionsByStatuses(statuses, searchPage);
    }

    @Test
    public void shouldUpdateBraintreePaymentTransactionStatusToRejectedWhenSettlementDeclined() {
        final List<PaymentTransactionModel> transactionModels = Arrays.asList(transactionModel);

        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(ID);
        when(brainTreePaymentService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(findTransactionResult);

        when(findTransactionResult.getTransactionData()).thenReturn(transactionData);
        when(transactionData.getTransactionEntries()).thenReturn(Arrays.asList(entryData));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(entryData.getId()).thenReturn(REQUEST_ID);
        when(entryData.getBraintreeTransactionStatus()).thenReturn(STATUS);
        doNothing().when(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);

        final BraintreeRefundData braintreeRefundData = mock(BraintreeRefundData.class);
        final BrainTreeFindTransactionResult refundResult = mock(BrainTreeFindTransactionResult.class);
        final BraintreeTransactionData refundData = mock(BraintreeTransactionData.class);
        final BraintreeTransactionEntryData refundEntry = mock(BraintreeTransactionEntryData.class);
        when(entryData.getRefundIds()).thenReturn(Arrays.asList(braintreeRefundData));
        when(brainTreePaymentService.findRefundedTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(refundResult);
        when(refundResult.getTransactionData()).thenReturn(refundData);
        when(refundData.getTransactionEntries()).thenReturn(Arrays.asList(refundEntry));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(refundEntry.getRefundedTransaction()).thenReturn(refundEntry);
        when(refundEntry.getId()).thenReturn(REQUEST_ID);

        when(refundEntry.getBraintreeTransactionStatus()).thenReturn(SETTLEMENT_DECLINED.name());
        doNothing().when(unit).saveRejectedTransactionEntry(entryModel, OrderStatus.REJECTED);
        doNothing().when(unit).saveTransactionLogEntry(entryModel, refundEntry);

        when(refundEntry.getBraintreeTransactionStatus()).thenReturn(SETTLEMENT_DECLINED.name());
        doNothing().when(unit).saveBraintreeTransactionStatus(SETTLEMENT_DECLINED.name(), transactionModel);

        unit.updateBraintreePaymentTransactionStatuses(transactionModels);

        verify(userService, times(2)).getCurrentUser();
        verify(customerModel, times(2)).getUid();
        verify(brainTreePaymentService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(findTransactionResult, times(2)).getTransactionData();
        verify(transactionData, times(2)).getTransactionEntries();
        verify(transactionModel, times(2)).getRequestId();
        verify(entryData).getId();
        verify(entryData, times(2)).getBraintreeTransactionStatus();
        verify(unit).saveBraintreeTransactionStatus(STATUS, transactionModel);

        verify(entryData, times(3)).getRefundIds();
        verify(brainTreePaymentService).findRefundedTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(refundResult).getTransactionData();
        verify(refundData).getTransactionEntries();
        verify(transactionModel, times(2)).getRequestId();
        verify(refundEntry).getRefundedTransaction();
        verify(refundEntry, times(3)).getId();

        verify(refundEntry, times(3)).getBraintreeTransactionStatus();
        verify(unit).saveRejectedTransactionEntry(entryModel, OrderStatus.REJECTED);
        verify(unit).saveTransactionLogEntry(entryModel, refundEntry);

        verify(refundEntry, times(3)).getBraintreeTransactionStatus();
        verify(unit).saveBraintreeTransactionStatus(SETTLEMENT_DECLINED.name(), transactionModel);
    }

    @Test
    public void shouldSaveVoidTransactionEntryOrderWasNotSubmittedForSettlement() {

        final List<PaymentTransactionModel> transactionModels = Arrays.asList(transactionModel);
        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(ID);
        when(brainTreePaymentService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(findTransactionResult);

        when(findTransactionResult.getTransactionData()).thenReturn(transactionData);
        when(transactionData.getTransactionEntries()).thenReturn(Arrays.asList(entryData));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(entryData.getId()).thenReturn(REQUEST_ID);

        final BraintreeStatusEventData statusEventData = mock(BraintreeStatusEventData.class);
        final List<BraintreeStatusEventData> statusEventDataList = Arrays.asList(statusEventData);
        when(entryData.getStatusHistory()).thenReturn(statusEventDataList);
        when(statusEventData.getStatus()).thenReturn(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        doReturn(entryModel).when(unit).createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);

        when(entryData.getBraintreeTransactionStatus()).thenReturn(VOIDED.name());
        doNothing().when(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        doNothing().when(unit).saveTransactionLogEntry(entryModel);

        doNothing().when(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);

        unit.updateBraintreePaymentTransactionStatuses(transactionModels);

        verify(userService).getCurrentUser();
        verify(customerModel).getUid();
        verify(brainTreePaymentService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(findTransactionResult, times(2)).getTransactionData();
        verify(transactionData, times(2)).getTransactionEntries();
        verify(transactionModel).getRequestId();
        verify(entryData).getId();
        verify(entryData).getStatusHistory();
        verify(statusEventData).getStatus();
        verify(unit).createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);
        verify(entryData, times(2)).getBraintreeTransactionStatus();
        verify(transactionModel).getEntries();
        verify(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        verify(unit, times(2)).saveTransactionLogEntry(entryModel);
        verify(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);
    }

    @Test
    public void shouldSaveVoidTransactionEntryOrderWasNotCaptured() {

        final List<PaymentTransactionModel> transactionModels = Arrays.asList(transactionModel);
        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(ID);
        when(brainTreePaymentService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(findTransactionResult);

        final List<PaymentTransactionEntryModel> entryModels = Arrays.asList(entryModel);
        when(findTransactionResult.getTransactionData()).thenReturn(transactionData);
        when(transactionData.getTransactionEntries()).thenReturn(Arrays.asList(entryData));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(entryData.getId()).thenReturn(REQUEST_ID);
        when(entryData.getBraintreeTransactionStatus()).thenReturn(VOIDED.name());
        when(transactionModel.getEntries()).thenReturn(entryModels);
        doNothing().when(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        doNothing().when(unit).saveTransactionLogEntry(entryModel);

        doNothing().when(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);

        unit.updateBraintreePaymentTransactionStatuses(transactionModels);

        verify(userService).getCurrentUser();
        verify(customerModel).getUid();
        verify(brainTreePaymentService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(findTransactionResult, times(2)).getTransactionData();
        verify(transactionData, times(2)).getTransactionEntries();
        verify(transactionModel).getRequestId();
        verify(entryData).getId();
        verify(entryData, times(2)).getBraintreeTransactionStatus();
        verify(transactionModel, times(3)).getEntries();
        verify(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        verify(unit).saveTransactionLogEntry(entryModel);
        verify(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);
    }

    @Test
    public void shouldSaveVoidTransactionEntryOrderWasCapture() {
        final List<PaymentTransactionModel> transactionModels = Arrays.asList(transactionModel);

        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(ID);
        when(brainTreePaymentService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(findTransactionResult);

        when(findTransactionResult.getTransactionData()).thenReturn(transactionData);
        when(transactionData.getTransactionEntries()).thenReturn(Arrays.asList(entryData));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(entryData.getId()).thenReturn(REQUEST_ID);

        final BraintreeStatusEventData statusEventData = mock(BraintreeStatusEventData.class);
        final List<BraintreeStatusEventData> statusEventDataList = Arrays.asList(statusEventData);
        when(entryData.getStatusHistory()).thenReturn(statusEventDataList);
        when(statusEventData.getStatus()).thenReturn(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        when(transactionModel.getEntries()).thenReturn(Arrays.asList(entryModel));
        when(entryModel.getType()).thenReturn(PaymentTransactionType.CAPTURE);
        when(entryModel.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL.name());
        when(entryData.getBraintreeTransactionStatus()).thenReturn(VOIDED.name());
        doNothing().when(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        doNothing().when(unit).saveTransactionLogEntry(entryModel);
        doNothing().when(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);

        unit.updateBraintreePaymentTransactionStatuses(transactionModels);

        verify(userService).getCurrentUser();
        verify(customerModel).getUid();
        verify(brainTreePaymentService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(findTransactionResult, times(2)).getTransactionData();
        verify(transactionData, times(2)).getTransactionEntries();
        verify(transactionModel).getRequestId();
        verify(entryData).getId();
        verify(entryData).getStatusHistory();
        verify(statusEventData).getStatus();
        verify(entryModel, times(2)).getType();
        verify(entryData, times(2)).getBraintreeTransactionStatus();
        verify(transactionModel, times(2)).getEntries();
        verify(unit).saveVoidTransactionEntry(entryModel, OrderStatus.CANCELLED);
        verify(unit).saveTransactionLogEntry(entryModel);
        verify(unit).saveBraintreeTransactionStatus(VOIDED.name(), transactionModel);
    }

    @Test
    public void shouldSaveTransactionEntryWhenOrderWasCaptured() {
        final List<PaymentTransactionModel> transactionModels = Arrays.asList(transactionModel);

        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(PAYMENT_PROVIDER);

        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getType()).thenReturn(PaymentTransactionType.CAPTURE);

        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(ID);
        when(brainTreePaymentService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(findTransactionResult);

        when(findTransactionResult.getTransactionData()).thenReturn(transactionData);
        when(transactionData.getTransactionEntries()).thenReturn(Arrays.asList(entryData));
        when(transactionModel.getRequestId()).thenReturn(REQUEST_ID);
        when(entryData.getId()).thenReturn(REQUEST_ID);

        final BraintreeStatusEventData statusEventData = mock(BraintreeStatusEventData.class);
        final List<BraintreeStatusEventData> statusEventDataList = Arrays.asList(statusEventData);
        when(entryData.getStatusHistory()).thenReturn(statusEventDataList);
        when(statusEventData.getStatus()).thenReturn(BraintreePaymentTransactionStatus.SUBMITTED_FOR_SETTLEMENT);
        when(transactionModel.getEntries()).thenReturn(Arrays.asList(entryModel));
        when(entryModel.getType()).thenReturn(PaymentTransactionType.AUTHORIZATION);
        when(entryData.getBraintreeTransactionStatus()).thenReturn(SETTLING.name());
        doNothing().when(unit).saveBraintreeTransactionStatus(SETTLING.name(), transactionModel);

        unit.updateBraintreePaymentTransactionStatuses(transactionModels);

        verify(userService).getCurrentUser();
        verify(customerModel).getUid();
        verify(brainTreePaymentService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(findTransactionResult, times(2)).getTransactionData();
        verify(transactionData, times(2)).getTransactionEntries();
        verify(transactionModel).getRequestId();
        verify(entryData).getStatusHistory();
        verify(statusEventData).getStatus();
        verify(entryData, times(2)).getBraintreeTransactionStatus();
        verify(transactionModel).getEntries();
        verify(unit).saveTransactionLogEntry(entryModel, entryData);
        verify(unit).createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);
        verify(modelService).save(entryModel);
        verify(brainTreePaymentTransactionService).processPaymentCapturedOrder(order);
    }

    @Test
    public void shouldCreatePaymentTransactionForLocalPaymentReversed() {
        final List<PaymentTransactionEntryModel> paymentTransactionEntries = new ArrayList<>();
        paymentTransactionEntries.add(entryModel);

        when(entryModel.getAmount()).thenReturn(amount);
        when(cart.getPaymentInfo()).thenReturn(paymentInfoModel);
        doNothing().when(modelService).save(transactionModel);
        doNothing().when(modelService).saveAll(entryModel, transactionModel, cart);

        unit.createPaymentTransactionForLocalPaymentReversed(cart, paymentTransactionEntries);

        verify(modelService).create(PaymentTransactionModel.class);
        verify(entryModel).getAmount();
        verify(cart, times(3)).getPaymentInfo();
        verify(modelService).save(transactionModel);
        verify(transactionModel).setEntries(paymentTransactionEntries);
        verify(modelService).saveAll(entryModel, transactionModel, cart);
    }

    @Test
    public void shouldSaveVoidTransactionEntry() {
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);

        unit.saveVoidTransactionEntry(entryModel, OrderStatus.COMPLETED);

        verify(entryModel).getPaymentTransaction();
        verify(transactionModel).getOrder();
        verify(entryModel).setTransactionStatus(VOIDED.name());
        verify(order).setStatus(OrderStatus.COMPLETED);
        verify(modelService).save(entryModel);
        verify(modelService).save(order);
    }

    @Test
    public void shouldSaveTransactionLogEntryEntryModel() {
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(PAYMENT_PROVIDER);
        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.REJECTED.name());
        when(entryModel.getType()).thenReturn(PaymentTransactionType.CANCEL);

        unit.saveTransactionLogEntry(entryModel);

        verify(modelService).save(logEntryModel);
    }

    @Test
    public void shouldSaveRejectedTransactionEntry() {
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);
        doNothing().when(modelService).save(entryModel);
        doNothing().when(modelService).save(order);

        unit.saveRejectedTransactionEntry(entryModel, OrderStatus.REJECTED);

        verify(entryModel).setTransactionStatus(TransactionStatus.REJECTED.name());
        verify(entryModel).setTransactionStatusDetails(TransactionStatusDetails.AMOUNTS_MUST_MATCH.name());
        verify(order).setStatus(OrderStatus.REJECTED);
        verify(modelService).save(entryModel);
        verify(modelService).save(order);
    }

    @Test
    public void shouldCreateTransactionEntry() {
        when(paymentService.getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.CAPTURE)).thenReturn(CODE);

        final PaymentTransactionEntryModel result = unit.createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);

        assertEquals(entryModel, result);

        verify(entryModel).setTransactionStatus(entryData.getStatus());
        verify(paymentService).getNewPaymentTransactionEntryCode(transactionModel, PaymentTransactionType.CAPTURE);
        verify(entryModel).setCode(CODE);
        verify(entryModel).setPaymentTransaction(transactionModel);
    }

    @Test
    public void shouldCreateTransactionEntrySetValidDateWhenEntryDataDateIsNotNullAndStatusEqualsSettlmentDeclinedAndTransactionTypeEqualsCaptureAndTransactionDatePresent() {
        final BraintreeStatusEventData statusEventData = mock(BraintreeStatusEventData.class);
        final Date DATE_FROM_TX_HISTORY = mock(Date.class);
        when(entryData.getStatus()).thenReturn(SETTLEMENT_DECLINED.name());
        when(entryData.getDate()).thenReturn(TEST_DATE);
        when(statusEventData.getTimestamp()).thenReturn(DATE_FROM_TX_HISTORY);
        when(entryData.getStatusHistory()).thenReturn(List.of(statusEventData));
        unit.createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);

        verify(entryModel).setTime(DATE_FROM_TX_HISTORY);
    }

    @Test
    public void shouldCreateTransactionEntrySetValidDateWhenEntryDataDateIsNotNullAndStatusEqualsSettlmentDeclinedAndTransactionTypeNotEqualsCaptureAndTransactionDateIsNotPresent() {
        when(entryData.getStatus()).thenReturn(SETTLEMENT_DECLINED.name());
        when(entryData.getDate()).thenReturn(TEST_DATE);
        when(entryData.getStatusHistory()).thenReturn(List.of());
        unit.createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);

        verify(entryModel).setTime(new Date(TEST_DATE));
    }

    @Test
    public void shouldCreateTransactionEntrySetValidDateWhenEntryDataDateIsNotNullAndStatusNotEqualSettlmentDeclinedAndTransactionTypeEqualsCapture() {
        when(entryData.getStatus()).thenReturn(SETTLED.name());
        when(entryData.getDate()).thenReturn(TEST_DATE);
        unit.createTransactionEntry(entryData, transactionModel, PaymentTransactionType.CAPTURE);

        verify(entryModel).setTime(new Date(TEST_DATE));
    }

    @Test
    public void shouldSaveTransactionLogEntryEntryModelAndEntryData() {
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(PAYMENT_PROVIDER);
        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.REJECTED.name());
        when(entryModel.getType()).thenReturn(PaymentTransactionType.CANCEL);

        unit.saveTransactionLogEntry(entryModel, entryData);

        verify(entryData).getCaptureId();
        verify(entryData).getRefundId();
        verify(modelService).save(logEntryModel);
    }

    @Test
    public void shouldCreateTransactionLogEntryWhenStatusIsSettlementDeclinedAndTransactionTypeCapture() {
        when(entryModel.getPaymentTransaction()).thenReturn(transactionModel);
        when(transactionModel.getOrder()).thenReturn(order);
        when(order.getPaymentInfo()).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(PAYMENT_PROVIDER);
        when(entryModel.getTransactionStatus()).thenReturn(SETTLEMENT_DECLINED.name());
        when(entryModel.getType()).thenReturn(PaymentTransactionType.CAPTURE);

        unit.saveTransactionLogEntry(entryModel, entryData);

        verify(logEntryModel).setInteractionType(PaymentTransactionType.CAPTURE.name());
        verify(logEntryModel).setStatus(BRAINTREE_NOT_SUCCESS);
    }

    @Test
    public void shouldFindTransactionByTransactionId() {
        when(braintreeTransactionDAO.findTransactionByTransactionId(TRANSACTION_ID)).thenReturn(transactionModel);

        PaymentTransactionModel actual = unit.findTransactionByTransactionId(TRANSACTION_ID);

        assertEquals(transactionModel, actual);
    }

    @Test(expected = ModelNotFoundException.class)
    public void shouldThrowExceptionWhenThereIsNoTransactionByTransactionId() {
        when(braintreeTransactionDAO.findTransactionByTransactionId(NOT_EXISTING_TRANSACTION_ID)).thenThrow(ModelNotFoundException.class);

        unit.findTransactionByTransactionId(NOT_EXISTING_TRANSACTION_ID);
    }

    @Test(expected = IllegalArgumentException.class)
    public void shouldThrowExceptionWhenParameterIsNull() {
        unit.findTransactionByTransactionId(null);
    }
}
