From ec5e0d52813669dbfae87ccd243cd84df0a5feb9 Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Thu, 19 Dec 2024 20:10:45 +0100 Subject: [PATCH] test: implement new tests for user location backend --- .../model/location/UserLocationDtoTest.kt | 31 +++++ .../location/UserLocationModelSupabaseTest.kt | 110 +++++++++++++++++ .../location/UserLocationViewModelTest.kt | 112 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 app/src/test/java/com/android/periodpals/model/location/UserLocationDtoTest.kt create mode 100644 app/src/test/java/com/android/periodpals/model/location/UserLocationModelSupabaseTest.kt create mode 100644 app/src/test/java/com/android/periodpals/model/location/UserLocationViewModelTest.kt diff --git a/app/src/test/java/com/android/periodpals/model/location/UserLocationDtoTest.kt b/app/src/test/java/com/android/periodpals/model/location/UserLocationDtoTest.kt new file mode 100644 index 000000000..c6b5b64e6 --- /dev/null +++ b/app/src/test/java/com/android/periodpals/model/location/UserLocationDtoTest.kt @@ -0,0 +1,31 @@ +package com.android.periodpals.model.location + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.junit.Assert.assertEquals +import org.junit.Test + +class UserLocationDtoTest { + + private val json = Json { ignoreUnknownKeys = true } + + @Test + fun serializeUserLocationDto() { + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val userLocationDto = UserLocationDto("user123", location) + val jsonString = json.encodeToString(userLocationDto) + val expectedJson = + """{"uid":"user123","locationGIS":{"type":"Point","coordinates":[12.34,56.78]}}""" + assertEquals(expectedJson, jsonString) + } + + @Test + fun deserializeUserLocationDto() { + val jsonString = + """{"uid":"user123","locationGIS":{"type":"Point","coordinates":[12.34,56.78]}}""" + val userLocationDto = json.decodeFromString(jsonString) + val expectedLocation = LocationGIS("Point", listOf(12.34, 56.78)) + val expectedUserLocationDto = UserLocationDto("user123", expectedLocation) + assertEquals(expectedUserLocationDto, userLocationDto) + } +} diff --git a/app/src/test/java/com/android/periodpals/model/location/UserLocationModelSupabaseTest.kt b/app/src/test/java/com/android/periodpals/model/location/UserLocationModelSupabaseTest.kt new file mode 100644 index 000000000..09cba05f7 --- /dev/null +++ b/app/src/test/java/com/android/periodpals/model/location/UserLocationModelSupabaseTest.kt @@ -0,0 +1,110 @@ +package com.android.periodpals.model.location + +import io.github.jan.supabase.createSupabaseClient +import io.github.jan.supabase.postgrest.Postgrest +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.client.engine.mock.respondBadRequest +import io.ktor.http.HttpStatusCode +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +class UserLocationModelSupabaseTest { + private lateinit var userLocationModelSupabase: UserLocationModelSupabase + + private val supabaseClientSuccess = + createSupabaseClient("", "") { + httpEngine = MockEngine { _ -> + respond( + content = + """{"uid":"user123","locationGIS":{"type":"Point","coordinates":[12.34,56.78]}}""", + status = HttpStatusCode.OK, + ) + } + install(Postgrest) + } + + private val supabaseClientFailure = + createSupabaseClient("", "") { + httpEngine = MockEngine { _ -> respondBadRequest() } + install(Postgrest) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Before + fun setUp() { + Dispatchers.setMain(Dispatchers.Unconfined) + userLocationModelSupabase = UserLocationModelSupabase(supabaseClientSuccess) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun createLocationSuccess() = runTest { + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val userLocationDto = UserLocationDto("user123", location) + var result = false + + userLocationModelSupabase.create( + locationDto = userLocationDto, + onSuccess = { result = true }, + onFailure = { fail("Should not call onFailure") }, + ) + assert(result) + } + + @Test + fun createLocationFailure() = runTest { + userLocationModelSupabase = UserLocationModelSupabase(supabaseClientFailure) + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val userLocationDto = UserLocationDto("user123", location) + var onFailureCalled = false + + userLocationModelSupabase.create( + locationDto = userLocationDto, + onSuccess = { fail("Should not call onSuccess") }, + onFailure = { onFailureCalled = true }, + ) + assert(onFailureCalled) + } + + @Test + fun updateLocationSuccess() = runTest { + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val userLocationDto = UserLocationDto("user123", location) + var result = false + + userLocationModelSupabase.update( + locationDto = userLocationDto, + onSuccess = { result = true }, + onFailure = { fail("Should not call onFailure") }, + ) + assert(result) + } + + @Test + fun updateLocationFailure() = runTest { + userLocationModelSupabase = UserLocationModelSupabase(supabaseClientFailure) + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val userLocationDto = UserLocationDto("user123", location) + var onFailureCalled = false + + userLocationModelSupabase.update( + locationDto = userLocationDto, + onSuccess = { fail("Should not call onSuccess") }, + onFailure = { onFailureCalled = true }, + ) + assert(onFailureCalled) + } +} diff --git a/app/src/test/java/com/android/periodpals/model/location/UserLocationViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/location/UserLocationViewModelTest.kt new file mode 100644 index 000000000..f0c9a6394 --- /dev/null +++ b/app/src/test/java/com/android/periodpals/model/location/UserLocationViewModelTest.kt @@ -0,0 +1,112 @@ +package com.android.periodpals.model.location + +import com.android.periodpals.MainCoroutineRule +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify + +@OptIn(ExperimentalCoroutinesApi::class) +class UserLocationViewModelTest { + + @ExperimentalCoroutinesApi @get:Rule var mainCoroutineRule = MainCoroutineRule() + + private lateinit var userLocationModel: UserLocationModel + + private lateinit var userLocationViewModel: UserLocationViewModel + + companion object { + val uid = "user123" + val location = LocationGIS("Point", listOf(12.34, 56.78)) + val locationDto = UserLocationDto(uid, location) + } + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + userLocationModel = mock(UserLocationModel::class.java) + userLocationViewModel = UserLocationViewModel(userLocationModel) + } + + @After + fun tearDown() { + // Clean up resources if needed + } + + @Test + fun `uploadUserLocation successful create`() = runTest { + var onSuccessCalled = false + + `when`(userLocationModel.create(eq(locationDto), any(), any())).thenAnswer { + it.getArgument<() -> Unit>(1)() + } + + userLocationViewModel.uploadUserLocation(uid, location, onSuccess = { onSuccessCalled = true }) + + assertTrue(onSuccessCalled) + } + + @Test + fun `uploadUserLocation failure create calls update`() = runTest { + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("create failed")) } + .`when`(userLocationModel) + .create(eq(locationDto), any(), any()) + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(userLocationModel) + .update(eq(locationDto), any(), any()) + + userLocationViewModel.uploadUserLocation(uid, location) + + verify(userLocationModel).update(eq(locationDto), any(), any()) + } + + @Test + fun `uploadUserLocation update failure`() = runTest { + var onFailureCalled = false + + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("create failed")) } + .`when`(userLocationModel) + .create(eq(locationDto), any(), any()) + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("update failed")) } + .`when`(userLocationModel) + .update(eq(locationDto), any(), any()) + + userLocationViewModel.uploadUserLocation( + "user123", + location, + onFailure = { onFailureCalled = true }, + ) + + assertTrue(onFailureCalled) + } + + @Test + fun `uploadUserLocation update success`() = runTest { + var onSuccessCalled = false + + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("create failed")) } + .`when`(userLocationModel) + .create(eq(locationDto), any(), any()) + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(userLocationModel) + .update(eq(locationDto), any(), any()) + + userLocationViewModel.uploadUserLocation( + "user123", + location, + onSuccess = { onSuccessCalled = true }, + ) + + assertTrue(onSuccessCalled) + } +}