Add NetworkResult Test for SafeApiCall and Mapping (#185)
* Add tests for NetworkResult and safeApiCall * Fix ktlint errors formatting
This commit is contained in:
1 file changed
+175
@@ -0,0 +1,175 @@
|
||||
package com.m57.hermescontrol.data.remote
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
|
||||
class NetworkResultTest {
|
||||
@Test
|
||||
fun testMapHttpError_knownCodes() {
|
||||
val codesAndExpectedMessages =
|
||||
mapOf(
|
||||
400 to "Bad Request (HTTP 400): The server could not understand the request.",
|
||||
401 to "Unauthorized (HTTP 401): Authentication token is invalid or expired.",
|
||||
403 to "Forbidden (HTTP 403): You do not have permission to access this resource.",
|
||||
404 to "Not Found (HTTP 404): The requested resource could not be found.",
|
||||
405 to "Method Not Allowed (HTTP 405).",
|
||||
408 to "Request Timeout (HTTP 408): The server timed out waiting for the request.",
|
||||
409 to "Conflict (HTTP 409): The request conflicts with current server state.",
|
||||
429 to "Too Many Requests (HTTP 429): Rate limit exceeded.",
|
||||
500 to "Server Error (HTTP 500): The server encountered an error.",
|
||||
599 to "Server Error (HTTP 599): The server encountered an error.",
|
||||
)
|
||||
|
||||
for ((code, expectedMessage) in codesAndExpectedMessages) {
|
||||
val error = mapHttpError(code)
|
||||
assertTrue(error is NetworkError.Http)
|
||||
error as NetworkError.Http
|
||||
assertEquals(code, error.code)
|
||||
assertEquals(expectedMessage, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMapHttpError_unknownCode() {
|
||||
val error = mapHttpError(418)
|
||||
assertTrue(error is NetworkError.Http)
|
||||
error as NetworkError.Http
|
||||
assertEquals(418, error.code)
|
||||
assertEquals("HTTP Error 418: Unexpected server response.", error.message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_successWithBody() =
|
||||
runBlocking {
|
||||
val result =
|
||||
safeApiCall {
|
||||
Response.success("Success")
|
||||
}
|
||||
assertEquals(NetworkResult.Success::class, result::class)
|
||||
assertEquals("Success", (result as NetworkResult.Success).data)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_successWithNullBody() =
|
||||
runBlocking {
|
||||
val result =
|
||||
safeApiCall<String?> {
|
||||
Response.success(null)
|
||||
}
|
||||
if (result is NetworkResult.Failure) {
|
||||
println("FAILURE: " + result.error)
|
||||
}
|
||||
assertEquals(NetworkResult.Success::class, result::class)
|
||||
assertEquals(null, (result as NetworkResult.Success).data)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_httpFailure_nonRetryable() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String> {
|
||||
callCount++
|
||||
Response.error(404, "Not Found".toResponseBody("text/plain".toMediaTypeOrNull()))
|
||||
}
|
||||
assertTrue(result is NetworkResult.Failure)
|
||||
val error = (result as NetworkResult.Failure).error
|
||||
assertTrue(error is NetworkError.Http)
|
||||
assertEquals(404, (error as NetworkError.Http).code)
|
||||
assertEquals(1, callCount) // Should not retry for 4xx errors
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_httpFailure_retryable_eventuallySucceeds() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String> {
|
||||
callCount++
|
||||
if (callCount == 1) {
|
||||
Response.error(500, "Server Error".toResponseBody("text/plain".toMediaTypeOrNull()))
|
||||
} else {
|
||||
Response.success("Success on retry")
|
||||
}
|
||||
}
|
||||
assertEquals(NetworkResult.Success::class, result::class)
|
||||
assertEquals("Success on retry", (result as NetworkResult.Success).data)
|
||||
assertEquals(2, callCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_httpFailure_retryable_exceedsMaxRetries() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String>(retries = 2) {
|
||||
callCount++
|
||||
Response.error(503, "Service Unavailable".toResponseBody("text/plain".toMediaTypeOrNull()))
|
||||
}
|
||||
assertTrue(result is NetworkResult.Failure)
|
||||
val error = (result as NetworkResult.Failure).error
|
||||
assertTrue(error is NetworkError.Http)
|
||||
assertEquals(503, (error as NetworkError.Http).code)
|
||||
assertEquals(3, callCount) // 1 initial + 2 retries
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_ioException_eventuallySucceeds() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String> {
|
||||
callCount++
|
||||
if (callCount == 1) {
|
||||
throw IOException("Network error")
|
||||
} else {
|
||||
Response.success("Success on retry")
|
||||
}
|
||||
}
|
||||
assertEquals(NetworkResult.Success::class, result::class)
|
||||
assertEquals("Success on retry", (result as NetworkResult.Success).data)
|
||||
assertEquals(2, callCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_ioException_exceedsMaxRetries() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String>(retries = 2) {
|
||||
callCount++
|
||||
throw IOException("Network timeout")
|
||||
}
|
||||
assertTrue(result is NetworkResult.Failure)
|
||||
val error = (result as NetworkResult.Failure).error
|
||||
assertTrue(error is NetworkError.Connection)
|
||||
val connectionError = error as NetworkError.Connection
|
||||
assertTrue(connectionError.message.contains("Network connection failure"))
|
||||
assertTrue(connectionError.message.contains("Network timeout"))
|
||||
assertEquals(3, callCount) // 1 initial + 2 retries
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSafeApiCall_otherException() =
|
||||
runBlocking {
|
||||
var callCount = 0
|
||||
val result =
|
||||
safeApiCall<String> {
|
||||
callCount++
|
||||
throw RuntimeException("Unexpected error")
|
||||
}
|
||||
assertTrue(result is NetworkResult.Failure)
|
||||
val error = (result as NetworkResult.Failure).error
|
||||
assertTrue(error is NetworkError.Unknown)
|
||||
val unknownError = error as NetworkError.Unknown
|
||||
assertTrue(unknownError.message.contains("An unexpected error occurred"))
|
||||
assertTrue(unknownError.message.contains("Unexpected error"))
|
||||
assertEquals(1, callCount) // Should not retry for unknown exceptions
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user