Repository Unit Tests #50

Merged
AmineB merged 38 commits from davidoskky/ReaderForSelfoss-multiplatform:repository_tests into master 2022-09-30 11:31:55 +00:00
3 changed files with 81 additions and 1 deletions
Showing only changes of commit a382fc89ea - Show all commits

View File

@ -269,6 +269,7 @@ class ArticleFragment : Fragment(), DIAware {
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
if (repository.isNetworkAvailable()) {
binding.progressBar.visibility = View.VISIBLE
// TODO: The api should be accessed through the repository
AmineB marked this conversation as resolved Outdated

This is a simple api call to fetch the artcile content. There is no need to put it inside the repository. It is only used here.

This is a simple api call to fetch the artcile content. There is no need to put it inside the repository. It is only used here.

Yes, I do understand.
I suggest this edit just because it would allow to keep all information about the connectivity private within the repository and increase incapsulation.
But there is no real significant difference.

Yes, I do understand. I suggest this edit just because it would allow to keep all information about the connectivity private within the repository and increase incapsulation. But there is no real significant difference.
val parser = MercuryApi()
parser.parseUrl(url).enqueue(

View File

@ -378,6 +378,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
api.refreshLoginInformation()
}
// TODO: This should be private
suspend fun updateApiVersion() {
val apiMajorVersion = appSettingsService.getApiVersion()
@ -389,6 +390,8 @@ 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> =
@ -440,7 +443,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
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>? {
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item> {
try {
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
val allItems = getMaxItemsForBackground(ItemType.ALL)

View File

@ -692,6 +692,82 @@ class RepositoryTest() {
coVerify(exactly = 1) { api.refreshLoginInformation() }
coVerify(exactly = 1) {appSettingsService.refreshLoginInformation("https://test.com/selfoss/", "login", "password")}
}
@Test
fun `cache items`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
repository.sourceFilter = SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
AmineB marked this conversation as resolved Outdated

This should be changed to coVerify(exactly = 3) { api.getItems(any(), 0, "Tag", 1, "search", null, 200) }

This should be changed to `coVerify(exactly = 3) { api.getItems(any(), 0, "Tag", 1, "search", null, 200) }`

This function is supposed to store articles for the background; it should store all articles independently of the tags selected by the user in the foreground.

This function is supposed to store articles for the background; it should store all articles independently of the tags selected by the user in the foreground.

I don't know what you wanted to test.

The thing is, passing null, null, null, null in the function will make it fail, because in your calls, you pass data.

I don't know what you wanted to test. The thing is, passing `null, null, null, null` in the function will make it fail, because in your calls, you pass data.

I expect the function to behave differently from how it is currently behaving.

Expected behavior:
When background sync starts, the last 200 articles for each item type are cached.
Thus I expect these 3 calls
getItems("unread", 0, null, null, null, null, 200)
getItems("all", 0, null, null, null, null, 200)
getItems("starred", 0, null, null, null, 200)

If one of those nulls is substituted by another value, it will not be the last 200 articles that will be stored, but 200 articles filtered according to some external factor.
This is not what I would expect as an user, I would expect that the latest articles are always available if I enable background syncronization.

I expect the function to behave differently from how it is currently behaving. Expected behavior: When background sync starts, the last 200 articles for each item type are cached. Thus I expect these 3 calls getItems("unread", 0, null, null, null, null, 200) getItems("all", 0, null, null, null, null, 200) getItems("starred", 0, null, null, null, 200) If one of those nulls is substituted by another value, it will not be the last 200 articles that will be stored, but 200 articles filtered according to some external factor. This is not what I would expect as an user, I would expect that the latest articles are always available if I enable background syncronization.

If one of those nulls is substituted by another value, it will not be the last 200 articles that will be stored, but 200 articles filtered according to some external factor.

Well, this is because you set filters from lines 701 to 711. When using the app, no filter would be set when calling this method, so you'll have what you expect.

> If one of those nulls is substituted by another value, it will not be the last 200 articles that will be stored, but 200 articles filtered according to some external factor. Well, this is because you set filters from lines 701 to 711. When using the app, no filter would be set when calling this method, so you'll have what you expect.

No, filters can be set when this method is called.
This method should deliberately ignore filters.

Imagine this:

  • Open the app
  • Select one tag
  • The background sync starts at this moment

Since the repository stores the filter internally, the filter will be set for this background function as well.
This function should make the call without any filter even when filters are set in the repository.

No, filters can be set when this method is called. This method should deliberately ignore filters. Imagine this: - Open the app - Select one tag - The background sync starts at this moment Since the repository stores the filter internally, the filter will be set for this background function as well. This function should make the call without any filter even when filters are set in the repository.

Ok.

So getMaxItemsForBackground should be changed, then.

Ok. So `getMaxItemsForBackground` should be changed, then.
assertSame(3, items.size)
AmineB marked this conversation as resolved Outdated

tryToCacheItemsAndGetNewOnes will only return 1 item as it only returns new items.

You will also need to add every { db.itemsQueries.transaction(any(), any()) } returns Unit to the setup.

`tryToCacheItemsAndGetNewOnes` will only return `1` item as it only returns new items. You will also need to add `every { db.itemsQueries.transaction(any(), any()) } returns Unit` to the setup.
}
@Test
fun `cache items but response fails`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = false, data = generateTestApiItem())
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
repository.sourceFilter = SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
AmineB marked this conversation as resolved Outdated

This should be coVerify(exactly = 3) { api.getItems(any(), 0, "Tag", 1, "search", null, 200) }

This should be `coVerify(exactly = 3) { api.getItems(any(), 0, "Tag", 1, "search", null, 200) }`

As above

As above

As above.

I don't know what you wanted to test.

The thing is, passing null, null, null, null in the function will make it fail, because in your calls, you pass data.

As above. I don't know what you wanted to test. The thing is, passing null, null, null, null in the function will make it fail, because in your calls, you pass data.
assertSame(0, items.size)
}
@Test
fun `cache items without connection`() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = false, data = generateTestApiItem())
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
val repository = Repository(api, appSettingsService, connectivityStatus, db)
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
repository.sourceFilter = SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
repository.searchFilter = "search"
var items = emptyList<SelfossModel.Item>()
runBlocking {
items = repository.tryToCacheItemsAndGetNewOnes()
}
coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
assertSame(emptyList<SelfossModel.Item>(), items)
}
}
fun generateTestDBItems(item : FakeItemParameters = FakeItemParameters()) : List<ITEM> {