diff --git a/.drone.yml b/.drone.yml index 679d8d0..93d6729 100644 --- a/.drone.yml +++ b/.drone.yml @@ -15,6 +15,7 @@ steps: - echo "---------------------------------------------------------" - echo "Testing..." - echo "---------------------------------------------------------" + - ./gradlew test -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false environment: SONAR_HOST_URL: from_secret: sonarScannerHostUrl diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt index 87828ad..29c7835 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt @@ -355,6 +355,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar val drawerData = DrawerData(repository.getDBTags().map { it.toView() }, repository.getDBSources().map { it.toView() }) runOnUiThread { + // TODO: All this logic should be handled by the repository, simplify and remove direct DB access // Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) { drawerApiCalls(drawerData) diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt index bd17f42..00e1e7e 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt @@ -163,7 +163,6 @@ class LoginActivity : AppCompatActivity(), DIAware { CoroutineScope(Dispatchers.IO).launch { val result = repository.login() if (result) { - repository.updateApiVersion() goToMain() } else { CoroutineScope(Dispatchers.Main).launch { diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt index 74a0739..a565507 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt @@ -52,11 +52,13 @@ override fun doWork(): Result { repository.handleDBActions() + val apiItems = repository.tryToCacheItemsAndGetNewOnes() if (appSettingsService.isNotifyNewItemsEnabled()) { launch { - handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notificationManager) + handleNewItemsNotification(apiItems, notificationManager) } } + apiItems.map { it.preloadImages(context) } } } return Result.success() @@ -66,6 +68,7 @@ override fun doWork(): Result { newItems: List?, notificationManager: NotificationManager ) { + // TODO: Check if this coroutine is actually required CoroutineScope(Dispatchers.IO).launch { val apiItems = newItems.orEmpty() @@ -102,7 +105,6 @@ override fun doWork(): Result { notificationManager.notify(2, newItemsNotification.build()) } } - apiItems.map { it.preloadImages(context) } Timer("", false).schedule(4000) { notificationManager.cancel(1) } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 6240aee..e80af33 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -56,6 +56,8 @@ kotlin { dependencies { implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) + implementation("io.mockk:mockk:1.12.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") } } val androidMain by getting { diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index e5dcc2f..8a1fa86 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -11,6 +11,7 @@ import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) { @@ -36,9 +37,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap var badgeStarred = 0 set(value) {field = if (value < 0) { 0 } else { value } } + private var fetchedSources = false + private var fetchedTags = false + init { // TODO: Dispatchers.IO not available in KMM, an alternative solution should be found - CoroutineScope(Dispatchers.Main).launch { + runBlocking { updateApiVersion() dateUtils = DateUtils(appSettingsService) reloadBadges() @@ -105,9 +109,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap val items = api.getItems( itemType.type, 0, - tagFilter?.tag, - sourceFilter?.id?.toLong(), - searchFilter, + null, + null, + null, null, 200 ) @@ -131,32 +135,40 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap badgeStarred = response.data.starred success = true } - } else { + } else if (appSettingsService.isItemCachingEnabled()) { // TODO: do this differently, because it's not efficient val dbItems = getDBItems() badgeUnread = dbItems.filter { item -> item.unread }.size badgeStarred = dbItems.filter { item -> item.starred }.size - badgeAll = items.size + badgeAll = dbItems.size + success = true } return success } - suspend fun getTags(): List? { - return if (isNetworkAvailable()) { + suspend fun getTags(): List { + val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() + return if (isNetworkAvailable() && !fetchedTags) { val apiTags = api.tags() - if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + if (apiTags.success && apiTags.data != null && isDatabaseEnabled) { resetDBTagsWithData(apiTags.data) + if (!appSettingsService.isUpdateSourcesEnabled()) { + fetchedTags = true + } } - apiTags.data - } else { + apiTags.data ?: emptyList() + } else if (isDatabaseEnabled) { getDBTags().map { it.toView() } + } else { + emptyList() } } - suspend fun getSpouts(): Map? { + // TODO: Add tests + suspend fun getSpouts(): Map { return if (isNetworkAvailable()) { val spouts = api.spouts() - return if (spouts.success && spouts.data != null) { + if (spouts.success && spouts.data != null) { spouts.data } else { emptyMap() // TODO: do something here @@ -166,18 +178,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap } } - suspend fun getSources(): ArrayList? { - return if (isNetworkAvailable()) { + suspend fun getSources(): ArrayList { + val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled() + return if (isNetworkAvailable() && !fetchedSources) { val apiSources = api.sources() - if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + if (apiSources.success && apiSources.data != null && isDatabaseEnabled) { resetDBSourcesWithData(apiSources.data) + if (!appSettingsService.isUpdateSourcesEnabled()) { + fetchedSources = true + } } - apiSources.data - } else { + apiSources.data ?: ArrayList() + } else if (isDatabaseEnabled) { ArrayList(getDBSources().map { it.toView() }) + } else { + ArrayList() } } + // TODO: Add tests suspend fun markAsRead(item: SelfossModel.Item): Boolean { val success = markAsReadById(item.id) @@ -189,14 +208,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun markAsReadById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.markAsRead(id.toString())?.isSuccess + api.markAsRead(id.toString()).isSuccess } else { insertDBAction(id.toString(), read = true) true } } - + // TODO: Add tests suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean { val success = unmarkAsReadById(item.id) @@ -208,13 +227,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unmarkAsReadById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unmarkAsRead(id.toString())?.isSuccess + api.unmarkAsRead(id.toString()).isSuccess } else { insertDBAction(id.toString(), unread = true) true } } + // TODO: Add tests suspend fun starr(item: SelfossModel.Item): Boolean { val success = starrById(item.id) @@ -226,13 +246,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun starrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.starr(id.toString())?.isSuccess + api.starr(id.toString()).isSuccess } else { insertDBAction(id.toString(), starred = true) true } } + // TODO: Add tests suspend fun unstarr(item: SelfossModel.Item): Boolean { val success = unstarrById(item.id) @@ -244,17 +265,18 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unstarrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unstarr(id.toString())?.isSuccess + api.unstarr(id.toString()).isSuccess } else { insertDBAction(id.toString(), starred = true) true } } + // TODO: Add tests suspend fun markAllAsRead(items: ArrayList): Boolean { var success = false - if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) { + if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() }).isSuccess) { success = true for (item in items) { markAsReadLocally(item) @@ -323,7 +345,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap tags, filter, appSettingsService.getApiVersion() - )?.isSuccess == true + ).isSuccess == true } return response @@ -333,9 +355,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap var success = false if (isNetworkAvailable()) { val response = api.deleteSource(id) - if (response != null) { - success = response.isSuccess - } + success = response.isSuccess } return success @@ -343,7 +363,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun updateRemote(): Boolean { return if (isNetworkAvailable()) { - api.update()?.equals("finished") + api.update().data.equals("finished") } else { false } @@ -354,7 +374,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap if (isNetworkAvailable()) { try { val response = api.login() - result = response?.isSuccess == true + result = response.isSuccess == true if (result) { updateApiVersion() } @@ -371,7 +391,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap api.refreshLoginInformation() } - suspend fun updateApiVersion() { + private suspend fun updateApiVersion() { val apiMajorVersion = appSettingsService.getApiVersion() if (isNetworkAvailable()) { @@ -390,8 +410,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private fun deleteDBAction(action: ACTION) = db.actionsQueries.deleteAction(action.id) + // TODO: This function should be private fun getDBTags(): List = db.tagsQueries.tags().executeAsList() + // TODO: This function should be private fun getDBSources(): List = db.sourcesQueries.sources().executeAsList() private fun resetDBTagsWithData(tagEntities: List) { @@ -430,8 +452,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private fun updateDBItem(item: SelfossModel.Item) = db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString()) - - suspend fun tryToCacheItemsAndGetNewOnes(): List? { + suspend fun tryToCacheItemsAndGetNewOnes(): List { try { val newItems = getMaxItemsForBackground(ItemType.UNREAD) val allItems = getMaxItemsForBackground(ItemType.ALL) @@ -444,6 +465,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap return emptyList() } + // TODO: Add tests suspend fun handleDBActions() { val actions: List = getDBActions() diff --git a/shared/src/commonTest/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryTest.kt b/shared/src/commonTest/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryTest.kt new file mode 100644 index 0000000..1d99897 --- /dev/null +++ b/shared/src/commonTest/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryTest.kt @@ -0,0 +1,1001 @@ +package bou.amine.apps.readerforselfossv2.repository + +import bou.amine.apps.readerforselfossv2.dao.ITEM +import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB +import bou.amine.apps.readerforselfossv2.dao.SOURCE +import bou.amine.apps.readerforselfossv2.dao.TAG +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.rest.SelfossApi +import bou.amine.apps.readerforselfossv2.service.AppSettingsService +import bou.amine.apps.readerforselfossv2.utils.ItemType +import bou.amine.apps.readerforselfossv2.utils.toView +import com.github.ln_12.library.ConnectivityStatus +import io.mockk.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.runBlocking +import kotlin.test.* + +class RepositoryTest() { + private val connectivityStatus = mockk() + private val db = mockk(relaxed = true) + private val appSettingsService = mockk() + private val api = mockk() + + private val NUMBER_ARTICLES = 100 + private val NUMBER_UNREAD = 50 + private val NUMBER_STARRED = 20 + + @BeforeTest + fun setup() { + clearAllMocks() + every { appSettingsService.getApiVersion() } returns 4 + every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/" + every { appSettingsService.isItemCachingEnabled() } returns false + every { appSettingsService.isUpdateSourcesEnabled() } returns false + + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(true) + + coEvery { api.version() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0")) + coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED)) + + every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems() + every { db.tagsQueries.deleteAllTags() } returns Unit + every { db.tagsQueries.transaction(any(), any()) } returns Unit + every { db.tagsQueries.insertTag(any()) } returns Unit + } + + @Test + fun `Instantiate repository`() { + Repository(api, appSettingsService, connectivityStatus, db) + + coVerify(exactly = 1) { api.version() } + coVerify(exactly = 1) { api.stats() } + } + + @Test + fun `Instantiate repository without api version`() { + every { appSettingsService.getApiVersion() } returns -1 + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + Repository(api, appSettingsService, connectivityStatus, db) + + coVerify(exactly = 0) { api.version() } + coVerify(exactly = 0) { api.stats() } + } + + @Test + fun `Instantiate repository with negative stats`() { + coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(-100, -50, -20)) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + + assertEquals(0, repository.badgeAll) + assertEquals(0, repository.badgeStarred) + assertEquals(0, repository.badgeUnread) + coVerify(exactly = 1) { api.version() } + coVerify(exactly = 1) { api.stats() } + } + + @Test + fun `Get api 4 date with api 1 version stored`() { + every { appSettingsService.getApiVersion() } returns 1 + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + every { appSettingsService.updateApiVersion(any()) } returns Unit + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + repository.getNewerItems() + } + + assertSame(repository.items.size, 1) + verify(exactly = 1) { appSettingsService.updateApiVersion(4) } + } + + @Test + fun `Get api 1 date with api 4 version stored`() { + every { appSettingsService.getApiVersion() } returns 4 + coEvery { api.version() } returns SelfossModel.StatusAndData(success = false, null) + val itemParameters = FakeItemParameters() + itemParameters.datetime = "2021-04-23 11:45:32" + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameters)) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + repository.getNewerItems() + } + + assertSame(1, repository.items.size) + } + + @Test + fun `Get newer items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + repository.getNewerItems() + } + + assertSame(repository.items.size, 1) + coVerify(exactly = 1) { api.getItems("unread", 0, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get all newer items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.displayedItems = ItemType.ALL + runBlocking { + repository.getNewerItems() + } + + assertSame(repository.items.size, 1) + coVerify(exactly = 1) { api.getItems("all", 0, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get newer starred items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.displayedItems = ItemType.STARRED + runBlocking { + repository.getNewerItems() + } + + assertSame(repository.items.size, 1) + coVerify(exactly = 1) { api.getItems("starred", 0, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get newer items without connectivity`() { + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + repository.getNewerItems() + } + + assertSame(repository.items.size, 1) + coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) } + verify(atLeast = 1) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get older items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.items = ArrayList(generateTestApiItem()) + runBlocking { + repository.getOlderItems() + } + + assertSame(repository.items.size, 2) + coVerify(exactly = 1) { api.getItems("unread", 1, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get all older items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.items = ArrayList(generateTestApiItem()) + repository.displayedItems = ItemType.ALL + runBlocking { + repository.getOlderItems() + } + + assertSame(repository.items.size, 2) + coVerify(exactly = 1) { api.getItems("all", 1, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get older starred items`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.displayedItems = ItemType.STARRED + repository.items = ArrayList(generateTestApiItem()) + runBlocking { + repository.getOlderItems() + } + + assertSame(repository.items.size, 2) + coVerify(exactly = 1) { api.getItems("starred", 1, null, null, null, null, any()) } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Reload badges`() { + var success = false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + success = repository.reloadBadges() + } + + assertSame(true, success) + assertSame(NUMBER_ARTICLES, repository.badgeAll) + assertSame(NUMBER_UNREAD, repository.badgeUnread) + assertSame(NUMBER_STARRED, repository.badgeStarred) + coVerify(atLeast = 1) { api.stats() } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Reload badges without response`() { + coEvery { api.stats() } returns SelfossModel.StatusAndData(success = false, data = null) + + var success = false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + success = repository.reloadBadges() + } + + assertSame(false, success) + assertSame(0, repository.badgeAll) + assertSame(0, repository.badgeUnread) + assertSame(0, repository.badgeStarred) + coVerify(atLeast = 1) { api.stats() } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Reload badges without connection`() { + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns true + every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems() + + var success = false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + success = repository.reloadBadges() + } + + assertTrue(success) + assertSame(1, repository.badgeAll) + assertSame(1, repository.badgeUnread) + assertSame(1, repository.badgeStarred) + coVerify(exactly = 0) { api.stats() } + verify(atLeast = 1) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Reload badges without connection and items caching disabled`() { + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns false + every { appSettingsService.isUpdateSourcesEnabled() } returns true + + var success = false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + runBlocking { + success = repository.reloadBadges() + } + + assertFalse(success) + assertSame(0, repository.badgeAll) + assertSame(0, repository.badgeUnread) + assertSame(0, repository.badgeStarred) + coVerify(exactly = 0) { api.stats() } + verify(exactly = 0) { db.itemsQueries.items().executeAsList()} + } + + @Test + fun `Get tags`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { appSettingsService.isUpdateSourcesEnabled() } returns true + every { appSettingsService.isItemCachingEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List? = null + runBlocking { + testTags = repository.getTags() + } + + assertSame(tags, testTags) + assertNotSame(tagsDB.map { it.toView() }, testTags) + coVerify(exactly = 1) { api.tags() } + } + + @Test + fun `Get tags with sources update disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + // Tags will be fetched from the database on the second call, thus testTags != tags + testTags = repository.getTags() + } + + coVerify(exactly = 1) { api.tags() } + assertNotSame(tags, testTags) + assertContentEquals(tagsDB.map { it.toView() }, testTags) + verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags with items caching disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { appSettingsService.isUpdateSourcesEnabled() } returns true + every { appSettingsService.isItemCachingEnabled() } returns false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + } + + assertSame(tags, testTags) + coVerify(exactly = 1) { api.tags() } + verify(exactly = 0) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags with sources update and items caching disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + testTags = repository.getTags() + } + + coVerify(exactly = 1) { api.tags() } + assertNotSame(tags, testTags) + assertContentEquals(tagsDB.map { it.toView() }, testTags) + verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags without connection`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isUpdateSourcesEnabled() } returns true + every { appSettingsService.isItemCachingEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + } + + assertNotSame(tags, testTags) + assertContentEquals(tagsDB.map { it.toView() }, testTags) + coVerify(exactly = 0) { api.tags() } + verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags without connection and items caching disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns false + every { appSettingsService.isUpdateSourcesEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + } + + assertSame(emptyList(), testTags) + coVerify(exactly = 0) { api.tags() } + verify(exactly = 0) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags without connection and sources update disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns true + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + } + + assertNotSame(tags, testTags) + assertContentEquals(tagsDB.map { it.toView() }, testTags) + coVerify(exactly = 0) { api.tags() } + verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `Get tags without connection and sources update and items caching disabled`() { + val tags = listOf(SelfossModel.Tag("test", "red", 6), + SelfossModel.Tag("second", "yellow", 0)) + val tagsDB = listOf(TAG("test_DB", "red", 6), + TAG("second_DB", "yellow", 0)) + + coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) + coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns false + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testTags: List = emptyList() + runBlocking { + testTags = repository.getTags() + } + + assertContentEquals(tagsDB.map { it.toView() }, testTags) + coVerify(exactly = 0) { api.tags() } + verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } + } + + @Test + fun `get sources`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First DB source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertSame(sources, testSources) + assertNotEquals(sourcesDB.map { it.toView() }, testSources) + coVerify(exactly = 1) { api.sources() } + } + + @Test + fun `get sources with sources update disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second DB source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns true + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + // Sources will be fetched from the database on the second call, thus testSources != sources + testSources = repository.getSources() + } + + coVerify(exactly = 1) { api.sources() } + assertNotSame(sources, testSources) + assertContentEquals(sourcesDB.map { it.toView() }, testSources) + verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() } + } + + @Test + fun `get sources with items caching disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { appSettingsService.isUpdateSourcesEnabled() } returns true + every { appSettingsService.isItemCachingEnabled() } returns false + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertSame(sources, testSources) + coVerify(exactly = 1) { api.sources() } + verify(exactly = 0) { db.sourcesQueries } + } + + @Test + fun `get sources with sources update and items caching disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { appSettingsService.isUpdateSourcesEnabled() } returns false + every { appSettingsService.isItemCachingEnabled() } returns false + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertSame(sources, testSources) + coVerify(exactly = 1) { api.sources() } + verify(atLeast = 1) { db.sourcesQueries } + } + + @Test + fun `get sources without connection`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First DB source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertContentEquals(sourcesDB.map { it.toView() }, testSources) + coVerify(exactly = 0) { api.sources() } + verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() } + } + + @Test + fun `get sources without connection and items caching disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First DB source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns false + every { appSettingsService.isUpdateSourcesEnabled() } returns true + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertContentEquals(ArrayList(), testSources) + coVerify(exactly = 0) { api.sources() } + verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() } + } + + @Test + fun `get sources without connection and sources update disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First DB source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns true + every { appSettingsService.isUpdateSourcesEnabled() } returns false + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertContentEquals(sourcesDB.map { it.toView() }, testSources) + coVerify(exactly = 0) { api.sources() } + verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() } + } + + @Test + fun `get sources without connection and items caching and sources update disabled`() { + val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + val sourcesDB = listOf(SOURCE("1", "First DB source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), + SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) + + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + every { appSettingsService.isItemCachingEnabled() } returns false + every { appSettingsService.isUpdateSourcesEnabled() } returns false + coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) + every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var testSources: List? = null + runBlocking { + testSources = repository.getSources() + } + + assertContentEquals(sourcesDB.map { it.toView() }, testSources) + coVerify(exactly = 0) { api.sources() } + verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() } + } + + @Test + fun `create source`() { + coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns + SelfossModel.SuccessResponse(true) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "") + } + + coVerify(exactly = 1) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } + assertSame(true, response) + } + + @Test + fun `create source but response fails`() { + coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns + SelfossModel.SuccessResponse(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "") + } + + coVerify(exactly = 1) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } + assertSame(false, response) + } + + @Test + fun `create source without connection`() { + coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns + SelfossModel.SuccessResponse(true) + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "") + } + + coVerify(exactly = 0) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } + assertSame(false, response) + } + + @Test + fun `delete source`() { + coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(true) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.deleteSource(5) + } + + coVerify(exactly = 1) { api.deleteSource(5) } + assertSame(true, response) + } + + @Test + fun `delete source but response fails`() { + coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.deleteSource(5) + } + + coVerify(exactly = 1) { api.deleteSource(5) } + assertSame(false, response) + } + + @Test + fun `delete source without connection`() { + coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(false) + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.deleteSource(5) + } + + coVerify(exactly = 0) { api.deleteSource(5) } + assertSame(false, response) + } + + @Test + fun `update remote`() { + coEvery { api.update()} returns SelfossModel.StatusAndData(success = true, data = "finished") + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.updateRemote() + } + + coVerify(exactly = 1) { api.update() } + assertTrue(response) + } + + @Test + fun `update remote but response fails`() { + coEvery { api.update()} returns SelfossModel.StatusAndData(success = false, data = "unallowed access") + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.updateRemote() + } + + coVerify(exactly = 1) { api.update() } + assertSame(false, response) + } + + @Test + fun `update remote with unallowed access`() { + coEvery { api.update()} returns SelfossModel.StatusAndData(success = true, data = "unallowed access") + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.updateRemote() + } + + coVerify(exactly = 1) { api.update() } + assertSame(false, response) + } + + @Test + fun `update remote without connection`() { + coEvery { api.update()} returns SelfossModel.StatusAndData(success = true, data = "undocumented...") + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.updateRemote() + } + + coVerify(exactly = 0) { api.update() } + assertSame(false, response) + } + + @Test + fun login() { + coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.login() + } + + coVerify(exactly = 1) { api.login() } + assertSame(true, response) + } + + @Test + fun `login but response fails`() { + coEvery { api.login() } returns SelfossModel.SuccessResponse(success = false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.login() + } + + coVerify(exactly = 1) { api.login() } + assertSame(false, response) + } + + @Test + fun `login but without connection`() { + coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true) + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + var response = false + runBlocking { + response = repository.login() + } + + coVerify(exactly = 0) { api.login() } + assertSame(false, response) + } + + @Test + fun `refresh login information`() { + coEvery { api.refreshLoginInformation() } returns Unit + coEvery { appSettingsService.refreshLoginInformation(any(), any(), any()) } returns Unit + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.refreshLoginInformation("https://test.com/selfoss/", "login", "password") + + coVerify(exactly = 1) { api.refreshLoginInformation() } + coVerify(exactly = 1) {appSettingsService.refreshLoginInformation("https://test.com/selfoss/", "login", "password")} + } + + @Test + fun `cache items`() { + val itemParameter1 = FakeItemParameters() + val itemParameter2 = FakeItemParameters() + val itemParameter3 = FakeItemParameters() + itemParameter2.id = "2" + itemParameter3.id = "3" + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returnsMany listOf( + SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), + SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter2)), + SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter1)), + ) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.tagFilter = SelfossModel.Tag("Tag", "read", 0) + repository.sourceFilter = SelfossModel.Source( + 1, + "First source", + listOf("Test", "second"), + "spouts\\rss\\fulltextrss", + "", + "d8c92cdb1ef119ea85c4b9205c879ca7.png" + ) + repository.searchFilter = "search" + runBlocking { + repository.tryToCacheItemsAndGetNewOnes() + } + + coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) } + } + + @Test + fun `cache items but response fails`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = false, data = generateTestApiItem()) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.tagFilter = SelfossModel.Tag("Tag", "read", 0) + repository.sourceFilter = SelfossModel.Source( + 1, + "First source", + listOf("Test", "second"), + "spouts\\rss\\fulltextrss", + "", + "d8c92cdb1ef119ea85c4b9205c879ca7.png" + ) + repository.searchFilter = "search" + runBlocking { + repository.tryToCacheItemsAndGetNewOnes() + } + + coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) } + } + + @Test + fun `cache items without connection`() { + coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns + SelfossModel.StatusAndData(success = false, data = generateTestApiItem()) + every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) + + val repository = Repository(api, appSettingsService, connectivityStatus, db) + repository.tagFilter = SelfossModel.Tag("Tag", "read", 0) + repository.sourceFilter = SelfossModel.Source( + 1, + "First source", + listOf("Test", "second"), + "spouts\\rss\\fulltextrss", + "", + "d8c92cdb1ef119ea85c4b9205c879ca7.png" + ) + repository.searchFilter = "search" + runBlocking { + repository.tryToCacheItemsAndGetNewOnes() + } + + coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) } + } +} + +fun generateTestDBItems(item : FakeItemParameters = FakeItemParameters()) : List { + return listOf(ITEM( + id = item.id, + datetime = item.datetime, + title = item.title, + content = item.content, + unread = item.unread, + starred = item.starred, + thumbnail = item.thumbnail, + icon = item.icon, + link = item.link, + sourcetitle = item.sourcetitle, + tags = item.tags +))} + +fun generateTestApiItem(item : FakeItemParameters = FakeItemParameters()) : List { + return listOf( + SelfossModel.Item( + id = item.id.toInt(), + datetime = item.datetime, + title = item.title, + content = item.content, + unread = item.unread, + starred = item.starred, + thumbnail = item.thumbnail, + icon = item.icon, + link = item.link, + sourcetitle = item.sourcetitle, + tags = item.tags.split(',') + ) + ) +} + +class FakeItemParameters() { + var id = "20" + var datetime = "2022-09-09T03:32:01-04:00" + val title = "Etica della ricerca sotto i riflettori." + val content = "

Luigi Campanella, già Presidente SCI

\n

L’etica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.

\n

L’ultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.

\n

Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui l’intelligenza artificiale parte.

\n

Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne l’origine.

\n

Come si comprende, si conferma che gli aspetti etici dell’innovazione e della ricerca si diversificato sempre di più.

\n

La biologia molecolare e la genetica già in passato hanno posto all’attenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.

\n

L’argomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.

\n\"\"\"\"

Magdalena Zernicka-Goetz

\n\"\"

Gianluca Amadei

\n

Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.

\n

L’embrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nell’offerta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso l’Ateneo di Padova per creare nuovi organi e nuovi farmaci.

" + var unread = true + var starred = true + val thumbnail = null + val icon = "ba79e238383ce83c23a169929c8906ef.png" + val link = "https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/" + val sourcetitle = "La Chimica e la Società" + val tags = "Chimica, Testing" +} \ No newline at end of file