From a74fe2b6ff2c4c00b99d9792d6a817a4353c5c0a Mon Sep 17 00:00:00 2001 From: Tim Chow Date: Tue, 17 Dec 2024 10:07:51 -0600 Subject: [PATCH] Rename HttpClient.getNumRetriesSoFar to getCurrentRetryCount, convert HttpClientUnitTest to Kotlin, and remove unused function and test class (#1242) --- .../api/sharedutils/ClassHelperTest.java | 27 --- .../api/sharedutils/HttpClient.kt | 7 +- .../api/sharedutils/TLSSocketFactory.java | 14 -- .../api/sharedutils/HttpClientUnitTest.java | 181 ------------------ .../api/sharedutils/HttpClientUnitTest.kt | 174 +++++++++++++++++ 5 files changed, 176 insertions(+), 227 deletions(-) delete mode 100644 SharedUtils/src/androidTest/java/com/braintreepayments/api/sharedutils/ClassHelperTest.java delete mode 100644 SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.java create mode 100644 SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.kt diff --git a/SharedUtils/src/androidTest/java/com/braintreepayments/api/sharedutils/ClassHelperTest.java b/SharedUtils/src/androidTest/java/com/braintreepayments/api/sharedutils/ClassHelperTest.java deleted file mode 100644 index 5544c6f49b..0000000000 --- a/SharedUtils/src/androidTest/java/com/braintreepayments/api/sharedutils/ClassHelperTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.braintreepayments.api.sharedutils; - -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.braintreepayments.api.sharedutils.ClassHelper; - -@RunWith(AndroidJUnit4ClassRunner.class) -public class ClassHelperTest { - - @Test - public void isClassAvailable_returnsTrueWhenClassOnClasspath() { - ClassHelper sut = new ClassHelper(); - assertTrue(sut.isClassAvailable("java.lang.String")); - } - - @Test - public void isClassAvailable_returnsFalseWhenClassNotOnClasspath() { - ClassHelper sut = new ClassHelper(); - assertFalse(sut.isClassAvailable("java.lang.NotAClass")); - } -} diff --git a/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/HttpClient.kt b/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/HttpClient.kt index 169d3e6eca..8d92149625 100644 --- a/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/HttpClient.kt +++ b/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/HttpClient.kt @@ -72,7 +72,7 @@ class HttpClient internal constructor( } if (url != null) { - val retryCount = getNumRetriesSoFar(url) + val retryCount = getCurrentRetryCount(url) val shouldRetry = ((retryCount + 1) < MAX_RETRY_ATTEMPTS) if (shouldRetry) { scheduleRequest(request, retryStrategy, callback) @@ -85,10 +85,7 @@ class HttpClient internal constructor( } } - private fun getNumRetriesSoFar(url: URL): Int { - val retryCount = retryCountMap[url] ?: return 0 - return retryCount - } + private fun getCurrentRetryCount(url: URL) = retryCountMap[url] ?: 0 private fun resetRetryCount(request: HttpRequest) { try { diff --git a/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/TLSSocketFactory.java b/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/TLSSocketFactory.java index ae71a72709..1c92b98339 100644 --- a/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/TLSSocketFactory.java +++ b/SharedUtils/src/main/java/com/braintreepayments/api/sharedutils/TLSSocketFactory.java @@ -27,20 +27,6 @@ public class TLSSocketFactory extends SSLSocketFactory { private final SSLSocketFactory internalSSLSocketFactory; - static TLSSocketFactory newInstance() throws SSLException { - return new TLSSocketFactory(); - } - - TLSSocketFactory() throws SSLException { - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, null, null); // use system security providers - internalSSLSocketFactory = sslContext.getSocketFactory(); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new SSLException(e.getMessage()); - } - } - /** * @see Android Documentation */ diff --git a/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.java b/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.java deleted file mode 100644 index 2d038b1f2e..0000000000 --- a/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.braintreepayments.api.sharedutils; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -public class HttpClientUnitTest { - - private SynchronousHttpClient syncHttpClient; - - private HttpRequest httpRequest; - private MockThreadScheduler threadScheduler; - - @Before - public void beforeEach() { - syncHttpClient = mock(SynchronousHttpClient.class); - threadScheduler = spy(new MockThreadScheduler()); - - httpRequest = new HttpRequest().path("https://example.com"); - } - - @Test - public void sendRequest_sendsRequestOnBackgroundThread() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY); - - verifyNoInteractions(syncHttpClient); - threadScheduler.flushBackgroundThread(); - - verify(syncHttpClient).request(httpRequest); - } - - @Test - public void sendRequest_whenBaseHttpClientThrowsException_notifiesErrorViaCallbackOnMainThread() - throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - - Exception exception = new Exception("error"); - when(syncHttpClient.request(httpRequest)).thenThrow(exception); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY); - - threadScheduler.flushBackgroundThread(); - verify(callback, never()).onResult(null, exception); - - threadScheduler.flushMainThread(); - verify(callback).onResult(null, exception); - } - - @Test - public void sendRequest_onBaseHttpClientRequestSuccess_notifiesSuccessViaCallbackOnMainThread() - throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - HttpResponse response = new HttpResponse("response body", new HttpResponseTiming(123, 456)); - - when(syncHttpClient.request(httpRequest)).thenReturn(response); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY); - - threadScheduler.flushBackgroundThread(); - verify(callback, never()).onResult(response, null); - - threadScheduler.flushMainThread(); - verify(callback).onResult(response, null); - } - - @Test - public void sendRequest_whenCallbackIsNull_doesNotNotifySuccess() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - HttpResponse response = new HttpResponse("response body", new HttpResponseTiming(123, 456)); - - when(syncHttpClient.request(httpRequest)).thenReturn(response); - sut.sendRequest(httpRequest, null, HttpClient.RetryStrategy.NO_RETRY); - - threadScheduler.flushBackgroundThread(); - verify(threadScheduler, never()).runOnMain(any(Runnable.class)); - } - - @Test - public void sendRequest_whenCallbackIsNull_doesNotNotifyError() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - - Exception exception = new Exception("error"); - when(syncHttpClient.request(httpRequest)).thenThrow(exception); - - sut.sendRequest(httpRequest, null, HttpClient.RetryStrategy.NO_RETRY); - - threadScheduler.flushBackgroundThread(); - verify(threadScheduler, never()).runOnMain(any(Runnable.class)); - } - - @Test - public void sendRequest_whenRetryMax3TimesEnabled_retriesRequest3Times() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - - Exception exception = new Exception("error"); - when(syncHttpClient.request(httpRequest)).thenThrow(exception); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES); - - threadScheduler.flushBackgroundThread(); - verify(syncHttpClient, times(3)).request(httpRequest); - } - - @Test - public void sendRequest_whenRetryMax3TimesEnabled_notifiesMaxRetriesLimitExceededOnForegroundThread() - throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - - Exception exception = new Exception("error"); - when(syncHttpClient.request(httpRequest)).thenThrow(exception); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES); - - threadScheduler.flushBackgroundThread(); - verify(callback, never()).onResult((HttpResponse) isNull(), any(Exception.class)); - - threadScheduler.flushMainThread(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(callback).onResult((HttpResponse) isNull(), captor.capture()); - - HttpClientException httpClientException = (HttpClientException) captor.getValue(); - String expectedMessage = "Retry limit has been exceeded. Try again later."; - assertEquals(expectedMessage, httpClientException.getMessage()); - } - - @Test - public void sendRequest_whenRetryMax3TimesEnabled_futureRequestsAreAllowed() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - HttpResponse response = new HttpResponse("response body", new HttpResponseTiming(123, 456)); - - Exception exception = new Exception("error"); - when(syncHttpClient.request(httpRequest)).thenThrow(exception); - - NetworkResponseCallback callback = mock(NetworkResponseCallback.class); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES); - - threadScheduler.flushBackgroundThread(); - - reset(syncHttpClient); - when(syncHttpClient.request(httpRequest)) - .thenThrow(exception) - .thenReturn(response); - sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES); - - threadScheduler.flushBackgroundThread(); - threadScheduler.flushMainThread(); - - verify(callback).onResult(response, null); - } - - @Test - public void sendRequestSynchronous_sendsHttpRequest() throws Exception { - HttpClient sut = new HttpClient(syncHttpClient, threadScheduler); - HttpResponse response = new HttpResponse("response body", new HttpResponseTiming(123, 456)); - - when(syncHttpClient.request(httpRequest)).thenReturn(response); - - String result = sut.sendRequest(httpRequest); - assertEquals("response body", result); - } -} diff --git a/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.kt b/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.kt new file mode 100644 index 0000000000..f7ebff5f64 --- /dev/null +++ b/SharedUtils/src/test/java/com/braintreepayments/api/sharedutils/HttpClientUnitTest.kt @@ -0,0 +1,174 @@ +package com.braintreepayments.api.sharedutils + +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mockito + +class HttpClientUnitTest { + private lateinit var syncHttpClient: SynchronousHttpClient + private lateinit var httpRequest: HttpRequest + private lateinit var threadScheduler: MockThreadScheduler + + private lateinit var sut: HttpClient + + @Before + fun beforeEach() { + syncHttpClient = Mockito.mock(SynchronousHttpClient::class.java) + threadScheduler = Mockito.spy(MockThreadScheduler()) + httpRequest = HttpRequest().path("https://example.com") + + sut = HttpClient(syncHttpClient, threadScheduler) + } + + @Test + @Throws(Exception::class) + fun sendRequest_sendsRequestOnBackgroundThread() { + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY) + + Mockito.verifyNoInteractions(syncHttpClient) + threadScheduler.flushBackgroundThread() + + Mockito.verify(syncHttpClient)?.request(httpRequest) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenBaseHttpClientThrowsException_notifiesErrorViaCallbackOnMainThread() { + val exception = Exception("error") + Mockito.`when`(syncHttpClient.request(httpRequest)).thenThrow(exception) + + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY) + + threadScheduler.flushBackgroundThread() + Mockito.verify(callback, Mockito.never()).onResult(null, exception) + + threadScheduler.flushMainThread() + Mockito.verify(callback).onResult(null, exception) + } + + @Test + @Throws(Exception::class) + fun sendRequest_onBaseHttpClientRequestSuccess_notifiesSuccessViaCallbackOnMainThread() { + val response = HttpResponse("response body", HttpResponseTiming(123, 456)) + + Mockito.`when`(syncHttpClient.request(httpRequest)).thenReturn(response) + + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.NO_RETRY) + + threadScheduler.flushBackgroundThread() + Mockito.verify(callback, Mockito.never()).onResult(response, null) + + threadScheduler.flushMainThread() + Mockito.verify(callback).onResult(response, null) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenCallbackIsNull_doesNotNotifySuccess() { + val response = HttpResponse("response body", HttpResponseTiming(123, 456)) + + Mockito.`when`(syncHttpClient.request(httpRequest)).thenReturn(response) + sut.sendRequest(httpRequest, null, HttpClient.RetryStrategy.NO_RETRY) + + threadScheduler.flushBackgroundThread() + Mockito.verify(threadScheduler, Mockito.never())?.runOnMain( + ArgumentMatchers.any(Runnable::class.java) + ) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenCallbackIsNull_doesNotNotifyError() { + val exception = Exception("error") + Mockito.`when`(syncHttpClient.request(httpRequest)).thenThrow(exception) + + sut.sendRequest(httpRequest, null, HttpClient.RetryStrategy.NO_RETRY) + + threadScheduler.flushBackgroundThread() + Mockito.verify(threadScheduler, Mockito.never())?.runOnMain( + ArgumentMatchers.any( + Runnable::class.java + ) + ) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenRetryMax3TimesEnabled_retriesRequest3Times() { + val exception = Exception("error") + Mockito.`when`(syncHttpClient.request(httpRequest)).thenThrow(exception) + + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES) + + threadScheduler.flushBackgroundThread() + Mockito.verify(syncHttpClient, Mockito.times(3))?.request(httpRequest) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenRetryMax3TimesEnabled_notifiesMaxRetriesLimitExceededOnForegroundThread() { + val exception = Exception("error") + Mockito.`when`(syncHttpClient.request(httpRequest)).thenThrow(exception) + + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES) + + threadScheduler.flushBackgroundThread() + Mockito.verify(callback, Mockito.never()).onResult( + ArgumentMatchers.isNull(), + ArgumentMatchers.any(Exception::class.java) + ) + + threadScheduler.flushMainThread() + + val captor = ArgumentCaptor.forClass(Exception::class.java) + Mockito.verify(callback).onResult(ArgumentMatchers.isNull(), captor.capture()) + + val httpClientException = captor.value as HttpClientException + val expectedMessage = "Retry limit has been exceeded. Try again later." + Assert.assertEquals(expectedMessage, httpClientException.message) + } + + @Test + @Throws(Exception::class) + fun sendRequest_whenRetryMax3TimesEnabled_futureRequestsAreAllowed() { + val response = HttpResponse("response body", HttpResponseTiming(123, 456)) + + val exception = Exception("error") + Mockito.`when`(syncHttpClient.request(httpRequest)).thenThrow(exception) + + val callback = Mockito.mock(NetworkResponseCallback::class.java) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES) + + threadScheduler.flushBackgroundThread() + + Mockito.reset(syncHttpClient) + Mockito.`when`(syncHttpClient.request(httpRequest)) + .thenThrow(exception) + .thenReturn(response) + sut.sendRequest(httpRequest, callback, HttpClient.RetryStrategy.RETRY_MAX_3_TIMES) + + threadScheduler.flushBackgroundThread() + threadScheduler.flushMainThread() + + Mockito.verify(callback).onResult(response, null) + } + + @Test + @Throws(Exception::class) + fun sendRequestSynchronous_sendsHttpRequest() { + val response = HttpResponse("response body", HttpResponseTiming(123, 456)) + + Mockito.`when`(syncHttpClient.request(httpRequest)).thenReturn(response) + + val result = sut.sendRequest(httpRequest) + Assert.assertEquals("response body", result) + } +}