correctly detect end of pagination in network timeline (#2296)

* correctly detect end of pagination in network timeline

closes #2293

* improve NetworkTimelineRemoteMediatorTest

* remove unused import
This commit is contained in:
Konrad Pozniak 2022-01-20 18:30:21 +01:00 committed by GitHub
parent f29e46ad55
commit a000228165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 1 deletions

View File

@ -47,7 +47,11 @@ class NetworkTimelineRemoteMediator(
}
LoadType.APPEND -> {
val maxId = viewModel.nextKey
viewModel.fetchStatusesForKind(maxId, null, limit = state.config.pageSize)
if (maxId != null) {
viewModel.fetchStatusesForKind(maxId, null, limit = state.config.pageSize)
} else {
return MediatorResult.Success(endOfPaginationReached = true)
}
}
}

View File

@ -6,6 +6,7 @@ import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineRemoteMediator
import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineViewModel
import com.keylesspalace.tusky.db.AccountEntity
@ -15,15 +16,21 @@ import com.nhaarman.mockitokotlin2.anyOrNull
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.doThrow
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import kotlinx.coroutines.runBlocking
import okhttp3.Headers
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import retrofit2.HttpException
import retrofit2.Response
import java.lang.RuntimeException
@Config(sdk = [29])
@RunWith(AndroidJUnit4::class)
class NetworkTimelineRemoteMediatorTest {
private val accountManager: AccountManager = mock {
@ -70,6 +77,52 @@ class NetworkTimelineRemoteMediatorTest {
assertTrue((result as RemoteMediator.MediatorResult.Error).throwable is RuntimeException)
}
@Test
@ExperimentalPagingApi
fun `should do initial loading`() {
val statuses: MutableList<StatusViewData> = mutableListOf()
val timelineViewModel: NetworkTimelineViewModel = mock {
on { statusData } doReturn statuses
on { nextKey } doReturn null
onBlocking { fetchStatusesForKind(null, null, 20) } doReturn Response.success(
listOf(
mockStatus("7"),
mockStatus("6"),
mockStatus("5")
),
Headers.headersOf(
"Link", "<https://mastodon.example/api/v1/favourites?limit=20&max_id=4>; rel=\"next\", <https://mastodon.example/api/v1/favourites?limit=20&min_id=8>; rel=\"prev\""
)
)
}
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
val state = state(
listOf(
PagingSource.LoadResult.Page(
data = emptyList(),
prevKey = null,
nextKey = null
)
)
)
val result = runBlocking { remoteMediator.load(LoadType.REFRESH, state) }
val newStatusData = mutableListOf(
mockStatusViewData("7"),
mockStatusViewData("6"),
mockStatusViewData("5"),
)
verify(timelineViewModel).nextKey = "4"
assertTrue(result is RemoteMediator.MediatorResult.Success)
assertEquals(false, (result as RemoteMediator.MediatorResult.Success).endOfPaginationReached)
assertEquals(newStatusData, statuses)
}
@Test
@ExperimentalPagingApi
fun `should not prepend statuses`() {
@ -246,6 +299,9 @@ class NetworkTimelineRemoteMediatorTest {
mockStatus("3"),
mockStatus("2"),
mockStatus("1")
),
Headers.headersOf(
"Link", "<https://mastodon.example/api/v1/favourites?limit=20&max_id=0>; rel=\"next\", <https://mastodon.example/api/v1/favourites?limit=20&min_id=4>; rel=\"prev\""
)
)
}
@ -277,11 +333,55 @@ class NetworkTimelineRemoteMediatorTest {
mockStatusViewData("1"),
)
verify(timelineViewModel).nextKey = "0"
assertTrue(result is RemoteMediator.MediatorResult.Success)
assertEquals(false, (result as RemoteMediator.MediatorResult.Success).endOfPaginationReached)
assertEquals(newStatusData, statuses)
}
@Test
@ExperimentalPagingApi
fun `should not append statuses when pagination end has been reached`() {
val statuses: MutableList<StatusViewData> = mutableListOf(
mockStatusViewData("8"),
mockStatusViewData("7"),
mockStatusViewData("5"),
)
val timelineViewModel: NetworkTimelineViewModel = mock {
on { statusData } doReturn statuses
on { nextKey } doReturn null
}
val remoteMediator = NetworkTimelineRemoteMediator(accountManager, timelineViewModel)
val state = state(
listOf(
PagingSource.LoadResult.Page(
data = listOf(
mockStatusViewData("8"),
mockStatusViewData("7"),
mockStatusViewData("5"),
),
prevKey = null,
nextKey = null
)
)
)
val result = runBlocking { remoteMediator.load(LoadType.APPEND, state) }
val newStatusData = mutableListOf(
mockStatusViewData("8"),
mockStatusViewData("7"),
mockStatusViewData("5")
)
assertTrue(result is RemoteMediator.MediatorResult.Success)
assertTrue((result as RemoteMediator.MediatorResult.Success).endOfPaginationReached)
assertEquals(newStatusData, statuses)
}
private fun state(pages: List<PagingSource.LoadResult.Page<String, StatusViewData>> = emptyList()) = PagingState(
pages = pages,
anchorPosition = null,