Compare commits

..

8 Commits

2 changed files with 264 additions and 107 deletions

View File

@ -37,6 +37,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
var badgeStarred = 0 var badgeStarred = 0
set(value) {field = if (value < 0) { 0 } else { value } } set(value) {field = if (value < 0) { 0 } else { value } }
private var fetchedSources = false
private var fetchedTags = false
init { init {
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found // TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
runBlocking { runBlocking {
@ -106,9 +109,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
val items = api.getItems( val items = api.getItems(
itemType.type, itemType.type,
0, 0,
tagFilter?.tag, null,
sourceFilter?.id?.toLong(), null,
searchFilter, null,
null, null,
200 200
) )
@ -132,25 +135,32 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
badgeStarred = response.data.starred badgeStarred = response.data.starred
success = true success = true
} }
} else { } else if (appSettingsService.isItemCachingEnabled()) {
// TODO: do this differently, because it's not efficient // TODO: do this differently, because it's not efficient
val dbItems = getDBItems() val dbItems = getDBItems()
badgeUnread = dbItems.filter { item -> item.unread }.size badgeUnread = dbItems.filter { item -> item.unread }.size
badgeStarred = dbItems.filter { item -> item.starred }.size badgeStarred = dbItems.filter { item -> item.starred }.size
badgeAll = items.size badgeAll = dbItems.size
success = true
} }
return success return success
} }
suspend fun getTags(): List<SelfossModel.Tag> { suspend fun getTags(): List<SelfossModel.Tag> {
return if (isNetworkAvailable()) { val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
return if (isNetworkAvailable() && !fetchedTags) {
val apiTags = api.tags() val apiTags = api.tags()
if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { if (apiTags.success && apiTags.data != null && isDatabaseEnabled) {
resetDBTagsWithData(apiTags.data) resetDBTagsWithData(apiTags.data)
if (!appSettingsService.isUpdateSourcesEnabled()) {
fetchedTags = true
}
} }
apiTags.data ?: emptyList() apiTags.data ?: emptyList()
} else { } else if (isDatabaseEnabled) {
getDBTags().map { it.toView() } getDBTags().map { it.toView() }
} else {
emptyList()
} }
} }
@ -168,15 +178,21 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
} }
} }
suspend fun getSources(): ArrayList<SelfossModel.Source>? { suspend fun getSources(): ArrayList<SelfossModel.Source> {
return if (isNetworkAvailable()) { val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
return if (isNetworkAvailable() && !fetchedSources) {
val apiSources = api.sources() val apiSources = api.sources()
if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { if (apiSources.success && apiSources.data != null && isDatabaseEnabled) {
resetDBSourcesWithData(apiSources.data) resetDBSourcesWithData(apiSources.data)
if (!appSettingsService.isUpdateSourcesEnabled()) {
fetchedSources = true
}
} }
apiSources.data apiSources.data ?: ArrayList()
} else { } else if (isDatabaseEnabled) {
ArrayList(getDBSources().map { it.toView() }) ArrayList(getDBSources().map { it.toView() })
} else {
ArrayList()
} }
} }
@ -347,7 +363,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
suspend fun updateRemote(): Boolean { suspend fun updateRemote(): Boolean {
return if (isNetworkAvailable()) { return if (isNetworkAvailable()) {
api.update().equals("finished") api.update().data.equals("finished")
} else { } else {
false false
} }
@ -439,7 +455,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
private fun updateDBItem(item: SelfossModel.Item) = 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()) 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())
// TODO: This function should check for duplicate items
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item> { suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item> {
try { try {
val newItems = getMaxItemsForBackground(ItemType.UNREAD) val newItems = getMaxItemsForBackground(ItemType.UNREAD)

View File

@ -17,7 +17,7 @@ import kotlin.test.*
class RepositoryTest() { class RepositoryTest() {
private val connectivityStatus = mockk<ConnectivityStatus>() private val connectivityStatus = mockk<ConnectivityStatus>()
private val db = mockk<ReaderForSelfossDB>() private val db = mockk<ReaderForSelfossDB>(relaxed = true)
private val appSettingsService = mockk<AppSettingsService>() private val appSettingsService = mockk<AppSettingsService>()
private val api = mockk<SelfossApi>() private val api = mockk<SelfossApi>()
@ -31,7 +31,7 @@ class RepositoryTest() {
every { appSettingsService.getApiVersion() } returns 4 every { appSettingsService.getApiVersion() } returns 4
every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/" every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/"
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(true) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(true)
@ -46,14 +46,10 @@ class RepositoryTest() {
@Test @Test
fun `Instantiate repository`() { fun `Instantiate repository`() {
val success = try { Repository(api, appSettingsService, connectivityStatus, db)
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
assertEquals(true, success) coVerify(exactly = 1) { api.version() }
coVerify(exactly = 1) { api.stats() }
} }
@Test @Test
@ -61,28 +57,23 @@ class RepositoryTest() {
every { appSettingsService.getApiVersion() } returns -1 every { appSettingsService.getApiVersion() } returns -1
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
val success = try { Repository(api, appSettingsService, connectivityStatus, db)
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
assertEquals(true, success) coVerify(exactly = 0) { api.version() }
coVerify(exactly = 0) { api.stats() }
} }
@Test @Test
fun `Instantiate repository with negative stats`() { fun `Instantiate repository with negative stats`() {
coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(-100, -50, -20)) coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(-100, -50, -20))
val success = try { val repository = Repository(api, appSettingsService, connectivityStatus, db)
Repository(api, appSettingsService, connectivityStatus, db)
true
} catch (e: Exception) {
false
}
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 @Test
@ -267,24 +258,19 @@ class RepositoryTest() {
} }
@Test @Test
fun `Reload badges with items caching`() { fun `Reload badges without connection`() {
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems() every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
val itemParameter = FakeItemParameters()
itemParameter.starred = true
itemParameter.unread = false
var success = false var success = false
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.items = ArrayList(generateTestApiItem(itemParameter))
runBlocking { runBlocking {
success = repository.reloadBadges() success = repository.reloadBadges()
} }
assertSame(true, success) assertTrue(success)
assertSame(1, repository.badgeAll) assertSame(1, repository.badgeAll)
assertSame(1, repository.badgeUnread) assertSame(1, repository.badgeUnread)
assertSame(1, repository.badgeStarred) assertSame(1, repository.badgeStarred)
@ -293,23 +279,19 @@ class RepositoryTest() {
} }
@Test @Test
fun `Reload badges without items caching`() { fun `Reload badges without connection and items caching disabled`() {
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true
val itemParameter = FakeItemParameters()
itemParameter.starred = true
itemParameter.unread = false
var success = false var success = false
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.items = ArrayList(generateTestApiItem(itemParameter))
runBlocking { runBlocking {
success = repository.reloadBadges() success = repository.reloadBadges()
} }
assertSame(false, success) assertFalse(success)
assertSame(0, repository.badgeAll) assertSame(0, repository.badgeAll)
assertSame(0, repository.badgeUnread) assertSame(0, repository.badgeUnread)
assertSame(0, repository.badgeStarred) assertSame(0, repository.badgeStarred)
@ -321,8 +303,13 @@ class RepositoryTest() {
fun `Get tags`() { fun `Get tags`() {
val tags = listOf(SelfossModel.Tag("test", "red", 6), val tags = listOf(SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)) 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 { 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) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testTags: List<SelfossModel.Tag>? = null var testTags: List<SelfossModel.Tag>? = null
@ -331,7 +318,8 @@ class RepositoryTest() {
} }
assertSame(tags, testTags) assertSame(tags, testTags)
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() } assertNotSame(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 1) { api.tags() }
} }
@Test @Test
@ -344,20 +332,45 @@ class RepositoryTest() {
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testTags: List<SelfossModel.Tag> = emptyList() var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
// Tags will be fetched from the database on the second call, thus testTags != tags
testTags = repository.getTags() testTags = repository.getTags()
} }
coVerify(exactly = 1) { api.tags() }
assertNotSame(tags, testTags) assertNotSame(tags, testTags)
assertContentEquals(tagsDB.map { it.toView() }, testTags) assertContentEquals(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 1) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } 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 @Test
fun `Get tags with sources update and items caching disabled`() { fun `Get tags with sources update and items caching disabled`() {
val tags = listOf(SelfossModel.Tag("test", "red", 6), val tags = listOf(SelfossModel.Tag("test", "red", 6),
@ -374,11 +387,13 @@ class RepositoryTest() {
var testTags: List<SelfossModel.Tag> = emptyList() var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
testTags = repository.getTags()
} }
assertSame(emptyList(), testTags) coVerify(exactly = 1) { api.tags() }
coVerify(exactly = 0) { api.tags() } assertNotSame(tags, testTags)
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() } assertContentEquals(tagsDB.map { it.toView() }, testTags)
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
} }
@Test @Test
@ -391,6 +406,8 @@ class RepositoryTest() {
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags) coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isUpdateSourcesEnabled() } returns true
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testTags: List<SelfossModel.Tag> = emptyList() var testTags: List<SelfossModel.Tag> = emptyList()
@ -399,7 +416,7 @@ class RepositoryTest() {
} }
assertNotSame(tags, testTags) assertNotSame(tags, testTags)
assertSame(tagsDB.first().name, testTags.first().tag) assertContentEquals(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 0) { api.tags() } coVerify(exactly = 0) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
} }
@ -415,6 +432,7 @@ class RepositoryTest() {
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testTags: List<SelfossModel.Tag> = emptyList() var testTags: List<SelfossModel.Tag> = emptyList()
@ -422,9 +440,9 @@ class RepositoryTest() {
testTags = repository.getTags() testTags = repository.getTags()
} }
assertContentEquals(tagsDB.map { it.toView() }, testTags) assertSame(emptyList(), testTags)
coVerify(exactly = 0) { api.tags() } coVerify(exactly = 0) { api.tags() }
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
} }
@Test @Test
@ -438,6 +456,7 @@ class RepositoryTest() {
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false) every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns true
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testTags: List<SelfossModel.Tag> = emptyList() var testTags: List<SelfossModel.Tag> = emptyList()
@ -452,30 +471,36 @@ class RepositoryTest() {
} }
@Test @Test
fun `get sources`() { fun `Get tags without connection and sources update and items caching disabled`() {
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"), val tags = listOf(SelfossModel.Tag("test", "red", 6),
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png")) 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) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var testSources: List<SelfossModel.Source>? = null var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testSources = repository.getSources() testTags = repository.getTags()
} }
assertSame(sources, testSources) assertContentEquals(tagsDB.map { it.toView() }, testTags)
coVerify(exactly = 1) { api.sources() } coVerify(exactly = 0) { api.tags() }
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() } verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
} }
@Test @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"), 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")) 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")) 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) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
val repository = Repository(api, appSettingsService, connectivityStatus, db) val repository = Repository(api, appSettingsService, connectivityStatus, db)
@ -484,19 +509,44 @@ class RepositoryTest() {
testSources = repository.getSources() testSources = repository.getSources()
} }
assertContentEquals(sources, testSources) assertSame(sources, testSources)
coVerify(exactly = 0) { api.sources() } 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() } verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
} }
@Test @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"), 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")) 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 source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.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 every { appSettingsService.isItemCachingEnabled() } returns false
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
@ -506,30 +556,9 @@ class RepositoryTest() {
testSources = repository.getSources() testSources = repository.getSources()
} }
assertSame(null, testSources) assertSame(sources, testSources)
coVerify(exactly = 0) { api.sources() } coVerify(exactly = 1) { api.sources() }
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() } verify(exactly = 0) { db.sourcesQueries }
}
@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() }
} }
@Test @Test
@ -549,11 +578,101 @@ class RepositoryTest() {
testSources = repository.getSources() 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() } coVerify(exactly = 0) { api.sources() }
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() } 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 @Test
fun `create source`() { fun `create source`() {
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
@ -654,12 +773,26 @@ class RepositoryTest() {
} }
coVerify(exactly = 1) { api.update() } coVerify(exactly = 1) { api.update() }
assertSame(true, response) assertTrue(response)
} }
@Test @Test
fun `update remote but response fails`() { 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) val repository = Repository(api, appSettingsService, connectivityStatus, db)
var response = false var response = false
@ -741,10 +874,19 @@ class RepositoryTest() {
coVerify(exactly = 1) {appSettingsService.refreshLoginInformation("https://test.com/selfoss/", "login", "password")} coVerify(exactly = 1) {appSettingsService.refreshLoginInformation("https://test.com/selfoss/", "login", "password")}
} }
// TODO: This function should check if duplicate items are added to the database
@Test @Test
fun `cache items`() { fun `cache items`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns val itemParameter1 = FakeItemParameters()
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) 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) val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0) repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
@ -763,7 +905,6 @@ class RepositoryTest() {
} }
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) } coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
assertSame(3, items.size)
} }
@Test @Test
@ -852,7 +993,7 @@ fun generateTestApiItem(item : FakeItemParameters = FakeItemParameters()) : List
} }
class FakeItemParameters() { class FakeItemParameters() {
val id = "20" var id = "20"
var datetime = "2022-09-09T03:32:01-04:00" var datetime = "2022-09-09T03:32:01-04:00"
val title = "Etica della ricerca sotto i riflettori." 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>" 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>"