content: package com.amazon.SellingPartnerAPIAA;
  
  import okhttp3.Call;
  import okhttp3.MediaType;
  import okhttp3.OkHttpClient;
  import okhttp3.Protocol;
  import okhttp3.Request;
  import okhttp3.Response;
  import okhttp3.ResponseBody;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.params.ParameterizedTest;
  import org.junit.jupiter.params.provider.Arguments;
  import org.junit.jupiter.params.provider.MethodSource;
  import org.mockito.ArgumentCaptor;
  import org.mockito.Mock;
  import org.mockito.MockitoAnnotations;
  
  import java.io.IOException;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.stream.Stream;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertNotSame;
  import static org.mockito.ArgumentMatchers.any;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.when;
  
  import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
  import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
  
  public class LWAAuthorizationSignerTest {
      private static final String TEST_REFRESH_TOKEN = "rToken";
      private static final String TEST_CLIENT_SECRET = "cSecret";
      private static final String TEST_CLIENT_ID = "cId";
      private static final String TEST_ENDPOINT = "https://www.amazon.com/lwa";
      private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API;
      private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API;
      private static final String SELLER_TYPE_SELLER = "seller";
      private static final String SELLER_TYPE_SELLERLESS = "sellerless";
      private static final MediaType EXPECTED_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
  
  
      private Request request;
      private static LWAAuthorizationSigner underTestSeller;
      private static LWAAuthorizationSigner underTestSellerless;
      
      @Mock
      private OkHttpClient mockOkHttpClient;
  
      @Mock
      private Call mockCall;
  
      static {
  
          underTestSeller = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder()
                  .clientId(TEST_CLIENT_ID)
                  .clientSecret(TEST_CLIENT_SECRET)
                  .refreshToken(TEST_REFRESH_TOKEN)
                  .endpoint(TEST_ENDPOINT)
                  .build());
  
          underTestSellerless = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder()
                  .clientId(TEST_CLIENT_ID)
                  .clientSecret(TEST_CLIENT_SECRET)
                  .withScopes(TEST_SCOPE_1, TEST_SCOPE_2)
                  .endpoint(TEST_ENDPOINT)
                  .build());
      }
  
      @Before @BeforeEach
      public void init() {
          request = new Request.Builder()
                  .url("https://www.amazon.com/api")
                  .build();
          MockitoAnnotations.initMocks(this);
  
      }
  
      public static Stream<Arguments> lwaAuthSigner(){
  
          return Stream.of(
                  Arguments.of(SELLER_TYPE_SELLER, underTestSeller),
                  Arguments.of(SELLER_TYPE_SELLERLESS, underTestSellerless)
          );
      }
  
      @ParameterizedTest
      @MethodSource("lwaAuthSigner")
      public void initializeLWAClientWithConfiguredEndpoint(String sellerType, LWAAuthorizationSigner testAuthSigner) {
          assertEquals(TEST_ENDPOINT, testAuthSigner.getLwaClient().getEndpoint());
      }
  
      @ParameterizedTest
      @MethodSource("lwaAuthSigner")
      public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
          LWAClient mockLWAClient = mock(LWAClient.class);
          ArgumentCaptor<LWAAccessTokenRequestMeta> lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(LWAAccessTokenRequestMeta.class);
  
          when(mockLWAClient.getAccessToken(lwaAccessTokenRequestMetaArgumentCaptor.capture()))
                  .thenReturn("foo");
  
          testAuthSigner.setLwaClient(mockLWAClient);
          testAuthSigner.sign(request);
  
          LWAAccessTokenRequestMeta actualLWAAccessTokenRequestMeta = lwaAccessTokenRequestMetaArgumentCaptor.getValue();
          assertEquals(TEST_CLIENT_SECRET, actualLWAAccessTokenRequestMeta.getClientSecret());
          assertEquals(TEST_CLIENT_ID, actualLWAAccessTokenRequestMeta.getClientId());
  
          if(sellerType.equals(SELLER_TYPE_SELLER)){
              assertEquals(TEST_REFRESH_TOKEN, actualLWAAccessTokenRequestMeta.getRefreshToken());
              Assert.assertTrue(actualLWAAccessTokenRequestMeta.getScopes().getScopes().isEmpty());
              assertEquals("refresh_token", actualLWAAccessTokenRequestMeta.getGrantType());
          }
          else if (sellerType.equals(SELLER_TYPE_SELLERLESS)){
              assertNull(actualLWAAccessTokenRequestMeta.getRefreshToken());
              assertEquals(new HashSet<String>(Arrays.asList(TEST_SCOPE_1, TEST_SCOPE_2)), actualLWAAccessTokenRequestMeta.getScopes().getScopes());
              assertEquals("client_credentials", actualLWAAccessTokenRequestMeta.getGrantType());
          }
      }
  
      @ParameterizedTest
      @MethodSource("lwaAuthSigner")
      public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
          LWAClient mockLWAClient = mock(LWAClient.class);
  
          when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class)))
                  .thenReturn("Azta|Foo");
  
          testAuthSigner.setLwaClient(mockLWAClient);
          Request actualSignedRequest = testAuthSigner.sign(request);
  
          assertEquals("Azta|Foo", actualSignedRequest.header("x-amz-access-token"));
      }
  
      @ParameterizedTest
      @MethodSource("lwaAuthSigner")
      public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException {
          LWAClient mockLWAClient = mock(LWAClient.class);
  
          when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class)))
                  .thenReturn("Azta|Foo");
  
          testAuthSigner.setLwaClient(mockLWAClient);
          assertNotSame(request, testAuthSigner.sign(request));
      }
      
      @Test
      public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException, LWAException {
          LWAClient testLWAClient = new LWAClient(TEST_ENDPOINT);
          testLWAClient.setOkHttpClient(mockOkHttpClient);
          
          when(mockOkHttpClient.newCall(any(Request.class)))
          .thenReturn(mockCall);
          when(mockCall.execute())     
          .thenReturn(buildResponse(200, "Azta|foo", "120"))
          .thenReturn(buildResponse(200, "Azta|foo1", "1"));
  
          LWAAccessTokenCache testLWACache = new LWAAccessTokenCacheImpl();        
          LWAAuthorizationSigner testlwaSigner = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder()
                  .clientId(TEST_CLIENT_ID)
                  .clientSecret(TEST_CLIENT_SECRET)
                  .refreshToken(TEST_REFRESH_TOKEN)
                  .endpoint(TEST_ENDPOINT)
                  .build() , testLWACache );  
          
          testlwaSigner.setLwaClient(testLWAClient);
          testLWAClient.setLWAAccessTokenCache(testLWACache);
          Request actualSignedRequest = testlwaSigner.sign(request);
          Request actualSignedSecondRequest = testlwaSigner.sign(request);
  
          assertEquals("Azta|foo", actualSignedSecondRequest.header("x-amz-access-token"));
      }
      
      private static Response buildResponse(int code, String accessToken, String expiryInSeconds) {
          ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE,
                  String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds));
  
          return new Response.Builder()
                  .request(new Request.Builder().url(TEST_ENDPOINT).build())
                  .code(code)
                  .body(responseBody)
                  .protocol(Protocol.HTTP_1_1)
                  .message("OK")
                  .build();
      }
  }
  