diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ada7858551..808857c754b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ ownCloud admins and users. ## Summary * Change - Replace auto-uploads with automatic uploads: [#4252](https://github.com/owncloud/android/issues/4252) +* Enhancement - Unit tests for repository classes - Part 2: [#4233](https://github.com/owncloud/android/issues/4233) * Enhancement - Add status message when (un)setting av. offline from preview: [#4382](https://github.com/owncloud/android/issues/4382) * Enhancement - Quota improvements from GraphAPI: [#4411](https://github.com/owncloud/android/issues/4411) * Enhancement - Upgraded AGP version to 8.7.2: [#4478](https://github.com/owncloud/android/issues/4478) @@ -51,6 +52,13 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4252 https://github.com/owncloud/android/pull/4492 +* Enhancement - Unit tests for repository classes - Part 2: [#4233](https://github.com/owncloud/android/issues/4233) + + Unit tests for OCFileRepository class have been completed. + + https://github.com/owncloud/android/issues/4233 + https://github.com/owncloud/android/pull/4389 + * Enhancement - Add status message when (un)setting av. offline from preview: [#4382](https://github.com/owncloud/android/issues/4382) A message has been added in all previews when the (un)setting av. offline diff --git a/changelog/unreleased/4389 b/changelog/unreleased/4389 new file mode 100644 index 00000000000..1a9bb42118a --- /dev/null +++ b/changelog/unreleased/4389 @@ -0,0 +1,6 @@ +Enhancement: Unit tests for repository classes - Part 2 + +Unit tests for OCFileRepository class have been completed. + +https://github.com/owncloud/android/issues/4233 +https://github.com/owncloud/android/pull/4389 diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index c161b5cb462..0ada7453f64 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -157,12 +157,12 @@ class OCFileRepository( override fun getFileById(fileId: Long): OCFile? = localFileDataSource.getFileById(fileId) - override fun getFileWithSyncInfoByIdAsFlow(fileId: Long): Flow = - localFileDataSource.getFileWithSyncInfoByIdAsFlow(fileId) - override fun getFileByIdAsFlow(fileId: Long): Flow = localFileDataSource.getFileByIdAsFlow(fileId) + override fun getFileWithSyncInfoByIdAsFlow(fileId: Long): Flow = + localFileDataSource.getFileWithSyncInfoByIdAsFlow(fileId) + override fun getFileByRemotePath(remotePath: String, owner: String, spaceId: String?): OCFile? = localFileDataSource.getFileByRemotePath(remotePath, owner, spaceId) @@ -328,46 +328,6 @@ class OCFileRepository( return filesNeedsAction } - private fun getFinalRemotePath( - replace: List, - expectedRemotePath: String, - targetFolder: OCFile, - targetSpaceWebDavUrl: String?, - filesNeedsAction: MutableList, - ocFile: OCFile, - position: Int, - isUserLogged: Boolean, - ) = - if (replace.isEmpty()) { - val pathExists = remoteFileDataSource.checkPathExistence( - path = expectedRemotePath, - isUserLogged = isUserLogged, - accountName = targetFolder.owner, - spaceWebDavUrl = targetSpaceWebDavUrl, - ) - if (pathExists) { - filesNeedsAction.add(ocFile) - null - } else { - if (ocFile.isFolder) expectedRemotePath.plus(File.separator) else expectedRemotePath - } - } else { - if (replace[position] == true) { - if (ocFile.isFolder) expectedRemotePath.plus(File.separator) else expectedRemotePath - } else if (replace[position] == false) { - remoteFileDataSource.getAvailableRemotePath( - remotePath = expectedRemotePath, - accountName = targetFolder.owner, - spaceWebDavUrl = targetSpaceWebDavUrl, - isUserLogged = isUserLogged, - ).let { - if (ocFile.isFolder) it.plus(File.separator) else it - } - } else { - null - } - } - override fun readFile(remotePath: String, accountName: String, spaceId: String?): OCFile { val spaceWebDavUrl = localSpacesDataSource.getWebDavUrlForSpace(spaceId, accountName) @@ -571,10 +531,6 @@ class OCFileRepository( localFileDataSource.updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory, newDirectory) } - override fun saveUploadWorkerUuid(fileId: Long, workerUuid: UUID) { - TODO("Not yet implemented") - } - override fun saveDownloadWorkerUuid(fileId: Long, workerUuid: UUID) { localFileDataSource.saveDownloadWorkerUuid(fileId, workerUuid) } @@ -583,6 +539,46 @@ class OCFileRepository( localFileDataSource.cleanWorkersUuid(fileId) } + private fun getFinalRemotePath( + replace: List, + expectedRemotePath: String, + targetFolder: OCFile, + targetSpaceWebDavUrl: String?, + filesNeedsAction: MutableList, + ocFile: OCFile, + position: Int, + isUserLogged: Boolean, + ) = + if (replace.isEmpty()) { + val pathExists = remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = isUserLogged, + accountName = targetFolder.owner, + spaceWebDavUrl = targetSpaceWebDavUrl, + ) + if (pathExists) { + filesNeedsAction.add(ocFile) + null + } else { + if (ocFile.isFolder) expectedRemotePath.plus(File.separator) else expectedRemotePath + } + } else { + if (replace[position] == true) { + if (ocFile.isFolder) expectedRemotePath.plus(File.separator) else expectedRemotePath + } else if (replace[position] == false) { + remoteFileDataSource.getAvailableRemotePath( + remotePath = expectedRemotePath, + accountName = targetFolder.owner, + spaceWebDavUrl = targetSpaceWebDavUrl, + isUserLogged = isUserLogged, + ).let { + if (ocFile.isFolder) it.plus(File.separator) else it + } + } else { + null + } + } + private fun deleteLocalFolderRecursively(ocFile: OCFile, onlyFromLocalStorage: Boolean) { val folderContent = localFileDataSource.getFolderContent(ocFile.id!!) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt index eb3fffd4343..f63a53c41aa 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/files/repository/OCFileRepositoryTest.kt @@ -3,6 +3,7 @@ * * @author Abel García de Prada * @author Aitor Ballesteros Pavón + * @author Juan Carlos Garrote Gascón * * Copyright (C) 2024 ownCloud GmbH. * @@ -25,382 +26,2004 @@ import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.data.providers.LocalStorageProvider import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.exceptions.ConflictException +import com.owncloud.android.domain.exceptions.FileAlreadyExistsException import com.owncloud.android.domain.exceptions.FileNotFoundException -import com.owncloud.android.domain.exceptions.NoConnectionWithServerException +import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.domain.files.model.MIME_DIR import com.owncloud.android.domain.files.model.OCFile -import com.owncloud.android.testutil.OC_ACCOUNT_NAME +import com.owncloud.android.domain.files.model.OCFile.Companion.ROOT_PATH +import com.owncloud.android.testutil.OC_AVAILABLE_OFFLINE_FILES import com.owncloud.android.testutil.OC_FILE +import com.owncloud.android.testutil.OC_FILE_AVAILABLE_OFFLINE +import com.owncloud.android.testutil.OC_FILE_DOWNLOADED +import com.owncloud.android.testutil.OC_FILE_WITH_SPACE_ID +import com.owncloud.android.testutil.OC_FILE_WITH_SYNC_INFO import com.owncloud.android.testutil.OC_FILE_WITH_SYNC_INFO_AND_SPACE -import com.owncloud.android.testutil.OC_FOLDER +import com.owncloud.android.testutil.OC_FOLDER_WITH_SPACE_ID +import com.owncloud.android.testutil.OC_META_FILE +import com.owncloud.android.testutil.OC_META_FILE_ROOT_FOLDER +import com.owncloud.android.testutil.OC_PARENT_FOLDER_WITH_SPACE_ID +import com.owncloud.android.testutil.OC_ROOT_FOLDER +import com.owncloud.android.testutil.OC_SPACE_PERSONAL +import com.owncloud.android.testutil.OC_SPACE_SHARES import io.mockk.every import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertNull -import org.junit.Ignore +import org.junit.Assert.assertThrows +import org.junit.Before import org.junit.Test +import java.util.UUID -@Ignore("Ignore temporary, pretty dependant on implementation... Will be reworked when finished") @ExperimentalCoroutinesApi class OCFileRepositoryTest { - private val remoteFileDataSource = mockk(relaxed = true) - private val localFileDataSource = mockk(relaxed = true) - private val localSpacesDataSource = mockk(relaxed = true) - private val localStorageProvider = mockk() - private val ocFileRepository: OCFileRepository = - OCFileRepository(localFileDataSource, remoteFileDataSource, localSpacesDataSource, localStorageProvider) - - private val folderToFetch = OC_FOLDER - private val listOfFilesRetrieved = listOf( - folderToFetch, - OC_FOLDER.copy(remoteId = "one"), - OC_FOLDER.copy(remoteId = "two") - ) - private val listOfFileToRemove = listOf( - OC_FOLDER.copy(id = 1), - OC_FILE.copy(id = 2), - OC_FILE.copy(id = 3) + private val localFileDataSource = mockk(relaxUnitFun = true) + private val remoteFileDataSource = mockk(relaxUnitFun = true) + private val localSpacesDataSource = mockk(relaxUnitFun = true) + private val localStorageProvider = mockk(relaxUnitFun = true) + private val ocFileRepository = OCFileRepository( + localFileDataSource, + remoteFileDataSource, + localSpacesDataSource, + localStorageProvider ) + private val ocFileRepositorySpy = spyk(ocFileRepository) + + private val expectedRemotePath = OC_FOLDER_WITH_SPACE_ID.remotePath + OC_FILE_WITH_SPACE_ID.fileName + private val storagePath = "/local/storage/path/username@demo.owncloud.com/Folder/Photos/image2.jpt" + private val remoteId = "remoteId" + private val searchText = "image" + private val etagInConflict = "5efb0c13c688i" + private val fileWithConflict = OC_FILE_WITH_SPACE_ID.copy(etagInConflict = etagInConflict) + + @Before + fun setUp() { + val commonSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val commonAccountName = OC_FILE_WITH_SPACE_ID.owner + every { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = commonSpaceId, + accountName = commonAccountName + ) + } returns OC_SPACE_PERSONAL.root.webDavUrl + } - private val timeInMilliseconds = 3600000L + @Test + fun `createFolder creates a new folder and saves it`() { + // The result of this method is not used, so it can be anything + every { localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(any(), any()) } returns emptyList() + + ocFileRepository.createFolder(OC_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.createFolder( + remotePath = OC_FOLDER_WITH_SPACE_ID.remotePath, + createFullPath = false, + isChunksFolder = false, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + listOfFiles = withArg> { + assertEquals(1, it.size) + assertEquals(OC_FOLDER_WITH_SPACE_ID.remotePath, it[0].remotePath) + assertEquals(OC_PARENT_FOLDER_WITH_SPACE_ID.owner, it[0].owner) + assertEquals(0, it[0].length) + assertEquals(MIME_DIR, it[0].mimeType) + assertEquals(OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, it[0].spaceId) + assertEquals("CK", it[0].permissions) + } + ) + } + } @Test - fun `create folder - ok`() { - every { remoteFileDataSource.createFolder(OC_FOLDER.remotePath, false, false, OC_ACCOUNT_NAME, null) } returns Unit + fun `copyFile returns a list with the OCFile in conflict (the copied OCFile) when replace parameter is empty and expected path already exists`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns true + val filesNeedAction = ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) - ocFileRepository.createFolder(OC_FOLDER.remotePath, OC_FOLDER) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), filesNeedAction) + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } verify(exactly = 1) { - remoteFileDataSource.createFolder(any(), false, false, OC_ACCOUNT_NAME, null) - localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(any(), OC_FOLDER) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) } } - @Test(expected = NoConnectionWithServerException::class) - fun `create folder - ko - no connection exception`() { + @Test + fun `copyFile returns an empty list with no OCFiles in conflict when replace parameter is empty and expected path doesn't exist`() { every { - remoteFileDataSource.createFolder(OC_FOLDER.remotePath, false, false, OC_ACCOUNT_NAME, null) - } throws NoConnectionWithServerException() + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } returns remoteId + + val filesNeedAction = ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) - ocFileRepository.createFolder(OC_FOLDER.remotePath, OC_FOLDER) + assertEquals(emptyList(), filesNeedAction) + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } verify(exactly = 1) { - remoteFileDataSource.createFolder(any(), false, false, OC_ACCOUNT_NAME, null) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.copyFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = expectedRemotePath, + remoteId = remoteId, + replace = null + ) + } + } + + @Test + fun `copyFile returns an empty list with no OCFiles in conflict when replace parameter is true`() { + every { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = true + ) + } returns remoteId + + val filesNeedAction = ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(true), true) + + assertEquals(emptyList(), filesNeedAction) + + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) } - verify(exactly = 0) { - localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(any(), OC_FOLDER) + verify(exactly = 1) { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = true + ) + localFileDataSource.copyFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = expectedRemotePath, + remoteId = remoteId, + replace = true + ) } } @Test - fun `get file by id - ok`() { - every { localFileDataSource.getFileById(OC_FOLDER.id!!) } returns OC_FOLDER + fun `copyFile returns an empty list with no OCFiles in conflict when replace parameter is false`() { + val availableRemotePath = "$expectedRemotePath (1)" + every { + remoteFileDataSource.getAvailableRemotePath( + remotePath = expectedRemotePath, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + isUserLogged = true + ) + } returns availableRemotePath + every { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = availableRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } returns remoteId - val ocFile = ocFileRepository.getFileById(OC_FOLDER.id!!) + val filesNeedAction = ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(false), true) - assertEquals(OC_FOLDER, ocFile) + assertEquals(emptyList(), filesNeedAction) + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } verify(exactly = 1) { - localFileDataSource.getFileById(OC_FOLDER.id!!) + remoteFileDataSource.getAvailableRemotePath( + remotePath = expectedRemotePath, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + isUserLogged = true + ) + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = availableRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.copyFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = availableRemotePath, + remoteId = remoteId, + replace = false + ) } } @Test - fun `getFileWithSyncInfoByIdAsFlow returns OCFileWithSyncInfo`() = runTest { - every { localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) } returns flowOf(OC_FILE_WITH_SYNC_INFO_AND_SPACE) + fun `copyFile returns an empty list with no OCFiles in conflict when replace parameter is null`() { + val filesNeedAction = ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(null), true) + + assertEquals(emptyList(), filesNeedAction) + + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } + } - val ocFile = ocFileRepository.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + @Test + fun `copyFile removes target folder locally and throws a ConflictException when replace parameter is empty and expected path doesn't exist but target folder doesn't exist anymore`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } throws ConflictException() + every { + localFileDataSource.getFolderContent(OC_FOLDER_WITH_SPACE_ID.id!!) + } returns emptyList() - ocFile.collect { result -> - assertEquals(OC_FILE_WITH_SYNC_INFO_AND_SPACE, result) + assertThrows(ConflictException::class.java) { + ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) } + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } verify(exactly = 1) { - localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.getFolderContent(OC_FOLDER_WITH_SPACE_ID.id!!) + localStorageProvider.deleteLocalFolderIfItHasNoFilesInside(OC_FOLDER_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_FOLDER_WITH_SPACE_ID.id!!) } } @Test - fun `getFileWithSyncInfoByIdAsFlow returns null`() = runTest { - every { localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) } returns flowOf(null) + fun `copyFile removes source file locally and throws a FileNotFoundException when replace parameter is empty and expected path doesn't exist but source file doesn't exist anymore`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } throws FileNotFoundException() + every { + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + } returns true - val ocFile = ocFileRepository.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + assertThrows(FileNotFoundException::class.java) { + ocFileRepository.copyFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) + } - ocFile.collect { result -> - assertNull(result) + val sourceAndTargetSpaceId = OC_FILE_WITH_SPACE_ID.spaceId + val sourceAndTargetOwner = OC_FILE_WITH_SPACE_ID.owner + verify(exactly = 2) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = sourceAndTargetSpaceId, + accountName = sourceAndTargetOwner + ) + } + verify(exactly = 1) { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + remoteFileDataSource.copyFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + sourceSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + targetSpaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_FILE_WITH_SPACE_ID.id!!) } + } + + @Test + fun `getFileById returns a OCFile`() { + every { + localFileDataSource.getFileById(OC_FILE_WITH_SPACE_ID.id!!) + } returns OC_FILE_WITH_SPACE_ID + + val ocFile = ocFileRepository.getFileById(OC_FILE_WITH_SPACE_ID.id!!) + assertEquals(OC_FILE_WITH_SPACE_ID, ocFile) verify(exactly = 1) { - localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + localFileDataSource.getFileById(OC_FILE_WITH_SPACE_ID.id!!) } } - @Test(expected = Exception::class) - fun `getFileWithSyncInfoByIdAsFlow returns an exception`() = runTest { - every { localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) } throws Exception() + @Test + fun `getFileById returns null when local datasource returns a null file`() { + every { + localFileDataSource.getFileById(OC_FILE_WITH_SPACE_ID.id!!) + } returns null - ocFileRepository.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + val ocFile = ocFileRepository.getFileById(OC_FILE_WITH_SPACE_ID.id!!) + assertNull(ocFile) verify(exactly = 1) { - localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE.id!!) + localFileDataSource.getFileById(OC_FILE_WITH_SPACE_ID.id!!) } } @Test - fun `get file by id - ok - null`() { - every { localFileDataSource.getFileById(OC_FOLDER.id!!) } returns null + fun `getFileByIdAsFlow returns a Flow with an OCFile`() = runTest { + every { + localFileDataSource.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!) + } returns flowOf(OC_FILE_WITH_SPACE_ID) + + val ocFile = ocFileRepository.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!).first() + assertEquals(OC_FILE_WITH_SPACE_ID, ocFile) - val ocFile = ocFileRepository.getFileById(OC_FOLDER.id!!) + verify(exactly = 1) { + localFileDataSource.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `getFileByIdAsFlow returns a Flow with null when local datasource returns a Flow with null`() = runTest { + every { + localFileDataSource.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!) + } returns flowOf(null) + val ocFile = ocFileRepository.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!).first() assertNull(ocFile) verify(exactly = 1) { - localFileDataSource.getFileById(OC_FOLDER.id!!) + localFileDataSource.getFileByIdAsFlow(OC_FILE_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `getFileWithSyncInfoByIdAsFlow returns a Flow with an OCFileWithSyncInfo`() = runTest { + every { + localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!) + } returns flowOf(OC_FILE_WITH_SYNC_INFO_AND_SPACE) + + val ocFile = ocFileRepository.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!).first() + assertEquals(OC_FILE_WITH_SYNC_INFO_AND_SPACE, ocFile) + + verify(exactly = 1) { + localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!) } } - @Test(expected = Exception::class) - fun `get file by id - ko`() { - every { localFileDataSource.getFileById(OC_FOLDER.id!!) } throws Exception() + @Test + fun `getFileWithSyncInfoByIdAsFlow returns a Flow with null when local datasource returns a Flow with null`() = runTest { + every { + localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!) + } returns flowOf(null) - ocFileRepository.getFileById(OC_FOLDER.id!!) + val ocFile = ocFileRepository.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!).first() + assertNull(ocFile) verify(exactly = 1) { - localFileDataSource.getFileById(OC_FOLDER.id!!) + localFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_WITH_SYNC_INFO_AND_SPACE.file.id!!) } } @Test - fun `get file by remote path - ok`() { - every { localFileDataSource.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner, null) } returns OC_FOLDER + fun `getFileByRemotePath returns a OCFile`() { + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + owner = OC_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns OC_FILE_WITH_SPACE_ID - ocFileRepository.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner) + val ocFile = ocFileRepository.getFileByRemotePath(OC_FILE_WITH_SPACE_ID.remotePath, OC_FOLDER_WITH_SPACE_ID.owner, OC_FOLDER_WITH_SPACE_ID.spaceId) + assertEquals(OC_FILE_WITH_SPACE_ID, ocFile) verify(exactly = 1) { - localFileDataSource.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner, null) + localFileDataSource.getFileByRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + owner = OC_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) } } - @Test(expected = Exception::class) - fun `get file by remote path - ko`() { + @Test + fun `getFileByRemotePath returns null when local datasource returns a null file`() { every { - localFileDataSource.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner, null) - } throws Exception() + localFileDataSource.getFileByRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + owner = OC_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns null - ocFileRepository.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner) + val ocFile = ocFileRepository.getFileByRemotePath(OC_FILE_WITH_SPACE_ID.remotePath, OC_FOLDER_WITH_SPACE_ID.owner, OC_FOLDER_WITH_SPACE_ID.spaceId) + assertNull(ocFile) verify(exactly = 1) { - localFileDataSource.getFileByRemotePath(OC_FOLDER.remotePath, OC_FOLDER.owner, null) + localFileDataSource.getFileByRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + owner = OC_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) } } @Test - fun `getDownloadedFilesForAccount returns a list of OCFile`() { + fun `getFileFromRemoteId returns a OCFile when the remoteId belongs to a normal file`() { every { - localFileDataSource.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) + remoteFileDataSource.getMetaFile(OC_FILE.remoteId!!, OC_FILE.owner) + } returns OC_META_FILE + // The result of this method is not used, so it can be anything + every { + ocFileRepositorySpy.refreshFolder( + remotePath = "", + accountName = OC_FILE.owner, + spaceId = null + ) + } returns emptyList() + every { + ocFileRepositorySpy.refreshFolder( + remotePath = "/Photos", + accountName = OC_FILE.owner, + spaceId = null + ) } returns listOf(OC_FILE) + // The result of this method is not used, so it can be anything + every { + ocFileRepositorySpy.refreshFolder( + remotePath = OC_FILE.remotePath, + accountName = OC_FILE.owner, + spaceId = null + ) + } returns emptyList() + + val ocFile = ocFileRepositorySpy.getFileFromRemoteId(OC_FILE.remoteId!!, OC_FILE.owner) + assertEquals(OC_FILE, ocFile) + + verify(exactly = 1) { + remoteFileDataSource.getMetaFile(OC_FILE.remoteId!!, OC_FILE.owner) + ocFileRepositorySpy.refreshFolder( + remotePath = "", + accountName = OC_FILE.owner, + spaceId = null + ) + ocFileRepositorySpy.refreshFolder( + remotePath = "/Photos", + accountName = OC_FILE.owner, + spaceId = null + ) + ocFileRepositorySpy.refreshFolder( + remotePath = OC_FILE.remotePath, + accountName = OC_FILE.owner, + spaceId = null + ) + } + } - val result = ocFileRepository.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) + @Test + fun `getFileFromRemoteId returns root folder as OCFile when the remoteId belongs to a root folder`() { + every { + remoteFileDataSource.getMetaFile(OC_ROOT_FOLDER.remoteId!!, OC_ROOT_FOLDER.owner) + } returns OC_META_FILE_ROOT_FOLDER + // The result of this method is not used, so it can be anything + every { + ocFileRepositorySpy.refreshFolder( + remotePath = "", + accountName = OC_ROOT_FOLDER.owner, + spaceId = null + ) + } returns emptyList() + // The result of this method is not used, so it can be anything + every { + ocFileRepositorySpy.refreshFolder( + remotePath = OC_ROOT_FOLDER.remotePath, + accountName = OC_ROOT_FOLDER.owner, + spaceId = null + ) + } returns emptyList() + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_ROOT_FOLDER.remotePath, + owner = OC_ROOT_FOLDER.owner, + spaceId = null + ) + } returns OC_ROOT_FOLDER - assertEquals(listOf(OC_FILE), result) + val ocFile = ocFileRepositorySpy.getFileFromRemoteId(OC_ROOT_FOLDER.remoteId!!, OC_ROOT_FOLDER.owner) + assertEquals(OC_ROOT_FOLDER, ocFile) verify(exactly = 1) { - localFileDataSource.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) + remoteFileDataSource.getMetaFile(OC_ROOT_FOLDER.remoteId!!, OC_ROOT_FOLDER.owner) + ocFileRepositorySpy.refreshFolder( + remotePath = "", + accountName = OC_ROOT_FOLDER.owner, + spaceId = null + ) + ocFileRepositorySpy.refreshFolder( + remotePath = OC_ROOT_FOLDER.remotePath, + accountName = OC_ROOT_FOLDER.owner, + spaceId = null + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_ROOT_FOLDER.remotePath, + owner = OC_ROOT_FOLDER.owner, + spaceId = null + ) } } @Test - fun `get folder content - ok`() { - every { localFileDataSource.getFolderContent(OC_FOLDER.parentId!!) } returns listOf(OC_FOLDER) + fun `getPersonalRootFolderForAccount returns root folder as OCFile when personal space is not null`() { + every { + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + } returns OC_SPACE_PERSONAL + every { + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = OC_SPACE_PERSONAL.root.id + ) + } returns OC_ROOT_FOLDER - val folderContent = ocFileRepository.getFolderContent(OC_FOLDER.parentId!!) + val ocFolder = ocFileRepository.getPersonalRootFolderForAccount(OC_SPACE_PERSONAL.accountName) + assertEquals(OC_ROOT_FOLDER, ocFolder) - assertEquals(listOf(OC_FOLDER), folderContent) + verify(exactly = 1) { + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = OC_SPACE_PERSONAL.root.id + ) + } + } + + @Test + fun `getPersonalRootFolderForAccount returns root folder as OCFile when personal space is null`() { + every { + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + } returns null + every { + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = null + ) + } returns OC_ROOT_FOLDER + + val ocFolder = ocFileRepository.getPersonalRootFolderForAccount(OC_SPACE_PERSONAL.accountName) + assertEquals(OC_ROOT_FOLDER, ocFolder) verify(exactly = 1) { - localFileDataSource.getFolderContent(OC_FOLDER.parentId!!) + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = null + ) } } @Test - fun `getFilesLastUsageIsOlderThanGivenTime returns a list of OCFile`() { + fun `getPersonalRootFolderForAccount throws NullPointerException when local datasource returns a null root folder`() { every { - localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) - } returns listOf(OC_FILE) + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + } returns OC_SPACE_PERSONAL + every { + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = OC_SPACE_PERSONAL.root.id + ) + } returns null + + assertThrows(NullPointerException::class.java) { + ocFileRepository.getPersonalRootFolderForAccount(OC_SPACE_PERSONAL.accountName) + } - val result = ocFileRepository.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) + verify(exactly = 1) { + localSpacesDataSource.getPersonalSpaceForAccount(OC_SPACE_PERSONAL.accountName) + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_PERSONAL.accountName, + spaceId = OC_SPACE_PERSONAL.root.id + ) + } + } + + @Test + fun `getSharesRootFolderForAccount returns root folder as OCFile when shares space is not null`() { + every { + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + } returns OC_SPACE_SHARES + every { + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_SHARES.accountName, + spaceId = OC_SPACE_SHARES.root.id + ) + } returns OC_ROOT_FOLDER - assertEquals(listOf(OC_FILE), result) + val ocFolder = ocFileRepository.getSharesRootFolderForAccount(OC_SPACE_SHARES.accountName) + assertEquals(OC_ROOT_FOLDER, ocFolder) verify(exactly = 1) { - localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_SHARES.accountName, + spaceId = OC_SPACE_SHARES.root.id + ) } } @Test - fun `getFilesLastUsageIsOlderThanGivenTime returns an empty list when datasource returns an empty list`() { + fun `getSharesRootFolderForAccount returns null when shares space is null`() { every { - localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) - } returns emptyList() + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + } returns null + + val ocFolder = ocFileRepository.getSharesRootFolderForAccount(OC_SPACE_SHARES.accountName) + assertNull(ocFolder) + + verify(exactly = 1) { + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + } + } - val result = ocFileRepository.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) + @Test + fun `getSharesRootFolderForAccount throws NullPointerException when local datasource returns a null root folder`() { + every { + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + } returns OC_SPACE_SHARES + every { + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_SHARES.accountName, + spaceId = OC_SPACE_SHARES.root.id + ) + } returns null - assertEquals(emptyList(), result) + assertThrows(NullPointerException::class.java) { + ocFileRepository.getSharesRootFolderForAccount(OC_SPACE_SHARES.accountName) + } verify(exactly = 1) { - localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) + localSpacesDataSource.getSharesSpaceForAccount(OC_SPACE_SHARES.accountName) + localFileDataSource.getFileByRemotePath( + remotePath = ROOT_PATH, + owner = OC_SPACE_SHARES.accountName, + spaceId = OC_SPACE_SHARES.root.id + ) } } - @Test(expected = Exception::class) - fun `get folder content - ko`() { - every { localFileDataSource.getFolderContent(OC_FOLDER.parentId!!) } throws Exception() + @Test + fun `getSearchFolderContent returns a list of OCFiles when the file list option is all files`() { + every { + localFileDataSource.getSearchFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + } returns listOf(OC_FILE_WITH_SPACE_ID) - ocFileRepository.getFolderContent(OC_FOLDER.parentId!!) + val listOfFiles = ocFileRepository.getSearchFolderContent(FileListOption.ALL_FILES, OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) verify(exactly = 1) { - localFileDataSource.getFolderContent(OC_FOLDER.parentId!!) + localFileDataSource.getSearchFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) } } @Test - fun `get folder images - ok`() { - every { localFileDataSource.getFolderImages(OC_FOLDER.parentId!!) } returns listOf(OC_FOLDER) + fun `getSearchFolderContent returns an empty list with no OCFiles when the file list option is spaces list`() { + val listOfFiles = ocFileRepository.getSearchFolderContent(FileListOption.SPACES_LIST, OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + assertEquals(emptyList(), listOfFiles) + } - val folderContent = ocFileRepository.getFolderImages(OC_FOLDER.parentId!!) + @Test + fun `getSearchFolderContent returns a list of OCFiles when the file list option is available offline`() { + every { + localFileDataSource.getSearchAvailableOfflineFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + } returns listOf(OC_FILE_WITH_SPACE_ID) - assertEquals(listOf(OC_FOLDER), folderContent) + val listOfFiles = ocFileRepository.getSearchFolderContent(FileListOption.AV_OFFLINE, OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) verify(exactly = 1) { - localFileDataSource.getFolderImages(OC_FOLDER.parentId!!) + localFileDataSource.getSearchAvailableOfflineFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) } } - @Test(expected = Exception::class) - fun `get folder images - ko`() { - every { localFileDataSource.getFolderImages(OC_FOLDER.parentId!!) } throws Exception() + @Test + fun `getSearchFolderContent returns a list of OCFiles when the file list option is shared by link`() { + every { + localFileDataSource.getSearchSharedByLinkFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + } returns listOf(OC_FILE_WITH_SPACE_ID) - ocFileRepository.getFolderImages(OC_FOLDER.parentId!!) + val listOfFiles = ocFileRepository.getSearchFolderContent(FileListOption.SHARED_BY_LINK, OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) verify(exactly = 1) { - localFileDataSource.getFolderImages(OC_FOLDER.parentId!!) + localFileDataSource.getSearchSharedByLinkFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!, searchText) } } @Test - fun `refresh folder - ok`() { + fun `getFolderContent returns a list of OCFiles`() { every { - remoteFileDataSource.refreshFolder(folderToFetch.remotePath, any()) - } returns listOfFilesRetrieved + localFileDataSource.getFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } returns listOf(OC_FILE_WITH_SPACE_ID) - ocFileRepository.refreshFolder(folderToFetch.remotePath, OC_ACCOUNT_NAME) + val listOfFiles = ocFileRepository.getFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) verify(exactly = 1) { - remoteFileDataSource.refreshFolder(folderToFetch.remotePath, OC_ACCOUNT_NAME) - localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( - listOfFiles = listOfFilesRetrieved.drop(1), - folder = listOfFilesRetrieved.first() - ) + localFileDataSource.getFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `getFolderContentWithSyncInfoAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest { + every { + localFileDataSource.getFolderContentWithSyncInfoAsFlow(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } returns flowOf(listOf(OC_FILE_WITH_SYNC_INFO)) + + val listOfFiles = ocFileRepository.getFolderContentWithSyncInfoAsFlow(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!).first() + assertEquals(listOf(OC_FILE_WITH_SYNC_INFO), listOfFiles) + + verify(exactly = 1) { + localFileDataSource.getFolderContentWithSyncInfoAsFlow(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `getFolderImages returns a list of OCFiles`() { + every { + localFileDataSource.getFolderImages(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } returns listOf(OC_FILE_WITH_SPACE_ID) + + val listOfFiles = ocFileRepository.getFolderImages(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) + + verify(exactly = 1) { + localFileDataSource.getFolderImages(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `getSharedByLinkWithSyncInfoForAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest { + every { + localFileDataSource.getSharedByLinkWithSyncInfoForAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner) + } returns flowOf(listOf(OC_FILE_WITH_SYNC_INFO)) + + val listOfFiles = ocFileRepository.getSharedByLinkWithSyncInfoForAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner).first() + assertEquals(listOf(OC_FILE_WITH_SYNC_INFO), listOfFiles) + + verify(exactly = 1) { + localFileDataSource.getSharedByLinkWithSyncInfoForAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner) } } - @Test(expected = NoConnectionWithServerException::class) - fun `refresh folder - ko - no connection exception`() { + @Test + fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest { every { - remoteFileDataSource.refreshFolder(folderToFetch.remotePath, OC_ACCOUNT_NAME) - } throws NoConnectionWithServerException() + localFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner) + } returns flowOf(listOf(OC_FILE_WITH_SYNC_INFO)) - ocFileRepository.refreshFolder(folderToFetch.remotePath, OC_ACCOUNT_NAME) + val listOfFiles = ocFileRepository.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner).first() + assertEquals(listOf(OC_FILE_WITH_SYNC_INFO), listOfFiles) verify(exactly = 1) { - remoteFileDataSource.refreshFolder(OC_FOLDER.remotePath, OC_ACCOUNT_NAME) + localFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_FILE_WITH_SYNC_INFO.file.owner) } - verify(exactly = 0) { - localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(any(), any()) + } + + @Test + fun `getFilesAvailableOfflineFromAccount returns a list of OCFiles`() { + every { + localFileDataSource.getFilesAvailableOfflineFromAccount(OC_FILE_AVAILABLE_OFFLINE.owner) + } returns listOf(OC_FILE_AVAILABLE_OFFLINE) + + val listOfFiles = ocFileRepository.getFilesAvailableOfflineFromAccount(OC_FILE_AVAILABLE_OFFLINE.owner) + assertEquals(listOf(OC_FILE_AVAILABLE_OFFLINE), listOfFiles) + + verify(exactly = 1) { + localFileDataSource.getFilesAvailableOfflineFromAccount(OC_FILE_AVAILABLE_OFFLINE.owner) } } @Test - fun `remove file - ok`() { - every { remoteFileDataSource.deleteFile(any(), any()) } returns Unit - every { localStorageProvider.deleteLocalFile(any()) } returns true + fun `getFilesAvailableOfflineFromEveryAccount returns a list of OCFiles`() { + every { + localFileDataSource.getFilesAvailableOfflineFromEveryAccount() + } returns OC_AVAILABLE_OFFLINE_FILES - ocFileRepository.deleteFiles(listOfFilesToDelete = listOfFileToRemove, removeOnlyLocalCopy = false) + val listOfFiles = ocFileRepository.getFilesAvailableOfflineFromEveryAccount() + assertEquals(OC_AVAILABLE_OFFLINE_FILES, listOfFiles) - verify(exactly = listOfFilesRetrieved.size) { - remoteFileDataSource.deleteFile(any(), any()) - localFileDataSource.deleteFile(any()) - localStorageProvider.deleteLocalFile(any()) + verify(exactly = 1) { + localFileDataSource.getFilesAvailableOfflineFromEveryAccount() } } @Test - fun `remove file - ok - only local copy`() { - every { localStorageProvider.deleteLocalFile(any()) } returns true + fun `getDownloadedFilesForAccount returns a list of OCFiles`() { + every { + localFileDataSource.getDownloadedFilesForAccount(OC_FILE_DOWNLOADED.owner) + } returns listOf(OC_FILE_DOWNLOADED) - ocFileRepository.deleteFiles(listOfFilesToDelete = listOfFileToRemove, removeOnlyLocalCopy = true) + val listOfFiles = ocFileRepository.getDownloadedFilesForAccount(OC_FILE_DOWNLOADED.owner) + assertEquals(listOf(OC_FILE_DOWNLOADED), listOfFiles) - verify(exactly = listOfFilesRetrieved.size) { localStorageProvider.deleteLocalFile(any()) } - verify(exactly = listOfFilesRetrieved.size) { localFileDataSource.saveFile(any()) } - verify(exactly = 0) { - remoteFileDataSource.deleteFile(any(), any()) - localFileDataSource.deleteFile(any()) + verify(exactly = 1) { + localFileDataSource.getDownloadedFilesForAccount(OC_FILE_DOWNLOADED.owner) } } @Test - fun `remove file - ok - folder recursively`() { - every { remoteFileDataSource.deleteFile(any(), any()) } returns Unit - every { localFileDataSource.getFolderContent(0) } returns listOfFileToRemove - every { localFileDataSource.getFolderContent(1) } returns listOf(OC_FILE) - every { localStorageProvider.deleteLocalFile(any()) } returns true + fun `getFilesWithLastUsageOlderThanGivenTime returns a list of OCFiles`() { + every { + localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(0) + } returns listOf(OC_FILE_WITH_SPACE_ID) - ocFileRepository.deleteFiles(listOfFilesToDelete = listOf(OC_FOLDER.copy(id = 0)), removeOnlyLocalCopy = false) + val listOfFiles = ocFileRepository.getFilesWithLastUsageOlderThanGivenTime(0) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) - verify(exactly = 1) { remoteFileDataSource.deleteFile(any(), any()) } - verify(exactly = 2) { localFileDataSource.getFolderContent(any()) } - // Removing initial folder + listOfFilesToRemove.size + file inside a folder in listOfFilesToRemove - verify(exactly = listOfFileToRemove.size + 2) { - localFileDataSource.deleteFile(any()) - localStorageProvider.deleteLocalFile(any()) + verify(exactly = 1) { + localFileDataSource.getFilesWithLastUsageOlderThanGivenTime(0) } } @Test - fun `remove file - ko - file not found exception`() { - every { remoteFileDataSource.deleteFile(any(), any()) } throws FileNotFoundException() - every { localStorageProvider.deleteLocalFile(any()) } returns true + fun `moveFile returns a list with the OCFile in conflict (the moved OCFile) when replace parameter is empty and expected path already exists`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns true + + val filesNeedAction = ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) - ocFileRepository.deleteFiles(listOfFilesToDelete = listOf(OC_FILE), removeOnlyLocalCopy = false) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), filesNeedAction) verify(exactly = 1) { - remoteFileDataSource.deleteFile(any(), any()) - localFileDataSource.deleteFile(any()) - localStorageProvider.deleteLocalFile(any()) + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) } } - @Test(expected = NoConnectionWithServerException::class) - fun `remove file - ko - no connection exception`() { + @Test + fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is empty and expected path doesn't exist`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false every { - remoteFileDataSource.deleteFile(any(), any()) - } throws NoConnectionWithServerException() + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns storagePath - ocFileRepository.deleteFiles(listOfFilesRetrieved, removeOnlyLocalCopy = false) + val filesNeedAction = ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) - verify(exactly = 1) { remoteFileDataSource.deleteFile(OC_FOLDER.remotePath, any()) } - verify(exactly = 0) { localFileDataSource.deleteFile(any()) } + assertEquals(emptyList(), filesNeedAction) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.moveFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = expectedRemotePath, + finalStoragePath = storagePath + ) + localStorageProvider.moveLocalFile( + ocFile = OC_FILE_WITH_SPACE_ID, + finalStoragePath = storagePath + ) + } } @Test - fun `save file - ok`() { - ocFileRepository.saveFile(OC_FILE) + fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is empty, expected path doesn't exist and file has a conflict`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns storagePath + + val filesNeedAction = ocFileRepository.moveFile(listOf(fileWithConflict), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) - verify(exactly = 1) { localFileDataSource.saveFile(OC_FILE) } + assertEquals(emptyList(), filesNeedAction) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = fileWithConflict.remotePath, + targetRemotePath = expectedRemotePath, + accountName = fileWithConflict.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.cleanConflict(fileWithConflict.id!!) + localFileDataSource.moveFile( + sourceFile = fileWithConflict, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = expectedRemotePath, + finalStoragePath = storagePath + ) + localFileDataSource.saveConflict(fileWithConflict.id!!, fileWithConflict.etagInConflict!!) + localStorageProvider.moveLocalFile( + ocFile = fileWithConflict, + finalStoragePath = storagePath + ) + } + } + + @Test + fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is true`() { + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns storagePath + + val filesNeedAction = ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(true), true) + + assertEquals(emptyList(), filesNeedAction) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = true + ) + localFileDataSource.moveFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = expectedRemotePath, + finalStoragePath = storagePath + ) + localStorageProvider.moveLocalFile( + ocFile = OC_FILE_WITH_SPACE_ID, + finalStoragePath = storagePath + ) + } + } + + @Test + fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is false`() { + val availableRemotePath = "$expectedRemotePath (1)" + val actualStoragePath = "/local/storage/path/username@demo.owncloud.com/Folder/Photos/image2 (1).jpt" + every { + remoteFileDataSource.getAvailableRemotePath( + remotePath = expectedRemotePath, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + isUserLogged = true + ) + } returns availableRemotePath + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = availableRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns actualStoragePath + + val filesNeedAction = ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(false), true) + + assertEquals(emptyList(), filesNeedAction) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.getAvailableRemotePath( + remotePath = expectedRemotePath, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + isUserLogged = true + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = availableRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = availableRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.moveFile( + sourceFile = OC_FILE_WITH_SPACE_ID, + targetFolder = OC_FOLDER_WITH_SPACE_ID, + finalRemotePath = availableRemotePath, + finalStoragePath = actualStoragePath + ) + localStorageProvider.moveLocalFile( + ocFile = OC_FILE_WITH_SPACE_ID, + finalStoragePath = actualStoragePath + ) + } + } + + @Test + fun `moveFile returns an empty list with no OCFiles in conflict when replace parameter is null`() { + val filesNeedAction = ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, listOf(null), true) + + assertEquals(emptyList(), filesNeedAction) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + } + } + + @Test + fun `moveFile removes target folder locally and throws a ConflictException when replace parameter is empty and expected path doesn't exist but target folder doesn't exist anymore`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns storagePath + every { + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } throws ConflictException() + every { + localFileDataSource.getFolderContent(OC_FOLDER_WITH_SPACE_ID.id!!) + } returns emptyList() + + assertThrows(ConflictException::class.java) { + ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) + } + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localFileDataSource.getFolderContent(OC_FOLDER_WITH_SPACE_ID.id!!) + localStorageProvider.deleteLocalFolderIfItHasNoFilesInside(OC_FOLDER_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_FOLDER_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `moveFile removes source file locally and throws a FileNotFoundException when replace parameter is empty and expected path doesn't exist but source file doesn't exist anymore`() { + every { + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns false + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns storagePath + every { + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + } throws FileNotFoundException() + every { + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + } returns true + + assertThrows(FileNotFoundException::class.java) { + ocFileRepository.moveFile(listOf(OC_FILE_WITH_SPACE_ID), OC_FOLDER_WITH_SPACE_ID, emptyList(), true) + } + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.checkPathExistence( + path = expectedRemotePath, + isUserLogged = true, + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + ) + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FOLDER_WITH_SPACE_ID.owner, + remotePath = expectedRemotePath, + spaceId = OC_FOLDER_WITH_SPACE_ID.spaceId + ) + remoteFileDataSource.moveFile( + sourceRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + targetRemotePath = expectedRemotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl, + replace = false + ) + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_FILE_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `readFile returns a OCFile`() { + val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) + every { + remoteFileDataSource.readFile( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns ocFileWithoutSpaceId + + val ocFile = ocFileRepository.readFile(OC_FILE_WITH_SPACE_ID.remotePath, OC_FILE_WITH_SPACE_ID.owner, OC_FILE_WITH_SPACE_ID.spaceId) + assertEquals(OC_FILE_WITH_SPACE_ID, ocFile) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FILE_WITH_SPACE_ID.spaceId, + accountName = OC_FILE_WITH_SPACE_ID.owner + ) + remoteFileDataSource.readFile( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } + } + + @Test + fun `refreshFolder returns an empty list of OCFiles when folder doesn't exist in database`() { + val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithSpaceIdAndNeedsThumbnailUpdate = OC_FILE_WITH_SPACE_ID.copy(needsToUpdateThumbnail = true) + every { + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns listOf(ocParentFolderWithoutSpaceId, ocFileWithoutSpaceId) + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns null + every { + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(ocFileWithSpaceIdAndNeedsThumbnailUpdate), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID + ) + } returns emptyList() + + val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + assertEquals(emptyList(), listOfFiles) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(ocFileWithSpaceIdAndNeedsThumbnailUpdate), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID + ) + } + } + + @Test + fun `refreshFolder returns an empty list of OCFiles when folder already exists in database but not its content`() { + val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithSpaceIdAndNoEtagAndNeedsThumbnailUpdate = OC_FILE_WITH_SPACE_ID.copy(needsToUpdateThumbnail = true, etag = "") + every { + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns listOf(ocParentFolderWithoutSpaceId, ocFileWithoutSpaceId) + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns OC_PARENT_FOLDER_WITH_SPACE_ID + every { + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + } returns emptyList() + every { + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(ocFileWithSpaceIdAndNoEtagAndNeedsThumbnailUpdate), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } returns emptyList() + + val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + assertEquals(emptyList(), listOfFiles) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(ocFileWithSpaceIdAndNoEtagAndNeedsThumbnailUpdate), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } + } + + @Test + fun `refreshFolder returns a list with the OCFile that changed when folder and its content already exists in database but needs to be updated`() { + val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithoutSpaceIdAndDifferentEtag = OC_FILE_WITH_SPACE_ID.copy(spaceId = null, etag = "5efb0c13c688i2") + every { + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns listOf(ocParentFolderWithoutSpaceId, ocFileWithoutSpaceIdAndDifferentEtag) + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns OC_PARENT_FOLDER_WITH_SPACE_ID + every { + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + } returns listOf(OC_FILE_WITH_SPACE_ID) + every { + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(OC_FILE_WITH_SPACE_ID), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } returns listOf(OC_FILE_WITH_SPACE_ID) + + val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, true) + assertEquals(listOf(OC_FILE_WITH_SPACE_ID), listOfFiles) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = listOf(OC_FILE_WITH_SPACE_ID), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } + } + + @Test + fun `refreshFolder returns an empty list of OCFiles when folder and its content already exists in database updated and the action is not set folder available offline or synchronize`() { + val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) + every { + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns listOf(ocParentFolderWithoutSpaceId, ocFileWithoutSpaceId) + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns OC_PARENT_FOLDER_WITH_SPACE_ID + every { + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + } returns listOf(OC_FILE_WITH_SPACE_ID) + every { + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = emptyList(), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } returns emptyList() + + val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + assertEquals(emptyList(), listOfFiles) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = emptyList(), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } + } + + @Test + fun `refreshFolder returns an empty list of OCFiles when folder and its content already exists in database but there are additional files in conflict in local to be removed`() { + val ocParentFolderWithoutSpaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.copy(spaceId = null) + val ocFileWithoutSpaceId = OC_FILE_WITH_SPACE_ID.copy(spaceId = null) + val additionalOcFile = OC_FILE_WITH_SPACE_ID.copy(id = 300, remotePath = "/Folder/image3.jpt", remoteId = "00000003oci9p7er2hox2", privateLink = "http://server.url/f/70", etagInConflict = etagInConflict) + every { + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } returns listOf(ocParentFolderWithoutSpaceId, ocFileWithoutSpaceId) + every { + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + } returns OC_PARENT_FOLDER_WITH_SPACE_ID + every { + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + } returns listOf(OC_FILE_WITH_SPACE_ID, additionalOcFile) + every { + localStorageProvider.deleteLocalFile(additionalOcFile) + } returns true + every { + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = emptyList(), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } returns emptyList() + + val listOfFiles = ocFileRepository.refreshFolder(OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, OC_PARENT_FOLDER_WITH_SPACE_ID.owner, OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, false) + assertEquals(emptyList(), listOfFiles) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.refreshFolder( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFileByRemotePath( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + owner = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId + ) + localFileDataSource.getFolderContent( + folderId = OC_PARENT_FOLDER_WITH_SPACE_ID.id!! + ) + localFileDataSource.cleanConflict(additionalOcFile.id!!) + localStorageProvider.deleteLocalFile(additionalOcFile) + localFileDataSource.deleteFile(additionalOcFile.id!!) + localFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged( + listOfFiles = emptyList(), + folder = OC_PARENT_FOLDER_WITH_SPACE_ID, + ) + } + } + + @Test + fun `deleteFiles removes a file and its conflict from local and remote correctly`() { + every { + localStorageProvider.deleteLocalFile(fileWithConflict) + } returns true + + ocFileRepository.deleteFiles(listOf(fileWithConflict), false) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = fileWithConflict.spaceId, + accountName = fileWithConflict.owner + ) + remoteFileDataSource.deleteFile( + remotePath = fileWithConflict.remotePath, + accountName = fileWithConflict.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.cleanConflict(fileWithConflict.id!!) + localStorageProvider.deleteLocalFile(fileWithConflict) + localFileDataSource.deleteFile(fileWithConflict.id!!) + } + } + + @Test + fun `deleteFiles removes a file and its conflict from local although it doesn't exist in remote because it throws a FileNotFoundException`() { + every { + remoteFileDataSource.deleteFile( + remotePath = fileWithConflict.remotePath, + accountName = fileWithConflict.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + } throws FileNotFoundException() + every { + localStorageProvider.deleteLocalFile(fileWithConflict) + } returns true + + ocFileRepository.deleteFiles(listOf(fileWithConflict), false) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = fileWithConflict.spaceId, + accountName = fileWithConflict.owner + ) + remoteFileDataSource.deleteFile( + remotePath = fileWithConflict.remotePath, + accountName = fileWithConflict.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.cleanConflict(fileWithConflict.id!!) + localStorageProvider.deleteLocalFile(fileWithConflict) + localFileDataSource.deleteFile(fileWithConflict.id!!) + } + } + + @Test + fun `deleteFiles removes a file and its conflict but only from local correctly`() { + every { + localStorageProvider.deleteLocalFile(fileWithConflict) + } returns true + + ocFileRepository.deleteFiles(listOf(fileWithConflict), true) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = fileWithConflict.spaceId, + accountName = fileWithConflict.owner + ) + localFileDataSource.cleanConflict(fileWithConflict.id!!) + localStorageProvider.deleteLocalFile(fileWithConflict) + localFileDataSource.saveFile(fileWithConflict.copy(storagePath = null, etagInConflict = null, lastUsage = null, etag = null)) + } + } + + @Test + fun `deleteFiles removes a folder recursively from local and remote correctly`() { + every { + localFileDataSource.getFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } returns listOf(OC_FILE_WITH_SPACE_ID) + every { + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + } returns true + + ocFileRepository.deleteFiles(listOf(OC_PARENT_FOLDER_WITH_SPACE_ID), false) + + verify(exactly = 1) { + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_PARENT_FOLDER_WITH_SPACE_ID.spaceId, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner + ) + remoteFileDataSource.deleteFile( + remotePath = OC_PARENT_FOLDER_WITH_SPACE_ID.remotePath, + accountName = OC_PARENT_FOLDER_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.getFolderContent(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + localStorageProvider.deleteLocalFile(OC_FILE_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_FILE_WITH_SPACE_ID.id!!) + localStorageProvider.deleteLocalFolderIfItHasNoFilesInside(OC_PARENT_FOLDER_WITH_SPACE_ID) + localFileDataSource.deleteFile(OC_PARENT_FOLDER_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `renameFile renames a file correctly`() { + val newName = "image3.jpt" + val newRemotePath = "/Folder/image3.jpt" + val newStoragePath = "/local/storage/path/username@demo.owncloud.com/Folder/image3.jpt" + every { + localStorageProvider.getExpectedRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + newName = newName, + isFolder = OC_FILE_WITH_SPACE_ID.isFolder + ) + } returns newRemotePath + every { + localFileDataSource.getFileByRemotePath( + remotePath = newRemotePath, + owner = OC_FILE_WITH_SPACE_ID.owner, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + } returns null + every { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FILE_WITH_SPACE_ID.owner, + remotePath = newRemotePath, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + } returns newStoragePath + + ocFileRepository.renameFile(OC_FILE_WITH_SPACE_ID, newName) + + verify(exactly = 1) { + localStorageProvider.getExpectedRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + newName = newName, + isFolder = OC_FILE_WITH_SPACE_ID.isFolder + ) + localFileDataSource.getFileByRemotePath( + remotePath = newRemotePath, + owner = OC_FILE_WITH_SPACE_ID.owner, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + localSpacesDataSource.getWebDavUrlForSpace( + spaceId = OC_FILE_WITH_SPACE_ID.spaceId, + accountName = OC_FILE_WITH_SPACE_ID.owner + ) + remoteFileDataSource.renameFile( + oldName = OC_FILE_WITH_SPACE_ID.fileName, + oldRemotePath = OC_FILE_WITH_SPACE_ID.remotePath, + newName = newName, + isFolder = OC_FILE_WITH_SPACE_ID.isFolder, + accountName = OC_FILE_WITH_SPACE_ID.owner, + spaceWebDavUrl = OC_SPACE_PERSONAL.root.webDavUrl + ) + localFileDataSource.renameFile( + fileToRename = OC_FILE_WITH_SPACE_ID, + finalRemotePath = newRemotePath, + finalStoragePath = newStoragePath + ) + localStorageProvider.moveLocalFile( + ocFile = OC_FILE_WITH_SPACE_ID, + finalStoragePath = newStoragePath + ) + } + verify(exactly = 2) { + localStorageProvider.getDefaultSavePathFor( + accountName = OC_FILE_WITH_SPACE_ID.owner, + remotePath = newRemotePath, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + } + } + + @Test + fun `renameFile throws FileAlreadyExistsException when new name is already taken by other file`() { + val newName = "image2.jpt" + val newRemotePath = "/Folder/image2.jpt" + every { + localStorageProvider.getExpectedRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + newName = newName, + isFolder = OC_FILE_WITH_SPACE_ID.isFolder + ) + } returns newRemotePath + every { + localFileDataSource.getFileByRemotePath( + remotePath = newRemotePath, + owner = OC_FILE_WITH_SPACE_ID.owner, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + } returns OC_FILE_WITH_SPACE_ID + + assertThrows(FileAlreadyExistsException::class.java) { + ocFileRepository.renameFile(OC_FILE_WITH_SPACE_ID, newName) + } + + verify(exactly = 1) { + localStorageProvider.getExpectedRemotePath( + remotePath = OC_FILE_WITH_SPACE_ID.remotePath, + newName = newName, + isFolder = OC_FILE_WITH_SPACE_ID.isFolder + ) + localFileDataSource.getFileByRemotePath( + remotePath = newRemotePath, + owner = OC_FILE_WITH_SPACE_ID.owner, + spaceId = OC_FILE_WITH_SPACE_ID.spaceId + ) + } + } + + @Test + fun `saveFile saves a file correctly`() { + ocFileRepository.saveFile(OC_FILE_WITH_SPACE_ID) + + verify(exactly = 1) { + localFileDataSource.saveFile(OC_FILE_WITH_SPACE_ID) + } + } + + @Test + fun `saveConflict saves the etagInConflict related to a file correctly`() { + ocFileRepository.saveConflict(OC_FILE_WITH_SPACE_ID.id!!, etagInConflict) + + verify(exactly = 1) { + localFileDataSource.saveConflict( + fileId = OC_FILE_WITH_SPACE_ID.id!!, + eTagInConflict = etagInConflict + ) + } + } + + @Test + fun `cleanConflict removes conflict for a file correctly`() { + ocFileRepository.cleanConflict(OC_FILE_WITH_SPACE_ID.id!!) + + verify(exactly = 1) { + localFileDataSource.cleanConflict(OC_FILE_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `disableThumbnailsForFile disables thumbnails for a file correctly`() { + ocFileRepository.disableThumbnailsForFile(OC_FILE_WITH_SPACE_ID.id!!) + + verify(exactly = 1) { + localFileDataSource.disableThumbnailsForFile(OC_FILE_WITH_SPACE_ID.id!!) + } + } + + @Test + fun `updateFileWithNewAvailableOfflineStatus updates available offline status for a file correctly`() { + ocFileRepository.updateFileWithNewAvailableOfflineStatus(OC_FILE_WITH_SPACE_ID, AvailableOfflineStatus.AVAILABLE_OFFLINE) + + verify(exactly = 1) { + localFileDataSource.updateAvailableOfflineStatusForFile( + ocFile = OC_FILE_WITH_SPACE_ID, + newAvailableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE + ) + } + } + + @Test + fun `updateFileWithLastUsage updates last usage for a file correctly`() { + val lastUsage = 12345L + + ocFileRepository.updateFileWithLastUsage(OC_FILE_WITH_SPACE_ID.id!!, lastUsage) + + verify(exactly = 1) { + localFileDataSource.updateFileWithLastUsage( + fileId = OC_FILE_WITH_SPACE_ID.id!!, + lastUsage = lastUsage + ) + } + } + + @Test + fun `updateDownloadedFilesStorageDirectoryInStoragePath updates storage path for downloaded files correctly`() { + val oldDirectory = "/old/directory" + + ocFileRepository.updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory, storagePath) + + verify(exactly = 1) { + localFileDataSource.updateDownloadedFilesStorageDirectoryInStoragePath( + oldDirectory = oldDirectory, + newDirectory = storagePath + ) + } + } + + @Test + fun `saveDownloadWorkerUuid saves the worker UUID for a file correctly`() { + val workerUuid = UUID.randomUUID() + + ocFileRepository.saveDownloadWorkerUuid(OC_FILE_WITH_SPACE_ID.id!!, workerUuid) + + verify(exactly = 1) { + localFileDataSource.saveDownloadWorkerUuid( + fileId = OC_FILE_WITH_SPACE_ID.id!!, + workerUuid = workerUuid + ) + } + } + + @Test + fun `cleanWorkersUuid cleans workers UUID for a file correctly`() { + ocFileRepository.cleanWorkersUuid(OC_FILE_WITH_SPACE_ID.id!!) + + verify(exactly = 1) { + localFileDataSource.cleanWorkersUuid(OC_FILE_WITH_SPACE_ID.id!!) + } } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt index 1e29e03f1a9..4674664ce10 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt @@ -68,13 +68,11 @@ interface FileRepository { fun saveFile(file: OCFile) fun saveConflict(fileId: Long, eTagInConflict: String) fun cleanConflict(fileId: Long) - fun saveUploadWorkerUuid(fileId: Long, workerUuid: UUID) - fun saveDownloadWorkerUuid(fileId: Long, workerUuid: UUID) - fun cleanWorkersUuid(fileId: Long) - fun disableThumbnailsForFile(fileId: Long) fun updateFileWithNewAvailableOfflineStatus(ocFile: OCFile, newAvailableOfflineStatus: AvailableOfflineStatus) - fun updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory: String, newDirectory: String) fun updateFileWithLastUsage(fileId: Long, lastUsage: Long?) + fun updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory: String, newDirectory: String) + fun saveDownloadWorkerUuid(fileId: Long, workerUuid: UUID) + fun cleanWorkersUuid(fileId: Long) } diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt index e0bba606233..ef7990e2ad5 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -4,7 +4,7 @@ * @author Abel García de Prada * @author Juan Carlos Garrote Gascón * - * Copyright (C) 2023 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -27,6 +27,7 @@ import com.owncloud.android.data.files.db.OCFileSyncEntity import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.model.OCFileWithSyncInfo +import com.owncloud.android.domain.files.model.OCMetaFile import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.RemoteMetaFile @@ -45,6 +46,53 @@ val OC_FOLDER = OCFile( length = 123123123 ) +val OC_ROOT_FOLDER = OCFile( + id = 1, + parentId = 0, + remotePath = "/", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay4", + privateLink = "http://server.url/f/8", + creationTimestamp = 0, + modificationTimestamp = 1593510589000, + etag = "5efb0c13c688k", + mimeType = "DIR", + length = 123123123 +) + +val OC_PARENT_FOLDER_WITH_SPACE_ID = OCFile( + id = 123, + parentId = 1, + remotePath = "/Folder", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay2", + privateLink = "http://server.url/f/5", + creationTimestamp = 0, + modificationTimestamp = 1593510589000, + etag = "5efb0c13c688g", + mimeType = "DIR", + length = 123123123, + spaceId = OC_SPACE_PERSONAL.id +) + +val OC_FOLDER_WITH_SPACE_ID = OCFile( + id = 125, + parentId = 123, + remotePath = "/Folder/Photos", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hay3", + privateLink = "http://server.url/f/6", + creationTimestamp = 0, + modificationTimestamp = 1593510589000, + etag = "5efb0c13c688h", + mimeType = "DIR", + length = 0, + spaceId = OC_SPACE_PERSONAL.id +) + val OC_FILE = OCFile( id = 124, parentId = 122, @@ -77,6 +125,27 @@ val OC_FILE_AVAILABLE_OFFLINE = OCFile( availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE ) +val OC_FILE_DOWNLOADED = OC_FILE.copy( + storagePath = "/local/storage/path/username@demo.owncloud.com/Photos/image.jpt" +) + +val OC_FILE_WITH_SPACE_ID = OCFile( + id = 126, + parentId = 123, + remotePath = "/Folder/image2.jpt", + owner = OC_ACCOUNT_NAME, + permissions = "RDNVCK", + remoteId = "00000003oci9p7er2hox", + privateLink = "http://server.url/f/7", + creationTimestamp = 1593510589000, + modificationTimestamp = 1593510589000, + etag = "5efb0c13c688i", + mimeType = "image/jpeg", + length = 3000000, + availableOfflineStatus = AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE, + spaceId = OC_SPACE_PERSONAL.id +) + val OC_FILE_WITH_SYNC_INFO = OCFileWithSyncInfo( file = OC_FILE, uploadWorkerUuid = null, @@ -105,6 +174,14 @@ val OC_FILE_WITH_SYNC_INFO_AVAILABLE_OFFLINE = OCFileWithSyncInfo( space = OC_SPACE_PROJECT_WITH_IMAGE ) +val OC_META_FILE = OCMetaFile( + path = OC_FILE.remotePath +) + +val OC_META_FILE_ROOT_FOLDER = OCMetaFile( + path = OC_ROOT_FOLDER.remotePath +) + val OC_FILES_WITH_SYNC_INFO = listOf(OC_FILE_WITH_SYNC_INFO, OC_FILE_WITH_SYNC_INFO, OC_FILE_WITH_SYNC_INFO) val OC_AVAILABLE_OFFLINE_FILES = listOf(OC_FILE_AVAILABLE_OFFLINE, OC_FILE_AVAILABLE_OFFLINE, OC_FILE_AVAILABLE_OFFLINE) val OC_FILES_EMPTY = emptyList() diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt index 3cbc85b0a39..26ff75cd45a 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCSpace.kt @@ -3,7 +3,7 @@ * * @author Juan Carlos Garrote Gascón * - * Copyright (C) 2023 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -126,6 +126,26 @@ val OC_SPACE_PERSONAL = OC_SPACE_PROJECT_WITH_IMAGE.copy( special = null ) +val OC_SPACE_SHARES = OCSpace( + accountName = OC_ACCOUNT_NAME, + driveAlias = "virtual/shares", + driveType = "virtual", + id = "a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc668", + lastModifiedDateTime = "2024-01-01T00:00:00.00000000Z", + name = "Shares", + owner = null, + quota = null, + root = SpaceRoot( + eTag = "989c7968dbbbde8c5fd9849b9123c384", + id = "a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc668", + webDavUrl = "https://server.com/dav/spaces/a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc668", + deleted = null + ), + webUrl = "https://server.com/f/a0ca6a90-a365-4782-871e-d44447bbc668\$a0ca6a90-a365-4782-871e-d44447bbc669", + description = null, + special = null +) + val OC_SPACE_PROJECT_DISABLED = OC_SPACE_PROJECT_WITH_IMAGE.copy( quota = SpaceQuota( remaining = null,