Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1,63 @@
package eu.qsfera.android.data
import android.accounts.AccountManager
import android.content.Context
import eu.qsfera.android.data.providers.SharedPreferencesProvider
import eu.qsfera.android.lib.common.ConnectionValidator
import io.mockk.mockk
import io.mockk.mockkStatic
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class ClientManagerTest {
private val accountManager: AccountManager = mockk()
private val preferencesProvider: SharedPreferencesProvider = mockk()
private val context: Context = mockk(relaxed = true)
private val connectionValidator: ConnectionValidator = mockk()
private lateinit var clientManager: ClientManager
@Before
fun setUp() {
mockkStatic(android.net.Uri::class)
val uriMock = mockk<android.net.Uri>()
io.mockk.every { android.net.Uri.parse(any()) } returns uriMock
io.mockk.every { uriMock.toString() } returns "https://demo.qsfera.eu"
clientManager = ClientManager(
accountManager,
preferencesProvider,
context,
"eu.qsfera.android.account",
connectionValidator
)
}
@org.junit.After
fun tearDown() {
io.mockk.unmockkStatic(android.net.Uri::class)
}
@Test
fun `getClientForAnonymousCredentials reuses client and resets credentials`() {
val url = "https://demo.qsfera.eu"
val mockClient = mockk<eu.qsfera.android.lib.common.QSferaClient>(relaxed = true)
val uriMock = android.net.Uri.parse(url)
io.mockk.every { mockClient.baseUri } returns uriMock
// Inject mock client into clientManager
val field = ClientManager::class.java.getDeclaredField("qsferaClient")
field.isAccessible = true
field.set(clientManager, mockClient)
// Call method - should reuse mockClient
val resultClient = clientManager.getClientForAnonymousCredentials(url, false)
assertEquals("Client should be reused", mockClient, resultClient)
// Verify credentials were set
io.mockk.verify { mockClient.credentials = any() }
}
}
@@ -0,0 +1,132 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.appregistry.datasources.implementation
import eu.qsfera.android.data.appregistry.db.AppRegistryDao
import eu.qsfera.android.domain.appregistry.model.AppRegistry
import eu.qsfera.android.domain.appregistry.model.AppRegistryMimeType
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_APP_REGISTRY_ENTITY
import eu.qsfera.android.testutil.OC_APP_REGISTRY_MIMETYPE
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
@ExperimentalCoroutinesApi
class OCLocalAppRegistryDataSourceTest {
private lateinit var ocLocalAppRegistryDataSource: OCLocalAppRegistryDataSource
private val appRegistryDao = mockk<AppRegistryDao>(relaxUnitFun = true)
private val mimeTypeDir = "DIR"
@Before
fun setUp() {
ocLocalAppRegistryDataSource = OCLocalAppRegistryDataSource(appRegistryDao)
}
@Test
fun `getAppRegistryForMimeTypeAsStream returns a Flow with an AppRegistryMimeType`() = runTest {
every { appRegistryDao.getAppRegistryForMimeType(OC_ACCOUNT_NAME, mimeTypeDir) } returns flowOf(OC_APP_REGISTRY_ENTITY)
val appRegistry = ocLocalAppRegistryDataSource.getAppRegistryForMimeTypeAsStream(OC_ACCOUNT_NAME, mimeTypeDir)
val result = appRegistry.first()
assertEquals(OC_APP_REGISTRY_MIMETYPE, result)
verify(exactly = 1) { appRegistryDao.getAppRegistryForMimeType(OC_ACCOUNT_NAME, mimeTypeDir) }
}
@Test
fun `getAppRegistryForMimeTypeAsStream returns a Flow with null when there are no app registries for that mime type`() = runTest {
every { appRegistryDao.getAppRegistryForMimeType(OC_ACCOUNT_NAME, mimeTypeDir) } returns flowOf(null)
val appRegistry = ocLocalAppRegistryDataSource.getAppRegistryForMimeTypeAsStream(OC_ACCOUNT_NAME, mimeTypeDir)
val result = appRegistry.first()
assertNull(result)
verify(exactly = 1) { appRegistryDao.getAppRegistryForMimeType(OC_ACCOUNT_NAME, mimeTypeDir) }
}
@Test
fun `getAppRegistryWhichAllowCreation returns a Flow with a list of AppRegistryMimeType`() = runTest {
every { appRegistryDao.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) } returns flowOf(listOf(OC_APP_REGISTRY_ENTITY))
val appRegistry = ocLocalAppRegistryDataSource.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME)
val result = appRegistry.first()
assertEquals(listOf(OC_APP_REGISTRY_MIMETYPE), result)
verify(exactly = 1) { appRegistryDao.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) }
}
@Test
fun `getAppRegistryWhichAllowCreation returns an empty list when there are no app registries that allow creation`() = runTest {
every { appRegistryDao.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) } returns flowOf(emptyList())
val appRegistry = ocLocalAppRegistryDataSource.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME)
val result = appRegistry.first()
assertEquals(emptyList<AppRegistryMimeType>(), result)
verify(exactly = 1) { appRegistryDao.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) }
}
@Test
fun `saveAppRegistryForAccount saves an AppRegistry correctly`() {
val appRegistryOtherName = "appRegistryMimeTypes.name2"
val appRegistry = AppRegistry(
OC_ACCOUNT_NAME, mutableListOf(
OC_APP_REGISTRY_MIMETYPE,
OC_APP_REGISTRY_MIMETYPE.copy(name = appRegistryOtherName)
)
)
ocLocalAppRegistryDataSource.saveAppRegistryForAccount(appRegistry)
verify(exactly = 1) {
appRegistryDao.deleteAppRegistryForAccount(appRegistry.accountName)
appRegistryDao.upsertAppRegistries(listOf(OC_APP_REGISTRY_ENTITY, OC_APP_REGISTRY_ENTITY.copy(name = appRegistryOtherName)))
}
}
@Test
fun `deleteAppRegistryForAccount removes the app registries for an account correctly`() {
ocLocalAppRegistryDataSource.deleteAppRegistryForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) { appRegistryDao.deleteAppRegistryForAccount(OC_ACCOUNT_NAME) }
}
}
@@ -0,0 +1,137 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.appregistry.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.lib.resources.appregistry.services.OCAppRegistryService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_APP_REGISTRY
import eu.qsfera.android.testutil.OC_APP_REGISTRY_RESPONSE
import eu.qsfera.android.testutil.OC_FILE
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCRemoteAppRegistryDataSourceTest {
private lateinit var ocRemoteAppRegistryDataSource: OCRemoteAppRegistryDataSource
private val clientManager: ClientManager = mockk(relaxed = true)
private val ocAppRegistryService: OCAppRegistryService = mockk()
private val appUrl = "app/list"
private val testEndpoint = "app/open-with-web"
@Before
fun setUp() {
every { clientManager.getAppRegistryService(OC_ACCOUNT_NAME) } returns ocAppRegistryService
ocRemoteAppRegistryDataSource = OCRemoteAppRegistryDataSource(clientManager)
}
@Test
fun `getAppRegistryForAccount returns an AppRegistry`() {
val getAppRegistryForAccountResult = createRemoteOperationResultMock(
data = OC_APP_REGISTRY_RESPONSE, isSuccess = true
)
every { ocAppRegistryService.getAppRegistry(appUrl) } returns getAppRegistryForAccountResult
val result = ocRemoteAppRegistryDataSource.getAppRegistryForAccount(OC_ACCOUNT_NAME, appUrl)
assertEquals(OC_APP_REGISTRY, result)
verify(exactly = 1) { ocAppRegistryService.getAppRegistry(appUrl) }
}
@Test
fun `getUrlToOpenInWeb returns a URL String`() {
val expectedUrl = "https://example.com/file123"
val appName = "TestApp"
val getUrlToOpenInWebResult = createRemoteOperationResultMock(
data = expectedUrl, isSuccess = true
)
every {
ocAppRegistryService.getUrlToOpenInWeb(
openWebEndpoint = testEndpoint,
fileId = OC_FILE.remoteId.toString(),
appName = appName,
)
} returns getUrlToOpenInWebResult
val result = ocRemoteAppRegistryDataSource.getUrlToOpenInWeb(
accountName = OC_ACCOUNT_NAME,
openWebEndpoint = testEndpoint,
fileId = OC_FILE.remoteId.toString(),
appName = appName,
)
assertEquals(expectedUrl, result)
verify {
ocAppRegistryService.getUrlToOpenInWeb(
openWebEndpoint = testEndpoint,
fileId = OC_FILE.remoteId.toString(),
appName = appName,
)
}
}
@Test
fun `createFileWithAppProvider returns a URL String to open the file in web`() {
val expectedFileUrl = "https://example.com/files/testFile.txt"
val createFileWithAppProviderResult = createRemoteOperationResultMock(
data = expectedFileUrl, isSuccess = true)
every {
ocAppRegistryService.createFileWithAppProvider(
createFileWithAppProviderEndpoint = testEndpoint,
parentContainerId = OC_FILE.remoteId.toString(),
filename = OC_FILE.fileName,
)
} returns createFileWithAppProviderResult
val result = ocRemoteAppRegistryDataSource.createFileWithAppProvider(
accountName = OC_ACCOUNT_NAME,
createFileWithAppProviderEndpoint = testEndpoint,
parentContainerId = OC_FILE.remoteId.toString(),
filename = OC_FILE.fileName,
)
assertEquals(expectedFileUrl, result)
verify(exactly = 1) {
ocAppRegistryService.createFileWithAppProvider(
createFileWithAppProviderEndpoint = testEndpoint,
parentContainerId = OC_FILE.remoteId.toString(),
filename = OC_FILE.fileName,
)
}
}
}
@@ -0,0 +1,173 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.appregistry.repository
import eu.qsfera.android.data.appregistry.datasources.LocalAppRegistryDataSource
import eu.qsfera.android.data.appregistry.datasources.RemoteAppRegistryDataSource
import eu.qsfera.android.data.capabilities.datasources.LocalCapabilitiesDataSource
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_APP_REGISTRY
import eu.qsfera.android.testutil.OC_APP_REGISTRY_MIMETYPE
import eu.qsfera.android.testutil.OC_CAPABILITY_WITH_FILES_APP_PROVIDERS
import eu.qsfera.android.testutil.OC_FILE
import eu.qsfera.android.testutil.OC_FOLDER
import io.mockk.every
import io.mockk.mockk
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.Test
@ExperimentalCoroutinesApi
class OCAppRegistryRepositoryTest {
private val localAppRegistryDataSource = mockk<LocalAppRegistryDataSource>(relaxUnitFun = true)
private val remoteAppRegistryDataSource = mockk<RemoteAppRegistryDataSource>()
private val localCapabilitiesDataSource = mockk<LocalCapabilitiesDataSource>(relaxUnitFun = true)
private val ocAppRegistryRepository = OCAppRegistryRepository(
localAppRegistryDataSource,
remoteAppRegistryDataSource,
localCapabilitiesDataSource,
)
@Test
fun `refreshAppRegistryForAccount fetches the AppRegistry of an account and saves it`() {
every { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns OC_CAPABILITY_WITH_FILES_APP_PROVIDERS
every {
remoteAppRegistryDataSource.getAppRegistryForAccount(
OC_ACCOUNT_NAME,
OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.appsUrl?.substring(1)
)
} returns OC_APP_REGISTRY
ocAppRegistryRepository.refreshAppRegistryForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteAppRegistryDataSource.getAppRegistryForAccount(
OC_ACCOUNT_NAME,
OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.appsUrl?.substring(1)
)
localAppRegistryDataSource.saveAppRegistryForAccount(OC_APP_REGISTRY)
}
}
@Test
fun `getAppRegistryForMimeTypeAsStream returns a Flow of AppRegistryMimeType`() = runTest {
val mimeType = "DIR"
every {
localAppRegistryDataSource.getAppRegistryForMimeTypeAsStream(
accountName = OC_ACCOUNT_NAME,
mimeType = mimeType
)
} returns flowOf(
OC_APP_REGISTRY_MIMETYPE
)
val resultActual =
ocAppRegistryRepository.getAppRegistryForMimeTypeAsStream(accountName = OC_ACCOUNT_NAME, mimeType = mimeType).first()
assertEquals(OC_APP_REGISTRY_MIMETYPE, resultActual)
verify(exactly = 1) {
localAppRegistryDataSource.getAppRegistryForMimeTypeAsStream(
accountName = OC_ACCOUNT_NAME,
mimeType = mimeType
)
}
}
@Test
fun `getAppRegistryWhichAllowCreation returns a Flow of List of AppRegistryMimeType`() = runTest {
every { localAppRegistryDataSource.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) } returns
flowOf(listOf(OC_APP_REGISTRY_MIMETYPE))
val resultActual = ocAppRegistryRepository.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME).first()
assertEquals(listOf(OC_APP_REGISTRY_MIMETYPE), resultActual)
verify(exactly = 1) { localAppRegistryDataSource.getAppRegistryWhichAllowCreation(OC_ACCOUNT_NAME) }
}
@Test
fun `getUrlToOpenInWeb returns a URL String`() {
val expectedUrl = "https://example.com/file123"
val appName = "qsfera"
every {
remoteAppRegistryDataSource.getUrlToOpenInWeb(
accountName = OC_ACCOUNT_NAME,
openWebEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.openWebUrl!!,
fileId = OC_FILE.remoteId!!,
appName = appName
)
} returns expectedUrl
val resultActual = ocAppRegistryRepository.getUrlToOpenInWeb(
accountName = OC_ACCOUNT_NAME,
openWebEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.openWebUrl!!,
fileId = OC_FILE.remoteId!!,
appName = appName
)
assertEquals(expectedUrl, resultActual)
verify(exactly = 1) {
remoteAppRegistryDataSource.getUrlToOpenInWeb(
accountName = OC_ACCOUNT_NAME,
openWebEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.openWebUrl!!,
fileId = OC_FILE.remoteId!!,
appName = appName
)
}
}
@Test
fun `createFileWithAppProvider returns a String with the new file ID`() {
every {
remoteAppRegistryDataSource.createFileWithAppProvider(
accountName = OC_ACCOUNT_NAME,
createFileWithAppProviderEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.newUrl!!,
parentContainerId = OC_FOLDER.remoteId!!,
filename = OC_FILE.fileName,
)
} returns OC_FILE.remoteId!!
val resultActual = ocAppRegistryRepository.createFileWithAppProvider(
accountName = OC_ACCOUNT_NAME,
createFileWithAppProviderEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.newUrl!!,
parentContainerId = OC_FOLDER.remoteId!!,
filename = OC_FILE.fileName,
)
assertEquals(OC_FILE.remoteId!!, resultActual)
verify(exactly = 1) {
remoteAppRegistryDataSource.createFileWithAppProvider(
accountName = OC_ACCOUNT_NAME,
createFileWithAppProviderEndpoint = OC_CAPABILITY_WITH_FILES_APP_PROVIDERS.filesAppProviders?.newUrl!!,
parentContainerId = OC_FOLDER.remoteId!!,
filename = OC_FILE.fileName,
)
}
}
}
@@ -0,0 +1,189 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author David González Verdugo
* @author Aitor Ballesteros Pavón
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.authentication.repository
import eu.qsfera.android.data.authentication.datasources.LocalAuthenticationDataSource
import eu.qsfera.android.data.authentication.datasources.RemoteAuthenticationDataSource
import eu.qsfera.android.testutil.OC_ACCESS_TOKEN
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_AUTH_TOKEN_TYPE
import eu.qsfera.android.testutil.OC_BASIC_PASSWORD
import eu.qsfera.android.testutil.OC_BASIC_USERNAME
import eu.qsfera.android.testutil.OC_REDIRECTION_PATH
import eu.qsfera.android.testutil.OC_REFRESH_TOKEN
import eu.qsfera.android.testutil.OC_SCOPE
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
import eu.qsfera.android.testutil.OC_USER_INFO
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class OCAuthenticationRepositoryTest {
private val localAuthenticationDataSource = mockk<LocalAuthenticationDataSource>()
private val remoteAuthenticationDataSource = mockk<RemoteAuthenticationDataSource>()
private val ocAuthenticationRepository: OCAuthenticationRepository =
OCAuthenticationRepository(localAuthenticationDataSource, remoteAuthenticationDataSource)
@Test
fun `loginBasic returns String with the account name`() {
every {
remoteAuthenticationDataSource.loginBasic(
serverPath = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
username = OC_BASIC_USERNAME,
password = OC_BASIC_PASSWORD
)
} returns Pair(
OC_USER_INFO,
OC_REDIRECTION_PATH.lastPermanentLocation
)
every {
localAuthenticationDataSource.addBasicAccount(
userName = OC_BASIC_USERNAME,
lastPermanentLocation = OC_REDIRECTION_PATH.lastPermanentLocation,
password = OC_BASIC_PASSWORD,
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
userInfo = OC_USER_INFO,
updateAccountWithUsername = null
)
} returns OC_ACCOUNT_NAME
val accountName = ocAuthenticationRepository.loginBasic(
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
username = OC_BASIC_USERNAME,
password = OC_BASIC_PASSWORD,
updateAccountWithUsername = null
)
assertEquals(OC_ACCOUNT_NAME, accountName)
verify(exactly = 1) {
remoteAuthenticationDataSource.loginBasic(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, OC_BASIC_USERNAME, OC_BASIC_PASSWORD)
localAuthenticationDataSource.addBasicAccount(
userName = OC_BASIC_USERNAME,
lastPermanentLocation = OC_REDIRECTION_PATH.lastPermanentLocation,
password = OC_BASIC_PASSWORD,
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
userInfo = OC_USER_INFO,
updateAccountWithUsername = null
)
}
}
@Test
fun `loginOAuth returns String with the account name`() {
every {
remoteAuthenticationDataSource.loginOAuth(
serverPath = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
username = OC_BASIC_USERNAME,
accessToken = OC_ACCESS_TOKEN
)
} returns Pair(
first = OC_USER_INFO,
second = OC_REDIRECTION_PATH.lastPermanentLocation
)
every {
localAuthenticationDataSource.addOAuthAccount(
userName = OC_BASIC_USERNAME,
lastPermanentLocation = OC_REDIRECTION_PATH.lastPermanentLocation,
authTokenType = OC_AUTH_TOKEN_TYPE,
accessToken = OC_ACCESS_TOKEN,
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
userInfo = OC_USER_INFO,
refreshToken = OC_REFRESH_TOKEN,
scope = OC_SCOPE,
updateAccountWithUsername = null,
clientRegistrationInfo = OC_CLIENT_REGISTRATION
)
} returns OC_ACCOUNT_NAME
val accountName = ocAuthenticationRepository.loginOAuth(
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
username = OC_BASIC_USERNAME,
authTokenType = OC_AUTH_TOKEN_TYPE,
accessToken = OC_ACCESS_TOKEN,
refreshToken = OC_REFRESH_TOKEN,
scope = OC_SCOPE,
updateAccountWithUsername = null,
clientRegistrationInfo = OC_CLIENT_REGISTRATION
)
assertEquals(OC_ACCOUNT_NAME, accountName)
verify(exactly = 1) {
remoteAuthenticationDataSource.loginOAuth(
serverPath = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
username = OC_BASIC_USERNAME,
accessToken = OC_ACCESS_TOKEN
)
localAuthenticationDataSource.addOAuthAccount(
userName = OC_BASIC_USERNAME,
lastPermanentLocation = OC_REDIRECTION_PATH.lastPermanentLocation,
authTokenType = OC_AUTH_TOKEN_TYPE,
accessToken = OC_ACCESS_TOKEN,
serverInfo = OC_SECURE_SERVER_INFO_BASIC_AUTH,
userInfo = OC_USER_INFO,
refreshToken = OC_REFRESH_TOKEN,
scope = OC_SCOPE,
updateAccountWithUsername = null,
clientRegistrationInfo = OC_CLIENT_REGISTRATION
)
}
}
@Test
fun `supportsOAuth2UseCase returns Boolean`() {
every {
localAuthenticationDataSource.supportsOAuth2(OC_ACCOUNT_NAME)
} returns true
val actualResult = ocAuthenticationRepository.supportsOAuth2UseCase(OC_ACCOUNT_NAME)
assertTrue(actualResult)
verify(exactly = 1) {
localAuthenticationDataSource.supportsOAuth2(OC_ACCOUNT_NAME)
}
}
@Test
fun `getBaseUrl returns a String with the base URL`() {
every {
localAuthenticationDataSource.getBaseUrl(OC_ACCOUNT_NAME)
} returns OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl
val resultActual = ocAuthenticationRepository.getBaseUrl(OC_ACCOUNT_NAME)
assertEquals(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, resultActual)
verify(exactly = 1) {
localAuthenticationDataSource.getBaseUrl(OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,131 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.capabilities.datasources.implementation
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.MutableLiveData
import eu.qsfera.android.data.capabilities.datasources.implementation.OCLocalCapabilitiesDataSource.Companion.toEntity
import eu.qsfera.android.data.capabilities.db.OCCapabilityDao
import eu.qsfera.android.data.capabilities.db.OCCapabilityEntity
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_CAPABILITY
import eu.qsfera.android.testutil.livedata.getLastEmittedValue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
class OCLocalCapabilitiesDataSourceTest {
private lateinit var ocLocalCapabilitiesDataSource: OCLocalCapabilitiesDataSource
private val ocCapabilityDao = mockk<OCCapabilityDao>(relaxUnitFun = true)
private val ocCapability = OC_CAPABILITY.copy(id = 0)
private val ocCapabilityEntity = ocCapability.toEntity()
@Rule
@JvmField
var rule: TestRule = InstantTaskExecutorRule()
@Before
fun setUp() {
ocLocalCapabilitiesDataSource =
OCLocalCapabilitiesDataSource(
ocCapabilityDao,
)
}
@Test
fun `getCapabilitiesForAccountAsLiveData returns a LiveData of OCCapability`() {
val capabilitiesLiveData = MutableLiveData(ocCapabilityEntity)
every { ocCapabilityDao.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME) } returns capabilitiesLiveData
val result = ocLocalCapabilitiesDataSource.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME).getLastEmittedValue()
assertEquals(ocCapability, result)
verify(exactly = 1) {
ocCapabilityDao.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME)
}
}
@Test
fun `getCapabilitiesForAccountAsLiveData returns null when DAO returns a null capability`() {
val capabilitiesLiveData = MutableLiveData<OCCapabilityEntity>(null)
every { ocCapabilityDao.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME) } returns capabilitiesLiveData
val result = ocLocalCapabilitiesDataSource.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME).getLastEmittedValue()
assertNull(result)
verify(exactly = 1) {
ocCapabilityDao.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME)
}
}
@Test
fun `getCapabilitiesForAccount returns a OCCapability`() {
every { ocCapabilityDao.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns ocCapabilityEntity
val result = ocLocalCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
assertEquals(ocCapability, result)
verify(exactly = 1) {
ocCapabilityDao.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getCapabilitiesForAccount returns null when DAO returns a null capability`() {
every { ocCapabilityDao.getCapabilitiesForAccount(OC_ACCOUNT_NAME) } returns null
val result = ocLocalCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
assertNull(result)
verify(exactly = 1) {
ocCapabilityDao.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `insertCapabilities saves a list of OCCapability correctly`() {
ocLocalCapabilitiesDataSource.insertCapabilities(listOf(ocCapability))
verify(exactly = 1) { ocCapabilityDao.replace(listOf(ocCapabilityEntity)) }
}
@Test
fun `deleteCapabilitiesForAccount removes capabilities correctly`() {
ocLocalCapabilitiesDataSource.deleteCapabilitiesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) { ocCapabilityDao.deleteByAccountName(OC_ACCOUNT_NAME) }
}
}
@@ -0,0 +1,75 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Jesús Recio
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.capabilities.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.capabilities.datasources.mapper.RemoteCapabilityMapper
import eu.qsfera.android.lib.resources.status.services.implementation.OCCapabilityService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_CAPABILITY
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCRemoteCapabilitiesDataSourceTest {
private lateinit var ocRemoteCapabilitiesDataSource: OCRemoteCapabilitiesDataSource
private val ocCapabilityService: OCCapabilityService = mockk()
private val clientManager: ClientManager = mockk(relaxed = true)
private val remoteCapabilityMapper = RemoteCapabilityMapper()
@Before
fun setUp() {
every { clientManager.getCapabilityService(OC_ACCOUNT_NAME) } returns ocCapabilityService
ocRemoteCapabilitiesDataSource =
OCRemoteCapabilitiesDataSource(
clientManager,
remoteCapabilityMapper
)
}
@Test
fun `getCapabilities returns a OCCapability`() {
val remoteCapability = remoteCapabilityMapper.toRemote(OC_CAPABILITY)!!
val getRemoteCapabilitiesOperationResult = createRemoteOperationResultMock(remoteCapability, true)
every { ocCapabilityService.getCapabilities() } returns getRemoteCapabilitiesOperationResult
val result = ocRemoteCapabilitiesDataSource.getCapabilities(OC_ACCOUNT_NAME)
assertEquals(OC_CAPABILITY, result)
verify(exactly = 1) {
clientManager.getCapabilityService(OC_ACCOUNT_NAME)
ocCapabilityService.getCapabilities()
}
}
}
@@ -0,0 +1,44 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.capabilities.datasources.mapper
import eu.qsfera.android.testutil.OC_CAPABILITY
import org.junit.Assert
import org.junit.Test
class OCRemoteCapabilityMapperTest {
private val ocRemoteCapabilityMapper = RemoteCapabilityMapper()
@Test
fun checkToModelNull() {
Assert.assertNull(ocRemoteCapabilityMapper.toModel(null))
}
@Test
fun checkToModelNotNull() {
val remoteCapability = ocRemoteCapabilityMapper.toRemote(OC_CAPABILITY)
Assert.assertNotNull(remoteCapability)
val capability = ocRemoteCapabilityMapper.toModel(remoteCapability)
Assert.assertNotNull(capability)
Assert.assertEquals(capability, OC_CAPABILITY)
}
}
@@ -0,0 +1,109 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.capabilities.repository
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.MutableLiveData
import eu.qsfera.android.data.capabilities.datasources.LocalCapabilitiesDataSource
import eu.qsfera.android.data.capabilities.datasources.RemoteCapabilitiesDataSource
import eu.qsfera.android.domain.appregistry.AppRegistryRepository
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_CAPABILITY
import eu.qsfera.android.testutil.OC_CAPABILITY_WITH_FILES_APP_PROVIDERS
import eu.qsfera.android.testutil.livedata.getLastEmittedValue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class OCCapabilityRepositoryTest {
@Rule
@JvmField
val instantExecutorRule = InstantTaskExecutorRule()
private val localCapabilitiesDataSource = mockk<LocalCapabilitiesDataSource>(relaxUnitFun = true)
private val remoteCapabilitiesDataSource = mockk<RemoteCapabilitiesDataSource>()
private val appRegistryRepository = mockk<AppRegistryRepository>(relaxUnitFun = true)
private val ocCapabilityRepository: OCCapabilityRepository =
OCCapabilityRepository(localCapabilitiesDataSource, remoteCapabilitiesDataSource, appRegistryRepository)
@Test
fun `getCapabilitiesAsLiveData returns a LiveData of OCCapability`() {
val capabilitiesLiveData = MutableLiveData(OC_CAPABILITY)
every {
localCapabilitiesDataSource.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME)
} returns capabilitiesLiveData
val capabilitiesToEmit = capabilitiesLiveData.getLastEmittedValue()
val capabilitiesEmitted = ocCapabilityRepository.getCapabilitiesAsLiveData(OC_ACCOUNT_NAME).getLastEmittedValue()
assertEquals(capabilitiesToEmit, capabilitiesEmitted)
verify(exactly = 1) { localCapabilitiesDataSource.getCapabilitiesForAccountAsLiveData(OC_ACCOUNT_NAME) }
}
@Test
fun `getStoredCapabilities returns an OCCapability`() {
every {
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
} returns OC_CAPABILITY
val actualResult = ocCapabilityRepository.getStoredCapabilities(OC_ACCOUNT_NAME)
assertEquals(OC_CAPABILITY, actualResult)
verify(exactly = 1) { localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `refreshCapabilitiesForAccount updates capabilities correctly when files app providers is not null`() {
every { remoteCapabilitiesDataSource.getCapabilities(OC_ACCOUNT_NAME) } returns OC_CAPABILITY_WITH_FILES_APP_PROVIDERS
ocCapabilityRepository.refreshCapabilitiesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteCapabilitiesDataSource.getCapabilities(OC_ACCOUNT_NAME)
localCapabilitiesDataSource.insertCapabilities(listOf(OC_CAPABILITY_WITH_FILES_APP_PROVIDERS))
appRegistryRepository.refreshAppRegistryForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `refreshCapabilitiesForAccount updates capabilities correctly but not refresh AppRegistry when filesAppProviders is null`() {
every { remoteCapabilitiesDataSource.getCapabilities(OC_ACCOUNT_NAME) } returns OC_CAPABILITY
ocCapabilityRepository.refreshCapabilitiesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteCapabilitiesDataSource.getCapabilities(OC_ACCOUNT_NAME)
localCapabilitiesDataSource.insertCapabilities(listOf(OC_CAPABILITY))
}
}
}
@@ -0,0 +1,659 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
* @author Aitor Ballesteros Pavón
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.files.datasources.implementation
import eu.qsfera.android.data.files.datasources.implementation.OCLocalFileDataSource.Companion.toEntity
import eu.qsfera.android.data.files.db.FileDao
import eu.qsfera.android.data.files.db.OCFileEntity
import eu.qsfera.android.domain.availableoffline.model.AvailableOfflineStatus
import eu.qsfera.android.domain.files.model.MIME_DIR
import eu.qsfera.android.domain.files.model.MIME_PREFIX_IMAGE
import eu.qsfera.android.domain.files.model.OCFile
import eu.qsfera.android.domain.files.model.OCFile.Companion.ROOT_PARENT_ID
import eu.qsfera.android.domain.files.model.OCFile.Companion.ROOT_PATH
import eu.qsfera.android.domain.files.model.OCFileWithSyncInfo
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_FILE
import eu.qsfera.android.testutil.OC_FILE_AND_FILE_SYNC
import eu.qsfera.android.testutil.OC_FILE_AVAILABLE_OFFLINE
import eu.qsfera.android.testutil.OC_FILE_AVAILABLE_OFFLINE_ENTITY
import eu.qsfera.android.testutil.OC_FILE_ENTITY
import eu.qsfera.android.testutil.OC_FILE_WITH_SYNC_INFO_AND_SPACE
import eu.qsfera.android.testutil.OC_FOLDER
import eu.qsfera.android.testutil.OC_FOLDER_ENTITY
import io.mockk.every
import io.mockk.mockk
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.Before
import org.junit.Test
import java.util.UUID
@ExperimentalCoroutinesApi
class OCLocalFileDataSourceTest {
private lateinit var ocLocalFileDataSource: OCLocalFileDataSource
private val fileDao = mockk<FileDao>(relaxUnitFun = true)
private val fileEntitySharedByLink = OC_FILE_ENTITY.copy(sharedByLink = true).apply { this.id = OC_FILE_ENTITY.id }
private val fileSharedByLink = OC_FILE.copy(sharedByLink = true)
private val timeInMilliseconds = 3600000L
@Before
fun setUp() {
ocLocalFileDataSource = OCLocalFileDataSource(fileDao)
}
@Test
fun `getFileById returns a OCFile`() {
every { fileDao.getFileById(OC_FILE_ENTITY.id) } returns OC_FILE_ENTITY
val result = ocLocalFileDataSource.getFileById(OC_FILE_ENTITY.id)
assertEquals(OC_FILE, result)
verify(exactly = 1) { fileDao.getFileById(OC_FILE_ENTITY.id) }
}
@Test
fun `getFileById returns null when DAO returns a null file`() {
every { fileDao.getFileById(OC_FILE_ENTITY.id) } returns null
val result = ocLocalFileDataSource.getFileById(OC_FILE_ENTITY.id)
assertNull(result)
verify(exactly = 1) { fileDao.getFileById(OC_FILE_ENTITY.id) }
}
@Test
fun `getFileByIdAsFlow returns a Flow with an OCFile`() = runTest {
every { fileDao.getFileByIdAsFlow(OC_FILE_ENTITY.id) } returns flowOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getFileByIdAsFlow(OC_FILE_ENTITY.id).first()
assertEquals(OC_FILE, result)
verify(exactly = 1) { fileDao.getFileByIdAsFlow(OC_FILE_ENTITY.id) }
}
@Test
fun `getFileByIdAsFlow returns a Flow with null when DAO returns a Flow with null`() = runTest {
every { fileDao.getFileByIdAsFlow(OC_FILE_ENTITY.id) } returns flowOf(null)
val result = ocLocalFileDataSource.getFileByIdAsFlow(OC_FILE_ENTITY.id).first()
assertNull(result)
verify(exactly = 1) { fileDao.getFileByIdAsFlow(OC_FILE_ENTITY.id) }
}
@Test
fun `getDownloadedFilesForAccount returns a list of OCFile`() {
every { fileDao.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getDownloadedFilesForAccount(OC_ACCOUNT_NAME)
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `getDownloadedFilesForAccount returns an empty list when DAO returns an empty list`() {
every { fileDao.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) } returns emptyList()
val result = ocLocalFileDataSource.getDownloadedFilesForAccount(OC_ACCOUNT_NAME)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getDownloadedFilesForAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `getFileWithSyncInfoByIdAsFlow returns a Flow with an OCFileWithSyncInfo`() = runTest {
every { fileDao.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id) } returns flowOf(OC_FILE_AND_FILE_SYNC)
val result = ocLocalFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id).first()
assertEquals(OC_FILE_WITH_SYNC_INFO_AND_SPACE, result)
verify(exactly = 1) { fileDao.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id) }
}
@Test
fun `getFileWithSyncInfoByIdAsFlow returns a Flow with null when DAO returns a Flow with null`() = runTest {
every { fileDao.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id) } returns flowOf(null)
val result = ocLocalFileDataSource.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id).first()
assertNull(result)
verify(exactly = 1) { fileDao.getFileWithSyncInfoByIdAsFlow(OC_FILE_ENTITY.id) }
}
@Test
fun `getFileByRemotePath returns a OCFile`() {
every { fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, OC_FILE.remotePath, OC_FILE.spaceId) } returns OC_FILE_ENTITY
val result = ocLocalFileDataSource.getFileByRemotePath(OC_FILE.remotePath, OC_FILE.owner, OC_FILE.spaceId)
assertEquals(OC_FILE, result)
verify(exactly = 1) { fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, OC_FILE.remotePath, OC_FILE.spaceId) }
}
@Test
fun `getFileByRemotePath returns null when DAO returns a null file`() {
every { fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, OC_FILE.remotePath, OC_FILE.spaceId) } returns null
val result = ocLocalFileDataSource.getFileByRemotePath(OC_FILE.remotePath, OC_FILE.owner, OC_FILE.spaceId)
assertNull(result)
verify(exactly = 1) { fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, OC_FILE.remotePath, OC_FILE.spaceId) }
}
@Test
fun `getFileByRemotePath returns OCFile of root folder when creating it`() {
val rootFolder = OCFile(
id = 0,
parentId = ROOT_PARENT_ID,
owner = OC_ACCOUNT_NAME,
remotePath = ROOT_PATH,
length = 0,
mimeType = MIME_DIR,
modificationTimestamp = 0,
spaceId = OC_FILE.spaceId,
permissions = "CK",
availableOfflineStatus = AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE,
)
val rootFolderEntity = OCFileEntity(
parentId = ROOT_PARENT_ID,
remotePath = ROOT_PATH,
owner = OC_ACCOUNT_NAME,
permissions = "CK",
remoteId = null,
creationTimestamp = 0,
modificationTimestamp = 0,
etag = "",
remoteEtag = "",
mimeType = MIME_DIR,
length = 0,
fileIsDownloading = false,
modifiedAtLastSyncForData = 0,
lastSyncDateForData = 0,
treeEtag = "",
privateLink = "",
)
every { fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, ROOT_PATH, OC_FILE.spaceId) } returns null
every { fileDao.mergeRemoteAndLocalFile(rootFolder.toEntity()) } returns 0
every { fileDao.getFileById(0) } returns rootFolderEntity
val result = ocLocalFileDataSource.getFileByRemotePath(ROOT_PATH, OC_FILE.owner, OC_FILE.spaceId)
assertEquals(rootFolder, result)
verify(exactly = 1) {
fileDao.getFileByOwnerAndRemotePath(OC_FILE.owner, ROOT_PATH, OC_FILE.spaceId)
fileDao.mergeRemoteAndLocalFile(rootFolder.toEntity())
fileDao.getFileById(0)
}
}
@Test
fun `getFileByRemoteId returns a OCFile`() {
every { fileDao.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!) } returns OC_FILE_ENTITY
val result = ocLocalFileDataSource.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!)
assertEquals(OC_FILE, result)
verify(exactly = 1) { fileDao.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!) }
}
@Test
fun `getFileByRemoteId returns null when DAO returns a null file`() {
every { fileDao.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!) } returns null
val result = ocLocalFileDataSource.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!)
assertNull(result)
verify(exactly = 1) { fileDao.getFileByRemoteId(OC_FILE_ENTITY.remoteId!!) }
}
@Test
fun `getFolderContent returns a list of OCFile`() {
every { fileDao.getFolderContent(OC_FILE_ENTITY.parentId!!) } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getFolderContent(OC_FILE_ENTITY.parentId!!)
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.getFolderContent(OC_FILE_ENTITY.parentId!!) }
}
@Test
fun `getFolderContent returns an empty list when DAO returns an empty list`() {
every { fileDao.getFolderContent(OC_FILE_ENTITY.parentId!!) } returns emptyList()
val result = ocLocalFileDataSource.getFolderContent(OC_FILE_ENTITY.parentId!!)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getFolderContent(OC_FILE_ENTITY.parentId!!) }
}
@Test
fun `getSearchFolderContent returns a list of OCFile`() {
every { fileDao.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test") } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test")
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test") }
}
@Test
fun `getSearchFolderContent returns an empty list when DAO returns an empty list`() {
every { fileDao.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test") } returns emptyList()
val result = ocLocalFileDataSource.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test")
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getSearchFolderContent(OC_FILE_ENTITY.parentId!!, "test") }
}
@Test
fun `getSearchAvailableOfflineFolderContent returns a list of OCFile`() {
every {
fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test")
} returns listOf(OC_FILE_AVAILABLE_OFFLINE_ENTITY)
val result = ocLocalFileDataSource.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test")
assertEquals(listOf(OC_FILE_AVAILABLE_OFFLINE), result)
verify(exactly = 1) { fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_AVAILABLE_OFFLINE_ENTITY.parentId!!, "test") }
}
@Test
fun `getSearchAvailableOfflineFolderContent returns an empty list when DAO returns an empty list`() {
every { fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_ENTITY.parentId!!, "test") } returns emptyList()
val result = ocLocalFileDataSource.getSearchAvailableOfflineFolderContent(OC_FILE_ENTITY.parentId!!, "test")
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getSearchAvailableOfflineFolderContent(OC_FILE_ENTITY.parentId!!, "test") }
}
@Test
fun `getSearchSharedByLinkFolderContent returns a list of OCFile`() {
every { fileDao.getSearchSharedByLinkFolderContent(fileEntitySharedByLink.parentId!!, "test") } returns listOf(fileEntitySharedByLink)
val result = ocLocalFileDataSource.getSearchSharedByLinkFolderContent(fileEntitySharedByLink.parentId!!, "test")
assertEquals(listOf(fileSharedByLink), result)
verify(exactly = 1) { fileDao.getSearchSharedByLinkFolderContent(fileEntitySharedByLink.parentId!!, "test") }
}
@Test
fun `getSearchSharedByLinkFolderContent returns an empty list when DAO returns an empty list`() {
every { fileDao.getSearchSharedByLinkFolderContent(OC_FILE_ENTITY.parentId!!, "test") } returns emptyList()
val result = ocLocalFileDataSource.getSearchSharedByLinkFolderContent(OC_FILE_ENTITY.parentId!!, "test")
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getSearchSharedByLinkFolderContent(OC_FILE_ENTITY.parentId!!, "test") }
}
@Test
fun `getFolderContentWithSyncInfoAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest {
every { fileDao.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!) } returns flowOf(listOf(OC_FILE_AND_FILE_SYNC))
val result = ocLocalFileDataSource.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!).first()
assertEquals(listOf(OC_FILE_WITH_SYNC_INFO_AND_SPACE), result)
verify(exactly = 1) { fileDao.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!) }
}
@Test
fun `getFolderContentWithSyncInfoAsFlow returns a Flow with an empty list when DAO returns a Flow with an empty list`() = runTest {
every { fileDao.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!) } returns flowOf(emptyList())
val result = ocLocalFileDataSource.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!).first()
assertEquals(emptyList<OCFileWithSyncInfo>(), result)
verify(exactly = 1) { fileDao.getFolderContentWithSyncInfoAsFlow(OC_FILE_ENTITY.parentId!!) }
}
@Test
fun `getFolderImages returns a list of OCFile`() {
every { fileDao.getFolderByMimeType(OC_FILE_ENTITY.parentId!!, MIME_PREFIX_IMAGE) } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getFolderImages(OC_FILE_ENTITY.parentId!!)
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.getFolderByMimeType(OC_FILE_ENTITY.parentId!!, MIME_PREFIX_IMAGE) }
}
@Test
fun `getFolderImages returns an empty list when DAO returns an empty list`() {
every { fileDao.getFolderByMimeType(OC_FILE_ENTITY.parentId!!, MIME_PREFIX_IMAGE) } returns emptyList()
val result = ocLocalFileDataSource.getFolderImages(OC_FILE_ENTITY.parentId!!)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getFolderByMimeType(OC_FILE_ENTITY.parentId!!, MIME_PREFIX_IMAGE) }
}
@Test
fun `getSharedByLinkWithSyncInfoForAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest {
val fileAndFileSyncEntitySharedByLink = OC_FILE_AND_FILE_SYNC.copy(file = OC_FILE_ENTITY.copy(sharedByLink = true).apply {
this.id = OC_FILE_ENTITY.id
})
val fileWithSyncInfoSharedByLink = OC_FILE_WITH_SYNC_INFO_AND_SPACE.copy(file = OC_FILE.copy(sharedByLink = true))
every { fileDao.getFilesWithSyncInfoSharedByLinkAsFlow(OC_ACCOUNT_NAME) } returns flowOf(listOf(fileAndFileSyncEntitySharedByLink))
val result = ocLocalFileDataSource.getSharedByLinkWithSyncInfoForAccountAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(listOf(fileWithSyncInfoSharedByLink), result)
verify(exactly = 1) { fileDao.getFilesWithSyncInfoSharedByLinkAsFlow(OC_ACCOUNT_NAME) }
}
@Test
fun `getSharedByLinkWithSyncInfoForAccountAsFlow returns a Flow with an empty list when DAO returns a Flow with an empty list`() = runTest {
every { fileDao.getFilesWithSyncInfoSharedByLinkAsFlow(OC_ACCOUNT_NAME) } returns flowOf(emptyList())
val result = ocLocalFileDataSource.getSharedByLinkWithSyncInfoForAccountAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(emptyList<OCFileWithSyncInfo>(), result)
verify(exactly = 1) { fileDao.getFilesWithSyncInfoSharedByLinkAsFlow(OC_ACCOUNT_NAME) }
}
@Test
fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with a list of OCFileWithSyncInfo`() = runTest {
val fileAndFileSyncEntityAvailableOffline = OC_FILE_AND_FILE_SYNC.copy(
file = OC_FILE_ENTITY.copy(availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE.ordinal).apply {
this.id = OC_FILE_ENTITY.id
}
)
val fileWithSyncInfoAvailableOffline = OC_FILE_WITH_SYNC_INFO_AND_SPACE.copy(
file = OC_FILE.copy(availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE)
)
every {
fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME)
} returns flowOf(listOf(fileAndFileSyncEntityAvailableOffline))
val result = ocLocalFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(listOf(fileWithSyncInfoAvailableOffline), result)
verify(exactly = 1) { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) }
}
@Test
fun `getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow returns a Flow with an empty list when DAO returns a Flow with an empty list`() =
runTest {
every { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) } returns flowOf(emptyList())
val result = ocLocalFileDataSource.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(emptyList<OCFileWithSyncInfo>(), result)
verify(exactly = 1) { fileDao.getFilesWithSyncInfoAvailableOfflineFromAccountAsFlow(OC_ACCOUNT_NAME) }
}
@Test
fun `getFilesAvailableOfflineFromAccount returns a list of OCFile`() {
every { fileDao.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME) } returns listOf(OC_FILE_AVAILABLE_OFFLINE_ENTITY)
val result = ocLocalFileDataSource.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME)
assertEquals(listOf(OC_FILE_AVAILABLE_OFFLINE), result)
verify(exactly = 1) { fileDao.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `getFilesAvailableOfflineFromAccount returns an empty list when DAO returns an empty list`() {
every { fileDao.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME) } returns emptyList()
val result = ocLocalFileDataSource.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getFilesAvailableOfflineFromAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `getFilesAvailableOfflineFromEveryAccount returns a list of OCFile`() {
every { fileDao.getFilesAvailableOfflineFromEveryAccount() } returns listOf(OC_FILE_AVAILABLE_OFFLINE_ENTITY)
val result = ocLocalFileDataSource.getFilesAvailableOfflineFromEveryAccount()
assertEquals(listOf(OC_FILE_AVAILABLE_OFFLINE), result)
verify(exactly = 1) { fileDao.getFilesAvailableOfflineFromEveryAccount() }
}
@Test
fun `getFilesAvailableOfflineFromEveryAccount returns an empty list when DAO returns an empty list`() {
every { fileDao.getFilesAvailableOfflineFromEveryAccount() } returns emptyList()
val result = ocLocalFileDataSource.getFilesAvailableOfflineFromEveryAccount()
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getFilesAvailableOfflineFromEveryAccount() }
}
@Test
fun `getFilesLastUsageIsOlderThanGivenTime returns a list of OCFile`() {
every { fileDao.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds)
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) }
}
@Test
fun `getFilesLastUsageIsOlderThanGivenTime returns an empty list when DAO returns an empty list`() {
every { fileDao.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) } returns emptyList()
val result = ocLocalFileDataSource.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.getFilesWithLastUsageOlderThanGivenTime(timeInMilliseconds) }
}
@Test
fun `moveFile moves a file correctly`() {
val finalRemotePath = "/final/remote/path"
val finalStoragePath = "/final/storage/path"
ocLocalFileDataSource.moveFile(OC_FILE, OC_FOLDER, finalRemotePath, finalStoragePath)
verify(exactly = 1) { fileDao.moveFile(OC_FILE_ENTITY, OC_FOLDER_ENTITY, finalRemotePath, finalStoragePath) }
}
@Test
fun `copyFile copies a file correctly`() {
val finalRemotePath = "/final/remote/path"
val remoteId = "testRemoteId"
ocLocalFileDataSource.copyFile(OC_FILE, OC_FOLDER, finalRemotePath, remoteId, false)
verify(exactly = 1) { fileDao.copy(OC_FILE_ENTITY, OC_FOLDER_ENTITY, finalRemotePath, remoteId, false) }
}
@Test
fun `saveFilesInFolderAndReturnTheFilesThatChanged saves a list of OCFile and returns only the changed files`() {
every { fileDao.insertFilesInFolderAndReturnTheFilesThatChanged(OC_FOLDER_ENTITY, listOf(OC_FILE_ENTITY)) } returns listOf(OC_FILE_ENTITY)
val result = ocLocalFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(listOf(OC_FILE), OC_FOLDER)
assertEquals(listOf(OC_FILE), result)
verify(exactly = 1) { fileDao.insertFilesInFolderAndReturnTheFilesThatChanged(OC_FOLDER_ENTITY, listOf(OC_FILE_ENTITY)) }
}
@Test
fun `saveFilesInFolderAndReturnTheFilesThatChanged returns an empty list when DAO returns an empty list`() {
every { fileDao.insertFilesInFolderAndReturnTheFilesThatChanged(OC_FOLDER_ENTITY, emptyList()) } returns emptyList()
val result = ocLocalFileDataSource.saveFilesInFolderAndReturnTheFilesThatChanged(emptyList(), OC_FOLDER)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) { fileDao.insertFilesInFolderAndReturnTheFilesThatChanged(OC_FOLDER_ENTITY, emptyList()) }
}
@Test
fun `saveFile saves a file correctly`() {
ocLocalFileDataSource.saveFile(OC_FILE)
verify(exactly = 1) { fileDao.upsert(OC_FILE_ENTITY) }
}
@Test
fun `saveConflict saves the etagInConflict related to a file correctly`() {
val etagInConflict = "error"
ocLocalFileDataSource.saveConflict(OC_FILE_ENTITY.id, etagInConflict)
verify(exactly = 1) { fileDao.updateConflictStatusForFile(OC_FILE_ENTITY.id, etagInConflict) }
}
@Test
fun `cleanConflict saves a null etagInConflict related to a file correctly`() {
ocLocalFileDataSource.cleanConflict(OC_FILE_ENTITY.id)
verify(exactly = 1) { fileDao.updateConflictStatusForFile(OC_FILE_ENTITY.id, null) }
}
@Test
fun `deleteFile removes a file correctly`() {
ocLocalFileDataSource.deleteFile(OC_FILE_ENTITY.id)
verify(exactly = 1) { fileDao.deleteFileById(OC_FILE_ENTITY.id) }
}
@Test
fun `deleteFilesForAccount removes files for an account correctly`() {
ocLocalFileDataSource.deleteFilesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) { fileDao.deleteFilesForAccount(OC_ACCOUNT_NAME) }
}
@Test
fun `renameFile performs a move in the file to change its name`() {
every { fileDao.getFileById(OC_FILE.parentId!!) } returns OC_FOLDER_ENTITY
val finalRemotePath = "/final/remote/path"
val finalStoragePath = "/final/storage/path"
ocLocalFileDataSource.renameFile(OC_FILE, finalRemotePath, finalStoragePath)
verify(exactly = 1) {
fileDao.getFileById(OC_FILE.parentId!!)
fileDao.moveFile(OC_FILE_ENTITY, OC_FOLDER_ENTITY, finalRemotePath, finalStoragePath)
}
}
@Test
fun `disableThumbnailsForFile disables thumbnails for a file correctly`() {
ocLocalFileDataSource.disableThumbnailsForFile(OC_FILE_ENTITY.id)
verify(exactly = 1) { fileDao.disableThumbnailsForFile(OC_FILE_ENTITY.id) }
}
@Test
fun `updateAvailableOfflineStatusForFile updates available offline status for a file correctly`() {
ocLocalFileDataSource.updateAvailableOfflineStatusForFile(OC_FILE, AvailableOfflineStatus.AVAILABLE_OFFLINE)
verify(exactly = 1) { fileDao.updateAvailableOfflineStatusForFile(OC_FILE, AvailableOfflineStatus.AVAILABLE_OFFLINE.ordinal) }
}
@Test
fun `updateDownloadedFilesStorageDirectoryInStoragePath updates storage path for downloaded files correctly`() {
val oldDirectory = "/old/directory"
val newDirectory = "/new/directory"
ocLocalFileDataSource.updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory, newDirectory)
verify(exactly = 1) { fileDao.updateDownloadedFilesStorageDirectoryInStoragePath(oldDirectory, newDirectory) }
}
@Test
fun `updateFileWithLastUsage updates last usage for a file correctly`() {
val lastUsage = 12345L
ocLocalFileDataSource.updateFileWithLastUsage(OC_FILE_ENTITY.id, lastUsage)
verify(exactly = 1) { fileDao.updateFileWithLastUsage(OC_FILE_ENTITY.id, lastUsage) }
}
@Test
fun `saveDownloadWorkerUuid saves the worker UUID for a file correctly`() {
val workerUuid = UUID.randomUUID()
ocLocalFileDataSource.saveDownloadWorkerUuid(OC_FILE_ENTITY.id, workerUuid)
verify(exactly = 1) { fileDao.updateSyncStatusForFile(OC_FILE_ENTITY.id, workerUuid) }
}
@Test
fun `cleanWorkersUuid saves a null worker UUID for a file correctly`() {
ocLocalFileDataSource.cleanWorkersUuid(OC_FILE_ENTITY.id)
verify(exactly = 1) { fileDao.updateSyncStatusForFile(OC_FILE_ENTITY.id, null) }
}
}
@@ -0,0 +1,405 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.files.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.files.datasources.implementation.OCRemoteFileDataSource.Companion.toModel
import eu.qsfera.android.domain.files.model.OCFile
import eu.qsfera.android.lib.resources.files.RemoteFile
import eu.qsfera.android.lib.resources.files.services.implementation.OCFileService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_FILE
import eu.qsfera.android.testutil.OC_FOLDER
import eu.qsfera.android.testutil.REMOTE_FILE
import eu.qsfera.android.testutil.REMOTE_META_FILE
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class OCRemoteFileDataSourceTest {
private lateinit var ocRemoteFileDataSource: OCRemoteFileDataSource
private val ocFileService: OCFileService = mockk()
private val clientManager: ClientManager = mockk(relaxed = true)
private val sourceRemotePath = "/source/remote/path/file.txt"
private val targetRemotePath = "/target/remote/path/file.txt"
private val remoteResult = createRemoteOperationResultMock(data = Unit, isSuccess = true)
@Before
fun setUp() {
every { clientManager.getFileService(OC_ACCOUNT_NAME) } returns ocFileService
ocRemoteFileDataSource = OCRemoteFileDataSource(clientManager)
}
@Test
fun `checkPathExistence returns true when the path exists in remote`() {
val checkPathExistenceRemoteResult = createRemoteOperationResultMock(data = true, isSuccess = true)
every {
ocFileService.checkPathExistence(sourceRemotePath, true)
} returns checkPathExistenceRemoteResult
val checkPathExistence = ocRemoteFileDataSource.checkPathExistence(sourceRemotePath, true, OC_ACCOUNT_NAME, null)
assertTrue(checkPathExistence)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.checkPathExistence(sourceRemotePath, true)
}
}
@Test
fun `checkPathExistence returns false when the path does not exist in remote`() {
val checkPathExistenceRemoteResult = createRemoteOperationResultMock(data = false, isSuccess = true)
every {
ocFileService.checkPathExistence(sourceRemotePath, true)
} returns checkPathExistenceRemoteResult
val checkPathExistence = ocRemoteFileDataSource.checkPathExistence(sourceRemotePath, true, OC_ACCOUNT_NAME, null)
assertFalse(checkPathExistence)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.checkPathExistence(sourceRemotePath, true)
}
}
@Test
fun `copyFile copies a file and returns a fileRemoteId`() {
val fileRemoteId = "fileRemoteId"
val remoteResult = createRemoteOperationResultMock(data = fileRemoteId as String?, isSuccess = true)
every {
ocFileService.copyFile(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
sourceSpaceWebDavUrl = null,
targetSpaceWebDavUrl = null,
replace = any(),
)
} returns remoteResult
val result = ocRemoteFileDataSource.copyFile(
sourceRemotePath,
targetRemotePath,
OC_ACCOUNT_NAME,
null,
null,
true,
)
assertEquals(fileRemoteId, result)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.copyFile(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
sourceSpaceWebDavUrl = null,
targetSpaceWebDavUrl = null,
replace = true,
)
}
}
@Test
fun `createFolder creates folder in remote correctly`() {
every {
ocFileService.createFolder(remotePath = sourceRemotePath, createFullPath = false)
} returns remoteResult
ocRemoteFileDataSource.createFolder(
remotePath = sourceRemotePath,
createFullPath = false,
isChunksFolder = false,
accountName = OC_ACCOUNT_NAME,
spaceWebDavUrl = null
)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.createFolder(sourceRemotePath, false)
}
}
@Test
fun `getAvailableRemotePath returns same String path if file does not exist`() {
val checkPathExistenceRemoteResult = createRemoteOperationResultMock(data = false, isSuccess = true)
every {
ocFileService.checkPathExistence(sourceRemotePath, true)
} returns checkPathExistenceRemoteResult
val firstCopyName = ocRemoteFileDataSource.getAvailableRemotePath(
remotePath = sourceRemotePath,
accountName = OC_ACCOUNT_NAME,
spaceWebDavUrl = null,
isUserLogged = true,
)
assertEquals(sourceRemotePath, firstCopyName)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.checkPathExistence(sourceRemotePath, true)
}
}
@Test
fun `getAvailableRemotePath returns String path with (1) if file already exists`() {
val checkPathExistenceRemoteResultFirst = createRemoteOperationResultMock(data = true, isSuccess = true)
val checkPathExistenceRemoteResultSecond = createRemoteOperationResultMock(data = false, isSuccess = true)
val finalRemotePath = "/source/remote/path/file (1).txt"
every {
ocFileService.checkPathExistence(sourceRemotePath, true)
} returns checkPathExistenceRemoteResultFirst
every {
ocFileService.checkPathExistence(finalRemotePath, true)
} returns checkPathExistenceRemoteResultSecond
val firstCopyName = ocRemoteFileDataSource.getAvailableRemotePath(
remotePath = sourceRemotePath,
accountName = OC_ACCOUNT_NAME,
spaceWebDavUrl = null,
isUserLogged = true,
)
assertEquals(finalRemotePath, firstCopyName)
verify(exactly = 2) { clientManager.getFileService(OC_ACCOUNT_NAME) }
verify(exactly = 1) {
ocFileService.checkPathExistence(sourceRemotePath, true)
ocFileService.checkPathExistence(finalRemotePath, true)
}
}
@Test
fun `getAvailableRemotePath returns String path with two (1) if file with (1) already exists`() {
val checkPathExistenceRemoteResultFirst = createRemoteOperationResultMock(data = true, isSuccess = true)
val checkPathExistenceRemoteResultSecond = createRemoteOperationResultMock(data = false, isSuccess = true)
val remotePath = "/remote/path/file (1).txt"
val finalRemotePath = "/remote/path/file (1) (1).txt"
every {
ocFileService.checkPathExistence(remotePath, true)
} returns checkPathExistenceRemoteResultFirst
every {
ocFileService.checkPathExistence(finalRemotePath, true)
} returns checkPathExistenceRemoteResultSecond
val firstCopyName = ocRemoteFileDataSource.getAvailableRemotePath(
remotePath = remotePath,
accountName = OC_ACCOUNT_NAME,
spaceWebDavUrl = null,
isUserLogged = true,
)
assertEquals(finalRemotePath, firstCopyName)
verify(exactly = 2) { clientManager.getFileService(OC_ACCOUNT_NAME) }
verify(exactly = 1) {
ocFileService.checkPathExistence(remotePath, true)
ocFileService.checkPathExistence(finalRemotePath, true)
}
}
@Test
fun `moveFile moves a file correctly`() {
every {
ocFileService.moveFile(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
spaceWebDavUrl = null,
replace = any(),
)
} returns remoteResult
ocRemoteFileDataSource.moveFile(
sourceRemotePath,
targetRemotePath,
OC_ACCOUNT_NAME,
null,
true,
)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.moveFile(
sourceRemotePath = sourceRemotePath,
targetRemotePath = targetRemotePath,
spaceWebDavUrl = null,
replace = true,
)
}
}
@Test
fun `readFile returns a OCFile`() {
val remoteResult = createRemoteOperationResultMock(data = REMOTE_FILE, isSuccess = true)
every {
ocFileService.readFile(
remotePath = REMOTE_FILE.remotePath,
spaceWebDavUrl = null
)
} returns remoteResult
val result = ocRemoteFileDataSource.readFile(
REMOTE_FILE.remotePath,
OC_ACCOUNT_NAME,
null,
)
assertEquals(REMOTE_FILE.toModel(), result)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.readFile(
REMOTE_FILE.remotePath,
null,
)
}
}
@Test
fun `refreshFolder returns a list of OCFile`() {
val remoteResult = createRemoteOperationResultMock(data = arrayListOf(REMOTE_FILE), isSuccess = true)
every {
ocFileService.refreshFolder(OC_FOLDER.remotePath, null)
} returns remoteResult
val result = ocRemoteFileDataSource.refreshFolder(
OC_FOLDER.remotePath,
OC_ACCOUNT_NAME,
null,
)
assertEquals(arrayListOf(REMOTE_FILE.toModel()), result)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.refreshFolder(OC_FOLDER.remotePath, null)
}
}
@Test
fun `refreshFolder returns an empty list if service returns an empty list`() {
val remoteResult = createRemoteOperationResultMock(data = arrayListOf<RemoteFile>(), isSuccess = true)
every {
ocFileService.refreshFolder(OC_FOLDER.remotePath, null)
} returns remoteResult
val result = ocRemoteFileDataSource.refreshFolder(
OC_FOLDER.remotePath,
OC_ACCOUNT_NAME,
null,
)
assertEquals(emptyList<OCFile>(), result)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.refreshFolder(OC_FOLDER.remotePath, null)
}
}
@Test
fun `deleteFile deletes a file correctly`() {
every {
ocFileService.removeFile(OC_FILE.remotePath, null)
} returns remoteResult
ocRemoteFileDataSource.deleteFile(
OC_FILE.remotePath,
OC_ACCOUNT_NAME,
null,
)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.removeFile(OC_FILE.remotePath, null)
}
}
@Test
fun `renameFile renames a file correctly`() {
val oldName = "oldName"
val oldRemotePath = "/old/remote/path"
val newName = "newName"
every {
ocFileService.renameFile(oldName, oldRemotePath, newName, false, null)
} returns remoteResult
ocRemoteFileDataSource.renameFile(
oldName,
oldRemotePath,
newName,
false,
OC_ACCOUNT_NAME,
null)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.renameFile(oldName, oldRemotePath, newName, false, null)
}
}
@Test
fun `getMetaFile returns a OCMetaFile`() {
val remoteResult = createRemoteOperationResultMock(data = REMOTE_META_FILE, isSuccess = true)
every {
ocFileService.getMetaFileInfo(
fileId = OC_FILE.remoteId!!,
)
} returns remoteResult
val result = ocRemoteFileDataSource.getMetaFile(
OC_FILE.remoteId!!,
OC_ACCOUNT_NAME,
)
assertEquals(REMOTE_META_FILE.toModel(), result)
verify(exactly = 1) {
clientManager.getFileService(OC_ACCOUNT_NAME)
ocFileService.getMetaFileInfo(OC_FILE.remoteId!!)
}
}
}
@@ -0,0 +1,113 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.folderbackup.datasources.implementation
import eu.qsfera.android.data.folderbackup.datasources.implementation.OCLocalFolderBackupDataSource.Companion.toModel
import eu.qsfera.android.data.folderbackup.db.FolderBackupDao
import eu.qsfera.android.domain.automaticuploads.model.FolderBackUpConfiguration
import eu.qsfera.android.testutil.OC_BACKUP
import eu.qsfera.android.testutil.OC_BACKUP_ENTITY
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
class OCLocalFolderBackupDataSourceTest {
private lateinit var ocLocalFolderBackupDataSource: OCLocalFolderBackupDataSource
private val folderBackupDao = mockk<FolderBackupDao>(relaxed = true)
@Before
fun setUp() {
ocLocalFolderBackupDataSource = OCLocalFolderBackupDataSource(folderBackupDao)
}
@Test
fun `getAutomaticUploadsConfiguration returns an AutomaticUploadsConfiguration when having valid configurations`() {
every { folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.pictureUploadsName) } returns OC_BACKUP_ENTITY
every { folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.videoUploadsName) } returns OC_BACKUP_ENTITY
val resultCurrent = ocLocalFolderBackupDataSource.getAutomaticUploadsConfiguration()
assertEquals(OC_BACKUP_ENTITY.toModel(), resultCurrent?.pictureUploadsConfiguration)
assertEquals(OC_BACKUP_ENTITY.toModel(), resultCurrent?.videoUploadsConfiguration)
verify(exactly = 1) {
folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.pictureUploadsName)
folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.videoUploadsName)
}
}
@Test
fun `getAutomaticUploadsConfiguration returns null when there are not configurations`() {
every { folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.pictureUploadsName) } returns null
every { folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.videoUploadsName) } returns null
val resultCurrent = ocLocalFolderBackupDataSource.getAutomaticUploadsConfiguration()
assertNull(resultCurrent)
verify(exactly = 1) {
folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.pictureUploadsName)
folderBackupDao.getFolderBackUpConfigurationByName(FolderBackUpConfiguration.videoUploadsName)
}
}
@Test
fun `getFolderBackupConfigurationByNameAsFlow returns a Flow of AutomaticUploadsConfiguration when having valid configurations`() = runBlocking {
every { folderBackupDao.getFolderBackUpConfigurationByNameAsFlow(FolderBackUpConfiguration.pictureUploadsName) } returns flowOf(
OC_BACKUP_ENTITY
)
val resultCurrent = ocLocalFolderBackupDataSource.getFolderBackupConfigurationByNameAsFlow(FolderBackUpConfiguration.pictureUploadsName)
val result = resultCurrent.first()
assertEquals(OC_BACKUP_ENTITY.toModel(), result)
verify(exactly = 1) {
folderBackupDao.getFolderBackUpConfigurationByNameAsFlow(FolderBackUpConfiguration.pictureUploadsName)
}
}
@Test
fun `saveFolderBackupConfiguration saves valid configurations correctly`() {
ocLocalFolderBackupDataSource.saveFolderBackupConfiguration(OC_BACKUP)
verify(exactly = 1) {
folderBackupDao.update(OC_BACKUP_ENTITY)
}
}
@Test
fun `resetFolderBackupConfigurationByName removes current folder backup configuration correctly`() {
ocLocalFolderBackupDataSource.resetFolderBackupConfigurationByName(FolderBackUpConfiguration.pictureUploadsName)
verify(exactly = 1) {
folderBackupDao.delete(FolderBackUpConfiguration.pictureUploadsName)
}
}
}
@@ -0,0 +1,115 @@
/**
* qsfera Android client application
*
* @author Jorge Aguado Recio
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.folderbackup.repository
import eu.qsfera.android.data.folderbackup.datasources.LocalFolderBackupDataSource
import eu.qsfera.android.testutil.OC_AUTOMATIC_UPLOADS_CONFIGURATION
import eu.qsfera.android.testutil.OC_BACKUP
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
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.Test
class OCFolderBackupRepositoryTest {
private val localFolderBackupDataSource = mockk<LocalFolderBackupDataSource>(relaxUnitFun = true)
private val ocFolderBackupRepository = OCFolderBackupRepository(localFolderBackupDataSource)
@Test
fun `getAutomaticUploadsConfiguration returns an AutomaticUploadsConfiguration`() {
every {
localFolderBackupDataSource.getAutomaticUploadsConfiguration()
} returns OC_AUTOMATIC_UPLOADS_CONFIGURATION
val automaticUploadsConfiguration = ocFolderBackupRepository.getAutomaticUploadsConfiguration()
assertEquals(OC_AUTOMATIC_UPLOADS_CONFIGURATION, automaticUploadsConfiguration)
verify(exactly = 1) {
localFolderBackupDataSource.getAutomaticUploadsConfiguration()
}
}
@Test
fun `getAutomaticUploadsConfiguration returns null when local datasource returns a null configuration`() {
every {
localFolderBackupDataSource.getAutomaticUploadsConfiguration()
} returns null
val automaticUploadsConfiguration = ocFolderBackupRepository.getAutomaticUploadsConfiguration()
assertNull(automaticUploadsConfiguration)
verify(exactly = 1) {
localFolderBackupDataSource.getAutomaticUploadsConfiguration()
}
}
@Test
fun `getFolderBackupConfigurationByNameAsFlow returns a Flow with a FolderBackUpConfiguration`() = runTest {
every {
localFolderBackupDataSource.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name)
} returns flowOf(OC_BACKUP)
val folderBackUpConfiguration = ocFolderBackupRepository.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name).first()
assertEquals(OC_BACKUP, folderBackUpConfiguration)
verify(exactly = 1) {
localFolderBackupDataSource.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name)
}
}
@Test
fun `getFolderBackupConfigurationByNameAsFlow returns a Flow with null when local datasource returns a Flow with null `() = runTest {
every {
localFolderBackupDataSource.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name)
} returns flowOf(null)
val folderBackUpConfiguration = ocFolderBackupRepository.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name).first()
assertNull(folderBackUpConfiguration)
verify(exactly = 1) {
localFolderBackupDataSource.getFolderBackupConfigurationByNameAsFlow(OC_BACKUP.name)
}
}
@Test
fun `saveFolderBackupConfiguration saves a folder backup configuration correctly`() {
ocFolderBackupRepository.saveFolderBackupConfiguration(OC_BACKUP)
verify(exactly = 1) {
localFolderBackupDataSource.saveFolderBackupConfiguration(OC_BACKUP)
}
}
@Test
fun `resetFolderBackupConfigurationByName resets a folder backup configuration by name correctly`() {
ocFolderBackupRepository.resetFolderBackupConfigurationByName(OC_BACKUP.name)
verify(exactly = 1) {
localFolderBackupDataSource.resetFolderBackupConfigurationByName(OC_BACKUP.name)
}
}
}
@@ -0,0 +1,91 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2021 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.oauth
import eu.qsfera.android.lib.resources.oauth.params.ClientRegistrationParams
import eu.qsfera.android.lib.resources.oauth.params.TokenRequestParams
import eu.qsfera.android.lib.resources.oauth.responses.ClientRegistrationResponse
import eu.qsfera.android.lib.resources.oauth.responses.OIDCDiscoveryResponse
import eu.qsfera.android.lib.resources.oauth.responses.TokenResponse
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION_REQUEST
import eu.qsfera.android.testutil.oauth.OC_OIDC_SERVER_CONFIGURATION
import eu.qsfera.android.testutil.oauth.OC_TOKEN_REQUEST_ACCESS
import eu.qsfera.android.testutil.oauth.OC_TOKEN_REQUEST_REFRESH
import eu.qsfera.android.testutil.oauth.OC_TOKEN_RESPONSE
val OC_REMOTE_OIDC_DISCOVERY_RESPONSE = OIDCDiscoveryResponse(
authorizationEndpoint = OC_OIDC_SERVER_CONFIGURATION.authorizationEndpoint,
checkSessionIframe = OC_OIDC_SERVER_CONFIGURATION.checkSessionIframe,
endSessionEndpoint = OC_OIDC_SERVER_CONFIGURATION.endSessionEndpoint,
issuer = OC_OIDC_SERVER_CONFIGURATION.issuer,
registrationEndpoint = OC_OIDC_SERVER_CONFIGURATION.registrationEndpoint,
responseTypesSupported = OC_OIDC_SERVER_CONFIGURATION.responseTypesSupported,
scopesSupported = OC_OIDC_SERVER_CONFIGURATION.scopesSupported,
tokenEndpoint = OC_OIDC_SERVER_CONFIGURATION.tokenEndpoint,
tokenEndpointAuthMethodsSupported = OC_OIDC_SERVER_CONFIGURATION.tokenEndpointAuthMethodsSupported,
userinfoEndpoint = OC_OIDC_SERVER_CONFIGURATION.userInfoEndpoint
)
val OC_REMOTE_TOKEN_REQUEST_PARAMS_ACCESS = TokenRequestParams.Authorization(
tokenEndpoint = OC_TOKEN_REQUEST_ACCESS.tokenEndpoint,
clientAuth = OC_TOKEN_REQUEST_ACCESS.clientAuth,
grantType = OC_TOKEN_REQUEST_ACCESS.grantType,
scope = OC_TOKEN_REQUEST_ACCESS.scope,
clientId = null,
clientSecret = null,
authorizationCode = OC_TOKEN_REQUEST_ACCESS.authorizationCode,
redirectUri = OC_TOKEN_REQUEST_ACCESS.redirectUri,
codeVerifier = OC_TOKEN_REQUEST_ACCESS.codeVerifier
)
val OC_REMOTE_TOKEN_REQUEST_PARAMS_REFRESH = TokenRequestParams.RefreshToken(
tokenEndpoint = OC_TOKEN_REQUEST_REFRESH.tokenEndpoint,
clientAuth = OC_TOKEN_REQUEST_REFRESH.clientAuth,
grantType = OC_TOKEN_REQUEST_REFRESH.grantType,
scope = OC_TOKEN_REQUEST_REFRESH.scope,
clientId = null,
clientSecret = null,
refreshToken = OC_TOKEN_REQUEST_REFRESH.refreshToken
)
val OC_REMOTE_TOKEN_RESPONSE = TokenResponse(
accessToken = OC_TOKEN_RESPONSE.accessToken,
expiresIn = OC_TOKEN_RESPONSE.expiresIn,
refreshToken = OC_TOKEN_RESPONSE.refreshToken,
tokenType = OC_TOKEN_RESPONSE.tokenType,
userId = OC_TOKEN_RESPONSE.userId,
scope = OC_TOKEN_RESPONSE.scope,
additionalParameters = OC_TOKEN_RESPONSE.additionalParameters
)
val OC_REMOTE_CLIENT_REGISTRATION_PARAMS = ClientRegistrationParams(
registrationEndpoint = OC_CLIENT_REGISTRATION_REQUEST.registrationEndpoint,
clientName = OC_CLIENT_REGISTRATION_REQUEST.clientName,
redirectUris = OC_CLIENT_REGISTRATION_REQUEST.redirectUris,
tokenEndpointAuthMethod = OC_CLIENT_REGISTRATION_REQUEST.tokenEndpointAuthMethod,
applicationType = OC_CLIENT_REGISTRATION_REQUEST.applicationType
)
val OC_REMOTE_CLIENT_REGISTRATION_RESPONSE = ClientRegistrationResponse(
clientId = OC_CLIENT_REGISTRATION.clientId,
clientSecret = OC_CLIENT_REGISTRATION.clientSecret,
clientIdIssuedAt = OC_CLIENT_REGISTRATION.clientIdIssuedAt,
clientSecretExpiration = OC_CLIENT_REGISTRATION.clientSecretExpiration
)
@@ -0,0 +1,157 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2021 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.oauth.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.oauth.OC_REMOTE_CLIENT_REGISTRATION_RESPONSE
import eu.qsfera.android.data.oauth.OC_REMOTE_OIDC_DISCOVERY_RESPONSE
import eu.qsfera.android.data.oauth.OC_REMOTE_TOKEN_RESPONSE
import eu.qsfera.android.lib.common.QSferaClient
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import eu.qsfera.android.lib.resources.oauth.responses.ClientRegistrationResponse
import eu.qsfera.android.lib.resources.oauth.responses.OIDCDiscoveryResponse
import eu.qsfera.android.lib.resources.oauth.responses.TokenResponse
import eu.qsfera.android.lib.resources.oauth.services.OIDCService
import eu.qsfera.android.testutil.OC_SECURE_BASE_URL
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION_REQUEST
import eu.qsfera.android.testutil.oauth.OC_OIDC_SERVER_CONFIGURATION
import eu.qsfera.android.testutil.oauth.OC_TOKEN_REQUEST_ACCESS
import eu.qsfera.android.testutil.oauth.OC_TOKEN_REQUEST_ACCESS_PUBLIC_CLIENT
import eu.qsfera.android.testutil.oauth.OC_TOKEN_RESPONSE
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class OCRemoteOAuthDataSourceTest {
private lateinit var remoteOAuthDataSource: OCRemoteOAuthDataSource
private val clientManager: ClientManager = mockk(relaxed = true)
private val ocClientMocked: QSferaClient = mockk()
private val oidcService: OIDCService = mockk()
@Before
fun setUp() {
every { clientManager.getClientForAnonymousCredentials(any(), any()) } returns ocClientMocked
remoteOAuthDataSource = OCRemoteOAuthDataSource(
clientManager = clientManager,
oidcService = oidcService,
)
}
@Test
fun `performOIDCDiscovery returns a OIDCServerConfiguration`() {
val oidcDiscoveryResult: RemoteOperationResult<OIDCDiscoveryResponse> =
createRemoteOperationResultMock(data = OC_REMOTE_OIDC_DISCOVERY_RESPONSE, isSuccess = true)
every {
oidcService.getOIDCServerDiscovery(ocClientMocked)
} returns oidcDiscoveryResult
val oidcDiscovery = remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_BASE_URL)
assertNotNull(oidcDiscovery)
assertEquals(OC_OIDC_SERVER_CONFIGURATION, oidcDiscovery)
verify(exactly = 1) {
clientManager.getClientForAnonymousCredentials(OC_SECURE_BASE_URL, false)
oidcService.getOIDCServerDiscovery(ocClientMocked)
}
}
@Test
fun `performTokenRequest returns a TokenResponse`() {
val tokenResponseResult: RemoteOperationResult<TokenResponse> =
createRemoteOperationResultMock(data = OC_REMOTE_TOKEN_RESPONSE, isSuccess = true)
every {
oidcService.performTokenRequest(ocClientMocked, any())
} returns tokenResponseResult
val tokenResponse = remoteOAuthDataSource.performTokenRequest(OC_TOKEN_REQUEST_ACCESS)
assertNotNull(tokenResponse)
assertEquals(OC_TOKEN_RESPONSE, tokenResponse)
verify(exactly = 1) {
clientManager.getClientForAnonymousCredentials(OC_SECURE_BASE_URL, any())
oidcService.performTokenRequest(ocClientMocked, any())
}
}
/**
* Test for public PKCE clients (RFC 7636).
* Public clients MUST NOT send Authorization header during token exchange.
* This test verifies that token requests work correctly with empty clientAuth.
*/
@Test
fun `performTokenRequest with public PKCE client returns a TokenResponse`() {
val tokenResponseResult: RemoteOperationResult<TokenResponse> =
createRemoteOperationResultMock(data = OC_REMOTE_TOKEN_RESPONSE, isSuccess = true)
every {
oidcService.performTokenRequest(ocClientMocked, any())
} returns tokenResponseResult
// Verify the fixture has empty clientAuth for public clients
assertTrue(
"clientAuth should be empty for public clients",
OC_TOKEN_REQUEST_ACCESS_PUBLIC_CLIENT.clientAuth.isEmpty()
)
val tokenResponse = remoteOAuthDataSource.performTokenRequest(OC_TOKEN_REQUEST_ACCESS_PUBLIC_CLIENT)
assertNotNull(tokenResponse)
assertEquals(OC_TOKEN_RESPONSE, tokenResponse)
verify(exactly = 1) {
clientManager.getClientForAnonymousCredentials(OC_SECURE_BASE_URL, any())
oidcService.performTokenRequest(ocClientMocked, any())
}
}
@Test
fun `registerClient returns a ClientRegistrationInfo`() {
val clientRegistrationResponse: RemoteOperationResult<ClientRegistrationResponse> =
createRemoteOperationResultMock(data = OC_REMOTE_CLIENT_REGISTRATION_RESPONSE, isSuccess = true)
every {
oidcService.registerClientWithRegistrationEndpoint(ocClientMocked, any())
} returns clientRegistrationResponse
val clientRegistrationInfo = remoteOAuthDataSource.registerClient(OC_CLIENT_REGISTRATION_REQUEST)
assertNotNull(clientRegistrationInfo)
assertEquals(OC_CLIENT_REGISTRATION, clientRegistrationInfo)
verify(exactly = 1) {
clientManager.getClientForAnonymousCredentials(OC_CLIENT_REGISTRATION_REQUEST.registrationEndpoint, false)
oidcService.registerClientWithRegistrationEndpoint(ocClientMocked, any())
}
}
}
@@ -0,0 +1,84 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.oauth.repository
import eu.qsfera.android.data.oauth.datasources.RemoteOAuthDataSource
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_OIDC_AUTH
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION
import eu.qsfera.android.testutil.oauth.OC_CLIENT_REGISTRATION_REQUEST
import eu.qsfera.android.testutil.oauth.OC_OIDC_SERVER_CONFIGURATION
import eu.qsfera.android.testutil.oauth.OC_TOKEN_REQUEST_ACCESS
import eu.qsfera.android.testutil.oauth.OC_TOKEN_RESPONSE
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Test
class OCOAuthRepositoryTest {
private val remoteOAuthDataSource = mockk<RemoteOAuthDataSource>()
private val oAuthRepository = OCOAuthRepository(remoteOAuthDataSource)
@Test
fun `performOIDCDiscovery returns an OIDCServerConfiguration`() {
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
} returns OC_OIDC_SERVER_CONFIGURATION
val oidcServerConfiguration = oAuthRepository.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
assertEquals(OC_OIDC_SERVER_CONFIGURATION, oidcServerConfiguration)
verify(exactly = 1) {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
}
}
@Test
fun `performTokenRequest returns a TokenResponse`() {
every {
remoteOAuthDataSource.performTokenRequest(OC_TOKEN_REQUEST_ACCESS)
} returns OC_TOKEN_RESPONSE
val tokenResponse = oAuthRepository.performTokenRequest(OC_TOKEN_REQUEST_ACCESS)
assertEquals(OC_TOKEN_RESPONSE, tokenResponse)
verify(exactly = 1) {
remoteOAuthDataSource.performTokenRequest(OC_TOKEN_REQUEST_ACCESS)
}
}
@Test
fun `registerClient returns a ClientRegistrationInfo`() {
every {
remoteOAuthDataSource.registerClient(OC_CLIENT_REGISTRATION_REQUEST)
} returns OC_CLIENT_REGISTRATION
val clientRegistrationInfo = oAuthRepository.registerClient(OC_CLIENT_REGISTRATION_REQUEST)
assertEquals(OC_CLIENT_REGISTRATION, clientRegistrationInfo)
verify(exactly = 1) {
remoteOAuthDataSource.registerClient(OC_CLIENT_REGISTRATION_REQUEST)
}
}
}
@@ -0,0 +1,293 @@
package eu.qsfera.android.data.providers
import android.content.Context
import android.net.Uri
import eu.qsfera.android.domain.transfers.model.OCTransfer
import eu.qsfera.android.testutil.OC_FILE
import eu.qsfera.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.io.File
import java.nio.file.Files
class ScopedStorageProviderTest {
private lateinit var scopedStorageProvider: ScopedStorageProvider
private lateinit var context: Context
private lateinit var filesDir: File
private val spaceId = OC_SPACE_PROJECT_WITH_IMAGE.id
private val accountName = "qsfera"
private val newName = "qsferaNewName.txt"
private val uriEncoded = "/path/to/remote/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
private val rootFolderName = "root_folder"
private val expectedSizeOfDirectoryValue: Long = 100
private val separator = File.separator
private val remotePath =
listOf("storage", "emulated", "0", "qsfera", "remotepath").joinToString(separator, prefix = separator)
private val rootFolderPath get() = filesDir.absolutePath + File.separator + rootFolderName
private lateinit var directory: File
@Before
fun setUp() {
context = mockk()
filesDir = Files.createTempDirectory("scoped-storage-provider").toFile().apply { deleteOnExit() }
directory = File(filesDir, "dir").apply {
mkdirs()
File(this, "child.bin").writeBytes(ByteArray(expectedSizeOfDirectoryValue.toInt()))
}
scopedStorageProvider = ScopedStorageProvider(rootFolderName, context)
every { context.filesDir } returns filesDir
}
@Test
fun `getPrimaryStorageDirectory returns filesDir`() {
val result = scopedStorageProvider.getPrimaryStorageDirectory()
assertEquals(filesDir, result)
verify(exactly = 1) {
context.filesDir
}
}
@Test
fun `getRootFolderPath returns the root folder path String`() {
val rootFolderPath = filesDir.absolutePath + File.separator + rootFolderName
val actualPath = scopedStorageProvider.getRootFolderPath()
assertEquals(rootFolderPath, actualPath)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getDefaultSavePathFor returns the path with spaces when there is a space`() {
mockkStatic(Uri::class)
every { Uri.encode(accountName, "@") } returns uriEncoded
val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + uriEncoded
val expectedPath = accountDirectoryPath + File.separator + spaceId + File.separator + remotePath
val actualPath = scopedStorageProvider.getDefaultSavePathFor(accountName, remotePath, spaceId)
assertEquals(expectedPath, actualPath)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getDefaultSavePathFor returns the path without spaces when there is not space`() {
val spaceId = null
mockkStatic(Uri::class)
every { Uri.encode(accountName, "@") } returns uriEncoded
val accountDirectoryPath = filesDir.absolutePath + File.separator + rootFolderName + File.separator + uriEncoded
val expectedPath = accountDirectoryPath + remotePath
val actualPath = scopedStorageProvider.getDefaultSavePathFor(accountName, remotePath, spaceId)
assertEquals(expectedPath, actualPath)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getExpectedRemotePath returns expected remote path with separator in the end when there is separator and is folder true`() {
val isFolder = true
val expectedPath = expectedRemotePath(remotePath, newName, isFolder)
val actualPath = scopedStorageProvider.getExpectedRemotePath(remotePath, newName, isFolder)
assertEquals(expectedPath, actualPath)
}
@Test
fun `getExpectedRemotePath returns expected remote path with separator in the end when is separator and is folder false`() {
val isFolder = false
val expectedPath = expectedRemotePath(remotePath, newName, isFolder)
val actualPath = scopedStorageProvider.getExpectedRemotePath(remotePath, newName, isFolder)
assertEquals(expectedPath, actualPath)
}
@Test
fun `getExpectedRemotePath returns expected remote path with separator in the end when is not separator and is folder true`() {
val isFolder = true
val expectedPath = expectedRemotePath(remotePath, newName, isFolder)
val actualPath = scopedStorageProvider.getExpectedRemotePath(remotePath, newName, isFolder)
assertEquals(expectedPath, actualPath)
}
@Test
fun `getExpectedRemotePath returns expected remote path with separator in the end when is not separator and is folder false`() {
val isFolder = false
val expectedPath = expectedRemotePath(remotePath, newName, isFolder)
val actualPath = scopedStorageProvider.getExpectedRemotePath(remotePath, newName, isFolder)
assertEquals(expectedPath, actualPath)
}
@Test(expected = IllegalArgumentException::class)
fun `getExpectedRemotePath returns a IllegalArgumentException when there is not file`() {
val isFolder = false
val remotePath = ""
scopedStorageProvider.getExpectedRemotePath(remotePath, newName, isFolder)
}
@Test
fun `getTemporalPath returns expected temporal path with separator and space when there is a space`() {
mockkStatic(Uri::class)
every { Uri.encode(accountName, "@") } returns uriEncoded
val temporalPathWithoutSpace = rootFolderPath + File.separator + "tmp" + File.separator + uriEncoded
val expectedValue = temporalPathWithoutSpace + File.separator + spaceId
val actualValue = scopedStorageProvider.getTemporalPath(accountName, spaceId)
assertEquals(expectedValue, actualValue)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getTemporalPath returns expected temporal path neither with separator not space when there is not a space`() {
val spaceId = null
mockkStatic(Uri::class)
every { Uri.encode(accountName, "@") } returns uriEncoded
val expectedValue = rootFolderPath + File.separator + TEMPORAL_FOLDER_NAME + File.separator + uriEncoded
val actualValue = scopedStorageProvider.getTemporalPath(accountName, spaceId)
assertEquals(expectedValue, actualValue)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getLogsPath returns logs path`() {
val expectedValue = rootFolderPath + File.separator + LOGS_FOLDER_NAME + File.separator
val actualValue = scopedStorageProvider.getLogsPath()
assertEquals(expectedValue, actualValue)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `getUsableSpace returns usable space from the storage directory`() {
val actualUsableSpace = scopedStorageProvider.getUsableSpace()
assertTrue(actualUsableSpace > 0)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `sizeOfDirectory returns the sum the file size in bytes (Long) when isDirectory is true doing a recursive call if it's a directory`() {
val actualValue = scopedStorageProvider.sizeOfDirectory(directory)
assertEquals(expectedSizeOfDirectoryValue, actualValue)
}
@Test
fun `sizeOfDirectory returns the sum the file size in bytes (Long) when isDirectory is false without doing a recursive call`() {
val tmpDir = Files.createTempDirectory("scoped-storage-size").toFile().apply {
deleteOnExit()
File(this, "single.bin").writeBytes(ByteArray(expectedSizeOfDirectoryValue.toInt()))
}
val actualValue = scopedStorageProvider.sizeOfDirectory(tmpDir)
assertEquals(expectedSizeOfDirectoryValue, actualValue)
}
@Test
fun `sizeOfDirectory returns zero value when directory not exists`() {
val expectedSizeOfDirectoryValue: Long = 0
val missingDir = File(filesDir, "does-not-exist")
val actualValue = scopedStorageProvider.sizeOfDirectory(missingDir)
assertEquals(expectedSizeOfDirectoryValue, actualValue)
}
@Test
fun `deleteLocalFile calls getPrimaryStorageDirectory()`() {
mockkStatic(Uri::class)
every { Uri.encode(any(), any()) } returns uriEncoded
scopedStorageProvider.deleteLocalFile(OC_FILE)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `moveLocalFile calls getPrimaryStorageDirectory()`() {
val finalStoragePath = "file.txt"
mockkStatic(Uri::class)
every { Uri.encode(any(), any()) } returns uriEncoded
scopedStorageProvider.moveLocalFile(OC_FILE, finalStoragePath)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
@Test
fun `deleteCacheIfNeeded delete cache file when transfer local path start with cacheDir`() {
val transfer: OCTransfer = mockk()
val accountName = "testAccount"
val localPath = "/file.txt"
mockkStatic(Uri::class)
every { Uri.encode(any(), any()) } returns uriEncoded
every { transfer.accountName } returns accountName
every { transfer.localPath } returns localPath
scopedStorageProvider.deleteCacheIfNeeded(transfer)
verify(exactly = 1) {
scopedStorageProvider.getPrimaryStorageDirectory()
}
}
private fun expectedRemotePath(current: String, newName: String, isFolder: Boolean): String {
var parent = File(current).parent ?: throw IllegalArgumentException("Parent path is null")
parent = if (parent.endsWith(File.separator)) parent else parent + File.separator
var newRemotePath = parent + newName
if (isFolder) {
newRemotePath += File.separator
}
return newRemotePath
}
companion object {
private const val LOGS_FOLDER_NAME = "logs"
private const val TEMPORAL_FOLDER_NAME = "tmp"
}
}
@@ -0,0 +1,155 @@
package eu.qsfera.android.data.providers.implementation
import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class OCSharedPreferencesProviderTest {
private lateinit var ocSharedPreferencesProvider: OCSharedPreferencesProvider
private lateinit var context: Context
private lateinit var sharedPreferences: SharedPreferences
private lateinit var editor: SharedPreferences.Editor
private val key = "test_key"
@Before
fun setUp() {
context = mockk(relaxed = true)
sharedPreferences = mockk(relaxed = true)
editor = mockk(relaxed = true)
mockkStatic(PreferenceManager::class)
every { PreferenceManager.getDefaultSharedPreferences(any()) } returns sharedPreferences
every { sharedPreferences.edit() } returns editor
ocSharedPreferencesProvider = OCSharedPreferencesProvider(context)
}
@Test
fun `putString put a String in sharedPreferences`() {
val value = "test_value"
ocSharedPreferencesProvider.putString(key, value)
verify(exactly = 1) {
editor.putString(key, value).apply()
}
}
@Test
fun `getString returns a String from sharedPreferences`() {
val defaultValue = "default_value"
val savedValue = "saved_value"
every { sharedPreferences.getString(key, defaultValue) } returns savedValue
val result = ocSharedPreferencesProvider.getString(key, defaultValue)
assertEquals(savedValue, result)
verify(exactly = 1) {
sharedPreferences.getString(key, defaultValue)
}
}
@Test
fun `putInt put a Int in sharedPreferences`() {
val value = 12
ocSharedPreferencesProvider.putInt(key, value)
verify(exactly = 1) {
editor.putInt(key, value).apply()
}
}
@Test
fun `getInt returns a Int from sharedPreferences`() {
val defaultValue = 111
val savedValue = 233
every { sharedPreferences.getInt(key, defaultValue) } returns savedValue
val result = ocSharedPreferencesProvider.getInt(key, defaultValue)
assertEquals(savedValue, result)
verify(exactly = 1) {
sharedPreferences.getInt(key, defaultValue)
}
}
@Test
fun `putLong put a Long in sharedPreferences`() {
val value = 12L
ocSharedPreferencesProvider.putLong(key, value)
verify(exactly = 1) {
editor.putLong(key, value).apply()
}
}
@Test
fun `getLong returns a Long from sharedPreferences`() {
val defaultValue = 1411L
val savedValue = 73L
every { sharedPreferences.getLong(key, defaultValue) } returns savedValue
val result = ocSharedPreferencesProvider.getLong(key, defaultValue)
assertEquals(savedValue, result)
verify(exactly = 1) {
sharedPreferences.getLong(key, defaultValue)
}
}
@Test
fun `putBoolean put a Boolean in sharedPreferences`() {
val value = true
ocSharedPreferencesProvider.putBoolean(key, value)
verify(exactly = 1) {
editor.putBoolean(key, value).apply()
}
}
@Test
fun `getBoolean returns a Boolean from sharedPreferences`() {
val defaultValue = false
val savedValue = true
every { sharedPreferences.getBoolean(key, defaultValue) } returns savedValue
val result = ocSharedPreferencesProvider.getBoolean(key, defaultValue)
assertTrue(result)
verify(exactly = 1) {
sharedPreferences.getBoolean(key, defaultValue)
}
}
@Test
fun `containsPreference verify if the value with the key is contained in sharedPreferences`() {
every { sharedPreferences.contains(key) } returns true
val result = ocSharedPreferencesProvider.containsPreference(key)
assertTrue(result)
verify(exactly = 1) {
sharedPreferences.contains(key)
}
}
@Test
fun `removePreferences remove a preference by key`() {
ocSharedPreferencesProvider.removePreference(key)
verify(exactly = 1) {
editor.remove(key).apply()
}
}
}
@@ -0,0 +1,293 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.server.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.domain.exceptions.NoConnectionWithServerException
import eu.qsfera.android.domain.exceptions.QSferaVersionNotSupportedException
import eu.qsfera.android.domain.exceptions.SpecificServiceUnavailableException
import eu.qsfera.android.domain.server.model.AuthenticationMethod
import eu.qsfera.android.lib.common.QSferaClient
import eu.qsfera.android.lib.common.http.HttpConstants.HTTP_SERVICE_UNAVAILABLE
import eu.qsfera.android.lib.common.http.HttpConstants.HTTP_UNAUTHORIZED
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import eu.qsfera.android.lib.common.operations.RemoteOperationResult.ResultCode.OK_NO_SSL
import eu.qsfera.android.lib.common.operations.RemoteOperationResult.ResultCode.OK_SSL
import eu.qsfera.android.lib.resources.status.QSferaVersion
import eu.qsfera.android.lib.resources.status.RemoteServerInfo
import eu.qsfera.android.lib.resources.status.services.implementation.OCServerInfoService
import eu.qsfera.android.testutil.OC_INSECURE_SERVER_INFO_BASIC_AUTH
import eu.qsfera.android.testutil.OC_INSECURE_SERVER_INFO_BEARER_AUTH
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BEARER_AUTH
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class OCRemoteServerInfoDataSourceTest {
private lateinit var ocRemoteServerInfoDatasource: OCRemoteServerInfoDataSource
private val ocServerInfoService: OCServerInfoService = mockk()
private val clientManager: ClientManager = mockk(relaxed = true)
private val ocClientMocked: QSferaClient = mockk(relaxed = true)
private val remoteServerInfo = RemoteServerInfo(
qsferaVersion = QSferaVersion(OC_SECURE_SERVER_INFO_BASIC_AUTH.qsferaVersion, "2.0.0"),
baseUrl = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
isSecureConnection = OC_SECURE_SERVER_INFO_BASIC_AUTH.isSecureConnection
)
private val basicAuthHeader = "basic realm=\"qsfera\", charset=\"utf-8\""
private val bearerHeader = "bearer realm=\"qsfera\""
private val authHeadersBasic = listOf(basicAuthHeader)
private val authHeaderBearer = listOf(basicAuthHeader, bearerHeader)
@Before
fun setUp() {
ocRemoteServerInfoDatasource = OCRemoteServerInfoDataSource(ocServerInfoService, clientManager)
every { clientManager.getClientForAnonymousCredentials(any(), any()) } returns ocClientMocked
}
@Test
fun `getAuthenticationMethod returns basic authentication`() {
val expectedValue = AuthenticationMethod.BASIC_HTTP_AUTH
prepareAuthorizationMethodToBeRetrieved(expectedValue)
val currentValue = ocRemoteServerInfoDatasource.getAuthenticationMethod(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertNotNull(expectedValue)
assertEquals(expectedValue, currentValue)
verify { ocServerInfoService.checkPathExistence(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false, ocClientMocked) }
}
@Test
fun `getAuthenticationMethod returns bearer authentication`() {
val expectedValue = AuthenticationMethod.BEARER_TOKEN
prepareAuthorizationMethodToBeRetrieved(expectedValue)
val currentValue = ocRemoteServerInfoDatasource.getAuthenticationMethod(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertNotNull(expectedValue)
assertEquals(expectedValue, currentValue)
verify { ocServerInfoService.checkPathExistence(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false, ocClientMocked) }
}
@Test
fun `getAuthenticationMethod returns none method`() {
val expectedValue = AuthenticationMethod.NONE
prepareAuthorizationMethodToBeRetrieved(expectedValue)
val currentValue = ocRemoteServerInfoDatasource.getAuthenticationMethod(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertNotNull(expectedValue)
assertEquals(expectedValue, currentValue)
verify { ocServerInfoService.checkPathExistence(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false, ocClientMocked) }
}
@Test(expected = SpecificServiceUnavailableException::class)
fun `getAuthenticationMethod throws exception when server is not available`() {
prepareAuthorizationMethodToBeRetrieved(
expectedAuthenticationMethod = AuthenticationMethod.BASIC_HTTP_AUTH,
isServerAvailable = false
)
ocRemoteServerInfoDatasource.getAuthenticationMethod(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
}
@Test
fun `getRemoteStatus returns RemoteServerInfo with secure connection`() {
val expectedValue = remoteServerInfo.copy(isSecureConnection = true)
prepareRemoteStatusToBeRetrieved(expectedValue)
val currentValue = ocRemoteServerInfoDatasource.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertNotNull(currentValue)
assertEquals(expectedValue, currentValue)
verify { ocServerInfoService.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, ocClientMocked) }
}
@Test
fun `getRemoteStatus returns RemoteServerInfo with insecure connection`() {
val expectedValue = remoteServerInfo.copy(isSecureConnection = false)
prepareRemoteStatusToBeRetrieved(expectedValue)
val currentValue = ocRemoteServerInfoDatasource.getRemoteStatus(OC_INSECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertNotNull(currentValue)
assertEquals(expectedValue, currentValue)
verify { ocServerInfoService.getRemoteStatus(OC_INSECURE_SERVER_INFO_BASIC_AUTH.baseUrl, ocClientMocked) }
}
@Test(expected = QSferaVersionNotSupportedException::class)
fun `getRemoteStatus throws exception when qsfera version is not supported`() {
val expectedValue = remoteServerInfo.copy(qsferaVersion = QSferaVersion("0.0.2", ""))
prepareRemoteStatusToBeRetrieved(expectedValue)
ocRemoteServerInfoDatasource.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
}
@Test
fun `getRemoteStatus returns RemoteServerInfo with hidden qsfera version`() {
val expectedValue = remoteServerInfo.copy(qsferaVersion = QSferaVersion("", ""))
prepareRemoteStatusToBeRetrieved(expectedValue)
ocRemoteServerInfoDatasource.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
val remoteStatus = ocRemoteServerInfoDatasource.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
assertTrue(remoteStatus.qsferaVersion.isVersionHidden)
verify { ocServerInfoService.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, ocClientMocked) }
}
@Test
fun `getServerInfo returns ServerInfo with basic and secure connection`() {
val expectedValue = OC_SECURE_SERVER_INFO_BASIC_AUTH
prepareRemoteStatusToBeRetrieved(remoteServerInfo)
prepareAuthorizationMethodToBeRetrieved(AuthenticationMethod.BASIC_HTTP_AUTH, true)
val currentValue = ocRemoteServerInfoDatasource.getServerInfo(expectedValue.baseUrl, false)
assertEquals(expectedValue, currentValue)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(expectedValue.baseUrl, ocClientMocked) }
verify(exactly = 1) { ocServerInfoService.checkPathExistence(expectedValue.baseUrl, false, ocClientMocked) }
}
@Test
fun `getServerInfo returns ServerInfo with basic and insecure connection`() {
val expectedValue = OC_INSECURE_SERVER_INFO_BASIC_AUTH
prepareRemoteStatusToBeRetrieved(remoteServerInfo.copy(baseUrl = expectedValue.baseUrl, isSecureConnection = false))
prepareAuthorizationMethodToBeRetrieved(AuthenticationMethod.BASIC_HTTP_AUTH, true)
val currentValue = ocRemoteServerInfoDatasource.getServerInfo(expectedValue.baseUrl, false)
assertEquals(expectedValue, currentValue)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(expectedValue.baseUrl, ocClientMocked) }
verify(exactly = 1) { ocServerInfoService.checkPathExistence(expectedValue.baseUrl, false, ocClientMocked) }
}
@Test
fun `getServerInfo returns ServerInfo with bearer and secure connection`() {
val expectedValue = OC_SECURE_SERVER_INFO_BEARER_AUTH
prepareRemoteStatusToBeRetrieved(remoteServerInfo.copy(isSecureConnection = true))
prepareAuthorizationMethodToBeRetrieved(AuthenticationMethod.BEARER_TOKEN, true)
val currentValue = ocRemoteServerInfoDatasource.getServerInfo(expectedValue.baseUrl, false)
assertEquals(expectedValue, currentValue)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(expectedValue.baseUrl, ocClientMocked) }
verify(exactly = 1) { ocServerInfoService.checkPathExistence(expectedValue.baseUrl, false, ocClientMocked) }
}
@Test
fun `getServerInfo returns ServerInfo with bearer and insecure connection`() {
val expectedValue = OC_INSECURE_SERVER_INFO_BEARER_AUTH
prepareRemoteStatusToBeRetrieved(remoteServerInfo.copy(baseUrl = expectedValue.baseUrl, isSecureConnection = false))
prepareAuthorizationMethodToBeRetrieved(AuthenticationMethod.BEARER_TOKEN, true)
val currentValue = ocRemoteServerInfoDatasource.getServerInfo(expectedValue.baseUrl, false)
assertEquals(expectedValue, currentValue)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(expectedValue.baseUrl, ocClientMocked) }
verify(exactly = 1) { ocServerInfoService.checkPathExistence(expectedValue.baseUrl, false, ocClientMocked) }
}
@Test
fun `getServerInfo returns ServerInfo with bearer and secure connection when OIDC enforcement is enabled`() {
val expectedValue = OC_SECURE_SERVER_INFO_BEARER_AUTH
prepareRemoteStatusToBeRetrieved(remoteServerInfo.copy(baseUrl = expectedValue.baseUrl, isSecureConnection = true))
val currentValue = ocRemoteServerInfoDatasource.getServerInfo(expectedValue.baseUrl, true)
assertEquals(expectedValue, currentValue)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(expectedValue.baseUrl, ocClientMocked) }
}
@Test(expected = NoConnectionWithServerException::class)
fun `getServerInfo throws exception when there is no connection with the server`() {
prepareRemoteStatusToBeRetrieved(remoteServerInfo, NoConnectionWithServerException())
ocRemoteServerInfoDatasource.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false)
verify(exactly = 1) { ocServerInfoService.getRemoteStatus(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, ocClientMocked) }
verify(exactly = 0) { ocServerInfoService.checkPathExistence(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false, ocClientMocked) }
}
private fun prepareAuthorizationMethodToBeRetrieved(
expectedAuthenticationMethod: AuthenticationMethod,
isServerAvailable: Boolean = true
) {
val expectedAuthHeader = when (expectedAuthenticationMethod) {
AuthenticationMethod.BEARER_TOKEN -> authHeaderBearer
AuthenticationMethod.BASIC_HTTP_AUTH -> authHeadersBasic
else -> listOf()
}
val checkPathExistenceResultMocked: RemoteOperationResult<Boolean> =
createRemoteOperationResultMock(
data = true,
isSuccess = true,
resultCode = OK_SSL,
authenticationHeader = expectedAuthHeader,
httpCode = if (isServerAvailable) HTTP_UNAUTHORIZED else HTTP_SERVICE_UNAVAILABLE
)
every {
ocServerInfoService.checkPathExistence(any(), false, ocClientMocked)
} returns checkPathExistenceResultMocked
}
private fun prepareRemoteStatusToBeRetrieved(
expectedConfig: RemoteServerInfo,
exception: Exception? = null
) {
val expectedResultCode = when (expectedConfig.isSecureConnection) {
true -> OK_SSL
false -> OK_NO_SSL
}
val remoteStatusResultMocked: RemoteOperationResult<RemoteServerInfo> =
createRemoteOperationResultMock(
data = expectedConfig,
isSuccess = true,
resultCode = expectedResultCode,
exception = exception
)
every {
ocServerInfoService.getRemoteStatus(any(), ocClientMocked)
} returns remoteStatusResultMocked
}
}
@@ -0,0 +1,292 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.server.repository
import eu.qsfera.android.data.oauth.datasources.RemoteOAuthDataSource
import eu.qsfera.android.data.server.datasources.RemoteServerInfoDataSource
import eu.qsfera.android.data.webfinger.datasources.RemoteWebFingerDataSource
import eu.qsfera.android.domain.server.model.ServerInfo
import eu.qsfera.android.domain.webfinger.model.WebFingerOidcInfo
import eu.qsfera.android.domain.webfinger.model.WebFingerRel
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BEARER_AUTH
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_OIDC_AUTH
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT
import eu.qsfera.android.testutil.OC_WEBFINGER_CLIENT_ID
import eu.qsfera.android.testutil.OC_WEBFINGER_INSTANCE_URL
import eu.qsfera.android.testutil.OC_WEBFINGER_SCOPES
import eu.qsfera.android.testutil.oauth.OC_OIDC_SERVER_CONFIGURATION
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Test
class OCServerInfoRepositoryTest {
private val remoteServerInfoDataSource = mockk<RemoteServerInfoDataSource>()
private val remoteWebFingerDataSource = mockk<RemoteWebFingerDataSource>()
private val remoteOAuthDataSource = mockk<RemoteOAuthDataSource>()
private val ocServerInfoRepository = OCServerInfoRepository(remoteServerInfoDataSource, remoteWebFingerDataSource, remoteOAuthDataSource)
@Test
fun `getServerInfo returns a BasicServer when creatingAccount parameter is false`() {
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_BASIC_AUTH
val basicServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
creatingAccount = false,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_BASIC_AUTH, basicServer)
verify(exactly = 1) {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false)
}
}
@Test
fun `getServerInfo returns a BasicServer when creatingAccount parameter is true and webfinger datasource throws an exception`() {
every {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl
)
} throws Exception()
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_BASIC_AUTH
val basicServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
creatingAccount = true,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_BASIC_AUTH, basicServer)
verify(exactly = 1) {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl
)
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false)
}
}
@Test
fun `getServerInfo returns an OAuth2Server when creatingAccount parameter is false`() {
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_BEARER_AUTH
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl)
} throws Exception()
val oAuthServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl,
creatingAccount = false,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_BEARER_AUTH, oAuthServer)
verify(exactly = 1) {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl, false)
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl)
}
}
@Test
fun `getServerInfo returns an OAuth2Server when creatingAccount parameter is true and webfinger datasource throws an exception`() {
every {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl
)
} throws Exception()
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_BEARER_AUTH
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl)
} throws Exception()
val oAuthServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl,
creatingAccount = true,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_BEARER_AUTH, oAuthServer)
verify(exactly = 1) {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl
)
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl, false)
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_BEARER_AUTH.baseUrl)
}
}
@Test
fun `getServerInfo returns an OIDCServer when creatingAccount parameter is false`() {
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_OIDC_AUTH
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
} returns OC_OIDC_SERVER_CONFIGURATION
val oIDCServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl,
creatingAccount = false,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_OIDC_AUTH, oIDCServer)
verify(exactly = 1) {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl, false)
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
}
}
@Test
fun `getServerInfo returns an OIDCServer when creatingAccount parameter is true and webfinger datasource throws an exception`() {
every {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl
)
} throws Exception()
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl, false)
} returns OC_SECURE_SERVER_INFO_OIDC_AUTH
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
} returns OC_OIDC_SERVER_CONFIGURATION
val oIDCServer = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl,
creatingAccount = true,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_OIDC_AUTH, oIDCServer)
verify(exactly = 1) {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl
)
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl, false)
remoteOAuthDataSource.performOIDCDiscovery(OC_SECURE_SERVER_INFO_OIDC_AUTH.baseUrl)
}
}
@Test
fun `getServerInfo returns an OIDCServer when creatingAccount is true and webfinger datasource returns an OIDC issuer`() {
every {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl
)
} returns WebFingerOidcInfo(issuer = OC_WEBFINGER_INSTANCE_URL, clientId = null, scopes = null)
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl, false)
} returns ServerInfo.OAuth2Server(baseUrl = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl, qsferaVersion = "10.12")
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_WEBFINGER_INSTANCE_URL)
} returns OC_OIDC_SERVER_CONFIGURATION
val oIDCServerWebfinger = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl,
creatingAccount = true,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE, oIDCServerWebfinger)
verify(exactly = 1) {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE.baseUrl
)
remoteOAuthDataSource.performOIDCDiscovery(OC_WEBFINGER_INSTANCE_URL)
}
}
@Test
fun `getServerInfo returns an OIDCServer with webfinger client id and scopes when webfinger provides them`() {
every {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl
)
} returns WebFingerOidcInfo(issuer = OC_WEBFINGER_INSTANCE_URL, clientId = OC_WEBFINGER_CLIENT_ID, scopes = OC_WEBFINGER_SCOPES)
every {
remoteServerInfoDataSource.getServerInfo(OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl, false)
} returns ServerInfo.OAuth2Server(
baseUrl = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl,
qsferaVersion = "10.12",
)
every {
remoteOAuthDataSource.performOIDCDiscovery(OC_WEBFINGER_INSTANCE_URL)
} returns OC_OIDC_SERVER_CONFIGURATION
val result = ocServerInfoRepository.getServerInfo(
path = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl,
creatingAccount = true,
enforceOIDC = false
)
assertEquals(OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT, result)
verify(exactly = 1) {
remoteWebFingerDataSource.getOidcInfoFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_OIDC_AUTH_WEBFINGER_INSTANCE_WITH_CLIENT.baseUrl
)
remoteOAuthDataSource.performOIDCDiscovery(OC_WEBFINGER_INSTANCE_URL)
}
}
}
@@ -0,0 +1,244 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.sharees.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.sharing.sharees.datasources.mapper.RemoteShareeMapper
import eu.qsfera.android.domain.sharing.sharees.model.OCSharee
import eu.qsfera.android.domain.sharing.shares.model.ShareType
import eu.qsfera.android.lib.resources.shares.responses.ExactSharees
import eu.qsfera.android.lib.resources.shares.responses.ShareeItem
import eu.qsfera.android.lib.resources.shares.responses.ShareeOcsResponse
import eu.qsfera.android.lib.resources.shares.responses.ShareeValue
import eu.qsfera.android.lib.resources.shares.services.implementation.OCShareeService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
class OCRemoteShareeDataSourceTest {
private lateinit var ocRemoteShareesDataSource: OCRemoteShareeDataSource
private val ocShareeService: OCShareeService = mockk()
private val clientManager: ClientManager = mockk()
private lateinit var sharees: List<OCSharee>
@Before
fun setUp() {
every { clientManager.getShareeService(OC_ACCOUNT_NAME) } returns ocShareeService
ocRemoteShareesDataSource =
OCRemoteShareeDataSource(clientManager, RemoteShareeMapper())
val getRemoteShareesOperationResult = createRemoteOperationResultMock(
REMOTE_SHAREES,
true
)
every {
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
} returns getRemoteShareesOperationResult
}
@Test
fun `getSharees returns a list of OCSharee entered as remote sharees`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
assertNotNull(sharees)
assertEquals(5, sharees.size)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when contains exact user match`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
val sharee = sharees[0]
assertEquals(sharee.label, "User")
assertEquals(sharee.shareType, ShareType.USER)
assertEquals(sharee.shareWith, "user")
assertEquals(sharee.additionalInfo, "user@exact.com")
assertTrue(sharee.isExactMatch)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when contains one user not exactly matched`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
val sharee = sharees[1]
assertEquals("User 1", sharee.label)
assertEquals(ShareType.USER, sharee.shareType)
assertEquals("user1", sharee.shareWith)
assertEquals("user1@mail.com", sharee.additionalInfo)
assertFalse(sharee.isExactMatch)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when contains one user without additional info`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
val sharee = sharees[2]
assertEquals("User 2", sharee.label)
assertEquals(ShareType.USER, sharee.shareType)
assertEquals("user2", sharee.shareWith)
assertEquals("", sharee.additionalInfo)
assertFalse(sharee.isExactMatch)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when contains one remote user`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
val sharee = sharees[3]
assertEquals("Remoteuser 1", sharee.label)
assertEquals(ShareType.FEDERATED, sharee.shareType)
assertEquals("remoteuser1", sharee.shareWith)
assertEquals("user1@remote.com", sharee.additionalInfo)
assertFalse(sharee.isExactMatch)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when contains one group`() {
sharees = ocRemoteShareesDataSource.getSharees(
"user",
1,
30,
OC_ACCOUNT_NAME,
)
val sharee = sharees[4]
assertEquals("Group 1", sharee.label)
assertEquals(ShareType.GROUP, sharee.shareType)
assertEquals("group1", sharee.shareWith)
assertEquals("group@group.com", sharee.additionalInfo)
assertFalse(sharee.isExactMatch)
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user", page = 1, perPage = 30)
}
}
@Test
fun `getSharees returns a list of OCSharee when handle empty response`() {
val getRemoteShareesOperationResult = createRemoteOperationResultMock(
EMPTY_REMOTE_SHAREES,
true
)
every {
ocShareeService.getSharees(searchString = "user2", page = 2, perPage = 32)
} returns getRemoteShareesOperationResult
val emptySharees = ocRemoteShareesDataSource.getSharees(
"user2",
2,
32,
OC_ACCOUNT_NAME,
)
assertTrue(emptySharees.isEmpty())
verify(exactly = 1) {
clientManager.getShareeService(OC_ACCOUNT_NAME)
ocShareeService.getSharees(searchString = "user2", page = 2, perPage = 32)
}
}
companion object {
val REMOTE_SHAREES = ShareeOcsResponse(
ExactSharees(
arrayListOf(), arrayListOf(), arrayListOf(
ShareeItem("User", ShareeValue(0, "user", "user@exact.com"))
)
),
arrayListOf(
ShareeItem("Group 1", ShareeValue(1, "group1", "group@group.com"))
),
arrayListOf(
ShareeItem("Remoteuser 1", ShareeValue(6, "remoteuser1", "user1@remote.com"))
),
arrayListOf(
ShareeItem("User 1", ShareeValue(0, "user1", "user1@mail.com")),
ShareeItem("User 2", ShareeValue(0, "user2", null))
)
)
val EMPTY_REMOTE_SHAREES = ShareeOcsResponse(
ExactSharees(arrayListOf(), arrayListOf(), arrayListOf()),
arrayListOf(), arrayListOf(), arrayListOf()
)
}
}
@@ -0,0 +1,55 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Jorge Aguado Recio
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.sharees.repository
import eu.qsfera.android.data.sharing.sharees.datasources.RemoteShareeDataSource
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_SHAREE
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Test
class OCShareeRepositoryTest {
private val remoteShareeDataSource = mockk<RemoteShareeDataSource>()
private val ocShareeRepository = OCShareeRepository(remoteShareeDataSource)
@Test
fun `getSharees returns a list of OCSharees`() {
val searchString = "user"
val requestedPage = 1
val resultsPerPage = 30
every {
remoteShareeDataSource.getSharees(searchString, requestedPage, resultsPerPage, OC_ACCOUNT_NAME)
} returns listOf(OC_SHAREE)
val listOfSharees = ocShareeRepository.getSharees(searchString, requestedPage, resultsPerPage, OC_ACCOUNT_NAME)
assertEquals(listOf(OC_SHAREE), listOfSharees)
verify(exactly = 1) {
remoteShareeDataSource.getSharees(searchString, requestedPage, resultsPerPage, OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,331 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.shares.datasources.implementation
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.MutableLiveData
import eu.qsfera.android.data.sharing.shares.datasources.implementation.OCLocalShareDataSource.Companion.toEntity
import eu.qsfera.android.data.sharing.shares.datasources.implementation.OCLocalShareDataSource.Companion.toModel
import eu.qsfera.android.data.sharing.shares.db.OCShareDao
import eu.qsfera.android.data.sharing.shares.db.OCShareEntity
import eu.qsfera.android.domain.sharing.shares.model.ShareType
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_PRIVATE_SHARE
import eu.qsfera.android.testutil.OC_PUBLIC_SHARE
import eu.qsfera.android.testutil.OC_SHARE
import eu.qsfera.android.testutil.livedata.getLastEmittedValue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
class OCLocalShareDataSourceTest {
private lateinit var ocLocalSharesDataSource: OCLocalShareDataSource
private val ocSharesDao = mockk<OCShareDao>(relaxUnitFun = true)
private val privateShares = listOf(
OC_PRIVATE_SHARE.copy(
path = "/Docs/doc1.doc",
shareWith = "username",
sharedWithDisplayName = "Sophie"
).toEntity(),
OC_PRIVATE_SHARE.copy(
path = "/Docs/doc1.doc",
shareWith = "user.name",
sharedWithDisplayName = "Nicole"
).toEntity()
)
private val privateShareTypes = listOf(ShareType.USER, ShareType.GROUP, ShareType.FEDERATED)
private val publicShares = listOf(
OC_PUBLIC_SHARE.copy(
path = "/Photos/",
isFolder = true,
name = "Photos link",
shareLink = "http://server:port/s/1"
).toEntity(),
OC_PUBLIC_SHARE.copy(
path = "/Photos/",
isFolder = true,
name = "Photos link 2",
shareLink = "http://server:port/s/2"
).toEntity()
)
@Rule
@JvmField
val instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setUp() {
ocLocalSharesDataSource =
OCLocalShareDataSource(
ocSharesDao,
)
}
/******************************************************************************************************
******************************************* PRIVATE SHARES *******************************************
******************************************************************************************************/
@Test
fun `getSharesAsLiveData returns a LiveData of a list of OCShare when read local private shares`() {
val privateSharesAsLiveData: MutableLiveData<List<OCShareEntity>> = MutableLiveData()
privateSharesAsLiveData.value = privateShares
every {
ocSharesDao.getSharesAsLiveData(
"/Docs/doc1.doc",
"admin@server",
privateShareTypes.map { it.value })
} returns privateSharesAsLiveData
val shares =
ocLocalSharesDataSource.getSharesAsLiveData(
"/Docs/doc1.doc", "admin@server", privateShareTypes
).getLastEmittedValue()!!
assertEquals(2, shares.size)
assertEquals("/Docs/doc1.doc", shares.first().path)
assertEquals(false, shares.first().isFolder)
assertEquals("username", shares.first().shareWith)
assertEquals("Sophie", shares.first().sharedWithDisplayName)
assertEquals("/Docs/doc1.doc", shares[1].path)
assertEquals(false, shares[1].isFolder)
assertEquals("user.name", shares[1].shareWith)
assertEquals("Nicole", shares[1].sharedWithDisplayName)
verify(exactly = 1) {
ocSharesDao.getSharesAsLiveData(
"/Docs/doc1.doc",
"admin@server",
privateShareTypes.map { it.value })
}
}
@Test
fun `getShareAsLiveData read local private share returns a OCShare`() {
val privateShareAsLiveData: MutableLiveData<OCShareEntity> = MutableLiveData()
privateShareAsLiveData.value = privateShares.first()
every { ocSharesDao.getShareAsLiveData(OC_SHARE.remoteId) } returns privateShareAsLiveData
val share = ocLocalSharesDataSource.getShareAsLiveData(OC_SHARE.remoteId).getLastEmittedValue()!!
assertEquals("/Docs/doc1.doc", share.path)
assertEquals(false, share.isFolder)
assertEquals("username", share.shareWith)
assertEquals("Sophie", share.sharedWithDisplayName)
verify(exactly = 1) { ocSharesDao.getShareAsLiveData(OC_SHARE.remoteId) }
}
@Test
fun `insert private OCShare saves it correctly`() {
every { ocSharesDao.insertOrReplace(privateShares[0]) } returns 10
val insertedShareId = ocLocalSharesDataSource.insert(
OC_PRIVATE_SHARE.copy(
path = "/Docs/doc1.doc",
shareWith = "username",
sharedWithDisplayName = "Sophie"
)
)
assertEquals(10, insertedShareId)
verify(exactly = 1) { ocSharesDao.insertOrReplace(privateShares[0]) }
}
@Test
fun `insert list of private OCShares saves it correctly`() {
val expectedValues = listOf<Long>(1, 2)
every { ocSharesDao.insertOrReplace(privateShares) } returns expectedValues
val insertedSharesid = ocLocalSharesDataSource.insert(
privateShares.map { it.toModel() }
)
assertEquals(expectedValues, insertedSharesid)
verify(exactly = 1) { ocSharesDao.insertOrReplace(privateShares) }
}
@Test
fun `update private OCShare changes it correctly`() {
every { ocSharesDao.update(privateShares[1]) } returns 3
val updatedShareId = ocLocalSharesDataSource.update(
OC_PRIVATE_SHARE.copy(
path = "/Docs/doc1.doc",
shareWith = "user.name",
sharedWithDisplayName = "Nicole"
)
)
assertEquals(3, updatedShareId)
verify(exactly = 1) { ocSharesDao.update(privateShares[1]) }
}
/******************************************************************************************************
******************************************* PUBLIC SHARES ********************************************
******************************************************************************************************/
@Test
fun `getSharesAsLiveData returns a LiveData of a list of OCShare when read local public shares`() {
val publicSharesAsLiveData: MutableLiveData<List<OCShareEntity>> = MutableLiveData()
publicSharesAsLiveData.value = publicShares
every {
ocSharesDao.getSharesAsLiveData(
"/Photos/",
"admin@server",
listOf(ShareType.PUBLIC_LINK.value)
)
} returns publicSharesAsLiveData
val shares = ocLocalSharesDataSource.getSharesAsLiveData(
"/Photos/",
"admin@server",
listOf(ShareType.PUBLIC_LINK)
).getLastEmittedValue()!!
assertEquals(2, shares.size)
assertEquals("/Photos/", shares.first().path)
assertEquals(true, shares.first().isFolder)
assertEquals("Photos link", shares.first().name)
assertEquals("http://server:port/s/1", shares.first().shareLink)
assertEquals("/Photos/", shares[1].path)
assertEquals(true, shares[1].isFolder)
assertEquals("Photos link 2", shares[1].name)
assertEquals("http://server:port/s/2", shares[1].shareLink)
verify(exactly = 1) {
ocSharesDao.getSharesAsLiveData(
"/Photos/",
"admin@server",
listOf(ShareType.PUBLIC_LINK.value))
}
}
@Test
fun `insert public OCShare saves it correctly`() {
every { ocSharesDao.insertOrReplace(publicShares[0]) } returns 7
val insertedShareId = ocLocalSharesDataSource.insert(
OC_PUBLIC_SHARE.copy(
path = "/Photos/",
isFolder = true,
name = "Photos link",
shareLink = "http://server:port/s/1"
)
)
assertEquals(7, insertedShareId)
verify(exactly = 1) { ocSharesDao.insertOrReplace(publicShares[0]) }
}
@Test
fun `insert list of public OCShares saves it correctly`() {
val expectedValues = listOf<Long>(1, 2)
every { ocSharesDao.insertOrReplace(publicShares) } returns expectedValues
val retrievedValues = ocLocalSharesDataSource.insert(
publicShares.map { it.toModel() }
)
assertEquals(expectedValues, retrievedValues)
verify(exactly = 1) { ocSharesDao.insertOrReplace(publicShares) }
}
@Test
fun `update public OCShare changes it correctly`() {
every { ocSharesDao.update(publicShares[1]) } returns 8
val updatedShareId = ocLocalSharesDataSource.update(
OC_PUBLIC_SHARE.copy(
path = "/Photos/",
isFolder = true,
name = "Photos link 2",
shareLink = "http://server:port/s/2"
)
)
assertEquals(8, updatedShareId)
verify(exactly = 1) { ocSharesDao.update(publicShares[1]) }
}
/**************************************************************************************************************
*************************************************** COMMON ***************************************************
**************************************************************************************************************/
@Test
fun `replaceShares updates a list of OCShare correctly`() {
val expectedValues = listOf<Long>(1, 2)
every { ocSharesDao.replaceShares(publicShares) } returns expectedValues
val retrievedValues = ocLocalSharesDataSource.replaceShares(
publicShares.map { it.toModel() }
)
assertEquals(expectedValues, retrievedValues)
verify(exactly = 1) { ocSharesDao.replaceShares(publicShares) }
}
@Test
fun `deleteSharesForFile removes shares related to a file`() {
ocLocalSharesDataSource.deleteSharesForFile("file", OC_ACCOUNT_NAME)
verify(exactly = 1) { ocSharesDao.deleteSharesForFile("file", OC_ACCOUNT_NAME) }
}
@Test
fun `deleteShare removes a share correctly`() {
every { ocSharesDao.deleteShare(OC_SHARE.remoteId) } returns 1
val deletedRows = ocLocalSharesDataSource.deleteShare(OC_SHARE.remoteId)
assertEquals(1, deletedRows)
verify(exactly = 1) { ocSharesDao.deleteShare(OC_SHARE.remoteId) }
}
@Test
fun `deleteSharesForAccount removes shares related to an account`() {
ocLocalSharesDataSource.deleteSharesForAccount(OC_SHARE.accountOwner)
verify(exactly = 1) { ocSharesDao.deleteSharesForAccount(OC_SHARE.accountOwner) }
}
}
@@ -0,0 +1,491 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Jesús Recio
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.shares.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.sharing.shares.datasources.mapper.RemoteShareMapper
import eu.qsfera.android.domain.exceptions.ShareForbiddenException
import eu.qsfera.android.domain.exceptions.ShareNotFoundException
import eu.qsfera.android.domain.sharing.shares.model.ShareType
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import eu.qsfera.android.lib.resources.shares.ShareResponse
import eu.qsfera.android.lib.resources.shares.services.implementation.OCShareService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_SHARE
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCRemoteShareDataSourceTest {
private lateinit var ocRemoteShareDataSource: OCRemoteShareDataSource
private val ocShareService: OCShareService = mockk()
private val remoteShareMapper = RemoteShareMapper()
private val clientManager: ClientManager = mockk(relaxed = true)
@Before
fun setUp() {
every { clientManager.getShareService(any()) } returns ocShareService
ocRemoteShareDataSource = OCRemoteShareDataSource(clientManager, remoteShareMapper)
}
/******************************************************************************************************
******************************************* PRIVATE SHARES *******************************************
******************************************************************************************************/
@Test
fun `insert private share returns OCShare`() {
val createRemoteShareOperationResult = createRemoteOperationResultMock(
ShareResponse(
listOf(
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.USER,
path = "Photos/",
isFolder = true,
shareWith = "user",
sharedWithDisplayName = "User"
)
)!!
)
),
true
)
every {
ocShareService.insertShare(
remoteFilePath = "Photos/",
shareType = eu.qsfera.android.lib.resources.shares.ShareType.fromValue(ShareType.USER.value)!!,
shareWith = "user",
permissions = 1,
name = "",
password = "",
expirationDate = 0,
)
} returns createRemoteShareOperationResult
val privateShareAdded = ocRemoteShareDataSource.insert(
remoteFilePath = "Photos/",
shareType = ShareType.USER,
shareWith = "user",
permissions = 1,
accountName = OC_ACCOUNT_NAME
)
assertEquals("Photos/", privateShareAdded.path)
assertEquals(true, privateShareAdded.isFolder)
assertEquals("user", privateShareAdded.shareWith)
assertEquals("User", privateShareAdded.sharedWithDisplayName)
assertEquals(1, privateShareAdded.permissions)
verify(exactly = 1) {
clientManager.getShareService(OC_ACCOUNT_NAME)
ocShareService.insertShare(
remoteFilePath = "Photos/",
shareType = eu.qsfera.android.lib.resources.shares.ShareType.fromValue(ShareType.USER.value)!!,
shareWith = "user",
permissions = 1,
name = "",
password = "",
expirationDate = 0,
)
}
}
@Test
fun `updateShare for private share returns OCShare`() {
val updateRemoteShareOperationResult = createRemoteOperationResultMock(
ShareResponse(
listOf(
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.USER,
path = "Images/image_1.mp4",
shareWith = "user",
sharedWithDisplayName = "User",
permissions = 17,
remoteId = "3"
)
)!!
)
),
true
)
every {
ocShareService.updateShare(
remoteId = "3",
name = "",
password = "",
expirationDate = 0,
permissions = 17,
)
} returns updateRemoteShareOperationResult
val privateShareUpdated = ocRemoteShareDataSource.updateShare(
remoteId = "3",
permissions = 17,
accountName = OC_ACCOUNT_NAME
)
assertEquals("Images/image_1.mp4", privateShareUpdated.path)
assertEquals("user", privateShareUpdated.shareWith)
assertEquals("User", privateShareUpdated.sharedWithDisplayName)
assertEquals(17, privateShareUpdated.permissions)
assertEquals(false, privateShareUpdated.isFolder)
verify(exactly = 1) {
clientManager.getShareService(OC_ACCOUNT_NAME)
ocShareService.updateShare(
remoteId = "3",
name = "",
password = "",
expirationDate = 0,
permissions = 17,
)
}
}
/******************************************************************************************************
******************************************* PUBLIC SHARES ********************************************
******************************************************************************************************/
@Test
fun `insert public share returns OCShare`() {
val createRemoteShareOperationResult = createRemoteOperationResultMock(
ShareResponse(
listOf(
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.PUBLIC_LINK,
path = "Photos/img1.png",
name = "img1 link",
shareLink = "http://server:port/s/112ejbhdasyd1"
)
)!!
)
),
true
)
every {
ocShareService.insertShare(
remoteFilePath = "Photos/img1.png",
shareType = eu.qsfera.android.lib.resources.shares.ShareType.fromValue(ShareType.PUBLIC_LINK.value)!!,
shareWith = "",
permissions = 1,
name = "",
password = "",
expirationDate = 0
)
} returns createRemoteShareOperationResult
val publicShareAdded = ocRemoteShareDataSource.insert(
"Photos/img1.png",
ShareType.PUBLIC_LINK,
"",
1,
accountName = OC_ACCOUNT_NAME
)
assertEquals("", publicShareAdded.shareWith)
assertEquals(1, publicShareAdded.permissions)
assertEquals("img1 link", publicShareAdded.name)
assertEquals("Photos/img1.png", publicShareAdded.path)
assertEquals(false, publicShareAdded.isFolder)
assertEquals("http://server:port/s/112ejbhdasyd1", publicShareAdded.shareLink)
verify(exactly = 1) {
clientManager.getShareService(OC_ACCOUNT_NAME)
ocShareService.insertShare(
remoteFilePath = "Photos/img1.png",
shareType = eu.qsfera.android.lib.resources.shares.ShareType.fromValue(ShareType.PUBLIC_LINK.value)!!,
shareWith = "",
permissions = 1,
name = "",
password = "",
expirationDate = 0
)
}
}
@Test
fun `updateShare for public share returns OCShare`() {
val updateRemoteShareOperationResult = createRemoteOperationResultMock(
ShareResponse(
listOf(
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.PUBLIC_LINK,
path = "Videos/video1.mp4",
expirationDate = 2000,
remoteId = "3",
name = "video1 link updated",
shareLink = "http://server:port/s/1275farv"
)
)!!
)
),
true
)
every {
ocShareService.updateShare(
remoteId = "3",
name = "",
password = "",
expirationDate = 0,
permissions = 17,
)
} returns updateRemoteShareOperationResult
val publicShareUpdated = ocRemoteShareDataSource.updateShare(
remoteId = "3",
permissions = 17,
accountName = OC_ACCOUNT_NAME
)
assertEquals("video1 link updated", publicShareUpdated.name)
assertEquals("Videos/video1.mp4", publicShareUpdated.path)
assertEquals(false, publicShareUpdated.isFolder)
assertEquals(2000, publicShareUpdated.expirationDate)
assertEquals(1, publicShareUpdated.permissions)
assertEquals("http://server:port/s/1275farv", publicShareUpdated.shareLink)
verify(exactly = 1) {
clientManager.getShareService(OC_ACCOUNT_NAME)
ocShareService.updateShare(
remoteId = "3",
name = "",
password = "",
expirationDate = 0,
permissions = 17,
)
}
}
/******************************************************************************************************
*********************************************** COMMON ***********************************************
******************************************************************************************************/
@Test
fun `getShares returns a list of OCShare`() {
val remoteShares = listOf(
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.PUBLIC_LINK,
path = "/Documents/doc",
name = "Doc link",
shareLink = "http://server:port/s/1"
)
)!!,
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.PUBLIC_LINK,
path = "/Documents/doc",
name = "Doc link 2",
shareLink = "http://server:port/s/2"
)
)!!,
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.USER,
path = "/Documents/doc",
shareWith = "steve",
sharedWithDisplayName = "Steve"
)
)!!,
remoteShareMapper.toRemote(
OC_SHARE.copy(
shareType = ShareType.GROUP,
path = "/Documents/doc",
shareWith = "family",
sharedWithDisplayName = "My family"
)
)!!
)
val getRemoteSharesOperationResult = createRemoteOperationResultMock(
ShareResponse(remoteShares),
true
)
every {
ocShareService.getShares(any(), any(), any())
} returns getRemoteSharesOperationResult
val shares = ocRemoteShareDataSource.getShares(
remoteFilePath = "/Documents/doc",
reshares = true,
subfiles = true,
accountName = OC_ACCOUNT_NAME
)
assertEquals(4, shares.size)
val publicShare1 = shares[0]
assertEquals(ShareType.PUBLIC_LINK, publicShare1.shareType)
assertEquals("/Documents/doc", publicShare1.path)
assertEquals(false, publicShare1.isFolder)
assertEquals("Doc link", publicShare1.name)
assertEquals("http://server:port/s/1", publicShare1.shareLink)
val publicShare2 = shares[1]
assertEquals(ShareType.PUBLIC_LINK, publicShare2.shareType)
assertEquals("/Documents/doc", publicShare2.path)
assertEquals(false, publicShare2.isFolder)
assertEquals("Doc link 2", publicShare2.name)
assertEquals("http://server:port/s/2", publicShare2.shareLink)
val userShare = shares[2]
assertEquals(ShareType.USER, userShare.shareType)
assertEquals("/Documents/doc", userShare.path)
assertEquals(false, userShare.isFolder)
assertEquals("steve", userShare.shareWith)
assertEquals("Steve", userShare.sharedWithDisplayName)
val groupShare = shares[3]
assertEquals(ShareType.GROUP, groupShare.shareType)
assertEquals("/Documents/doc", groupShare.path)
assertEquals(false, groupShare.isFolder)
assertEquals("family", groupShare.shareWith)
assertEquals("My family", groupShare.sharedWithDisplayName)
verify(exactly = 1) {
clientManager.getShareService(OC_ACCOUNT_NAME)
ocShareService.getShares(
remoteFilePath = "/Documents/doc",
reshares = true,
subfiles = true,
)
}
}
@Test(expected = ShareNotFoundException::class)
fun `insert throws a ShareNotFoundException when share is not found`() {
insertShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND)
}
@Test(expected = ShareForbiddenException::class)
fun `insert throws a ShareForbiddenException when share is forbidden`() {
insertShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_FORBIDDEN)
}
private fun insertShareOperationWithError(resultCode: RemoteOperationResult.ResultCode? = null) {
val createRemoteSharesOperationResult = createRemoteOperationResultMock(
ShareResponse(arrayListOf()),
false,
null,
resultCode
)
every {
ocShareService.insertShare(any(), any(), any(), any(), any(), any(), any())
} returns createRemoteSharesOperationResult
ocRemoteShareDataSource.insert(
"Photos/img2.png",
ShareType.PUBLIC_LINK,
"",
1,
accountName = OC_ACCOUNT_NAME
)
}
@Test(expected = ShareNotFoundException::class)
fun `updateShare throws a ShareNotFoundException when share is not found`() {
updateShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND)
}
@Test(expected = ShareForbiddenException::class)
fun `updateShare throws a ShareForbiddenException when share is forbidden`() {
updateShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_FORBIDDEN)
}
private fun updateShareOperationWithError(resultCode: RemoteOperationResult.ResultCode? = null) {
val updateRemoteShareOperationResult = createRemoteOperationResultMock(
ShareResponse(arrayListOf()),
false,
null,
resultCode
)
every {
ocShareService.updateShare(any(), any(), any(), any(), any())
} returns updateRemoteShareOperationResult
ocRemoteShareDataSource.updateShare(
"3",
permissions = 17,
accountName = "user@server"
)
}
@Test
fun `deleteShare removes a share correctly`() {
val removeRemoteShareOperationResult = createRemoteOperationResultMock(
Unit,
isSuccess = true
)
every {
ocShareService.deleteShare(any())
} returns removeRemoteShareOperationResult
ocRemoteShareDataSource.deleteShare(remoteId = "3", accountName = "user@server")
verify(exactly = 1) {
ocShareService.deleteShare(any())
}
}
@Test(expected = ShareNotFoundException::class)
fun `deleteShare throws ShareNotFoundException when share is not found`() {
deleteShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND)
}
@Test(expected = ShareForbiddenException::class)
fun `deleteShare throws ShareForbiddenException when share is forbidden`() {
deleteShareOperationWithError(RemoteOperationResult.ResultCode.SHARE_FORBIDDEN)
}
private fun deleteShareOperationWithError(resultCode: RemoteOperationResult.ResultCode? = null) {
val removeRemoteShareOperationResult = createRemoteOperationResultMock(
Unit,
false,
null,
resultCode
)
every {
ocShareService.deleteShare(any())
} returns removeRemoteShareOperationResult
ocRemoteShareDataSource.deleteShare(remoteId = "1", accountName = "user@server")
}
}
@@ -0,0 +1,46 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.shares.datasources.mapper
import eu.qsfera.android.testutil.OC_SHARE
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test
class RemoteShareMapperTest {
private val ocRemoteShareMapper = RemoteShareMapper()
@Test
fun checkToModelNull() {
assertNull(ocRemoteShareMapper.toModel(null))
}
@Test
fun checkToModelNotNull() {
val remoteShare = ocRemoteShareMapper.toRemote(OC_SHARE)
assertNotNull(remoteShare)
val share = ocRemoteShareMapper.toModel(remoteShare)
assertNotNull(share)
assertEquals(OC_SHARE, share)
}
}
@@ -0,0 +1,150 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.shares.db
import org.junit.Assert.assertTrue
import org.junit.Assert.assertFalse
import org.junit.Test
class OCShareEntityTest {
@Test
fun testEqualsNamedParams() {
val item1 = OCShareEntity(
shareType = 0,
shareWith = "",
path = "/Photos/image2.jpg",
permissions = 1,
sharedDate = 1542628397,
expirationDate = 0,
token = "pwdasd12dasdWZ",
sharedWithDisplayName = "",
sharedWithAdditionalInfo = "",
isFolder = false,
remoteId = "remoteId",
accountOwner = "admin@server",
name = "",
shareLink = ""
)
val item2 = OCShareEntity(
0,
"",
"/Photos/image2.jpg",
1,
1542628397,
0,
"pwdasd12dasdWZ",
"",
"",
false,
"remoteId",
"admin@server",
"",
""
)
// Autogenerate Id should differ but it is not generated at this moment
// Tested on DAO
assertTrue(item1 == item2)
assertFalse(item1 === item2)
}
@Test
fun testEqualsNamedParamsNullValues() {
val item1 = OCShareEntity(
shareType = 0,
shareWith = null,
path = "/Photos/image2.jpg",
permissions = 1,
sharedDate = 1542628397,
expirationDate = 0,
token = null,
sharedWithDisplayName = null,
sharedWithAdditionalInfo = null,
isFolder = false,
remoteId = "remoteId",
accountOwner = "admin@server",
name = null,
shareLink = null
)
val item2 = OCShareEntity(
0,
null,
"/Photos/image2.jpg",
1,
1542628397,
0,
null,
null,
null,
false,
"remoteId",
"admin@server",
null,
null
)
// Autogenerate Id should differ but it is not generated at this moment
assertTrue(item1 == item2)
assertFalse(item1 === item2)
}
@Test
fun testNotEqualsNamedParams() {
val item1 = OCShareEntity(
shareType = 0,
shareWith = "",
path = "/Photos/image2.jpg",
permissions = 1,
sharedDate = 1542628397,
expirationDate = 0,
token = "pwdasd12dasdWZ",
sharedWithDisplayName = "",
sharedWithAdditionalInfo = "",
isFolder = false,
remoteId = "remoteId",
accountOwner = "admin@server",
name = "",
shareLink = ""
)
val item2 = OCShareEntity(
0,
"",
"/Photos/image2.jpg",
1,
1542628397,
0,
"pwdasd12dasdWZ",
"",
"",
false,
"remoteId",
"AnyServer",
"",
""
)
assertFalse(item1 == item2)
assertFalse(item1 === item2)
}
}
@@ -0,0 +1,313 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.sharing.shares.repository
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import eu.qsfera.android.data.sharing.shares.datasources.LocalShareDataSource
import eu.qsfera.android.data.sharing.shares.datasources.RemoteShareDataSource
import eu.qsfera.android.domain.sharing.shares.model.OCShare
import eu.qsfera.android.domain.sharing.shares.model.ShareType
import eu.qsfera.android.lib.resources.shares.RemoteShare
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_PRIVATE_SHARE
import eu.qsfera.android.testutil.OC_PUBLIC_SHARE
import eu.qsfera.android.testutil.OC_SHARE
import eu.qsfera.android.testutil.OC_SHAREE
import eu.qsfera.android.testutil.livedata.getLastEmittedValue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import org.junit.Rule
import org.junit.Test
class OCShareRepositoryTest {
@Rule
@JvmField
val instantExecutorRule = InstantTaskExecutorRule()
private val localShareDataSource = mockk<LocalShareDataSource>(relaxUnitFun = true)
private val remoteShareDataSource = mockk<RemoteShareDataSource>(relaxUnitFun = true)
private val ocShareRepository = OCShareRepository(localShareDataSource, remoteShareDataSource)
private val listOfShares = listOf(OC_PRIVATE_SHARE, OC_PUBLIC_SHARE)
private val filePath = OC_SHARE.path
private val password = "password"
private val permissions = OC_SHARE.permissions
private val expiration = RemoteShare.INIT_EXPIRATION_DATE_IN_MILLIS
@Test
fun `insertPrivateShare inserts a private OCShare correctly`() {
every {
remoteShareDataSource.insert(
remoteFilePath = filePath,
shareType = OC_PRIVATE_SHARE.shareType,
shareWith = OC_SHAREE.shareWith,
permissions = permissions,
name = "",
password = "",
expirationDate = expiration,
accountName = OC_ACCOUNT_NAME
)
} returns OC_PRIVATE_SHARE
// The result of this method is not used, so it can be anything
every {
localShareDataSource.insert(OC_PRIVATE_SHARE)
} returns 1
ocShareRepository.insertPrivateShare(filePath, OC_PRIVATE_SHARE.shareType, OC_SHAREE.shareWith, permissions, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.insert(
remoteFilePath = filePath,
shareType = OC_PRIVATE_SHARE.shareType,
shareWith = OC_SHAREE.shareWith,
permissions = permissions,
name = "",
password = "",
expirationDate = expiration,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.insert(OC_PRIVATE_SHARE)
}
}
@Test
fun `updatePrivateShare updates a private OCShare correctly`() {
every {
remoteShareDataSource.updateShare(
remoteId = OC_PRIVATE_SHARE.remoteId,
name = "",
password = "",
expirationDateInMillis = expiration,
permissions = permissions,
accountName = OC_ACCOUNT_NAME
)
} returns OC_PRIVATE_SHARE
// The result of this method is not used, so it can be anything
every {
localShareDataSource.update(OC_PRIVATE_SHARE)
} returns 1
ocShareRepository.updatePrivateShare(OC_PRIVATE_SHARE.remoteId, permissions, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.updateShare(
remoteId = OC_PRIVATE_SHARE.remoteId,
name = "",
password = "",
expirationDateInMillis = expiration,
permissions = permissions,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.update(OC_PRIVATE_SHARE)
}
}
@Test
fun `insertPublicShare inserts a public OCShare correctly`() {
every {
remoteShareDataSource.insert(
remoteFilePath = filePath,
shareType = OC_PUBLIC_SHARE.shareType,
shareWith = "",
permissions = permissions,
name = OC_PUBLIC_SHARE.name!!,
password = password,
expirationDate = expiration,
accountName = OC_ACCOUNT_NAME
)
} returns OC_PUBLIC_SHARE
// The result of this method is not used, so it can be anything
every {
localShareDataSource.insert(OC_PUBLIC_SHARE)
} returns 1
ocShareRepository.insertPublicShare(filePath, permissions, OC_PUBLIC_SHARE.name!!, password, expiration, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.insert(
remoteFilePath = filePath,
shareType = OC_PUBLIC_SHARE.shareType,
shareWith = "",
permissions = permissions,
name = OC_PUBLIC_SHARE.name!!,
password = password,
expirationDate = expiration,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.insert(OC_PUBLIC_SHARE)
}
}
@Test
fun `updatePublicShare updates a public OCShare correctly`() {
every {
remoteShareDataSource.updateShare(
remoteId = OC_PUBLIC_SHARE.remoteId,
name = OC_PUBLIC_SHARE.name!!,
password = password,
expirationDateInMillis = expiration,
permissions = permissions,
accountName = OC_ACCOUNT_NAME
)
} returns OC_PUBLIC_SHARE
// The result of this method is not used, so it can be anything
every {
localShareDataSource.update(OC_PUBLIC_SHARE)
} returns 1
ocShareRepository.updatePublicShare(OC_PUBLIC_SHARE.remoteId, OC_PUBLIC_SHARE.name!!, password, expiration, permissions, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.updateShare(
remoteId = OC_PUBLIC_SHARE.remoteId,
name = OC_PUBLIC_SHARE.name!!,
password = password,
expirationDateInMillis = expiration,
permissions = permissions,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.update(OC_PUBLIC_SHARE)
}
}
@Test
fun `getSharesAsLiveData returns a LiveData with a list of OCShares`() {
val sharesLiveDataList: LiveData<List<OCShare>> = MutableLiveData(listOf(OC_SHARE))
every {
localShareDataSource.getSharesAsLiveData(
filePath = filePath,
accountName = OC_ACCOUNT_NAME,
shareTypes = listOf(ShareType.PUBLIC_LINK, ShareType.USER, ShareType.GROUP, ShareType.FEDERATED)
)
} returns sharesLiveDataList
val sharesResult = ocShareRepository.getSharesAsLiveData(filePath, OC_ACCOUNT_NAME).getLastEmittedValue()!!
assertEquals(1, sharesResult.size)
assertEquals(OC_SHARE, sharesResult.first())
verify(exactly = 1) {
localShareDataSource.getSharesAsLiveData(
filePath = filePath,
accountName = OC_ACCOUNT_NAME,
shareTypes = listOf(ShareType.PUBLIC_LINK, ShareType.USER, ShareType.GROUP, ShareType.FEDERATED)
)
}
}
@Test
fun `getShareAsLiveData returns a LiveData with an OCShare`() {
val shareAsLiveData: LiveData<OCShare> = MutableLiveData(OC_SHARE)
every {
localShareDataSource.getShareAsLiveData(OC_SHARE.remoteId)
} returns shareAsLiveData
val shareResult = ocShareRepository.getShareAsLiveData(OC_SHARE.remoteId).getLastEmittedValue()!!
assertEquals(OC_SHARE, shareResult)
verify(exactly = 1) {
localShareDataSource.getShareAsLiveData(OC_SHARE.remoteId)
}
}
@Test
fun `refreshSharesFromNetwork refreshes shares correctly when the list of shares received is not empty`() {
every {
remoteShareDataSource.getShares(
remoteFilePath = filePath,
reshares = true,
subfiles = false,
accountName = OC_ACCOUNT_NAME
)
} returns listOfShares
// The result of this method is not used, so it can be anything
every {
localShareDataSource.replaceShares(listOfShares)
} returns listOf(1, 1)
ocShareRepository.refreshSharesFromNetwork(filePath, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.getShares(
remoteFilePath = filePath,
reshares = true,
subfiles = false,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.replaceShares(listOfShares)
}
}
@Test
fun `refreshSharesFromNetwork deletes local shares and refreshes shares correctly when the list of shares received is empty`() {
every {
remoteShareDataSource.getShares(
remoteFilePath = filePath,
reshares = true,
subfiles = false,
accountName = OC_ACCOUNT_NAME
)
} returns emptyList()
every {
localShareDataSource.replaceShares(emptyList())
} returns emptyList()
ocShareRepository.refreshSharesFromNetwork(filePath, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.getShares(
remoteFilePath = filePath,
reshares = true,
subfiles = false,
accountName = OC_ACCOUNT_NAME
)
localShareDataSource.deleteSharesForFile(filePath, OC_ACCOUNT_NAME)
localShareDataSource.replaceShares(emptyList())
}
}
@Test
fun `deleteShare deletes a share correctly`() {
// The result of this method is not used, so it can be anything
every {
localShareDataSource.deleteShare(OC_SHARE.remoteId)
} returns 1
ocShareRepository.deleteShare(OC_SHARE.remoteId, OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteShareDataSource.deleteShare(OC_SHARE.remoteId, OC_ACCOUNT_NAME)
localShareDataSource.deleteShare(OC_SHARE.remoteId)
}
}
}
@@ -0,0 +1,266 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.spaces.datasources.implementation
import eu.qsfera.android.data.spaces.datasources.implementation.OCLocalSpacesDataSource.Companion.toEntity
import eu.qsfera.android.data.spaces.datasources.implementation.OCLocalSpacesDataSource.Companion.toModel
import eu.qsfera.android.data.spaces.db.SpaceSpecialEntity
import eu.qsfera.android.data.spaces.db.SpacesDao
import eu.qsfera.android.data.spaces.db.SpacesEntity
import eu.qsfera.android.domain.spaces.model.OCSpace
import eu.qsfera.android.domain.spaces.model.OCSpace.Companion.SPACE_ID_SHARES
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_SPACE_PERSONAL
import eu.qsfera.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE
import eu.qsfera.android.testutil.SPACE_ENTITY_SHARE
import eu.qsfera.android.testutil.SPACE_ENTITY_WITH_SPECIALS
import eu.qsfera.android.testutil.WEB_DAV_URL
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCLocalSpacesDataSourceTest {
private lateinit var ocLocalSpacesDataSource: OCLocalSpacesDataSource
private val spacesDao = mockk<SpacesDao>()
@Before
fun setUp() {
ocLocalSpacesDataSource = OCLocalSpacesDataSource(spacesDao)
}
@Test
fun `saveSpacesForAccount inserts spaces and special spaces correctly`() {
val spaceEntities = mutableListOf<SpacesEntity>()
val spaceSpecialEntities = mutableListOf<SpaceSpecialEntity>()
listOf(OC_SPACE_PROJECT_WITH_IMAGE).forEach { spaceModel ->
spaceEntities.add(spaceModel.toEntity())
spaceModel.special?.let { listOfSpacesSpecials ->
spaceSpecialEntities.addAll(listOfSpacesSpecials.map { it.toEntity(spaceModel.accountName, spaceModel.id) })
}
}
every {
spacesDao.insertOrDeleteSpaces(any(), any())
} just Runs
ocLocalSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PROJECT_WITH_IMAGE))
verify(exactly = 1) {
spacesDao.insertOrDeleteSpaces(spaceEntities, spaceSpecialEntities)
}
}
@Test
fun `getPersonalSpaceForAccount by drive type returns a OCSpace`() {
every {
spacesDao.getSpacesByDriveTypeForAccount(any(), any())
} returns listOf(OC_SPACE_PERSONAL.toEntity())
val resultActual = ocLocalSpacesDataSource.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
assertEquals(OC_SPACE_PERSONAL, resultActual)
verify(exactly = 1) {
spacesDao.getSpacesByDriveTypeForAccount(OC_ACCOUNT_NAME, setOf(OCSpace.DRIVE_TYPE_PERSONAL))
}
}
@Test
fun `getSharesSpaceForAccount returns a OCSpace`() {
every {
spacesDao.getSpaceByIdForAccount(SPACE_ID_SHARES, OC_ACCOUNT_NAME)
} returns SPACE_ENTITY_SHARE.space
val resultActual = ocLocalSpacesDataSource.getSharesSpaceForAccount(OC_ACCOUNT_NAME)
assertEquals(SPACE_ENTITY_SHARE.space.toModel(), resultActual)
verify(exactly = 1) {
spacesDao.getSpaceByIdForAccount(SPACE_ID_SHARES, OC_ACCOUNT_NAME)
}
}
@Test
fun `getSpacesFromEveryAccountAsStream returns a Flow with a list of OCSpace`() = runBlocking {
every {
spacesDao.getSpacesByDriveTypeFromEveryAccountAsStream(
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
} returns flowOf(listOf(OC_SPACE_PERSONAL.toEntity()))
val resultActual = ocLocalSpacesDataSource.getSpacesFromEveryAccountAsStream().first()
assertEquals(listOf(OC_SPACE_PERSONAL), resultActual)
verify(exactly = 1) {
spacesDao.getSpacesByDriveTypeFromEveryAccountAsStream(
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
}
}
@Test
fun `getSpacesByDriveTypeWithSpecialsForAccountAsFlow returns a Flow with a list of OCSpace`() = runBlocking {
every {
spacesDao.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(
OC_ACCOUNT_NAME,
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
} returns flowOf(listOf(SPACE_ENTITY_WITH_SPECIALS))
val resultActual = ocLocalSpacesDataSource.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(
OC_ACCOUNT_NAME, setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
val result = resultActual.first()
assertEquals(listOf(SPACE_ENTITY_WITH_SPECIALS.toModel()), result)
verify(exactly = 1) {
spacesDao.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(
OC_ACCOUNT_NAME,
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
}
}
@Test
fun `getPersonalAndProjectSpacesForAccount returns a list of OCSpace`() {
every {
spacesDao.getSpacesByDriveTypeForAccount(
OC_ACCOUNT_NAME,
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
} returns listOf(OC_SPACE_PERSONAL.toEntity())
val resultActual = ocLocalSpacesDataSource.getPersonalAndProjectSpacesForAccount(OC_ACCOUNT_NAME)
assertEquals(listOf(OC_SPACE_PERSONAL), resultActual)
verify(exactly = 1) {
spacesDao.getSpacesByDriveTypeForAccount(
OC_ACCOUNT_NAME,
setOf(
OCSpace.DRIVE_TYPE_PERSONAL,
OCSpace.DRIVE_TYPE_PROJECT
)
)
}
}
@Test
fun `getSpaceWithSpecialsByIdForAccount returns a OCSpace`() {
every {
spacesDao.getSpaceWithSpecialsByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
} returns SPACE_ENTITY_WITH_SPECIALS
val resultActual = ocLocalSpacesDataSource.getSpaceWithSpecialsByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
assertEquals(SPACE_ENTITY_WITH_SPECIALS.toModel(), resultActual)
verify(exactly = 1) {
spacesDao.getSpaceWithSpecialsByIdForAccount(
OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME
)
}
}
@Test
fun `getSpaceByIdForAccount returns a OCSpace`() {
every {
spacesDao.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
} returns OC_SPACE_PERSONAL.toEntity()
val result = ocLocalSpacesDataSource.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
assertEquals(OC_SPACE_PERSONAL, result)
verify(exactly = 1) {
spacesDao.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
}
}
@Test
fun `getWebDavUrlForSpace returns a String of webDavUrl`() {
every {
spacesDao.getWebDavUrlForSpace(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
} returns WEB_DAV_URL
val resultActual = ocLocalSpacesDataSource.getWebDavUrlForSpace(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
assertEquals(WEB_DAV_URL, resultActual)
verify(exactly = 1) {
spacesDao.getWebDavUrlForSpace(
OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME
)
}
}
@Test
fun `deleteSpacesForAccount removes the spaces for an account correctly`() {
every {
spacesDao.deleteSpacesForAccount(OC_ACCOUNT_NAME)
} returns Unit
ocLocalSpacesDataSource.deleteSpacesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
spacesDao.deleteSpacesForAccount(OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,67 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
* @author Juan Carlos Garrote Gascón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.spaces.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.data.spaces.datasources.implementation.OCRemoteSpacesDataSource.Companion.toModel
import eu.qsfera.android.lib.resources.spaces.services.OCSpacesService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.SPACE_RESPONSE
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCRemoteSpacesDataSourceTest {
private lateinit var ocRemoteSpacesDataSource: OCRemoteSpacesDataSource
private val ocSpaceService: OCSpacesService = mockk()
private val clientManager: ClientManager = mockk(relaxed = true)
@Before
fun setUp() {
ocRemoteSpacesDataSource = OCRemoteSpacesDataSource(clientManager)
every { clientManager.getSpacesService(OC_ACCOUNT_NAME) } returns ocSpaceService
}
@Test
fun `refreshSpacesForAccount returns a list of OCSpace`() {
val getRemoteSpacesOperationResult = createRemoteOperationResultMock(
listOf(SPACE_RESPONSE), isSuccess = true
)
every { ocSpaceService.getSpaces() } returns getRemoteSpacesOperationResult
val resultActual = ocRemoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
assertEquals(listOf(SPACE_RESPONSE.toModel(OC_ACCOUNT_NAME)), resultActual)
verify(exactly = 1) {
clientManager.getSpacesService(OC_ACCOUNT_NAME)
ocSpaceService.getSpaces()
}
}
}
@@ -0,0 +1,277 @@
/**
* qsfera Android client application
*
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.spaces.repository
import eu.qsfera.android.data.capabilities.datasources.LocalCapabilitiesDataSource
import eu.qsfera.android.data.spaces.datasources.LocalSpacesDataSource
import eu.qsfera.android.data.spaces.datasources.RemoteSpacesDataSource
import eu.qsfera.android.data.user.datasources.LocalUserDataSource
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_CAPABILITY
import eu.qsfera.android.testutil.OC_CAPABILITY_WITH_MULTIPERSONAL_ENABLED
import eu.qsfera.android.testutil.OC_SPACE_PERSONAL
import eu.qsfera.android.testutil.OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA
import eu.qsfera.android.testutil.OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA
import eu.qsfera.android.testutil.OC_SPACE_PROJECT_WITH_IMAGE
import eu.qsfera.android.testutil.OC_USER_QUOTA_LIMITED
import eu.qsfera.android.testutil.OC_USER_QUOTA_UNLIMITED
import eu.qsfera.android.testutil.OC_USER_QUOTA_WITHOUT_PERSONAL
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
class OCSpacesRepositoryTest {
private val localSpacesDataSource = mockk<LocalSpacesDataSource>(relaxUnitFun = true)
private val localUserDataSource = mockk<LocalUserDataSource>(relaxUnitFun = true)
private val remoteSpacesDataSource = mockk<RemoteSpacesDataSource>()
private val localCapabilitiesDataSource = mockk<LocalCapabilitiesDataSource>(relaxUnitFun = true)
private val ocSpacesRepository = OCSpacesRepository(localSpacesDataSource, localUserDataSource, remoteSpacesDataSource,
localCapabilitiesDataSource)
@Test
fun `refreshSpacesForAccount refreshes spaces for account correctly when multipersonal is enabled`() {
every {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
} returns listOf(OC_SPACE_PERSONAL)
every {
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
} returns OC_CAPABILITY_WITH_MULTIPERSONAL_ENABLED
ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL))
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_WITHOUT_PERSONAL)
}
}
@Test
fun `refreshSpacesForAccount refreshes spaces for account correctly when quota is unlimited`() {
every {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
} returns listOf(OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA)
every {
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
} returns OC_CAPABILITY
ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL_WITH_UNLIMITED_QUOTA))
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_UNLIMITED)
}
}
@Test
fun `refreshSpacesForAccount refreshes spaces for account correctly when quota is limited`() {
every {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
} returns listOf(OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA)
every {
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
} returns OC_CAPABILITY
ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PERSONAL_WITH_LIMITED_QUOTA))
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_LIMITED)
}
}
@Test
fun `refreshSpacesForAccount refreshes spaces for account correctly when personal space does not exist`() {
every {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
} returns listOf(OC_SPACE_PROJECT_WITH_IMAGE)
every {
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
} returns OC_CAPABILITY
ocSpacesRepository.refreshSpacesForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
remoteSpacesDataSource.refreshSpacesForAccount(OC_ACCOUNT_NAME)
localSpacesDataSource.saveSpacesForAccount(listOf(OC_SPACE_PROJECT_WITH_IMAGE))
localCapabilitiesDataSource.getCapabilitiesForAccount(OC_ACCOUNT_NAME)
localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA_WITHOUT_PERSONAL)
}
}
@Test
fun `getSpacesFromEveryAccountAsStream returns a Flow with a list of OCSpace`() = runTest {
every {
localSpacesDataSource.getSpacesFromEveryAccountAsStream()
} returns flowOf(listOf(OC_SPACE_PROJECT_WITH_IMAGE))
val listOfSpaces = ocSpacesRepository.getSpacesFromEveryAccountAsStream().first()
assertEquals(listOf(OC_SPACE_PROJECT_WITH_IMAGE), listOfSpaces)
verify(exactly = 1) {
localSpacesDataSource.getSpacesFromEveryAccountAsStream()
}
}
@Test
fun `getSpacesByDriveTypeWithSpecialsForAccountAsFlow returns a Flow with a list of OCSpace`() = runTest {
every {
localSpacesDataSource.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(OC_ACCOUNT_NAME, setOf(OC_SPACE_PROJECT_WITH_IMAGE.driveType))
} returns flowOf(listOf(OC_SPACE_PROJECT_WITH_IMAGE))
val listOfSpaces = ocSpacesRepository.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(OC_ACCOUNT_NAME,
setOf(OC_SPACE_PROJECT_WITH_IMAGE.driveType)).first()
assertEquals(listOf(OC_SPACE_PROJECT_WITH_IMAGE), listOfSpaces)
verify(exactly = 1) {
localSpacesDataSource.getSpacesByDriveTypeWithSpecialsForAccountAsFlow(OC_ACCOUNT_NAME, setOf(OC_SPACE_PROJECT_WITH_IMAGE.driveType))
}
}
@Test
fun `getPersonalSpaceForAccount returns an OCSpace`() {
every {
localSpacesDataSource.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
} returns OC_SPACE_PERSONAL
val personalSpace = ocSpacesRepository.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
assertEquals(OC_SPACE_PERSONAL, personalSpace)
verify(exactly = 1) {
localSpacesDataSource.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getPersonalSpaceForAccount returns null when local datasource returns a null personal space`() {
every {
localSpacesDataSource.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
} returns null
val personalSpace = ocSpacesRepository.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
assertNull(personalSpace)
verify(exactly = 1) {
localSpacesDataSource.getPersonalSpaceForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getPersonalAndProjectSpacesForAccount returns a list of OCSpace`() {
every {
localSpacesDataSource.getPersonalAndProjectSpacesForAccount(OC_ACCOUNT_NAME)
} returns listOf(OC_SPACE_PERSONAL, OC_SPACE_PROJECT_WITH_IMAGE)
val listOfSpaces = ocSpacesRepository.getPersonalAndProjectSpacesForAccount(OC_ACCOUNT_NAME)
assertEquals(listOf(OC_SPACE_PERSONAL, OC_SPACE_PROJECT_WITH_IMAGE), listOfSpaces)
verify(exactly = 1) {
localSpacesDataSource.getPersonalAndProjectSpacesForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getSpaceWithSpecialsByIdForAccount returns an OCSpace`() {
every {
localSpacesDataSource.getSpaceWithSpecialsByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
} returns OC_SPACE_PROJECT_WITH_IMAGE
val spaceWithSpecials = ocSpacesRepository.getSpaceWithSpecialsByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
assertEquals(OC_SPACE_PROJECT_WITH_IMAGE, spaceWithSpecials)
verify(exactly = 1) {
localSpacesDataSource.getSpaceWithSpecialsByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
}
}
@Test
fun `getSpaceByIdForAccount returns an OCSpace`() {
every {
localSpacesDataSource.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
} returns OC_SPACE_PERSONAL
val space = ocSpacesRepository.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
assertEquals(OC_SPACE_PERSONAL, space)
verify(exactly = 1) {
localSpacesDataSource.getSpaceByIdForAccount(OC_SPACE_PERSONAL.id, OC_ACCOUNT_NAME)
}
}
@Test
fun `getSpaceByIdForAccount returns null when local datasource returns a null space`() {
every {
localSpacesDataSource.getSpaceByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
} returns null
val space = ocSpacesRepository.getSpaceByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
assertNull(space)
verify(exactly = 1) {
localSpacesDataSource.getSpaceByIdForAccount(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
}
}
@Test
fun `getWebDavUrlForSpace returns a String of webdav url`() {
every {
localSpacesDataSource.getWebDavUrlForSpace(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
} returns OC_SPACE_PROJECT_WITH_IMAGE.webUrl
val webDavUrl = ocSpacesRepository.getWebDavUrlForSpace(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id)
assertEquals(OC_SPACE_PROJECT_WITH_IMAGE.webUrl, webDavUrl)
verify(exactly = 1) {
localSpacesDataSource.getWebDavUrlForSpace(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
}
}
@Test
fun `getWebDavUrlForSpace returns null when local datasource returns null`() {
every {
localSpacesDataSource.getWebDavUrlForSpace(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
} returns null
val webDavUrl = ocSpacesRepository.getWebDavUrlForSpace(OC_ACCOUNT_NAME, OC_SPACE_PROJECT_WITH_IMAGE.id)
assertNull(webDavUrl)
verify(exactly = 1) {
localSpacesDataSource.getWebDavUrlForSpace(OC_SPACE_PROJECT_WITH_IMAGE.id, OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,323 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.transfers.datasources.implementation
import eu.qsfera.android.data.transfers.datasources.implementation.OCLocalTransferDataSource.Companion.toEntity
import eu.qsfera.android.data.transfers.datasources.implementation.OCLocalTransferDataSource.Companion.toModel
import eu.qsfera.android.data.transfers.db.OCTransferEntity
import eu.qsfera.android.data.transfers.db.TransferDao
import eu.qsfera.android.domain.transfers.model.TransferResult
import eu.qsfera.android.domain.transfers.model.TransferStatus
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_TRANSFER
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCLocalTransferDataSourceTest {
private lateinit var ocLocalTransferDataSource: OCLocalTransferDataSource
private val transferDao = mockk<TransferDao>(relaxUnitFun = true)
private var transferEntity: OCTransferEntity = OC_TRANSFER.toEntity()
@Before
fun setUp() {
ocLocalTransferDataSource = OCLocalTransferDataSource(transferDao)
}
@Test
fun `saveTransfer inserts a transfer correctly`() {
val resultExpected = 1L
every {
transferDao.insertOrReplace(any())
} returns resultExpected
val resultActual = ocLocalTransferDataSource.saveTransfer(OC_TRANSFER)
assertEquals(resultExpected, resultActual)
verify(exactly = 1) {
transferDao.insertOrReplace(OC_TRANSFER.toEntity())
}
}
@Test
fun `updateTransfer updates the transfer correctly`() {
val resultExpected = 1L
every {
transferDao.insertOrReplace(any())
} returns resultExpected
ocLocalTransferDataSource.updateTransfer(OC_TRANSFER)
verify(exactly = 1) {
transferDao.insertOrReplace(OC_TRANSFER.toEntity())
}
}
@Test
fun `updateTransferStatusToInProgressById changes transfer status correctly`() {
val resultExpected = 10L
ocLocalTransferDataSource.updateTransferStatusToInProgressById(resultExpected)
verify(exactly = 1) {
transferDao.updateTransferStatusWithId(resultExpected, TransferStatus.TRANSFER_IN_PROGRESS.value)
}
}
@Test
fun `updateTransferStatusToEnqueuedById changes transfer status correctly`() {
val resultExpected = 10L
ocLocalTransferDataSource.updateTransferStatusToEnqueuedById(resultExpected)
verify(exactly = 1) {
transferDao.updateTransferStatusWithId(resultExpected, TransferStatus.TRANSFER_QUEUED.value)
}
}
@Test
fun `updateTransferWhenFinished changes transfer status correctly`() {
val timestamp = System.currentTimeMillis()
ocLocalTransferDataSource.updateTransferWhenFinished(OC_TRANSFER.id!!, TransferStatus.TRANSFER_SUCCEEDED, timestamp, TransferResult.UPLOADED)
verify(exactly = 1) {
transferDao.updateTransferWhenFinished(
OC_TRANSFER.id!!,
TransferStatus.TRANSFER_SUCCEEDED.value,
timestamp,
TransferResult.UPLOADED.value
)
}
}
@Test
fun `updateTransferLocalPath changes transfer local path correctly`() {
ocLocalTransferDataSource.updateTransferLocalPath(OC_TRANSFER.id!!, OC_TRANSFER.localPath)
verify(exactly = 1) {
transferDao.updateTransferLocalPath(OC_TRANSFER.id!!, OC_TRANSFER.localPath)
}
}
@Test
fun `updateTransferSourcePath changes transfer source path correctly`() {
ocLocalTransferDataSource.updateTransferSourcePath(OC_TRANSFER.id!!, OC_TRANSFER.sourcePath!!)
verify(exactly = 1) {
transferDao.updateTransferSourcePath(OC_TRANSFER.id!!, OC_TRANSFER.sourcePath!!)
}
}
@Test
fun `updateTransferStorageDirectoryInLocalPath changes directory correctly`() {
val oldDirectory = "oldDirectory"
val newDirectory = "newDirectory"
ocLocalTransferDataSource.updateTransferStorageDirectoryInLocalPath(OC_TRANSFER.id!!, oldDirectory, newDirectory)
verify(exactly = 1) {
transferDao.updateTransferStorageDirectoryInLocalPath(OC_TRANSFER.id!!, oldDirectory, newDirectory)
}
}
@Test
fun `deleteTransferById removes a transfer correctly`() {
ocLocalTransferDataSource.deleteTransferById(OC_TRANSFER.id!!)
verify(exactly = 1) {
transferDao.deleteTransferWithId(OC_TRANSFER.id!!)
}
}
@Test
fun `deleteAllTransfersFromAccount removes all transfers for an account correctly`() {
ocLocalTransferDataSource.deleteAllTransfersFromAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
transferDao.deleteTransfersWithAccountName(OC_ACCOUNT_NAME)
}
}
@Test
fun `getTransferById returns a OCTransfer`() {
every {
transferDao.getTransferWithId(any())
} returns OC_TRANSFER.toEntity()
val actualResult = ocLocalTransferDataSource.getTransferById(OC_TRANSFER.id!!)
assertEquals(OC_TRANSFER, actualResult)
verify(exactly = 1) {
transferDao.getTransferWithId(OC_TRANSFER.id!!)
}
}
@Test
fun `getAllTransfers returns a list of OCTransfer`() {
every {
transferDao.getAllTransfers()
} returns listOf(transferEntity)
val actualResult = ocLocalTransferDataSource.getAllTransfers()
assertEquals(listOf(transferEntity.toModel()), actualResult)
verify(exactly = 1) {
transferDao.getAllTransfers()
}
}
@Test
fun `getAllTransfersAsStream returns a Flow with a list of OCTransfer ordered by status`() = runBlocking {
val transferEntityInProgress: OCTransferEntity = OC_TRANSFER.copy(status = TransferStatus.TRANSFER_IN_PROGRESS).toEntity()
val transferEntityQueue: OCTransferEntity = OC_TRANSFER.copy(status = TransferStatus.TRANSFER_QUEUED).toEntity()
val transferEntityFailed: OCTransferEntity = OC_TRANSFER.copy(status = TransferStatus.TRANSFER_FAILED).toEntity()
val transferEntitySucceeded: OCTransferEntity = OC_TRANSFER.copy(status = TransferStatus.TRANSFER_SUCCEEDED).toEntity()
val transferListRandom = listOf(transferEntityQueue, transferEntityFailed, transferEntityInProgress, transferEntitySucceeded)
val transferQueue = OC_TRANSFER.copy()
transferQueue.status = TransferStatus.TRANSFER_QUEUED
val transferFailed = OC_TRANSFER.copy()
transferFailed.status = TransferStatus.TRANSFER_FAILED
val transferSucceeded = OC_TRANSFER.copy()
transferSucceeded.status = TransferStatus.TRANSFER_SUCCEEDED
val transferListOrdered = listOf(OC_TRANSFER, transferQueue, transferFailed, transferSucceeded)
every {
transferDao.getAllTransfersAsStream()
} returns flowOf(transferListRandom)
val actualResult = ocLocalTransferDataSource.getAllTransfersAsStream().first()
assertEquals(transferListOrdered, actualResult)
verify(exactly = 1) {
transferDao.getAllTransfersAsStream()
}
}
@Test
fun `getLastTransferFor returns a OCTransfer`() {
every {
transferDao.getLastTransferWithRemotePathAndAccountName(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
} returns transferEntity
val actualResult = ocLocalTransferDataSource.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
assertEquals(transferEntity.toModel(), actualResult)
verify(exactly = 1) {
transferDao.getLastTransferWithRemotePathAndAccountName(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
}
}
@Test
fun `getCurrentAndPendingTransfers returns a list of OCTransfer`() {
every {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_IN_PROGRESS.value, TransferStatus.TRANSFER_QUEUED.value))
} returns listOf(transferEntity)
val actualResult = ocLocalTransferDataSource.getCurrentAndPendingTransfers()
assertEquals(listOf(transferEntity.toModel()), actualResult)
verify(exactly = 1) {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_IN_PROGRESS.value, TransferStatus.TRANSFER_QUEUED.value))
}
}
@Test
fun `getFailedTransfers returns a list of OCTransfer`() {
every {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_FAILED.value))
} returns listOf(transferEntity)
val actualResult = ocLocalTransferDataSource.getFailedTransfers()
assertEquals(listOf(transferEntity.toModel()), actualResult)
verify(exactly = 1) {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_FAILED.value))
}
}
@Test
fun `getFinishedTransfers returns a list of OCTransfer`() {
every {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_SUCCEEDED.value))
} returns listOf(transferEntity)
val actualResult = ocLocalTransferDataSource.getFinishedTransfers()
assertEquals(listOf(transferEntity.toModel()), actualResult)
verify(exactly = 1) {
transferDao.getTransfersWithStatus(listOf(TransferStatus.TRANSFER_SUCCEEDED.value))
}
}
@Test
fun `clearFailedTransfers removes failed transfers correctly`() {
ocLocalTransferDataSource.clearFailedTransfers()
verify(exactly = 1) {
transferDao.deleteTransfersWithStatus(TransferStatus.TRANSFER_FAILED.value)
}
}
@Test
fun `clearSuccessfulTransfers removes successful transfers correctly`() {
ocLocalTransferDataSource.clearSuccessfulTransfers()
verify(exactly = 1) {
transferDao.deleteTransfersWithStatus(TransferStatus.TRANSFER_SUCCEEDED.value)
}
}
}
@@ -0,0 +1,288 @@
/**
* qsfera Android client application
*
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.transfers.repository
import eu.qsfera.android.data.transfers.datasources.LocalTransferDataSource
import eu.qsfera.android.domain.transfers.model.TransferResult
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_FAILED_TRANSFER
import eu.qsfera.android.testutil.OC_FINISHED_TRANSFER
import eu.qsfera.android.testutil.OC_PENDING_TRANSFER
import eu.qsfera.android.testutil.OC_TRANSFER
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
class OCTransferRepositoryTest {
private val localTransferDataSource = mockk<LocalTransferDataSource>(relaxUnitFun = true)
private val ocTransferRepository = OCTransferRepository(localTransferDataSource)
@Test
fun `saveTransfer inserts a transfer correctly`() {
// The result of this method is not used, so it can be anything
every {
localTransferDataSource.saveTransfer(OC_TRANSFER)
} returns 1L
val result = ocTransferRepository.saveTransfer(OC_TRANSFER)
assertEquals(1L, result)
verify(exactly = 1) {
localTransferDataSource.saveTransfer(OC_TRANSFER)
}
}
@Test
fun `updateTransfer updates a transfer correctly`() {
ocTransferRepository.updateTransfer(OC_TRANSFER)
verify(exactly = 1) {
localTransferDataSource.updateTransfer(OC_TRANSFER)
}
}
@Test
fun `updateTransferStatusToInProgressById changes transfer status correctly`() {
ocTransferRepository.updateTransferStatusToInProgressById(OC_TRANSFER.id!!)
verify(exactly = 1) {
localTransferDataSource.updateTransferStatusToInProgressById(OC_TRANSFER.id!!)
}
}
@Test
fun `updateTransferStatusToEnqueuedById changes transfer status correctly`() {
ocTransferRepository.updateTransferStatusToEnqueuedById(OC_TRANSFER.id!!)
verify(exactly = 1) {
localTransferDataSource.updateTransferStatusToEnqueuedById(OC_TRANSFER.id!!)
}
}
@Test
fun `updateTransferLocalPath updates transfer local path correctly`() {
ocTransferRepository.updateTransferLocalPath(OC_TRANSFER.id!!, OC_TRANSFER.localPath)
verify(exactly = 1) {
localTransferDataSource.updateTransferLocalPath(OC_TRANSFER.id!!, OC_TRANSFER.localPath)
}
}
@Test
fun `updateTransferSourcePath updates transfer source path correctly`() {
ocTransferRepository.updateTransferSourcePath(OC_TRANSFER.id!!, OC_TRANSFER.sourcePath!!)
verify(exactly = 1) {
localTransferDataSource.updateTransferSourcePath(OC_TRANSFER.id!!, OC_TRANSFER.sourcePath!!)
}
}
@Test
fun `updateTransferWhenFinished changes transfer status correctly`() {
ocTransferRepository.updateTransferWhenFinished(OC_TRANSFER.id!!, OC_FINISHED_TRANSFER.status, 1_000, TransferResult.UPLOADED)
verify(exactly = 1) {
localTransferDataSource.updateTransferWhenFinished(OC_TRANSFER.id!!, OC_FINISHED_TRANSFER.status, 1_000, TransferResult.UPLOADED)
}
}
@Test
fun `updateTransferStorageDirectoryInLocalPath updates transfer storage directory correctly`() {
val oldDirectory = "/oldDirectory/path"
val newDirectory = "/newDirectory/path"
ocTransferRepository.updateTransferStorageDirectoryInLocalPath(OC_TRANSFER.id!!, oldDirectory, newDirectory)
verify(exactly = 1) {
localTransferDataSource.updateTransferStorageDirectoryInLocalPath(OC_TRANSFER.id!!, oldDirectory, newDirectory)
}
}
@Test
fun `deleteTransferById removes a transfer correctly`() {
ocTransferRepository.deleteTransferById(OC_TRANSFER.id!!)
verify(exactly = 1) {
localTransferDataSource.deleteTransferById(OC_TRANSFER.id!!)
}
}
@Test
fun `deleteAllTransfersFromAccount removes all transfers correctly`() {
ocTransferRepository.deleteAllTransfersFromAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
localTransferDataSource.deleteAllTransfersFromAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getTransferById returns a OCTransfer`() {
every {
localTransferDataSource.getTransferById(OC_TRANSFER.id!!)
} returns OC_TRANSFER
val transfer = ocTransferRepository.getTransferById(OC_TRANSFER.id!!)
assertEquals(OC_TRANSFER, transfer)
verify(exactly = 1) {
localTransferDataSource.getTransferById(OC_TRANSFER.id!!)
}
}
@Test
fun `getTransferById returns null when local datasource returns null`() {
every {
localTransferDataSource.getTransferById(OC_TRANSFER.id!!)
} returns null
val transfer = ocTransferRepository.getTransferById(OC_TRANSFER.id!!)
assertNull(transfer)
verify(exactly = 1) {
localTransferDataSource.getTransferById(OC_TRANSFER.id!!)
}
}
@Test
fun `getAllTransfers returns a list of OCTransfer`() {
every {
localTransferDataSource.getAllTransfers()
} returns listOf(OC_TRANSFER)
val listOfTransfers = ocTransferRepository.getAllTransfers()
assertEquals(listOf(OC_TRANSFER), listOfTransfers)
verify(exactly = 1) {
localTransferDataSource.getAllTransfers()
}
}
@Test
fun `getAllTransfersAsStream returns a Flow with a list of OCTransfer`() = runTest {
every {
localTransferDataSource.getAllTransfersAsStream()
} returns flowOf(listOf(OC_TRANSFER))
val listOfTransfers = ocTransferRepository.getAllTransfersAsStream().first()
assertEquals(listOf(OC_TRANSFER), listOfTransfers)
verify(exactly = 1) {
localTransferDataSource.getAllTransfersAsStream()
}
}
@Test
fun `getLastTransferFor returns a OCTransfer`() {
every {
localTransferDataSource.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
} returns OC_TRANSFER
val lastTransfer = ocTransferRepository.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
assertEquals(OC_TRANSFER, lastTransfer)
verify(exactly = 1) {
localTransferDataSource.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
}
}
@Test
fun `getLastTransferFor returns null when local datasource returns null`() {
every {
localTransferDataSource.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
} returns null
val lastTransfer = ocTransferRepository.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
assertNull(lastTransfer)
verify(exactly = 1) {
localTransferDataSource.getLastTransferFor(OC_TRANSFER.remotePath, OC_ACCOUNT_NAME)
}
}
@Test
fun `getCurrentAndPendingTransfers returns a list of OCTransfer`() {
every {
localTransferDataSource.getCurrentAndPendingTransfers()
} returns listOf(OC_TRANSFER, OC_PENDING_TRANSFER)
val listOfCurrentAndPendingTransfers = ocTransferRepository.getCurrentAndPendingTransfers()
assertEquals(listOf(OC_TRANSFER, OC_PENDING_TRANSFER), listOfCurrentAndPendingTransfers)
verify(exactly = 1) {
localTransferDataSource.getCurrentAndPendingTransfers()
}
}
@Test
fun `getFailedTransfers returns a list of OCTransfer`() {
every {
localTransferDataSource.getFailedTransfers()
} returns listOf(OC_FAILED_TRANSFER)
val listOfFailedTransfers = ocTransferRepository.getFailedTransfers()
assertEquals(listOf(OC_FAILED_TRANSFER), listOfFailedTransfers)
verify(exactly = 1) {
localTransferDataSource.getFailedTransfers()
}
}
@Test
fun `getFinishedTransfers returns a list of OCTransfer`() {
every {
localTransferDataSource.getFinishedTransfers()
} returns listOf(OC_FINISHED_TRANSFER)
val listOfFinishedTransfers = ocTransferRepository.getFinishedTransfers()
assertEquals(listOf(OC_FINISHED_TRANSFER), listOfFinishedTransfers)
verify(exactly = 1) {
localTransferDataSource.getFinishedTransfers()
}
}
@Test
fun `clearFailedTransfers removes failed transfers correctly`() {
ocTransferRepository.clearFailedTransfers()
verify(exactly = 1) {
localTransferDataSource.clearFailedTransfers()
}
}
@Test
fun `clearSuccessfulTransfers removes successful transfers correctly`() {
ocTransferRepository.clearSuccessfulTransfers()
verify(exactly = 1) {
localTransferDataSource.clearSuccessfulTransfers()
}
}
}
@@ -0,0 +1,139 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
* @author Jorge Aguado Recio
*
* 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.user.datasources.implementation
import eu.qsfera.android.data.user.datasources.implementation.OCLocalUserDataSource.Companion.toEntity
import eu.qsfera.android.data.user.db.UserDao
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_USER_QUOTA
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
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.Before
import org.junit.Test
class OCLocalUserDataSourceTest {
private lateinit var ocLocalUserDataSource: OCLocalUserDataSource
private val ocUserQuotaDao = mockk<UserDao>(relaxUnitFun = true)
private val userQuotaEntity = OC_USER_QUOTA.toEntity()
@Before
fun setUp() {
ocLocalUserDataSource = OCLocalUserDataSource(ocUserQuotaDao)
}
@Test
fun `saveQuotaForAccount saves user quota correctly`() {
ocLocalUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA)
verify(exactly = 1) {
ocUserQuotaDao.insertOrReplace(userQuotaEntity)
}
}
@Test
fun `getQuotaForAccount returns a UserQuota`() {
every { ocUserQuotaDao.getQuotaForAccount(any()) } returns userQuotaEntity
val userQuota = ocLocalUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
assertEquals(OC_USER_QUOTA, userQuota)
verify(exactly = 1) {
ocUserQuotaDao.getQuotaForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getQuotaForAccount returns null when DAO returns a null quota`() {
every { ocUserQuotaDao.getQuotaForAccount(any()) } returns null
val quotaEntity = ocLocalUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
assertNull(quotaEntity)
verify(exactly = 1) {
ocUserQuotaDao.getQuotaForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getQuotaForAccountAsFlow returns a Flow with an UserQuota`() = runTest {
every {
ocUserQuotaDao.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME)
} returns flowOf(userQuotaEntity)
val userQuota = ocLocalUserDataSource.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(OC_USER_QUOTA, userQuota)
verify(exactly = 1) {
ocUserQuotaDao.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME)
}
}
@Test
fun `getAllUserQuotas returns a list of UserQuota`() {
every { ocUserQuotaDao.getAllUserQuotas() } returns listOf(userQuotaEntity)
val resultActual = ocLocalUserDataSource.getAllUserQuotas()
assertEquals(listOf(OC_USER_QUOTA), resultActual)
verify(exactly = 1) {
ocUserQuotaDao.getAllUserQuotas()
}
}
@Test
fun `getAllUserQuotasAsFlow returns a Flow with a list of UserQuota`() = runTest {
every {
ocUserQuotaDao.getAllUserQuotasAsFlow()
} returns flowOf(listOf(userQuotaEntity))
val listOfUserQuotas = ocLocalUserDataSource.getAllUserQuotasAsFlow().first()
assertEquals(listOf(OC_USER_QUOTA), listOfUserQuotas)
verify(exactly = 1) {
ocUserQuotaDao.getAllUserQuotasAsFlow()
}
}
@Test
fun `deleteQuotaForAccount removes user quota correctly`() {
ocLocalUserDataSource.deleteQuotaForAccount(OC_ACCOUNT_NAME)
verify(exactly = 1) {
ocUserQuotaDao.deleteQuotaForAccount(OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,127 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.user.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import eu.qsfera.android.lib.resources.users.GetRemoteUserQuotaOperation
import eu.qsfera.android.lib.resources.users.RemoteAvatarData
import eu.qsfera.android.lib.resources.users.RemoteUserInfo
import eu.qsfera.android.lib.resources.users.services.implementation.OCUserService
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_USER_AVATAR
import eu.qsfera.android.testutil.OC_USER_INFO
import eu.qsfera.android.testutil.OC_USER_QUOTA
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
class OCRemoteUserDataSourceTest {
private lateinit var ocRemoteUserDataSource: OCRemoteUserDataSource
private val clientManager: ClientManager = mockk(relaxed = true)
private val ocUserService: OCUserService = mockk()
private val avatarDimension = 128
private val remoteUserInfo = RemoteUserInfo(
id = OC_USER_INFO.id,
displayName = OC_USER_INFO.displayName,
email = OC_USER_INFO.email
)
private val remoteQuota = GetRemoteUserQuotaOperation.RemoteQuota(
used = OC_USER_QUOTA.used,
free = OC_USER_QUOTA.available,
relative = OC_USER_QUOTA.getRelative(),
total = OC_USER_QUOTA.getTotal()
)
private val remoteAvatar = RemoteAvatarData(
avatarData = OC_USER_AVATAR.avatarData,
eTag = OC_USER_AVATAR.eTag,
mimeType = OC_USER_AVATAR.mimeType
)
@Before
fun setUp() {
every { clientManager.getUserService(any()) } returns ocUserService
ocRemoteUserDataSource = OCRemoteUserDataSource(
clientManager,
avatarDimension
)
}
@Test
fun `getUserInfo returns UserInfo`() {
val getUserInfoResult: RemoteOperationResult<RemoteUserInfo> =
createRemoteOperationResultMock(data = remoteUserInfo, isSuccess = true)
every {
ocUserService.getUserInfo()
} returns getUserInfoResult
val userInfo = ocRemoteUserDataSource.getUserInfo(OC_ACCOUNT_NAME)
assertNotNull(userInfo)
assertEquals(OC_USER_INFO, userInfo)
verify(exactly = 1) { ocUserService.getUserInfo() }
}
@Test
fun `getUserQuota returns UserQuota`() {
val getUserQuotaResult: RemoteOperationResult<GetRemoteUserQuotaOperation.RemoteQuota> =
createRemoteOperationResultMock(data = remoteQuota, isSuccess = true)
every {
ocUserService.getUserQuota()
} returns getUserQuotaResult
val userQuota = ocRemoteUserDataSource.getUserQuota(OC_ACCOUNT_NAME)
assertNotNull(userQuota)
assertEquals(OC_USER_QUOTA, userQuota)
verify(exactly = 1) { ocUserService.getUserQuota() }
}
@Test
fun `getUserAvatar returns UserAvatar`() {
val getUserAvatarResult: RemoteOperationResult<RemoteAvatarData> =
createRemoteOperationResultMock(data = remoteAvatar, isSuccess = true)
every {
ocUserService.getUserAvatar(avatarDimension)
} returns getUserAvatarResult
val userAvatar = ocRemoteUserDataSource.getUserAvatar(OC_ACCOUNT_NAME)
assertNotNull(userAvatar)
assertEquals(OC_USER_AVATAR, userAvatar)
verify(exactly = 1) { ocUserService.getUserAvatar(avatarDimension) }
}
}
@@ -0,0 +1,76 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.user.db
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class UserQuotaEntityTest {
@Test
fun testConstructor() {
val item = UserQuotaEntity(
"accountName",
200,
800
)
assertEquals("accountName", item.accountName)
assertEquals(800, item.available)
assertEquals(200, item.used)
}
@Test
fun testEqualsOk() {
val item1 = UserQuotaEntity(
accountName = "accountName",
available = 200,
used = 800
)
val item2 = UserQuotaEntity(
"accountName",
800,
200
)
assertTrue(item1 == item2)
assertFalse(item1 === item2)
}
@Test
fun testEqualsKo() {
val item1 = UserQuotaEntity(
accountName = "accountName",
available = 800,
used = 200
)
val item2 = UserQuotaEntity(
"accountName2",
200,
800
)
assertFalse(item1 == item2)
assertFalse(item1 === item2)
}
}
@@ -0,0 +1,158 @@
/**
* qsfera Android client application
*
* @author Abel García de Prada
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.user.repository
import eu.qsfera.android.data.user.datasources.LocalUserDataSource
import eu.qsfera.android.data.user.datasources.RemoteUserDataSource
import eu.qsfera.android.testutil.OC_ACCOUNT_NAME
import eu.qsfera.android.testutil.OC_USER_AVATAR
import eu.qsfera.android.testutil.OC_USER_INFO
import eu.qsfera.android.testutil.OC_USER_QUOTA
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
class OCUserRepositoryTest {
private val remoteUserDataSource = mockk<RemoteUserDataSource>()
private val localUserDataSource = mockk<LocalUserDataSource>(relaxUnitFun = true)
private val ocUserRepository = OCUserRepository(localUserDataSource, remoteUserDataSource)
@Test
fun `getUserInfo returns an UserInfo`() {
every {
remoteUserDataSource.getUserInfo(OC_ACCOUNT_NAME)
} returns OC_USER_INFO
val userInfo = ocUserRepository.getUserInfo(OC_ACCOUNT_NAME)
assertEquals(OC_USER_INFO, userInfo)
verify(exactly = 1) {
remoteUserDataSource.getUserInfo(OC_ACCOUNT_NAME)
}
}
@Test
fun `getUserQuota returns an UserQuota`() {
every {
remoteUserDataSource.getUserQuota(OC_ACCOUNT_NAME)
} returns OC_USER_QUOTA
val userQuota = ocUserRepository.getUserQuota(OC_ACCOUNT_NAME)
assertEquals(OC_USER_QUOTA, userQuota)
verify(exactly = 1) {
remoteUserDataSource.getUserQuota(OC_ACCOUNT_NAME)
localUserDataSource.saveQuotaForAccount(OC_ACCOUNT_NAME, OC_USER_QUOTA)
}
}
@Test
fun `getStoredUserQuota returns an UserQuota`() {
every {
localUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
} returns OC_USER_QUOTA
val storedQuota = ocUserRepository.getStoredUserQuota(OC_ACCOUNT_NAME)
assertEquals(OC_USER_QUOTA, storedQuota)
verify(exactly = 1) {
localUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getStoredUserQuota returns null when local datasource returns null`() {
every {
localUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
} returns null
val storedQuota = ocUserRepository.getStoredUserQuota(OC_ACCOUNT_NAME)
assertNull(storedQuota)
verify(exactly = 1) {
localUserDataSource.getQuotaForAccount(OC_ACCOUNT_NAME)
}
}
@Test
fun `getStoredUserQuotaAsFlow returns a Flow with an UserQuota`() = runTest {
every {
localUserDataSource.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME)
} returns flowOf(OC_USER_QUOTA)
val userQuota = ocUserRepository.getStoredUserQuotaAsFlow(OC_ACCOUNT_NAME).first()
assertEquals(OC_USER_QUOTA, userQuota)
verify(exactly = 1) {
localUserDataSource.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME)
}
}
@Test
fun `getAllUserQuotas returns a list of UserQuota`() {
every {
localUserDataSource.getAllUserQuotas()
} returns listOf(OC_USER_QUOTA)
val listOfUserQuotas = ocUserRepository.getAllUserQuotas()
assertEquals(listOf(OC_USER_QUOTA), listOfUserQuotas)
verify(exactly = 1) {
localUserDataSource.getAllUserQuotas()
}
}
@Test
fun `getAllUserQuotasAsFlow returns a Flow with a list of UserQuota`() = runTest {
every {
localUserDataSource.getAllUserQuotasAsFlow()
} returns flowOf(listOf(OC_USER_QUOTA))
val listOfUserQuotas = ocUserRepository.getAllUserQuotasAsFlow().first()
assertEquals(listOf(OC_USER_QUOTA), listOfUserQuotas)
verify(exactly = 1) {
localUserDataSource.getAllUserQuotasAsFlow()
}
}
@Test
fun `getUserAvatar returns an UserAvatar`() {
every {
remoteUserDataSource.getUserAvatar(OC_ACCOUNT_NAME)
} returns OC_USER_AVATAR
val userAvatar = ocUserRepository.getUserAvatar(OC_ACCOUNT_NAME)
assertEquals(OC_USER_AVATAR, userAvatar)
verify(exactly = 1) {
remoteUserDataSource.getUserAvatar(OC_ACCOUNT_NAME)
}
}
}
@@ -0,0 +1,133 @@
/**
* qsfera Android client application
*
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.webfinger.datasources.implementation
import eu.qsfera.android.data.ClientManager
import eu.qsfera.android.domain.webfinger.model.WebFingerRel
import eu.qsfera.android.lib.common.QSferaClient
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import eu.qsfera.android.lib.resources.webfinger.services.implementation.OCWebFingerService
import eu.qsfera.android.testutil.OC_ACCESS_TOKEN
import eu.qsfera.android.testutil.OC_ACCOUNT_ID
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
import eu.qsfera.android.utils.createRemoteOperationResultMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
class OCRemoteWebFingerDataSourceTest {
private lateinit var ocRemoteWebFingerDatasource: OCRemoteWebFingerDataSource
private val clientManager: ClientManager = mockk(relaxed = true)
private val qsferaClient: QSferaClient = mockk(relaxed = true)
private val ocWebFingerService: OCWebFingerService = mockk()
private val urls: List<String> = listOf(
"http://webfinger.qsfera/tests/server-instance1",
"http://webfinger.qsfera/tests/server-instance2",
"http://webfinger.qsfera/tests/server-instance3",
)
@Before
fun setUp() {
ocRemoteWebFingerDatasource = OCRemoteWebFingerDataSource(
ocWebFingerService,
clientManager,
)
every { clientManager.getClientForAnonymousCredentials(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, false) } returns qsferaClient
}
@Test
fun `getInstancesFromWebFinger returns a list of String of web finger urls`() {
val getInstancesFromWebFingerResult: RemoteOperationResult<List<String>> =
createRemoteOperationResultMock(data = urls, isSuccess = true)
every {
ocWebFingerService.getInstancesFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY.uri,
qsferaClient,
)
} returns getInstancesFromWebFingerResult
val actualResult = ocRemoteWebFingerDatasource.getInstancesFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
)
assertEquals(getInstancesFromWebFingerResult.data, actualResult)
verify(exactly = 1) {
clientManager.getClientForAnonymousCredentials(any(), false)
ocWebFingerService.getInstancesFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY.uri,
qsferaClient,
)
}
}
@Test
fun `getInstancesFromAuthenticatedWebFinger returns a list of String of web finger urls`() {
val getInstancesFromAuthenticatedWebFingerResult: RemoteOperationResult<List<String>> =
createRemoteOperationResultMock(data = urls, isSuccess = true)
every {
ocWebFingerService.getInstancesFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY.uri,
qsferaClient,
)
} returns getInstancesFromAuthenticatedWebFingerResult
val actualResult = ocRemoteWebFingerDatasource.getInstancesFromAuthenticatedWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
username = OC_ACCOUNT_ID,
accessToken = OC_ACCESS_TOKEN
)
assertEquals(getInstancesFromAuthenticatedWebFingerResult.data, actualResult)
verify(exactly = 1) {
qsferaClient.credentials = any()
clientManager.getClientForAnonymousCredentials(any(), false)
ocWebFingerService.getInstancesFromWebFinger(
lookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
resource = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
rel = WebFingerRel.OIDC_ISSUER_DISCOVERY.uri,
any(),
)
}
}
}
@@ -0,0 +1,103 @@
/**
* qsfera Android client application
*
* @author Jorge Aguado Recio
*
* Copyright (C) 2025 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.data.webfinger.repository
import eu.qsfera.android.data.webfinger.datasources.RemoteWebFingerDataSource
import eu.qsfera.android.domain.webfinger.model.WebFingerRel
import eu.qsfera.android.testutil.OC_ACCESS_TOKEN
import eu.qsfera.android.testutil.OC_ACCOUNT_ID
import eu.qsfera.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import org.junit.Test
class OCWebFingerRepositoryTest {
private val remoteWebFingerDatasource = mockk<RemoteWebFingerDataSource>()
private val ocWebFingerRepository = OCWebFingerRepository(remoteWebFingerDatasource)
private val webFingerInstance = "http://webfinger.qsfera/tests/server-instance1"
@Test
fun `getInstancesFromWebFinger returns a list of String of webfinger url`() {
val webFingerResource = "admin"
every {
remoteWebFingerDatasource.getInstancesFromWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerResource
)
} returns listOf(webFingerInstance)
val webFingerResult = ocWebFingerRepository.getInstancesFromWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerResource
)
assertEquals(listOf(webFingerInstance), webFingerResult)
verify(exactly = 1) {
remoteWebFingerDatasource.getInstancesFromWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerResource
)
}
}
@Test
fun `getInstancesFromAuthenticatedWebFinger returns a list of String of webfinger url`() {
val webFingerAuthenticatedResource = "acct:me@demo.qsfera.eu"
every {
remoteWebFingerDatasource.getInstancesFromAuthenticatedWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerAuthenticatedResource,
OC_ACCOUNT_ID,
OC_ACCESS_TOKEN
)
} returns listOf(webFingerInstance)
val webFingerResult = ocWebFingerRepository.getInstancesFromAuthenticatedWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerAuthenticatedResource,
OC_ACCOUNT_ID,
OC_ACCESS_TOKEN
)
assertEquals(listOf(webFingerInstance), webFingerResult)
verify(exactly = 1) {
remoteWebFingerDatasource.getInstancesFromAuthenticatedWebFinger(
OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl,
WebFingerRel.OIDC_ISSUER_DISCOVERY,
webFingerAuthenticatedResource,
OC_ACCOUNT_ID,
OC_ACCESS_TOKEN
)
}
}
}
@@ -0,0 +1,67 @@
/**
* qsfera Android client application
*
* @author David González Verdugo
* @author Abel García de Prada
* Copyright (C) 2020 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,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.qsfera.android.utils
import eu.qsfera.android.lib.common.operations.RemoteOperationResult
import io.mockk.every
import io.mockk.mockk
fun <T> createRemoteOperationResultMock(
data: T,
isSuccess: Boolean,
httpPhrase: String? = null,
resultCode: RemoteOperationResult.ResultCode? = null,
exception: Exception? = null,
authenticationHeader: List<String> = listOf(),
httpCode: Int? = null,
redirectedLocation: String? = null
): RemoteOperationResult<T> {
val remoteOperationResult = mockk<RemoteOperationResult<T>>(relaxed = true)
every { remoteOperationResult.data } returns data
every { remoteOperationResult.isSuccess } returns isSuccess
if (httpPhrase != null) {
every { remoteOperationResult.httpPhrase } returns httpPhrase
}
if (resultCode != null) {
every { remoteOperationResult.code } returns resultCode
}
if (exception != null) {
throw exception
}
if (authenticationHeader.isNotEmpty()) {
every { remoteOperationResult.authenticateHeaders } returns authenticationHeader
}
if (httpCode != null) {
every { remoteOperationResult.httpCode } returns httpCode
}
if (redirectedLocation != null) {
every { remoteOperationResult.redirectedLocation } returns redirectedLocation
}
return remoteOperationResult
}