Compare commits

..
37 Commits
Author SHA1 Message Date
Amine Louveau 8b0bbe71c9 Merge pull request 'Allow offline filtering' (#75) from davidoskky/ReaderForSelfoss-multiplatform:offline_filters into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/75
2022-10-14 07:18:42 +00:00
davidoskky 8bfe14c019 Actually filter database items 2022-10-14 00:10:35 +02:00
davidoskky 208babbce3 Correct tests 2022-10-14 00:03:20 +02:00
davidoskky 02098a7aa9 Rearrange filtering steps 2022-10-11 00:52:12 +02:00
davidoskky d0a982f385 Add tests for offline filtering 2022-10-08 17:15:41 +02:00
davidoskky 1d1c121aab Filter items from database according to tag and source 2022-10-08 17:15:22 +02:00
davidoskky fe12819163 Correct database source title 2022-10-08 17:14:12 +02:00
Amine Louveau 023a30c008 Merge pull request 'Simplify sources and tags handling' (#70) from davidoskky/ReaderForSelfoss-multiplatform:drawer_data into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/70
2022-10-04 18:52:39 +00:00
Amine Louveau a2862a2587 Merge pull request 'Correct mechanism of mark and unmark snackbars' (#74) from davidoskky/ReaderForSelfoss-multiplatform:snackbar-mark into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/74
2022-10-04 18:43:44 +00:00
Amine Louveau 054e936657 Merge pull request 'Correct boolean serialization' (#73) from davidoskky/ReaderForSelfoss-multiplatform:swiping into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/73
2022-10-04 18:40:14 +00:00
davidoskky 1d2e5069b8 Avoid double snackbar generation 2022-10-04 16:47:13 +02:00
davidoskky a147646743 Correct mechanism of mark and unmark snackbars 2022-10-04 16:43:21 +02:00
davidoskky 32e7fc0038 Correct boolean serialization 2022-10-04 15:01:22 +02:00
davidoskky c15bf44032 Adjust repository tests 2022-10-02 01:01:39 +02:00
davidoskky 0bcd55bd4e Add translated strings 2022-10-01 22:51:09 +02:00
davidoskky ebef0b3511 Start monitoring connectivity status when the repository is initiated. 2022-10-01 22:43:48 +02:00
davidoskky 713ceb05bf Remove unnecessary data class 2022-09-30 15:07:17 +02:00
davidoskky dc8381b661 Add missing string 2022-09-30 15:00:25 +02:00
davidoskky b5b820c64b Remove database access from the Home 2022-09-30 15:00:01 +02:00
davidoskky f7055626d9 Start monitoring the connectivity before loading the Repository 2022-09-30 14:56:10 +02:00
Amine Louveau 6ec3e96909 Merge pull request 'Repository Unit Tests' (#50) from davidoskky/ReaderForSelfoss-multiplatform:repository_tests into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/50
2022-09-30 11:31:55 +00:00
davidoskky 22da30eaa8 Remove unnecessary call to api 2022-09-30 13:17:40 +02:00
davidoskky 79fd115f5e Only return new cached items 2022-09-30 13:16:42 +02:00
davidoskky 8dc3d319cd Cleanup 2022-09-30 11:59:08 +02:00
davidoskky 27bb056397 Cleanup 2022-09-30 11:49:31 +02:00
davidoskky f9ba13dc32 Always cache images in background 2022-09-30 11:23:43 +02:00
davidoskky 6f60ef4346 Remove unnecessary return value 2022-09-30 09:11:55 +02:00
davidoskky 28b950f467 Merge branch 'master' into repository_tests 2022-09-30 07:04:09 +00:00
davidoskky a9c7ec3dc1 Cache items in background without filtering 2022-09-30 01:19:28 +02:00
davidoskky 920d4ac1ef Correctly implement disabling sources update 2022-09-29 19:37:33 +02:00
davidoskky 0e96d313ec Add tags parameters explicitly 2022-09-29 19:09:09 +02:00
davidoskky 7211fdb1a3 Fix update remote tests 2022-09-29 18:58:00 +02:00
aminecmi 381d6acc82 Analyzing should be with the rest. 2022-09-29 15:34:12 +02:00
davidoskky d311c2cdeb Fix sources tests 2022-09-28 18:45:21 +02:00
davidoskky 219cae5d74 Fix tags tests 2022-09-28 18:22:06 +02:00
davidoskky 2968aee309 Fix badges tests 2022-09-28 09:43:28 +02:00
davidoskky 6cb4b35c93 Introduce useful assertions in repository instantiation tests 2022-09-28 09:14:47 +02:00
28 changed files with 392 additions and 191 deletions

No files matched your search

+1 -11
View File
@@ -3,23 +3,13 @@ type: docker
name: test
steps:
- name: Anylyse
- name: AnylyseBuildTest
image: mingc/android-build-box:latest
failure: ignore
detach: true
commands:
- echo "---------------------------------------------------------"
- echo "Analysing..."
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\""
- echo "---------------------------------------------------------"
environment:
SONAR_HOST_URL:
from_secret: sonarScannerHostUrl
SONAR_LOGIN:
from_secret: sonarScannerLogin
- name: BuildAndTest
image: mingc/android-build-box:latest
commands:
- echo "Building..."
- ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
- echo "---------------------------------------------------------"
@@ -15,9 +15,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.appcompat.widget.SearchView
import androidx.core.view.doOnNextLayout
import androidx.drawerlayout.widget.DrawerLayout
@@ -98,8 +95,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
data class DrawerData(val tags: List<SelfossModel.Tag>?, val sources: List<SelfossModel.Source>?)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
@@ -352,27 +347,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
)
CoroutineScope(Dispatchers.IO).launch {
val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
repository.getDBSources().map { it.toView() })
val tags = repository.getTags()
val sources = repository.getSources()
runOnUiThread {
// 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)
} else {
handleDrawerData(drawerData, loadedFromCache = true)
}
handleDrawerData(tags, sources)
}
}
}
private fun drawerApiCalls(drawerData: DrawerData) {
CoroutineScope(Dispatchers.Main).launch {
val apiDrawerData = DrawerData(repository.getTags(), repository.getSources())
handleDrawerData(if (drawerData != apiDrawerData) apiDrawerData else drawerData)
}
}
private fun handleDrawerData(drawerData: DrawerData, loadedFromCache: Boolean = false) {
private fun handleDrawerData(tags: List<SelfossModel.Tag>, sources: List<SelfossModel.Source>) {
binding.mainDrawer.itemAdapter.clear()
// Filters title with clear action
@@ -386,24 +369,24 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
}
// Hidden tags
if (drawerData.tags != null && drawerData.tags.isNotEmpty() && appSettingsService.getHiddenTags().isNotEmpty()) {
if (tags.isNotEmpty() && appSettingsService.getHiddenTags().isNotEmpty()) {
secondaryItem(
withDivider = true,
R.string.drawer_item_hidden_tags,
DRAWER_ID_HIDDEN_TAGS
)
handleHiddenTags(drawerData.tags)
handleHiddenTags(tags)
}
// Tags
secondaryItem(withDivider = true, R.string.drawer_item_tags, DRAWER_ID_TAGS)
if (drawerData.tags == null && !loadedFromCache) {
if (tags.isEmpty()) {
binding.mainDrawer.itemAdapter.add(
SecondaryDrawerItem()
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
)
} else {
handleTags(drawerData.tags!!)
handleTags(tags)
}
// Sources
@@ -411,15 +394,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
startActivity(Intent(v!!.context, SourcesActivity::class.java))
false
}
if (drawerData.sources == null && !loadedFromCache) {
if (sources.isEmpty()) {
binding.mainDrawer.itemAdapter.add(
SecondaryDrawerItem().apply {
nameRes = R.string.drawer_error_loading_tags
nameRes = R.string.drawer_error_loading_sources
isSelectable = false
}
)
} else {
handleSources(drawerData.sources!!)
handleSources(sources)
}
// About action
@@ -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 {
@@ -75,7 +75,6 @@ class MyApp : MultiDexApplication(), DIAware {
).show()
}
}
}
private fun handleNotificationChannels() {
@@ -28,7 +28,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
updateItems(this.items)
}
private fun unmarkSnackbar(position: Int) {
private fun unmarkSnackbar(item: SelfossModel.Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
@@ -37,7 +37,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
)
.setAction(R.string.undo_string) {
CoroutineScope(Dispatchers.IO).launch {
unreadItemAtIndex(position, false)
unreadItemAtIndex(item, position, false)
}
}
@@ -47,7 +47,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
s.show()
}
private fun markSnackbar(position: Int) {
private fun markSnackbar(item: SelfossModel.Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
@@ -55,7 +55,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) {
readItemAtIndex(position)
readItemAtIndex(item, position, false)
}
val view = s.view
@@ -66,37 +66,36 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
fun handleItemAtIndex(position: Int) {
if (items[position].unread) {
readItemAtIndex(position)
readItemAtIndex(items[position], position)
} else {
unreadItemAtIndex(position)
unreadItemAtIndex(items[position], position)
}
}
private fun readItemAtIndex(position: Int, showSnackbar: Boolean = true) {
val i = items[position]
private fun readItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(i)
repository.markAsRead(item)
}
if (repository.displayedItems == ItemType.UNREAD) {
items.remove(i)
items.remove(item)
notifyItemRemoved(position)
updateItems(items)
} else {
notifyItemChanged(position)
}
if (showSnackbar) {
unmarkSnackbar(position)
unmarkSnackbar(item, position)
}
}
private fun unreadItemAtIndex(position: Int, showSnackbar: Boolean = true) {
private fun unreadItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) {
CoroutineScope(Dispatchers.IO).launch {
repository.unmarkAsRead(items[position])
repository.unmarkAsRead(item)
}
notifyItemChanged(position)
if (showSnackbar) {
markSnackbar(position)
markSnackbar(item, position)
}
}
@@ -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<SelfossModel.Item>?,
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)
}
@@ -249,7 +249,6 @@ class ArticleFragment : Fragment(), DIAware {
private fun getContentFromMercury() {
if (repository.isNetworkAvailable()) {
binding.progressBar.visibility = View.VISIBLE
// TODO: The api should be accessed through the repository
val parser = MercuryApi()
parser.parseUrl(url).enqueue(
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">深色模式</string>
<string name="mode_system">遵循系统设置</string>
<string name="mode_light">浅色模式</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
@@ -132,4 +132,5 @@
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
</resources>
+2 -1
View File
@@ -63,6 +63,7 @@
<string name="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string>
<string name="drawer_error_loading_tags">Error loading tags…</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="drawer_item_filters">Filters</string>
<string name="drawer_action_clear">clear</string>
<string name="drawer_item_tags">Tags</string>
@@ -109,7 +110,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string>
<string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string>
@@ -138,7 +138,11 @@ class SelfossModel {
object BooleanSerializer : KSerializer<Boolean> {
override fun deserialize(decoder: Decoder): Boolean {
val json = ((decoder as JsonDecoder).decodeJsonElement()).jsonPrimitive
return json.booleanOrNull ?: json.int == 1
return if (json.booleanOrNull != null) {
json.boolean
} else {
json.int == 1
}
}
override val descriptor: SerialDescriptor
@@ -37,8 +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
connectivityStatus.start()
runBlocking {
updateApiVersion()
dateUtils = DateUtils(appSettingsService)
@@ -62,12 +66,19 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} else {
if (appSettingsService.isItemCachingEnabled()) {
fromDB = true
var dbItems = getDBItems().filter {
displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) ||
(it.starred && displayedItems == ItemType.STARRED)
}
if (tagFilter != null) {
dbItems = dbItems.filter { it.tags.split(',').contains(tagFilter!!.tag) }
}
if (sourceFilter != null) {
dbItems = dbItems.filter { it.sourcetitle == sourceFilter!!.title }
}
fetchedItems = SelfossModel.StatusAndData.succes(
getDBItems().filter {
displayedItems == ItemType.ALL ||
(it.unread && displayedItems == ItemType.UNREAD) ||
(it.starred && displayedItems == ItemType.STARRED)
}.map { it.toView() }
dbItems.map { it.toView() }
)
}
}
@@ -106,9 +117,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
)
@@ -132,25 +143,32 @@ 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<SelfossModel.Tag> {
return if (isNetworkAvailable()) {
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 ?: emptyList()
} else {
} else if (isDatabaseEnabled) {
getDBTags().map { it.toView() }
} else {
emptyList()
}
}
@@ -168,15 +186,21 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
}
}
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
return if (isNetworkAvailable()) {
suspend fun getSources(): ArrayList<SelfossModel.Source> {
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()
}
}
@@ -347,7 +371,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
}
@@ -375,8 +399,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
api.refreshLoginInformation()
}
// TODO: This should be private
suspend fun updateApiVersion() {
private suspend fun updateApiVersion() {
val apiMajorVersion = appSettingsService.getApiVersion()
if (isNetworkAvailable()) {
@@ -387,8 +410,6 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
}
}
// TODO: This should be private (since all api calls are made through the repository
// no other entity needs to know about the connectivity status)
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride
private fun getDBActions(): List<ACTION> =
@@ -397,11 +418,9 @@ 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<TAG> = db.tagsQueries.tags().executeAsList()
private fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
// TODO: This function should be private
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
private fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
db.tagsQueries.deleteAllTags()
@@ -439,7 +458,6 @@ 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<SelfossModel.Item> {
try {
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
@@ -65,6 +65,6 @@ fun SelfossModel.Item.toEntity(): ITEM =
this.thumbnail,
this.icon,
this.link,
this.title.getHtmlDecoded(),
this.sourcetitle.getHtmlDecoded(),
this.tags.joinToString(",")
)
@@ -17,7 +17,7 @@ import kotlin.test.*
class RepositoryTest() {
private val connectivityStatus = mockk<ConnectivityStatus>()
private val db = mockk<ReaderForSelfossDB>()
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
private val appSettingsService = mockk<AppSettingsService>()
private val api = mockk<SelfossApi>()
@@ -31,7 +31,7 @@ class RepositoryTest() {
every { appSettingsService.getApiVersion() } returns 4
every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/"
every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true
every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(true)
@@ -42,18 +42,16 @@ class RepositoryTest() {
every { db.tagsQueries.deleteAllTags() } returns Unit
every { db.tagsQueries.transaction(any(), any()) } returns Unit
every { db.tagsQueries.insertTag(any()) } returns Unit
every { connectivityStatus.start() } returns Unit
}
@Test
fun `Instantiate repository`() {
val success = try {
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
Repository(api, appSettingsService, connectivityStatus, db)
assertEquals(true, success)
coVerify(exactly = 1) { api.version() }
coVerify(exactly = 1) { api.stats() }
}
@Test
@@ -61,28 +59,23 @@ class RepositoryTest() {
every { appSettingsService.getApiVersion() } returns -1
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
val success = try {
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
Repository(api, appSettingsService, connectivityStatus, db)
assertEquals(true, success)
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 success = try {
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
val repository = Repository(api, appSettingsService, connectivityStatus, db)
assertEquals(true, success)
assertEquals(0, repository.badgeAll)
assertEquals(0, repository.badgeStarred)
assertEquals(0, repository.badgeUnread)
coVerify(exactly = 1) { api.version() }
coVerify(exactly = 1) { api.stats() }
}
@Test
@@ -180,6 +173,60 @@ class RepositoryTest() {
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
}
@Test
fun `Get newer items without connectivity and tag filter`() {
val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters()
itemParameter2.tags = "Test, Stuff"
itemParameter2.id = "2"
itemParameter3.tags = "Other, Tag"
itemParameter3.id = "3"
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(itemParameter1) +
generateTestDBItems(itemParameter2) +
generateTestDBItems(itemParameter3)
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Test", "red", 3)
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 newer items without connectivity and source filter`() {
val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters()
itemParameter2.sourcetitle = "Test"
itemParameter2.id = "2"
itemParameter3.sourcetitle = "Other"
itemParameter3.id = "3"
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(itemParameter1) +
generateTestDBItems(itemParameter2) +
generateTestDBItems(itemParameter3)
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.sourceFilter = SelfossModel.Source(1, "Test", listOf("tags"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")
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
@@ -267,24 +314,19 @@ class RepositoryTest() {
}
@Test
fun `Reload badges with items caching`() {
fun `Reload badges without connection`() {
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
val itemParameter = FakeItemParameters()
itemParameter.starred = true
itemParameter.unread = false
var success = false
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.items = ArrayList(generateTestApiItem(itemParameter))
runBlocking {
success = repository.reloadBadges()
}
assertSame(true, success)
assertTrue(success)
assertSame(1, repository.badgeAll)
assertSame(1, repository.badgeUnread)
assertSame(1, repository.badgeStarred)
@@ -293,23 +335,19 @@ class RepositoryTest() {
}
@Test
fun `Reload badges without items caching`() {
fun `Reload badges without connection and items caching disabled`() {
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns false
val itemParameter = FakeItemParameters()
itemParameter.starred = true
itemParameter.unread = false
every { appSettingsService.isUpdateSourcesEnabled() } returns true
var success = false
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.items = ArrayList(generateTestApiItem(itemParameter))
runBlocking {
success = repository.reloadBadges()
}
assertSame(false, success)
assertFalse(success)
assertSame(0, repository.badgeAll)
assertSame(0, repository.badgeUnread)
assertSame(0, repository.badgeStarred)
@@ -321,8 +359,13 @@ class RepositoryTest() {
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<SelfossModel.Tag>? = null
@@ -331,7 +374,8 @@ class RepositoryTest() {
}
assertSame(tags, testTags)
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
assertNotSame(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 1) { api.tags() }
}
@Test
@@ -344,20 +388,45 @@ class RepositoryTest() {
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<SelfossModel.Tag> = 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)
coVerify(exactly = 1) { api.tags() }
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<SelfossModel.Tag> = 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),
@@ -374,11 +443,13 @@ class RepositoryTest() {
var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking {
testTags = repository.getTags()
testTags = repository.getTags()
}
assertSame(emptyList(), testTags)
coVerify(exactly = 0) { api.tags() }
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
coVerify(exactly = 1) { api.tags() }
assertNotSame(tags, testTags)
assertContentEquals(tagsDB.map { it.toView() }, testTags)
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
}
@Test
@@ -391,6 +462,8 @@ class RepositoryTest() {
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<SelfossModel.Tag> = emptyList()
@@ -399,7 +472,7 @@ class RepositoryTest() {
}
assertNotSame(tags, testTags)
assertSame(tagsDB.first().name, testTags.first().tag)
assertContentEquals(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 0) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
}
@@ -415,6 +488,7 @@ class RepositoryTest() {
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<SelfossModel.Tag> = emptyList()
@@ -422,9 +496,9 @@ class RepositoryTest() {
testTags = repository.getTags()
}
assertContentEquals(tagsDB.map { it.toView() }, testTags)
assertSame(emptyList(), testTags)
coVerify(exactly = 0) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
}
@Test
@@ -438,6 +512,7 @@ class RepositoryTest() {
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<SelfossModel.Tag> = emptyList()
@@ -452,30 +527,36 @@ class RepositoryTest() {
}
@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"))
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
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testSources: List<SelfossModel.Source>? = null
var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking {
testSources = repository.getSources()
testTags = repository.getTags()
}
assertSame(sources, testSources)
coVerify(exactly = 1) { api.sources() }
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
assertContentEquals(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 0) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
}
@Test
fun `get sources without connection`() {
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>(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
val sourcesDB = listOf<SOURCE>(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)
@@ -484,19 +565,44 @@ class RepositoryTest() {
testSources = repository.getSources()
}
assertContentEquals(sources, testSources)
coVerify(exactly = 0) { api.sources() }
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>(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<SelfossModel.Source>? = 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 without connection and items caching disabled`() {
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>(SOURCE("1", "First 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.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
@@ -506,30 +612,9 @@ class RepositoryTest() {
testSources = repository.getSources()
}
assertSame(null, testSources)
coVerify(exactly = 0) { api.sources() }
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
}
@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 source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
val sourcesDB = listOf<SOURCE>(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
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<SelfossModel.Source>? = null
runBlocking {
testSources = repository.getSources()
}
assertContentEquals(sources, testSources)
coVerify(exactly = 0) { api.sources() }
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
assertSame(sources, testSources)
coVerify(exactly = 1) { api.sources() }
verify(exactly = 0) { db.sourcesQueries }
}
@Test
@@ -549,11 +634,101 @@ class RepositoryTest() {
testSources = repository.getSources()
}
assertSame(null, testSources)
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>(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<SelfossModel.Source>? = 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>(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<SelfossModel.Source>? = 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>(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<SelfossModel.Source>? = 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>(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<SelfossModel.Source>? = 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
@@ -654,12 +829,26 @@ class RepositoryTest() {
}
coVerify(exactly = 1) { api.update() }
assertSame(true, response)
assertTrue(response)
}
@Test
fun `update remote but response fails`() {
coEvery { api.update()} returns SelfossModel.StatusAndData(success = false, data = "undocumented...")
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
@@ -743,8 +932,16 @@ class RepositoryTest() {
@Test
fun `cache items`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
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)
@@ -757,13 +954,11 @@ class RepositoryTest() {
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
assertSame(3, items.size)
}
@Test
@@ -782,13 +977,11 @@ class RepositoryTest() {
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
assertSame(0, items.size)
}
@Test
@@ -808,13 +1001,11 @@ class RepositoryTest() {
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
assertSame(emptyList<SelfossModel.Item>(), items)
}
}
@@ -852,7 +1043,7 @@ fun generateTestApiItem(item : FakeItemParameters = FakeItemParameters()) : List
}
class FakeItemParameters() {
val id = "20"
var id = "20"
var datetime = "2022-09-09T03:32:01-04:00"
val title = "Etica della ricerca sotto i riflettori."
val content = "<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>Letica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>Lultimo è 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.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui lintelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne lorigine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dellinnovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto allattenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>Largomento, 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.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>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.</p>\n<p>Lembrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nellofferta 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 lAteneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
@@ -861,6 +1052,6 @@ class FakeItemParameters() {
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"
var sourcetitle = "La Chimica e la Società"
var tags = "Chimica, Testing"
}