Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add image data deletion in account deletion #237

Merged
merged 4 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.sample.R
import com.android.streetworkapp.model.event.EventRepository
import com.android.streetworkapp.model.event.EventViewModel
import com.android.streetworkapp.model.image.ImageRepository
import com.android.streetworkapp.model.image.ImageViewModel
import com.android.streetworkapp.model.park.ParkRepository
import com.android.streetworkapp.model.park.ParkViewModel
import com.android.streetworkapp.model.preferences.PreferencesRepository
Expand All @@ -30,13 +32,15 @@ import com.android.streetworkapp.ui.navigation.Route
import com.android.streetworkapp.utils.GoogleAuthService
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.RETURNS_DEFAULTS
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever

@RunWith(AndroidJUnit4::class)
Expand All @@ -56,6 +60,8 @@ class SettingsTest {
private val workoutRepository = mock(WorkoutRepository::class.java)
private val workoutViewModel = WorkoutViewModel(workoutRepository)
private val preferencesViewModel = PreferencesViewModel(mock(PreferencesRepository::class.java))
private val imageRepository = mock(ImageRepository::class.java)
private val imageViewModel = ImageViewModel(imageRepository)

@Test
fun isSettingsContentDisplayedForNullUser() {
Expand All @@ -75,6 +81,7 @@ class SettingsTest {
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingDialog)
context = LocalContext.current
Expand Down Expand Up @@ -121,6 +128,7 @@ class SettingsTest {
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingDialog)
context = LocalContext.current
Expand Down Expand Up @@ -161,6 +169,7 @@ class SettingsTest {
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingDialog)
context = LocalContext.current
Expand All @@ -175,14 +184,15 @@ class SettingsTest {
}

@Test
fun testDeleteAccountButtonNavigatesToAuthScreen() {
fun testDeleteAccountButtonNavigatesToAuthScreen() = runTest {
val showSettingDialog = mutableStateOf(false)
var context: Context? = null
val alice = User("uid-alice", "Alice", "alice@gmail.com", 42, emptyList(), "")
userViewModel.setCurrentUser(alice)
val firebaseAuth = mock(FirebaseAuth::class.java, RETURNS_DEFAULTS)
val currentUser = mock(FirebaseUser::class.java)
whenever(firebaseAuth.currentUser).thenReturn(currentUser)
whenever(imageRepository.deleteAllDataFromUser(any())).thenReturn(true)

composeTestRule.setContent {
val authService =
Expand All @@ -197,6 +207,7 @@ class SettingsTest {
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingDialog)
context = LocalContext.current
Expand Down Expand Up @@ -235,6 +246,7 @@ class SettingsTest {
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingDialog)
context = LocalContext.current
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ fun StreetWorkApp(
progressionViewModel,
workoutViewModel,
preferencesViewModel,
imageViewModel,
authService,
showSettingsDialog)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ class ImageRepositoryFirestore(
}

/**
* Deletes all the images related to a user
* Deletes all the images related to a user, this includes his uploaded images and ratings.
*
* @param userId The user id to whom we delete all the data from. (pictures uploaded and ratings)
* @param userId The user id to whom we delete all the data from.
*/
override suspend fun deleteAllDataFromUser(userId: String): Boolean {
require(userId.isNotEmpty()) { "Empty userId." }
Expand All @@ -270,21 +270,12 @@ class ImageRepositoryFirestore(
for (document in querySnapshot.documents) {
val collection = document.toObject(ParkImageCollection::class.java)
collection?.let {
val (imagesUploadedByUser, imagesNotUploadedByUser) =
collection.images.partition { it.userId == userId }
if (imagesUploadedByUser.isNotEmpty())
document.reference
.update("images", FieldValue.arrayRemove(imagesUploadedByUser))
.await()

imagesUploadedByUser.forEach {
val imageKey = this.storageClient.extractKeyFromUrl(it.imageUrl) ?: return false
if (!this.storageClient.deleteObjectFromKey(imageKey)) return false
}

// remove any potential votes the user has done
for (image in imagesNotUploadedByUser) {
if (image.rating.positiveVotesUids.contains(userId) ||
for (image in collection.images) {
if (image.userId == userId) {
document.reference.update("images", FieldValue.arrayRemove(image)).await()
val imageKey = storageClient.extractKeyFromUrl(image.imageUrl) ?: return false
if (!this.storageClient.deleteObjectFromKey(imageKey)) return false
} else if (image.rating.positiveVotesUids.contains(userId) ||
SaturneV marked this conversation as resolved.
Show resolved Hide resolved
image.rating.negativeVotesUids.contains(userId)) {
if (!this.retractImageVote(collection.id, image.imageUrl, userId)) {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import com.android.sample.R
import com.android.streetworkapp.model.event.EventViewModel
import com.android.streetworkapp.model.image.ImageViewModel
import com.android.streetworkapp.model.park.ParkViewModel
import com.android.streetworkapp.model.preferences.PreferencesViewModel
import com.android.streetworkapp.model.progression.ProgressionViewModel
Expand All @@ -45,6 +46,7 @@ fun SettingsContent(
progressionViewModel: ProgressionViewModel,
workoutViewModel: WorkoutViewModel,
preferencesViewModel: PreferencesViewModel,
imageViewModel: ImageViewModel,
authService: GoogleAuthService,
showParentDialog: MutableState<Boolean>
) {
Expand Down Expand Up @@ -120,7 +122,8 @@ fun SettingsContent(
parkViewModel,
eventViewModel,
progressionViewModel,
workoutViewModel)
workoutViewModel,
imageViewModel)

if (deletionSucceed) {
logout(authService, userViewModel, preferencesViewModel)
Expand Down Expand Up @@ -181,7 +184,8 @@ fun deleteAccount(
parkViewModel: ParkViewModel,
eventViewModel: EventViewModel,
progressionViewModel: ProgressionViewModel,
workoutViewModel: WorkoutViewModel
workoutViewModel: WorkoutViewModel,
imageViewModel: ImageViewModel
): Boolean {
// Get the current UID and abort if it is empty
val currentUserUid = userViewModel.currentUser.value?.uid ?: ""
Expand Down Expand Up @@ -211,6 +215,7 @@ fun deleteAccount(
parkViewModel.deleteRatingFromAllParks(currentUserUid)
progressionViewModel.deleteProgressionByUid(currentUserUid)
workoutViewModel.deleteWorkoutDataByUid(currentUserUid)
imageViewModel.deleteAllDataFromUser(currentUserUid, {}, {})

return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@ class ImageRepositoryFirestoreTest {
val elementsField =
capturedValue.javaClass.getDeclaredField("elements").apply { isAccessible = true }
elementsField.isAccessible = true
val elements = elementsField.get(capturedValue) as List<List<ParkImage>>
assert(elements[0][0] == imageToBeDeleted)
val elements = elementsField.get(capturedValue) as List<ParkImage>
assert(elements[0] == imageToBeDeleted)

verify(storageClient).deleteObjectFromKey(imageToDeleteKey)
verify(spyImageRepositoryFirestore)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.android.streetworkapp.ui.profile

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.streetworkapp.model.event.EventViewModel
import com.android.streetworkapp.model.image.ImageViewModel
import com.android.streetworkapp.model.park.ParkViewModel
import com.android.streetworkapp.model.progression.ProgressionViewModel
import com.android.streetworkapp.model.user.User
Expand Down Expand Up @@ -30,6 +31,7 @@ class SettingsTest {
private lateinit var eventViewModel: EventViewModel
private lateinit var progressionViewModel: ProgressionViewModel
private lateinit var workoutViewModel: WorkoutViewModel
private lateinit var imageViewModel: ImageViewModel
private lateinit var currentUser: User

@Before
Expand All @@ -40,6 +42,7 @@ class SettingsTest {
eventViewModel = mock(EventViewModel::class.java)
progressionViewModel = mock(ProgressionViewModel::class.java)
workoutViewModel = mock(WorkoutViewModel::class.java)
imageViewModel = mock(ImageViewModel::class.java)
currentUser = User("uid-alice", "Alice", "alice@gmail.com", 42, emptyList(), "")

whenever(userViewModel.currentUser).thenReturn(MutableStateFlow(currentUser))
Expand All @@ -56,7 +59,8 @@ class SettingsTest {
parkViewModel,
eventViewModel,
progressionViewModel,
workoutViewModel)
workoutViewModel,
imageViewModel)

assertTrue(result)
verify(authService).deleteAuthUser()
Expand All @@ -76,7 +80,8 @@ class SettingsTest {
parkViewModel,
eventViewModel,
progressionViewModel,
workoutViewModel)
workoutViewModel,
imageViewModel)

assertFalse(result)
verify(authService, never()).deleteAuthUser()
Expand All @@ -98,7 +103,8 @@ class SettingsTest {
parkViewModel,
eventViewModel,
progressionViewModel,
workoutViewModel)
workoutViewModel,
imageViewModel)

assertFalse(result)
verify(authService, never()).deleteAuthUser()
Expand Down
Loading