content: /*
   * Copyright 2018-2024 adorsys GmbH & Co KG
   *
   * This program is free software: you can redistribute it and/or modify it
   * under the terms of the GNU Affero General Public License as published
   * by the Free Software Foundation, either version 3 of the License, or (at
   * your option) any later version. This program is distributed in the hope that
   * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   * See the GNU Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with this program. If not, see https://www.gnu.org/licenses/.
   *
   * This project is also available under a separate commercial license. You can
   * contact us at sales@adorsys.com.
   */
  
  package de.adorsys.psd2.xs2a.service.payment.support.status;
  
  import de.adorsys.psd2.consent.api.pis.PisCommonPaymentResponse;
  import de.adorsys.psd2.xs2a.core.domain.ErrorHolder;
  import de.adorsys.psd2.xs2a.core.domain.TppMessageInformation;
  import de.adorsys.psd2.xs2a.core.error.ErrorType;
  import de.adorsys.psd2.xs2a.core.error.MessageErrorCode;
  import de.adorsys.psd2.xs2a.core.mapper.ServiceType;
  import de.adorsys.psd2.xs2a.core.pis.TransactionStatus;
  import de.adorsys.psd2.xs2a.domain.ContentType;
  import de.adorsys.psd2.xs2a.domain.pis.ReadPaymentStatusResponse;
  import de.adorsys.psd2.xs2a.service.mapper.MediaTypeMapper;
  import de.adorsys.psd2.xs2a.service.mapper.spi_xs2a_mappers.SpiErrorMapper;
  import de.adorsys.psd2.xs2a.service.mapper.spi_xs2a_mappers.SpiToXs2aLinksMapper;
  import de.adorsys.psd2.xs2a.service.mapper.spi_xs2a_mappers.SpiToXs2aTppMessageInformationMapper;
  import de.adorsys.psd2.xs2a.service.mapper.spi_xs2a_mappers.SpiToXs2aTransactionStatusMapper;
  import de.adorsys.psd2.xs2a.service.payment.support.SpiPaymentFactoryImpl;
  import de.adorsys.psd2.xs2a.service.payment.support.TestSpiDataProvider;
  import de.adorsys.psd2.xs2a.service.spi.SpiAspspConsentDataProviderFactory;
  import de.adorsys.psd2.xs2a.spi.domain.SpiAspspConsentDataProvider;
  import de.adorsys.psd2.xs2a.spi.domain.SpiContextData;
  import de.adorsys.psd2.xs2a.spi.domain.error.SpiMessageErrorCode;
  import de.adorsys.psd2.xs2a.spi.domain.error.SpiTppMessage;
  import de.adorsys.psd2.xs2a.spi.domain.payment.SpiPeriodicPayment;
  import de.adorsys.psd2.xs2a.spi.domain.payment.SpiTransactionStatus;
  import de.adorsys.psd2.xs2a.spi.domain.payment.response.SpiGetPaymentStatusResponse;
  import de.adorsys.psd2.xs2a.spi.domain.response.SpiResponse;
  import de.adorsys.psd2.xs2a.spi.service.PeriodicPaymentSpi;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.extension.ExtendWith;
  import org.mockito.InjectMocks;
  import org.mockito.Mock;
  import org.mockito.junit.jupiter.MockitoExtension;
  import org.springframework.http.MediaType;
  
  import java.util.Optional;
  
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.mockito.ArgumentMatchers.any;
  import static org.mockito.ArgumentMatchers.anyString;
  import static org.mockito.Mockito.*;
  
  @ExtendWith(MockitoExtension.class)
  class ReadPeriodicPaymentStatusServiceTest {
      private static final String PRODUCT = "sepa-credit-transfers";
      private static final SpiContextData SPI_CONTEXT_DATA = TestSpiDataProvider.defaultSpiContextData();
      private static final SpiPeriodicPayment SPI_PERIODIC_PAYMENT = new SpiPeriodicPayment(PRODUCT);
      private static final String JSON_MEDIA_TYPE = ContentType.JSON.getType();
      private static final String PSU_MESSAGE = "PSU message";
      private static final TransactionStatus TRANSACTION_STATUS = TransactionStatus.ACSP;
      private static final SpiGetPaymentStatusResponse TRANSACTION_STATUS_RESPONSE = new SpiGetPaymentStatusResponse(SpiTransactionStatus.ACSP, null, JSON_MEDIA_TYPE, null, PSU_MESSAGE, null, null);
      private static final SpiResponse<SpiGetPaymentStatusResponse> TRANSACTION_RESPONSE = buildSpiResponseTransactionStatus();
      private static final SpiResponse<SpiGetPaymentStatusResponse> TRANSACTION_RESPONSE_FAILURE = buildFailSpiResponseTransactionStatus();
      private static final ReadPaymentStatusResponse READ_PAYMENT_STATUS_RESPONSE = new ReadPaymentStatusResponse(TRANSACTION_STATUS, TRANSACTION_RESPONSE.getPayload().getFundsAvailable(), MediaType.APPLICATION_JSON, null, PSU_MESSAGE, null, null);
      private static final String SOME_ENCRYPTED_PAYMENT_ID = "Encrypted Payment Id";
      private static final byte[] PAYMENT_BODY = "some payment body".getBytes();
  
      @InjectMocks
      private ReadPeriodicPaymentStatusService readPeriodicPaymentStatusService;
  
      @Mock
      private SpiPaymentFactoryImpl spiPaymentFactory;
      @Mock
      private SpiErrorMapper spiErrorMapper;
      @Mock
      private PeriodicPaymentSpi periodicPaymentSpi;
      @Mock
      private SpiAspspConsentDataProviderFactory spiAspspConsentDataProviderFactory;
      @Mock
      private SpiAspspConsentDataProvider spiAspspConsentDataProvider;
      @Mock
      private MediaTypeMapper mediaTypeMapper;
      @Mock
      private SpiToXs2aLinksMapper spiToXs2aLinksMapper;
      @Mock
      private SpiToXs2aTppMessageInformationMapper tppMessageInformationMapper;
      @Mock
      private SpiToXs2aTransactionStatusMapper spiToXs2aTransactionStatusMapper;
  
      private PisCommonPaymentResponse commonPaymentData;
  
      @BeforeEach
      void init() {
          commonPaymentData = getCommonPaymentData();
      }
  
      @Test
      void readPaymentStatus_success() {
          // Given
          when(spiAspspConsentDataProviderFactory.getSpiAspspDataProviderFor(anyString()))
              .thenReturn(spiAspspConsentDataProvider);
  
          doReturn(Optional.of(SPI_PERIODIC_PAYMENT)).when(spiPaymentFactory).getSpiPayment(commonPaymentData);
          when(periodicPaymentSpi.getPaymentStatusById(SPI_CONTEXT_DATA, JSON_MEDIA_TYPE, SPI_PERIODIC_PAYMENT, spiAspspConsentDataProvider))
              .thenReturn(TRANSACTION_RESPONSE);
          when(mediaTypeMapper.mapToMediaType(JSON_MEDIA_TYPE))
              .thenReturn(MediaType.APPLICATION_JSON);
          when(tppMessageInformationMapper.toTppMessageInformationSet(null)).thenReturn(null);
          when(spiToXs2aTransactionStatusMapper.mapToXs2aTransactionStatus(SpiTransactionStatus.ACSP)).thenReturn(TRANSACTION_STATUS);
  
          // When
          ReadPaymentStatusResponse actualResponse = readPeriodicPaymentStatusService.readPaymentStatus(commonPaymentData, SPI_CONTEXT_DATA, SOME_ENCRYPTED_PAYMENT_ID, JSON_MEDIA_TYPE);
  
          // Then
          assertThat(actualResponse).isEqualTo(READ_PAYMENT_STATUS_RESPONSE);
      }
  
      @Test
      void readPaymentStatus_emptyPaymentData() {
          // Given
          ErrorHolder expectedError = ErrorHolder.builder(ErrorType.PIS_400)
                                          .tppMessages(TppMessageInformation.of(MessageErrorCode.FORMAT_ERROR_PAYMENT_NOT_FOUND))
                                          .build();
          commonPaymentData.setPaymentData(null);
  
          // When
          ReadPaymentStatusResponse actualResponse = readPeriodicPaymentStatusService.readPaymentStatus(commonPaymentData, SPI_CONTEXT_DATA, SOME_ENCRYPTED_PAYMENT_ID, JSON_MEDIA_TYPE);
  
          // Then
          verify(spiPaymentFactory, never()).getSpiPayment(any());
  
          assertThat(actualResponse.hasError()).isTrue();
          assertThat(actualResponse.getErrorHolder()).isEqualToComparingFieldByField(expectedError);
      }
  
      @Test
      void readPaymentStatus_periodicPaymentSpi_getPaymentStatusById_failed() {
          // Given
          ErrorHolder expectedError = ErrorHolder.builder(ErrorType.PIS_404)
                                          .tppMessages(TppMessageInformation.of(MessageErrorCode.RESOURCE_UNKNOWN_404_NO_PAYMENT))
                                          .build();
  
          when(spiPaymentFactory.getSpiPayment(commonPaymentData))
              .thenReturn(Optional.empty());
  
          // When
          ReadPaymentStatusResponse actualResponse = readPeriodicPaymentStatusService.readPaymentStatus(commonPaymentData, SPI_CONTEXT_DATA, SOME_ENCRYPTED_PAYMENT_ID, JSON_MEDIA_TYPE);
  
          // Then
          assertThat(actualResponse.hasError()).isTrue();
          assertThat(actualResponse.getErrorHolder()).isEqualToComparingFieldByField(expectedError);
      }
  
      @Test
      void readPaymentStatus_spiPaymentFactory_createSpiPeriodicPayment_failed() {
          // Given
          when(spiAspspConsentDataProviderFactory.getSpiAspspDataProviderFor(anyString()))
              .thenReturn(spiAspspConsentDataProvider);
  
          ErrorHolder expectedError = ErrorHolder.builder(ErrorType.PIS_404)
                                          .tppMessages(TppMessageInformation.of(MessageErrorCode.RESOURCE_UNKNOWN_404_NO_PAYMENT))
                                          .build();
  
          doReturn(Optional.of(SPI_PERIODIC_PAYMENT)).when(spiPaymentFactory).getSpiPayment(commonPaymentData);
          when(periodicPaymentSpi.getPaymentStatusById(SPI_CONTEXT_DATA, JSON_MEDIA_TYPE, SPI_PERIODIC_PAYMENT, spiAspspConsentDataProvider))
              .thenReturn(TRANSACTION_RESPONSE_FAILURE);
          when(spiErrorMapper.mapToErrorHolder(TRANSACTION_RESPONSE_FAILURE, ServiceType.PIS))
              .thenReturn(expectedError);
  
          // When
          ReadPaymentStatusResponse actualResponse = readPeriodicPaymentStatusService.readPaymentStatus(commonPaymentData, SPI_CONTEXT_DATA, SOME_ENCRYPTED_PAYMENT_ID, JSON_MEDIA_TYPE);
  
          // Then
          assertThat(actualResponse.hasError()).isTrue();
          assertThat(actualResponse.getErrorHolder()).isEqualToComparingFieldByField(expectedError);
      }
  
      private static SpiResponse<SpiGetPaymentStatusResponse> buildSpiResponseTransactionStatus() {
          return SpiResponse.<SpiGetPaymentStatusResponse>builder()
                     .payload(TRANSACTION_STATUS_RESPONSE)
                     .build();
      }
  
      private static SpiResponse<SpiGetPaymentStatusResponse> buildFailSpiResponseTransactionStatus() {
          return SpiResponse.<SpiGetPaymentStatusResponse>builder()
                     .error(new SpiTppMessage(SpiMessageErrorCode.FORMAT_ERROR))
                     .build();
      }
  
      private static PisCommonPaymentResponse getCommonPaymentData() {
          PisCommonPaymentResponse paymentData = new PisCommonPaymentResponse();
          paymentData.setPaymentProduct(PRODUCT);
          paymentData.setPaymentData(PAYMENT_BODY);
          return paymentData;
      }
  }
  