From bff9d9501d53969c9a35edc5e69427775dd0236d Mon Sep 17 00:00:00 2001 From: agonzalez-r Date: Sun, 10 Nov 2024 14:59:24 +0100 Subject: [PATCH 01/19] fix: add `TAMPON_AND_PAD` to `Product` enum --- app/src/main/java/com/android/periodpals/model/alert/Alert.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/periodpals/model/alert/Alert.kt b/app/src/main/java/com/android/periodpals/model/alert/Alert.kt index fb05a9ca4..1a29835c6 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/Alert.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/Alert.kt @@ -29,7 +29,8 @@ data class Alert( /** Enum class representing the product requested with the alert. */ enum class Product { TAMPON, - PAD + PAD, + TAMPON_AND_PAD, } /** Enum class representing the urgency level of the alert. */ From e12b25ddf941314a371b6ba23844660ae751bc74 Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Sat, 9 Nov 2024 14:47:27 +0100 Subject: [PATCH 02/19] feat: integrate userVM into `Profile` User profile picture, name and description are now synced with Supabase. --- .../com/android/periodpals/MainActivity.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/MainActivity.kt b/app/src/main/java/com/android/periodpals/MainActivity.kt index 62a2e9d3e..7b016444b 100644 --- a/app/src/main/java/com/android/periodpals/MainActivity.kt +++ b/app/src/main/java/com/android/periodpals/MainActivity.kt @@ -41,13 +41,13 @@ class MainActivity : ComponentActivity() { private val locationService = GPSServiceImpl(this) private val supabaseClient = - createSupabaseClient( - supabaseUrl = BuildConfig.SUPABASE_URL, - supabaseKey = BuildConfig.SUPABASE_KEY, - ) { - install(Auth) - install(Postgrest) - } + createSupabaseClient( + supabaseUrl = BuildConfig.SUPABASE_URL, + supabaseKey = BuildConfig.SUPABASE_KEY, + ) { + install(Auth) + install(Postgrest) + } private val authModel = AuthenticationModelSupabase(supabaseClient) private val authenticationViewModel = AuthenticationViewModel(authModel) @@ -74,9 +74,9 @@ class MainActivity : ComponentActivity() { @Composable fun PeriodPalsApp( - locationService: GPSServiceImpl, - authenticationViewModel: AuthenticationViewModel, - userViewModel: UserViewModel, + locationService: GPSServiceImpl, + authenticationViewModel: AuthenticationViewModel, + userViewModel: UserViewModel, ) { val navController = rememberNavController() val navigationActions = NavigationActions(navController) @@ -111,7 +111,7 @@ fun PeriodPalsApp( // Profile navigation(startDestination = Screen.PROFILE, route = Route.PROFILE) { - composable(Screen.PROFILE) { ProfileScreen(navigationActions) } + composable(Screen.PROFILE) { ProfileScreen(userViewModel, navigationActions) } composable(Screen.EDIT_PROFILE) { EditProfileScreen(navigationActions) } } } From acb22a406e4b04947f9092840639119618c057f8 Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Sat, 9 Nov 2024 16:46:30 +0100 Subject: [PATCH 03/19] style: format code using ktfmt for readability --- .../com/android/periodpals/MainActivity.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/MainActivity.kt b/app/src/main/java/com/android/periodpals/MainActivity.kt index 7b016444b..9d3a14c70 100644 --- a/app/src/main/java/com/android/periodpals/MainActivity.kt +++ b/app/src/main/java/com/android/periodpals/MainActivity.kt @@ -41,13 +41,13 @@ class MainActivity : ComponentActivity() { private val locationService = GPSServiceImpl(this) private val supabaseClient = - createSupabaseClient( - supabaseUrl = BuildConfig.SUPABASE_URL, - supabaseKey = BuildConfig.SUPABASE_KEY, - ) { - install(Auth) - install(Postgrest) - } + createSupabaseClient( + supabaseUrl = BuildConfig.SUPABASE_URL, + supabaseKey = BuildConfig.SUPABASE_KEY, + ) { + install(Auth) + install(Postgrest) + } private val authModel = AuthenticationModelSupabase(supabaseClient) private val authenticationViewModel = AuthenticationViewModel(authModel) @@ -74,9 +74,9 @@ class MainActivity : ComponentActivity() { @Composable fun PeriodPalsApp( - locationService: GPSServiceImpl, - authenticationViewModel: AuthenticationViewModel, - userViewModel: UserViewModel, + locationService: GPSServiceImpl, + authenticationViewModel: AuthenticationViewModel, + userViewModel: UserViewModel, ) { val navController = rememberNavController() val navigationActions = NavigationActions(navController) From b7f2aa80acc9cda5536b58e6d9d200427ff073eb Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Sun, 10 Nov 2024 15:43:37 +0100 Subject: [PATCH 04/19] docs: add documentation to `ProfileScreen.kt` components --- .../periodpals/ui/profile/ProfileScreen.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt b/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt index 58041bbab..d77c82134 100644 --- a/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt +++ b/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt @@ -48,6 +48,17 @@ private const val NUMBER_INTERACTIONS = "Number of interactions: " private const val REVIEWS = "Reviews" private const val NO_REVIEWS = "No reviews yet..." +/** + * A composable function that displays the user's profile screen. + * + * This screen includes the user's profile picture, name, description, contribution information, and + * a section for reviews. It also includes a top app bar with an edit button and a bottom navigation + * menu. + * + * @param userViewModel The ViewModel that handles user data. + * @param navigationActions The navigation actions to navigate between screens. + * @sample ProfileScreen + */ @OptIn(ExperimentalGlideComposeApi::class) @Composable fun ProfileScreen(navigationActions: NavigationActions) { @@ -136,6 +147,13 @@ fun ProfileScreen(navigationActions: NavigationActions) { } } +/** + * A composable function that displays a card indicating that there are no reviews available. + * + * This card contains an icon and a text message informing the user that no reviews are present. + * + * @sample NoReviewCard + */ @Composable private fun NoReviewCard() { Card( From 20ea2938c27d36ce16a1d2751dbce595fb59818a Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Sun, 10 Nov 2024 16:02:36 +0100 Subject: [PATCH 05/19] test: finish merging tests Merged the tests for `ProfileScreen`, which were overwritten by inattentive merge (c358f6a) --- .../ui/profile/ProfileScreenTest.kt | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/android/periodpals/ui/profile/ProfileScreenTest.kt b/app/src/androidTest/java/com/android/periodpals/ui/profile/ProfileScreenTest.kt index f4e282404..929fd06fb 100644 --- a/app/src/androidTest/java/com/android/periodpals/ui/profile/ProfileScreenTest.kt +++ b/app/src/androidTest/java/com/android/periodpals/ui/profile/ProfileScreenTest.kt @@ -1,11 +1,14 @@ package com.android.periodpals.ui.profile +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import com.android.periodpals.model.user.User +import com.android.periodpals.model.user.UserViewModel import com.android.periodpals.resources.C.Tag.BottomNavigationMenu import com.android.periodpals.resources.C.Tag.ProfileScreen import com.android.periodpals.resources.C.Tag.TopAppBar @@ -22,17 +25,31 @@ import org.mockito.Mockito.`when` class ProfileScreenTest { private lateinit var navigationActions: NavigationActions + private lateinit var userViewModel: UserViewModel @get:Rule val composeTestRule = createComposeRule() + companion object { + private val name = "John Doe" + private val imageUrl = "https://example.com" + private val description = "A short description" + private val dob = "01/01/2000" + private val userState = + mutableStateOf(User(name = name, imageUrl = imageUrl, description = description, dob = dob)) + } + @Before fun setUp() { navigationActions = mock(NavigationActions::class.java) + userViewModel = mock(UserViewModel::class.java) + `when`(navigationActions.currentRoute()).thenReturn(Route.PROFILE) - composeTestRule.setContent { ProfileScreen(navigationActions) } } @Test fun allComponentsAreDisplayed() { + `when`(userViewModel.user).thenReturn(userState) + composeTestRule.setContent { ProfileScreen(userViewModel, navigationActions) } + composeTestRule.onNodeWithTag(ProfileScreen.SCREEN).assertIsDisplayed() composeTestRule.onNodeWithTag(ProfileScreen.PROFILE_PICTURE).assertIsDisplayed() composeTestRule.onNodeWithTag(ProfileScreen.NAME_FIELD).assertIsDisplayed() @@ -57,17 +74,32 @@ class ProfileScreenTest { @Test fun editButtonNavigatesToEditProfileScreen() { + `when`(userViewModel.user).thenReturn(userState) + composeTestRule.setContent { ProfileScreen(userViewModel, navigationActions) } + composeTestRule.onNodeWithTag(TopAppBar.EDIT_BUTTON).performClick() verify(navigationActions).navigateTo(Screen.EDIT_PROFILE) } @Test - fun profileScreenHasCorrectContent() { - composeTestRule.onNodeWithTag(ProfileScreen.NAME_FIELD).assertTextEquals("Name") + fun profileScreenHasCorrectContentVMSuccess() { + `when`(userViewModel.user).thenReturn(userState) + composeTestRule.setContent { ProfileScreen(userViewModel, navigationActions) } + + composeTestRule.onNodeWithTag(ProfileScreen.NAME_FIELD).assertTextEquals(name) + composeTestRule.onNodeWithTag(ProfileScreen.DESCRIPTION_FIELD).assertTextEquals(description) + } + + @Test + fun profileScreenHasCorrectContentVMFailure() { + `when`(userViewModel.user).thenReturn(mutableStateOf(null)) + composeTestRule.setContent { ProfileScreen(userViewModel, navigationActions) } + + composeTestRule + .onNodeWithTag(ProfileScreen.NAME_FIELD) + .assertTextEquals("Error loading name, try again later.") composeTestRule .onNodeWithTag(ProfileScreen.DESCRIPTION_FIELD) - .assertTextEquals( - "(Description) Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" + - "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.") + .assertTextEquals("Error loading description, try again later.") } } From 621e311b15dee51dadd39541171d78219312a5c0 Mon Sep 17 00:00:00 2001 From: agonzalez-r Date: Sun, 10 Nov 2024 19:09:53 +0100 Subject: [PATCH 06/19] fix: make uid parameter in Alert data class not nullable (must be filled when Alert is created) --- app/src/main/java/com/android/periodpals/model/alert/Alert.kt | 2 +- .../main/java/com/android/periodpals/model/alert/AlertDto.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/model/alert/Alert.kt b/app/src/main/java/com/android/periodpals/model/alert/Alert.kt index 1a29835c6..013468ad6 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/Alert.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/Alert.kt @@ -16,7 +16,7 @@ package com.android.periodpals.model.alert */ data class Alert( val id: String?, // given when created in supabase - val uid: String?, + val uid: String, val name: String, val product: Product, val urgency: Urgency, diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertDto.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertDto.kt index 3a06e19e2..bf032f111 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertDto.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertDto.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.Serializable @Serializable data class AlertDto( @SerialName("id") val id: String?, - @SerialName("uid") val uid: String?, + @SerialName("uid") val uid: String, @SerialName("name") val name: String, @SerialName("product") val product: Product, @SerialName("urgency") val urgency: Urgency, From d08278a14e6c90f508ecae9fe97395d21fdfaf3a Mon Sep 17 00:00:00 2001 From: agonzalez-r Date: Sun, 10 Nov 2024 20:43:10 +0100 Subject: [PATCH 07/19] fix: addAlert() should not return the id of the Alert --- .../java/com/android/periodpals/model/alert/AlertModel.kt | 5 ++--- .../com/android/periodpals/model/alert/AlertModelSupabase.kt | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertModel.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertModel.kt index 660203dd7..3239e0fac 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertModel.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertModel.kt @@ -9,11 +9,10 @@ interface AlertModel { * Adds a new alert. * * @param alert The alert to be added. - * @param onSuccess Callback function to be called on successful addition, with the ID of the - * created alert as a parameter. + * @param onSuccess Callback function to be called on successful addition * @param onFailure Callback function to be called on failure, with the exception as a parameter. */ - suspend fun addAlert(alert: Alert, onSuccess: (String) -> Unit, onFailure: (Exception) -> Unit) + suspend fun addAlert(alert: Alert, onSuccess: () -> Unit, onFailure: (Exception) -> Unit) /** * Retrieves an alert by its ID. diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertModelSupabase.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertModelSupabase.kt index a9939f04e..66c08d8bf 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertModelSupabase.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertModelSupabase.kt @@ -28,7 +28,7 @@ class AlertModelSupabase( */ override suspend fun addAlert( alert: Alert, - onSuccess: (String) -> Unit, + onSuccess: () -> Unit, onFailure: (Exception) -> Unit ) { try { @@ -40,7 +40,7 @@ class AlertModelSupabase( val insertedAlert = insertedAlertDto.toAlert() if (insertedAlert.id != null) { Log.d(TAG, "addAlert: Success") - onSuccess(insertedAlert.id) + onSuccess() } else { Log.e(TAG, "addAlert: fail to create alert: ID is null") onFailure(Exception("ID is null")) From 9b7d0d7ae58cb985cbd721310a743614f42753b6 Mon Sep 17 00:00:00 2001 From: agonzalez-r Date: Sun, 10 Nov 2024 20:49:49 +0100 Subject: [PATCH 08/19] fix: fix test addAlertSuccess --- .../periodpals/model/alert/AlertModelSupabaseTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertModelSupabaseTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertModelSupabaseTest.kt index 6ba345eb5..6ceca95e8 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertModelSupabaseTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertModelSupabaseTest.kt @@ -82,14 +82,14 @@ class AlertModelSupabaseTest { @Test fun addAlertSuccess() = runBlocking { - var result = "" + var result = false alertModelSupabase.addAlert( alert = defaultAlert, - onSuccess = { result = it }, // Ensuring match with test expectation + onSuccess = { result = true }, // Ensuring match with test expectation onFailure = { fail("should not call onFailure") }) - assertEquals(defaultAlert.id, result) + assertEquals(true, result) } @Test From 849abc3a4b675ec9b6aaff5601b99fe4ad855d31 Mon Sep 17 00:00:00 2001 From: agonzalez-r Date: Sun, 10 Nov 2024 21:01:22 +0100 Subject: [PATCH 09/19] feat: create AlertViewModel draft and begin testing(wip) --- .../periodpals/model/alert/AlertViewModel.kt | 161 ++++++++++++++++++ .../model/alert/AlertViewModelTest.kt | 80 +++++++++ 2 files changed, 241 insertions(+) create mode 100644 app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt create mode 100644 app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt new file mode 100644 index 000000000..968773297 --- /dev/null +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt @@ -0,0 +1,161 @@ +package com.android.periodpals.model.alert + +import android.util.Log +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.State +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.compose.viewModel +import com.android.periodpals.model.authentication.AuthenticationModel +import com.android.periodpals.model.user.UserViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +private const val TAG = "AlertViewModel" + +/** + * ViewModel for managing alert data. + * + * @property alertModelSupabase The repository used for loading and saving alerts. + * @property _alerts Mutable state holding the list of alerts. + * @property alerts Public state exposing the list of alerts. + */ +class AlertViewModel(private val alertModelSupabase: AlertModelSupabase): ViewModel() { + //remove this? + private var _alerts = mutableStateOf?>(listOf()) + val alerts: State?> = _alerts + + /** + * Creates a new alert. + * + * @param alert The alert to be created. + */ + fun createAlert(alert: Alert){ + viewModelScope.launch { + alertModelSupabase.addAlert( + alert = alert, + onSuccess = { + Log.d(TAG, "createAlert: Success") + getAllAlerts() // refresh the alerts list + }, + onFailure = { e -> + Log.e(TAG, "createAlert: fail to create alert: ${e.message}") + } + ) + } + } + + /** + * Retrieves an alert by its ID. + * + * @param idAlert The ID of the alert to be retrieved. + * @return The alert if found, null otherwise. + */ + fun getAlert(idAlert: String): Alert?{ + var alert: Alert? = null + viewModelScope.launch { + alertModelSupabase.getAlert( + idAlert = idAlert, + onSuccess = { fetched -> + Log.d(TAG, "getAlert: Success") + alert = fetched + }, + onFailure = { e -> + Log.e(TAG, "getAlert: fail to get alert: ${e.message}") + } + ) + } + return alert + } + + /** + * Retrieves all alerts. + * + * @return The list of all alerts. + */ + fun getAllAlerts(): List? { + var alertsList: List? = null + viewModelScope.launch { + alertModelSupabase.getAllAlerts( + onSuccess = { alerts -> + Log.d(TAG, "getAllAlerts: Success") + _alerts.value = alerts + alertsList = alerts + }, + onFailure = { e -> + Log.e(TAG, "getAllAlerts: fail to get alerts: ${e.message}") + } + ) + } + return alertsList + } + + /** + * Retrieves alerts for a specific user by their UID. + * + * @param uid The UID of the user. + * @return The list of alerts for the user. + */ + fun getAlertsByUser(uid: String): List? { + var alertsList: List? = null + viewModelScope.launch { + alertModelSupabase.getAlertsFilteredBy( + // ideally the uid would not be passed as argument and instead we could get uid by UserViewModel.currentUser?.uid + cond = { eq("uid", uid) }, + onSuccess = { alerts -> + Log.d(TAG, "getMyAlerts: Success") + alertsList = alerts + }, + onFailure = { e -> + Log.e(TAG, "getMyAlerts: fail to get alerts: ${e.message}") + } + ) + } + return alertsList + } + + /** + * Updates an existing alert. + * + * @param alert The alert with updated parameters. + */ + fun updateAlert(alert: Alert){ + viewModelScope.launch { + alertModelSupabase.updateAlert( + alert = alert, + onSuccess = { + Log.d(TAG, "updateAlert: Success") + getAllAlerts() + }, + onFailure = { e -> + Log.e(TAG, "updateAlert: fail to update alert: ${e.message}") + } + ) + } + } + + /** + * Deletes an alert. + * + * @param alert The alert to be deleted. + */ + fun deleteAlert(alert: Alert){ + viewModelScope.launch { + alert.id?.let { + alertModelSupabase.deleteAlertById( + idAlert = it, + onSuccess = { + Log.d(TAG, "deleteAlert: Success") + getAllAlerts() + }, + onFailure = { e -> + Log.e(TAG, "deleteAlert: fail to delete alert: ${e.message}") + } + ) + } ?: run { + Log.e(TAG, "deleteAlert: fail to delete alert: id of the Alert is null") + } + } + } +} \ No newline at end of file diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt new file mode 100644 index 000000000..09357e059 --- /dev/null +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -0,0 +1,80 @@ +package com.android.periodpals.model.alert + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.android.periodpals.MainCoroutineRule +import com.android.periodpals.model.user.UserDto +import io.mockk.MockKAnnotations +import io.mockk.mockk +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.datetime.LocalDateTime +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.doAnswer +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any + +@OptIn(ExperimentalCoroutinesApi::class) +class AlertViewModelTest { + @Mock private lateinit var alertModelSupabase: AlertModelSupabase + private lateinit var viewModel: AlertViewModel + + @ExperimentalCoroutinesApi @get:Rule + var mainCoroutineRule = MainCoroutineRule() + + companion object{ + const val id = "idAlert" + const val uid = "mock_uid" + val name = "test_name" + val product = Product.PAD + val urgency = Urgency.LOW + val createdAt = LocalDateTime(2022, 1, 1, 0, 0).toString() + val location = "test_location" + val message = "test_message" + val status = Status.CREATED + } + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + // Create ViewModel with mocked AlertModelSupabase + viewModel = AlertViewModel(alertModelSupabase) + } + + @Test + fun createAlertSuccess() = runBlocking{ + val alert = Alert( + id = id, + uid = uid, + name = name, + product = product, + urgency = urgency, + createdAt = createdAt, + location = location, + message = message, + status = status + ) + + // Mock addAlert success behavior + doAnswer { it.getArgument<(Alert) -> Unit>(0)(alert) + }.`when`(alertModelSupabase).addAlert(eq(alert), any<() -> Unit>(), any<(Exception) -> Unit>()) + + // Mock getAllAlerts to verify it is called after successful addition + doAnswer { invocation -> + val onSuccess = invocation.getArgument<(List) -> Unit>(0) + onSuccess(listOf(alert)) // Return a list with our mock alert + null + }.`when`(alertModelSupabase).getAllAlerts(any(), any()) + + viewModel.createAlert(alert) + + assertEquals(listOf(alert), viewModel.alerts.value) + + + } +} \ No newline at end of file From 81b1b272000e71a91a5dc03caf5af44a15c5d808 Mon Sep 17 00:00:00 2001 From: Alonso Date: Mon, 18 Nov 2024 17:58:28 +0100 Subject: [PATCH 10/19] tests: add tests to `createAlert` --- .../model/alert/AlertViewModelTest.kt | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index 09357e059..c530ad002 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.datetime.LocalDateTime import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Rule import org.junit.Test @@ -37,17 +38,7 @@ class AlertViewModelTest { val location = "test_location" val message = "test_message" val status = Status.CREATED - } - @Before - fun setup() { - MockitoAnnotations.openMocks(this) - // Create ViewModel with mocked AlertModelSupabase - viewModel = AlertViewModel(alertModelSupabase) - } - - @Test - fun createAlertSuccess() = runBlocking{ val alert = Alert( id = id, uid = uid, @@ -59,10 +50,20 @@ class AlertViewModelTest { message = message, status = status ) + } + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + // Create ViewModel with mocked AlertModelSupabase + viewModel = AlertViewModel(alertModelSupabase) + } + + @Test + fun createAlertSuccess() = runBlocking{ // Mock addAlert success behavior - doAnswer { it.getArgument<(Alert) -> Unit>(0)(alert) - }.`when`(alertModelSupabase).addAlert(eq(alert), any<() -> Unit>(), any<(Exception) -> Unit>()) + doAnswer { it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase).addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) // Mock getAllAlerts to verify it is called after successful addition doAnswer { invocation -> @@ -74,7 +75,39 @@ class AlertViewModelTest { viewModel.createAlert(alert) assertEquals(listOf(alert), viewModel.alerts.value) + } + + @Test + fun createAlertAddAlertFailure() = runBlocking{ + // Mock addAlert success behavior + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("createAlert failure")) + }.`when`(alertModelSupabase).addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isEmpty()) + } + + @Test + fun createAlertGetAlertFailure() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase). + addAlert( + any(), any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<(Exception) -> Unit>(1)(Exception(" ")) + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isEmpty()) } + } \ No newline at end of file From 9e887e1683a20914f03831bd206c9ed9ca21771b Mon Sep 17 00:00:00 2001 From: Alonso Date: Mon, 18 Nov 2024 21:45:12 +0100 Subject: [PATCH 11/19] tests: add tests to `deleteAlert` --- .../com/android/periodpals/MainActivity.kt | 2 +- .../model/alert/AlertViewModelTest.kt | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/periodpals/MainActivity.kt b/app/src/main/java/com/android/periodpals/MainActivity.kt index 9d3a14c70..62a2e9d3e 100644 --- a/app/src/main/java/com/android/periodpals/MainActivity.kt +++ b/app/src/main/java/com/android/periodpals/MainActivity.kt @@ -111,7 +111,7 @@ fun PeriodPalsApp( // Profile navigation(startDestination = Screen.PROFILE, route = Route.PROFILE) { - composable(Screen.PROFILE) { ProfileScreen(userViewModel, navigationActions) } + composable(Screen.PROFILE) { ProfileScreen(navigationActions) } composable(Screen.EDIT_PROFILE) { EditProfileScreen(navigationActions) } } } diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index c530ad002..cb603569e 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.android.periodpals.MainCoroutineRule import com.android.periodpals.model.user.UserDto +import io.mockk.Invocation import io.mockk.MockKAnnotations import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -19,6 +20,7 @@ import org.mockito.Mock import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations import org.mockito.kotlin.any +import org.mockito.stubbing.Answer @OptIn(ExperimentalCoroutinesApi::class) class AlertViewModelTest { @@ -50,6 +52,17 @@ class AlertViewModelTest { message = message, status = status ) + val alertNullID = Alert( + id = null, + uid = uid, + name = name, + product = product, + urgency = urgency, + createdAt = createdAt, + location = location, + message = message, + status = status + ) } @Before @@ -110,4 +123,109 @@ class AlertViewModelTest { assert(viewModel.alerts.value!!.isEmpty()) } + @Test + fun deleteAlertSuccess() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + var calls = 0 + doAnswer { + if (calls == 0) { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + } else { + it.getArgument<(List) -> Unit>(0)(listOf()) + } + calls++ + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .deleteAlertById( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.deleteAlert(alert) + assert(viewModel.alerts.value!!.isEmpty()) + + } + + @Test + fun deleteAlertNullIdFailure() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + viewModel.createAlert(alert) + viewModel.deleteAlert(alertNullID) + + assert(!viewModel.alerts.value!!.isEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + } + + @Test + fun deleteAlertDeleteFailure() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer{ + it.getArgument<(Exception) -> Unit>(2)(Exception("deleteAlertFailure")) + }.`when`(alertModelSupabase) + .deleteAlertById( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.deleteAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + } } \ No newline at end of file From cb447b7a0de4ddbd6709291021d2f344bb372d6f Mon Sep 17 00:00:00 2001 From: Alonso Date: Tue, 19 Nov 2024 12:36:12 +0100 Subject: [PATCH 12/19] fix: correct imports in `ProfileScreen` --- .../android/periodpals/ui/profile/ProfileScreen.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt b/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt index b64550f70..0e87a411a 100644 --- a/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt +++ b/app/src/main/java/com/android/periodpals/ui/profile/ProfileScreen.kt @@ -1,12 +1,21 @@ package com.android.periodpals.ui.profile import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.SentimentVeryDissatisfied import androidx.compose.material3.Card From f162abf1c15bca731b3b9a44d7fc5325fd3784b3 Mon Sep 17 00:00:00 2001 From: Alonso Date: Tue, 19 Nov 2024 15:50:18 +0100 Subject: [PATCH 13/19] format: ktfmt format --- .../periodpals/model/alert/AlertViewModel.kt | 238 ++++++------ .../model/alert/AlertViewModelTest.kt | 344 ++++++++---------- 2 files changed, 254 insertions(+), 328 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt index 968773297..7a4318313 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt @@ -1,15 +1,10 @@ package com.android.periodpals.model.alert import android.util.Log -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel -import com.android.periodpals.model.authentication.AuthenticationModel -import com.android.periodpals.model.user.UserViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch private const val TAG = "AlertViewModel" @@ -21,141 +16,122 @@ private const val TAG = "AlertViewModel" * @property _alerts Mutable state holding the list of alerts. * @property alerts Public state exposing the list of alerts. */ -class AlertViewModel(private val alertModelSupabase: AlertModelSupabase): ViewModel() { - //remove this? - private var _alerts = mutableStateOf?>(listOf()) - val alerts: State?> = _alerts +class AlertViewModel(private val alertModelSupabase: AlertModelSupabase) : ViewModel() { + // remove this? + private var _alerts = mutableStateOf?>(listOf()) + val alerts: State?> = _alerts - /** - * Creates a new alert. - * - * @param alert The alert to be created. - */ - fun createAlert(alert: Alert){ - viewModelScope.launch { - alertModelSupabase.addAlert( - alert = alert, - onSuccess = { - Log.d(TAG, "createAlert: Success") - getAllAlerts() // refresh the alerts list - }, - onFailure = { e -> - Log.e(TAG, "createAlert: fail to create alert: ${e.message}") - } - ) - } + /** + * Creates a new alert. + * + * @param alert The alert to be created. + */ + fun createAlert(alert: Alert) { + viewModelScope.launch { + alertModelSupabase.addAlert( + alert = alert, + onSuccess = { + Log.d(TAG, "createAlert: Success") + getAllAlerts() // refresh the alerts list + }, + onFailure = { e -> Log.e(TAG, "createAlert: fail to create alert: ${e.message}") }) } + } - /** - * Retrieves an alert by its ID. - * - * @param idAlert The ID of the alert to be retrieved. - * @return The alert if found, null otherwise. - */ - fun getAlert(idAlert: String): Alert?{ - var alert: Alert? = null - viewModelScope.launch { - alertModelSupabase.getAlert( - idAlert = idAlert, - onSuccess = { fetched -> - Log.d(TAG, "getAlert: Success") - alert = fetched - }, - onFailure = { e -> - Log.e(TAG, "getAlert: fail to get alert: ${e.message}") - } - ) - } - return alert + /** + * Retrieves an alert by its ID. + * + * @param idAlert The ID of the alert to be retrieved. + * @return The alert if found, null otherwise. + */ + fun getAlert(idAlert: String): Alert? { + var alert: Alert? = null + viewModelScope.launch { + alertModelSupabase.getAlert( + idAlert = idAlert, + onSuccess = { fetched -> + Log.d(TAG, "getAlert: Success") + alert = fetched + }, + onFailure = { e -> Log.e(TAG, "getAlert: fail to get alert: ${e.message}") }) } + return alert + } - /** - * Retrieves all alerts. - * - * @return The list of all alerts. - */ - fun getAllAlerts(): List? { - var alertsList: List? = null - viewModelScope.launch { - alertModelSupabase.getAllAlerts( - onSuccess = { alerts -> - Log.d(TAG, "getAllAlerts: Success") - _alerts.value = alerts - alertsList = alerts - }, - onFailure = { e -> - Log.e(TAG, "getAllAlerts: fail to get alerts: ${e.message}") - } - ) + /** + * Retrieves all alerts. + * + * @return The list of all alerts. + */ + fun getAllAlerts(): List? { + var alertsList: List? = null + viewModelScope.launch { + alertModelSupabase.getAllAlerts( + onSuccess = { alerts -> + Log.d(TAG, "getAllAlerts: Success") + _alerts.value = alerts + alertsList = alerts + }, + onFailure = { e -> Log.e(TAG, "getAllAlerts: fail to get alerts: ${e.message}") }) } return alertsList - } + } - /** - * Retrieves alerts for a specific user by their UID. - * - * @param uid The UID of the user. - * @return The list of alerts for the user. - */ - fun getAlertsByUser(uid: String): List? { - var alertsList: List? = null - viewModelScope.launch { - alertModelSupabase.getAlertsFilteredBy( - // ideally the uid would not be passed as argument and instead we could get uid by UserViewModel.currentUser?.uid - cond = { eq("uid", uid) }, - onSuccess = { alerts -> - Log.d(TAG, "getMyAlerts: Success") - alertsList = alerts - }, - onFailure = { e -> - Log.e(TAG, "getMyAlerts: fail to get alerts: ${e.message}") - } - ) - } - return alertsList + /** + * Retrieves alerts for a specific user by their UID. + * + * @param uid The UID of the user. + * @return The list of alerts for the user. + */ + fun getAlertsByUser(uid: String): List? { + var alertsList: List? = null + viewModelScope.launch { + alertModelSupabase.getAlertsFilteredBy( + // ideally the uid would not be passed as argument and instead we could get uid by + // UserViewModel.currentUser?.uid + cond = { eq("uid", uid) }, + onSuccess = { alerts -> + Log.d(TAG, "getMyAlerts: Success") + alertsList = alerts + }, + onFailure = { e -> Log.e(TAG, "getMyAlerts: fail to get alerts: ${e.message}") }) } + return alertsList + } - /** - * Updates an existing alert. - * - * @param alert The alert with updated parameters. - */ - fun updateAlert(alert: Alert){ - viewModelScope.launch { - alertModelSupabase.updateAlert( - alert = alert, - onSuccess = { - Log.d(TAG, "updateAlert: Success") - getAllAlerts() - }, - onFailure = { e -> - Log.e(TAG, "updateAlert: fail to update alert: ${e.message}") - } - ) - } + /** + * Updates an existing alert. + * + * @param alert The alert with updated parameters. + */ + fun updateAlert(alert: Alert) { + viewModelScope.launch { + alertModelSupabase.updateAlert( + alert = alert, + onSuccess = { + Log.d(TAG, "updateAlert: Success") + getAllAlerts() + }, + onFailure = { e -> Log.e(TAG, "updateAlert: fail to update alert: ${e.message}") }) } + } - /** - * Deletes an alert. - * - * @param alert The alert to be deleted. - */ - fun deleteAlert(alert: Alert){ - viewModelScope.launch { - alert.id?.let { - alertModelSupabase.deleteAlertById( - idAlert = it, - onSuccess = { - Log.d(TAG, "deleteAlert: Success") - getAllAlerts() - }, - onFailure = { e -> - Log.e(TAG, "deleteAlert: fail to delete alert: ${e.message}") - } - ) - } ?: run { - Log.e(TAG, "deleteAlert: fail to delete alert: id of the Alert is null") - } - } + /** + * Deletes an alert. + * + * @param alert The alert to be deleted. + */ + fun deleteAlert(alert: Alert) { + viewModelScope.launch { + alert.id?.let { + alertModelSupabase.deleteAlertById( + idAlert = it, + onSuccess = { + Log.d(TAG, "deleteAlert: Success") + getAllAlerts() + }, + onFailure = { e -> Log.e(TAG, "deleteAlert: fail to delete alert: ${e.message}") }) + } ?: run { Log.e(TAG, "deleteAlert: fail to delete alert: id of the Alert is null") } } -} \ No newline at end of file + } +} diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index cb603569e..67cc02fd4 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -1,47 +1,38 @@ package com.android.periodpals.model.alert -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import com.android.periodpals.MainCoroutineRule -import com.android.periodpals.model.user.UserDto -import io.mockk.Invocation -import io.mockk.MockKAnnotations -import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.datetime.LocalDateTime import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.doAnswer import org.mockito.MockitoAnnotations import org.mockito.kotlin.any -import org.mockito.stubbing.Answer @OptIn(ExperimentalCoroutinesApi::class) class AlertViewModelTest { - @Mock private lateinit var alertModelSupabase: AlertModelSupabase - private lateinit var viewModel: AlertViewModel - - @ExperimentalCoroutinesApi @get:Rule - var mainCoroutineRule = MainCoroutineRule() - - companion object{ - const val id = "idAlert" - const val uid = "mock_uid" - val name = "test_name" - val product = Product.PAD - val urgency = Urgency.LOW - val createdAt = LocalDateTime(2022, 1, 1, 0, 0).toString() - val location = "test_location" - val message = "test_message" - val status = Status.CREATED - - val alert = Alert( + @Mock private lateinit var alertModelSupabase: AlertModelSupabase + private lateinit var viewModel: AlertViewModel + + @ExperimentalCoroutinesApi @get:Rule var mainCoroutineRule = MainCoroutineRule() + + companion object { + const val id = "idAlert" + const val uid = "mock_uid" + val name = "test_name" + val product = Product.PAD + val urgency = Urgency.LOW + val createdAt = LocalDateTime(2022, 1, 1, 0, 0).toString() + val location = "test_location" + val message = "test_message" + val status = Status.CREATED + + val alert = + Alert( id = id, uid = uid, name = name, @@ -50,9 +41,9 @@ class AlertViewModelTest { createdAt = createdAt, location = location, message = message, - status = status - ) - val alertNullID = Alert( + status = status) + val alertNullID = + Alert( id = null, uid = uid, name = name, @@ -61,171 +52,130 @@ class AlertViewModelTest { createdAt = createdAt, location = location, message = message, - status = status - ) - } - - @Before - fun setup() { - MockitoAnnotations.openMocks(this) - // Create ViewModel with mocked AlertModelSupabase - viewModel = AlertViewModel(alertModelSupabase) - } - - @Test - fun createAlertSuccess() = runBlocking{ - // Mock addAlert success behavior - doAnswer { it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase).addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) - - // Mock getAllAlerts to verify it is called after successful addition - doAnswer { invocation -> - val onSuccess = invocation.getArgument<(List) -> Unit>(0) - onSuccess(listOf(alert)) // Return a list with our mock alert - null - }.`when`(alertModelSupabase).getAllAlerts(any(), any()) - - viewModel.createAlert(alert) - - assertEquals(listOf(alert), viewModel.alerts.value) - } - - @Test - fun createAlertAddAlertFailure() = runBlocking{ - // Mock addAlert success behavior - doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("createAlert failure")) - }.`when`(alertModelSupabase).addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) - - viewModel.createAlert(alert) - assert(viewModel.alerts.value!!.isEmpty()) - } - - @Test - fun createAlertGetAlertFailure() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase). - addAlert( - any(), any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { - it.getArgument<(Exception) -> Unit>(1)(Exception(" ")) - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - - assert(viewModel.alerts.value!!.isEmpty()) - } - - @Test - fun deleteAlertSuccess() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - var calls = 0 - doAnswer { - if (calls == 0) { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) - } else { - it.getArgument<(List) -> Unit>(0)(listOf()) - } - calls++ - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .deleteAlertById( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - assert(viewModel.alerts.value!!.isNotEmpty()) - assertEquals(listOf(alert), viewModel.alerts.value) - - viewModel.deleteAlert(alert) - assert(viewModel.alerts.value!!.isEmpty()) - - } - - @Test - fun deleteAlertNullIdFailure() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { + status = status) + } + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + // Create ViewModel with mocked AlertModelSupabase + viewModel = AlertViewModel(alertModelSupabase) + } + + @Test + fun createAlertSuccess() = runBlocking { + // Mock addAlert success behavior + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + // Mock getAllAlerts to verify it is called after successful addition + doAnswer { invocation -> + val onSuccess = invocation.getArgument<(List) -> Unit>(0) + onSuccess(listOf(alert)) // Return a list with our mock alert + null + } + .`when`(alertModelSupabase) + .getAllAlerts(any(), any()) + + viewModel.createAlert(alert) + + assertEquals(listOf(alert), viewModel.alerts.value) + } + + @Test + fun createAlertAddAlertFailure() = runBlocking { + // Mock addAlert success behavior + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("createAlert failure")) } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isEmpty()) + } + + @Test + fun createAlertGetAlertFailure() = runBlocking { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(Exception) -> Unit>(1)(Exception(" ")) } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + + assert(viewModel.alerts.value!!.isEmpty()) + } + + @Test + fun deleteAlertSuccess() = runBlocking { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + var calls = 0 + doAnswer { + if (calls == 0) { it.getArgument<(List) -> Unit>(0)(listOf(alert)) - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - viewModel.deleteAlert(alertNullID) - - assert(!viewModel.alerts.value!!.isEmpty()) - assertEquals(listOf(alert), viewModel.alerts.value) - } - - @Test - fun deleteAlertDeleteFailure() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer{ - it.getArgument<(Exception) -> Unit>(2)(Exception("deleteAlertFailure")) - }.`when`(alertModelSupabase) - .deleteAlertById( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - assert(viewModel.alerts.value!!.isNotEmpty()) - assertEquals(listOf(alert), viewModel.alerts.value) - - viewModel.deleteAlert(alert) - assert(viewModel.alerts.value!!.isNotEmpty()) - assertEquals(listOf(alert), viewModel.alerts.value) - } -} \ No newline at end of file + } else { + it.getArgument<(List) -> Unit>(0)(listOf()) + } + calls++ + } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .deleteAlertById(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.deleteAlert(alert) + assert(viewModel.alerts.value!!.isEmpty()) + } + + @Test + fun deleteAlertNullIdFailure() = runBlocking { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(List) -> Unit>(0)(listOf(alert)) } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + viewModel.deleteAlert(alertNullID) + + assert(!viewModel.alerts.value!!.isEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + } + + @Test + fun deleteAlertDeleteFailure() = runBlocking { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(List) -> Unit>(0)(listOf(alert)) } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("deleteAlertFailure")) } + .`when`(alertModelSupabase) + .deleteAlertById(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.deleteAlert(alert) + assert(viewModel.alerts.value!!.isNotEmpty()) + assertEquals(listOf(alert), viewModel.alerts.value) + } +} From 7b16513dcd8c3db849d70df5ba4d14d30c3db4e3 Mon Sep 17 00:00:00 2001 From: Alonso Date: Tue, 19 Nov 2024 22:45:15 +0100 Subject: [PATCH 14/19] test: add tests for `updateAlert`, `getAlert` & `getAlertsByUser` --- .../model/alert/AlertViewModelTest.kt | 167 +++++++++++++++++- 1 file changed, 162 insertions(+), 5 deletions(-) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index 67cc02fd4..bd6e8b9d8 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -1,10 +1,12 @@ package com.android.periodpals.model.alert import com.android.periodpals.MainCoroutineRule +import io.github.jan.supabase.postgrest.query.filter.PostgrestFilterBuilder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.datetime.LocalDateTime import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Rule import org.junit.Test @@ -21,20 +23,27 @@ class AlertViewModelTest { @ExperimentalCoroutinesApi @get:Rule var mainCoroutineRule = MainCoroutineRule() companion object { - const val id = "idAlert" - const val uid = "mock_uid" + const val ID = "idAlert" + const val UID = "mock_uid" val name = "test_name" + val name_update = "test_update_name" val product = Product.PAD + val product_update = Product.TAMPON val urgency = Urgency.LOW + val urgency_update = Urgency.MEDIUM val createdAt = LocalDateTime(2022, 1, 1, 0, 0).toString() + val createdAt_update = LocalDateTime(2023, 2, 2, 1, 1).toString() val location = "test_location" + val location_update = "test_update_location" val message = "test_message" + val message_update = "test_update_message" val status = Status.CREATED + val status_update = Status.PENDING val alert = Alert( - id = id, - uid = uid, + id = ID, + uid = UID, name = name, product = product, urgency = urgency, @@ -45,7 +54,7 @@ class AlertViewModelTest { val alertNullID = Alert( id = null, - uid = uid, + uid = UID, name = name, product = product, urgency = urgency, @@ -53,6 +62,17 @@ class AlertViewModelTest { location = location, message = message, status = status) + val alertUpdated = + Alert( + id = ID, + uid = UID, + name = name_update, + product = product_update, + urgency = urgency_update, + createdAt = createdAt_update, + location = location_update, + message = message_update, + status = status_update) } @Before @@ -178,4 +198,141 @@ class AlertViewModelTest { assert(viewModel.alerts.value!!.isNotEmpty()) assertEquals(listOf(alert), viewModel.alerts.value) } + + @Test + fun getAlertSuccess() = runBlocking { + doAnswer{ + assertEquals(ID, it.getArgument(0)) + it.getArgument<(Alert) -> Unit>(1)(alert) + }.`when`(alertModelSupabase) + .getAlert( + any(), + any<(Alert) -> Unit>(), + any<(Exception) -> Unit>() + ) + + val result = viewModel.getAlert(ID) + assertEquals(alert, result) + } + + @Test + fun getAlertGetAlertFailure() = runBlocking { + doAnswer { + assertEquals(ID, it.getArgument(0)) + it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase Fails :(")) + }.`when`(alertModelSupabase) + .getAlert( + any(), + any<(Alert) -> Unit>(), + any<(Exception) -> Unit>() + ) + + val result = viewModel.getAlert(ID) + assertNull(result) + } + + @Test + fun getAlertsByUserSuccess() = runBlocking { + doAnswer { + it.getArgument<(List) -> Unit>(1)(listOf(alert)) + }.`when`(alertModelSupabase) + .getAlertsFilteredBy( + any Unit>(), + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + val result = viewModel.getAlertsByUser(UID) + assertEquals(listOf(alert), result) + } + + @Test + fun getAlertByUserFailure() = runBlocking { + doAnswer { + it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase Fails :(")) + }.`when`(alertModelSupabase) + .getAlertsFilteredBy( + any Unit>(), + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + val result = viewModel.getAlertsByUser(UID) + assertNull(result) + } + + @Test + fun updateAlertSuccess() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .updateAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + var count = 0 + doAnswer { + if (count < 1) { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + } else { + it.getArgument<(List) -> Unit>(0)(listOf(alertUpdated)) + } + count ++ + } + .`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.updateAlert(alertUpdated) + assertEquals(listOf(alertUpdated), viewModel.alerts.value) + } + + @Test + fun updateAlertFailure() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + + doAnswer { + it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase fail")) + }.`when`(alertModelSupabase) + .updateAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.updateAlert(alertUpdated) + assertEquals(listOf(alert), viewModel.alerts.value) + } } From 2161cbf5aee4556f9362167ecbdac71506a7b48e Mon Sep 17 00:00:00 2001 From: Alonso Date: Tue, 19 Nov 2024 22:46:04 +0100 Subject: [PATCH 15/19] style: ktfmt format --- .../model/alert/AlertViewModelTest.kt | 197 +++++++----------- 1 file changed, 80 insertions(+), 117 deletions(-) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index bd6e8b9d8..397ca14c5 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -62,17 +62,17 @@ class AlertViewModelTest { location = location, message = message, status = status) - val alertUpdated = - Alert( - id = ID, - uid = UID, - name = name_update, - product = product_update, - urgency = urgency_update, - createdAt = createdAt_update, - location = location_update, - message = message_update, - status = status_update) + val alertUpdated = + Alert( + id = ID, + uid = UID, + name = name_update, + product = product_update, + urgency = urgency_update, + createdAt = createdAt_update, + location = location_update, + message = message_update, + status = status_update) } @Before @@ -201,138 +201,101 @@ class AlertViewModelTest { @Test fun getAlertSuccess() = runBlocking { - doAnswer{ + doAnswer { assertEquals(ID, it.getArgument(0)) it.getArgument<(Alert) -> Unit>(1)(alert) - }.`when`(alertModelSupabase) - .getAlert( - any(), - any<(Alert) -> Unit>(), - any<(Exception) -> Unit>() - ) - - val result = viewModel.getAlert(ID) - assertEquals(alert, result) + } + .`when`(alertModelSupabase) + .getAlert(any(), any<(Alert) -> Unit>(), any<(Exception) -> Unit>()) + + val result = viewModel.getAlert(ID) + assertEquals(alert, result) } @Test fun getAlertGetAlertFailure() = runBlocking { - doAnswer { + doAnswer { assertEquals(ID, it.getArgument(0)) it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase Fails :(")) - }.`when`(alertModelSupabase) - .getAlert( - any(), - any<(Alert) -> Unit>(), - any<(Exception) -> Unit>() - ) - - val result = viewModel.getAlert(ID) - assertNull(result) + } + .`when`(alertModelSupabase) + .getAlert(any(), any<(Alert) -> Unit>(), any<(Exception) -> Unit>()) + + val result = viewModel.getAlert(ID) + assertNull(result) } @Test fun getAlertsByUserSuccess() = runBlocking { - doAnswer { - it.getArgument<(List) -> Unit>(1)(listOf(alert)) - }.`when`(alertModelSupabase) - .getAlertsFilteredBy( - any Unit>(), - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - val result = viewModel.getAlertsByUser(UID) - assertEquals(listOf(alert), result) + doAnswer { it.getArgument<(List) -> Unit>(1)(listOf(alert)) } + .`when`(alertModelSupabase) + .getAlertsFilteredBy( + any Unit>(), + any<(List) -> Unit>(), + any<(Exception) -> Unit>()) + + val result = viewModel.getAlertsByUser(UID) + assertEquals(listOf(alert), result) } @Test fun getAlertByUserFailure() = runBlocking { - doAnswer { - it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase Fails :(")) - }.`when`(alertModelSupabase) - .getAlertsFilteredBy( - any Unit>(), - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - val result = viewModel.getAlertsByUser(UID) - assertNull(result) + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase Fails :(")) } + .`when`(alertModelSupabase) + .getAlertsFilteredBy( + any Unit>(), + any<(List) -> Unit>(), + any<(Exception) -> Unit>()) + + val result = viewModel.getAlertsByUser(UID) + assertNull(result) } @Test fun updateAlertSuccess() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .updateAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - var count = 0 - doAnswer { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .updateAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + var count = 0 + doAnswer { if (count < 1) { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) + it.getArgument<(List) -> Unit>(0)(listOf(alert)) } else { - it.getArgument<(List) -> Unit>(0)(listOf(alertUpdated)) + it.getArgument<(List) -> Unit>(0)(listOf(alertUpdated)) } - count ++ - } - .`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - assertEquals(listOf(alert), viewModel.alerts.value) - - viewModel.updateAlert(alertUpdated) - assertEquals(listOf(alertUpdated), viewModel.alerts.value) + count++ + } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.updateAlert(alertUpdated) + assertEquals(listOf(alertUpdated), viewModel.alerts.value) } @Test fun updateAlertFailure() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - - doAnswer { - it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase fail")) - }.`when`(alertModelSupabase) - .updateAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - - viewModel.createAlert(alert) - assertEquals(listOf(alert), viewModel.alerts.value) - - viewModel.updateAlert(alertUpdated) - assertEquals(listOf(alert), viewModel.alerts.value) + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(List) -> Unit>(0)(listOf(alert)) } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + + doAnswer { it.getArgument<(Exception) -> Unit>(2)(Exception("Supabase fail")) } + .`when`(alertModelSupabase) + .updateAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + + viewModel.updateAlert(alertUpdated) + assertEquals(listOf(alert), viewModel.alerts.value) } } From 292eba075b6033dd45b87078f2ffdd0ed0c7ba69 Mon Sep 17 00:00:00 2001 From: Alonso Date: Wed, 20 Nov 2024 22:32:52 +0100 Subject: [PATCH 16/19] feat: add `getPalAlerts` to get non-user alerts --- .../com/android/periodpals/model/alert/AlertViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt index 7a4318313..6ad88bcca 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt @@ -2,6 +2,7 @@ package com.android.periodpals.model.alert import android.util.Log import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -77,6 +78,11 @@ class AlertViewModel(private val alertModelSupabase: AlertModelSupabase) : ViewM return alertsList } + fun getPalAlerts(uid: String): List? { + val palAlertList: List? = getAllAlerts() + return palAlertList?.filter { it.uid != uid } + } + /** * Retrieves alerts for a specific user by their UID. * From 4163c78cb0621a7f924091589251e3a11d0de0c4 Mon Sep 17 00:00:00 2001 From: Alonso Date: Wed, 20 Nov 2024 22:33:09 +0100 Subject: [PATCH 17/19] test: add tests for `getPalAlerts` --- .../model/alert/AlertViewModelTest.kt | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index 397ca14c5..6464c71d5 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.datetime.LocalDateTime import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Rule @@ -24,7 +25,9 @@ class AlertViewModelTest { companion object { const val ID = "idAlert" + const val ID2 = "idAlert2" const val UID = "mock_uid" + const val UID2 = "mock_uid2" val name = "test_name" val name_update = "test_update_name" val product = Product.PAD @@ -73,6 +76,17 @@ class AlertViewModelTest { location = location_update, message = message_update, status = status_update) + val alertOther = + Alert( + id = ID2, + uid = UID2, + name = name, + product = product, + urgency = urgency, + createdAt = createdAt, + location = location, + message = message, + status = status) } @Before @@ -298,4 +312,75 @@ class AlertViewModelTest { viewModel.updateAlert(alertUpdated) assertEquals(listOf(alert), viewModel.alerts.value) } + + @Test + fun getPalAlertsSuccess() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + var count = 0 + doAnswer { + if (count == 0) { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + } else { + it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) + } + count++ + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + assertEquals(listOf(), viewModel.alerts.value) + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + viewModel.createAlert(alertOther) + assertEquals(listOf(alert, alertOther), viewModel.alerts.value) + + val result: List? = viewModel.getPalAlerts(alert.uid) + + assertNotNull(result) + assert(result!!.isNotEmpty()) + assertEquals(listOf(alertOther), result) + } + + @Test + fun getPalAlertsFailure() = runBlocking { + doAnswer { + it.getArgument<() -> Unit>(1)() + }.`when`(alertModelSupabase) + .addAlert( + any(), + any<() -> Unit>(), + any<(Exception) -> Unit>() + ) + var count = 0 + doAnswer { + if (count == 0) { + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + } else if (count == 1){ + it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) + } else { + it.getArgument<(Exception) -> Unit>(1)(Exception("Supabase fail :(")) + } + count++ + }.`when`(alertModelSupabase) + .getAllAlerts( + any<(List) -> Unit>(), + any<(Exception) -> Unit>() + ) + assertEquals(listOf(), viewModel.alerts.value) + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + viewModel.createAlert(alertOther) + assertEquals(listOf(alert, alertOther), viewModel.alerts.value) + + val result: List? = viewModel.getPalAlerts(alert.uid) + assertNull(result) + } } From 292c8b83b00a7265ef308bb4654c82a5d435bc8f Mon Sep 17 00:00:00 2001 From: Alonso Date: Wed, 20 Nov 2024 22:33:59 +0100 Subject: [PATCH 18/19] style: ktfmt format --- .../com/android/periodpals/model/alert/AlertViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt index 6ad88bcca..6076a3b0f 100644 --- a/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt +++ b/app/src/main/java/com/android/periodpals/model/alert/AlertViewModel.kt @@ -2,7 +2,6 @@ package com.android.periodpals.model.alert import android.util.Log import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -79,8 +78,8 @@ class AlertViewModel(private val alertModelSupabase: AlertModelSupabase) : ViewM } fun getPalAlerts(uid: String): List? { - val palAlertList: List? = getAllAlerts() - return palAlertList?.filter { it.uid != uid } + val palAlertList: List? = getAllAlerts() + return palAlertList?.filter { it.uid != uid } } /** From e5ca8f0c798c4d944f0563ab6fc2a879eac5cc3a Mon Sep 17 00:00:00 2001 From: Alonso Date: Wed, 20 Nov 2024 22:34:08 +0100 Subject: [PATCH 19/19] style: ktfmt format --- .../model/alert/AlertViewModelTest.kt | 96 ++++++++----------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt index 6464c71d5..d6571becc 100644 --- a/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt +++ b/app/src/test/java/com/android/periodpals/model/alert/AlertViewModelTest.kt @@ -315,72 +315,58 @@ class AlertViewModelTest { @Test fun getPalAlertsSuccess() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - var count = 0 - doAnswer { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + var count = 0 + doAnswer { if (count == 0) { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) + it.getArgument<(List) -> Unit>(0)(listOf(alert)) } else { - it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) + it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) } count++ - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - assertEquals(listOf(), viewModel.alerts.value) - viewModel.createAlert(alert) - assertEquals(listOf(alert), viewModel.alerts.value) - viewModel.createAlert(alertOther) - assertEquals(listOf(alert, alertOther), viewModel.alerts.value) - - val result: List? = viewModel.getPalAlerts(alert.uid) - - assertNotNull(result) - assert(result!!.isNotEmpty()) - assertEquals(listOf(alertOther), result) + } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + assertEquals(listOf(), viewModel.alerts.value) + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + viewModel.createAlert(alertOther) + assertEquals(listOf(alert, alertOther), viewModel.alerts.value) + + val result: List? = viewModel.getPalAlerts(alert.uid) + + assertNotNull(result) + assert(result!!.isNotEmpty()) + assertEquals(listOf(alertOther), result) } @Test fun getPalAlertsFailure() = runBlocking { - doAnswer { - it.getArgument<() -> Unit>(1)() - }.`when`(alertModelSupabase) - .addAlert( - any(), - any<() -> Unit>(), - any<(Exception) -> Unit>() - ) - var count = 0 - doAnswer { + doAnswer { it.getArgument<() -> Unit>(1)() } + .`when`(alertModelSupabase) + .addAlert(any(), any<() -> Unit>(), any<(Exception) -> Unit>()) + var count = 0 + doAnswer { if (count == 0) { - it.getArgument<(List) -> Unit>(0)(listOf(alert)) - } else if (count == 1){ - it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) + it.getArgument<(List) -> Unit>(0)(listOf(alert)) + } else if (count == 1) { + it.getArgument<(List) -> Unit>(0)(listOf(alert, alertOther)) } else { - it.getArgument<(Exception) -> Unit>(1)(Exception("Supabase fail :(")) + it.getArgument<(Exception) -> Unit>(1)(Exception("Supabase fail :(")) } count++ - }.`when`(alertModelSupabase) - .getAllAlerts( - any<(List) -> Unit>(), - any<(Exception) -> Unit>() - ) - assertEquals(listOf(), viewModel.alerts.value) - viewModel.createAlert(alert) - assertEquals(listOf(alert), viewModel.alerts.value) - viewModel.createAlert(alertOther) - assertEquals(listOf(alert, alertOther), viewModel.alerts.value) - - val result: List? = viewModel.getPalAlerts(alert.uid) - assertNull(result) + } + .`when`(alertModelSupabase) + .getAllAlerts(any<(List) -> Unit>(), any<(Exception) -> Unit>()) + assertEquals(listOf(), viewModel.alerts.value) + viewModel.createAlert(alert) + assertEquals(listOf(alert), viewModel.alerts.value) + viewModel.createAlert(alertOther) + assertEquals(listOf(alert, alertOther), viewModel.alerts.value) + + val result: List? = viewModel.getPalAlerts(alert.uid) + assertNull(result) } }