mirror of https://github.com/readrops/Readrops.git
Add some tests to FeverDataSource
This commit is contained in:
parent
0265b88ff3
commit
7a8f255b72
|
@ -27,7 +27,7 @@ class FeverDataSource(private val service: FeverService) {
|
|||
login: String,
|
||||
password: String,
|
||||
syncType: SyncType,
|
||||
syncData: FeverSyncData
|
||||
lastSinceId: String,
|
||||
): FeverSyncResult = with(CoroutineScope(Dispatchers.IO)) {
|
||||
val body = getFeverRequestBody(login, password)
|
||||
|
||||
|
@ -39,14 +39,16 @@ class FeverDataSource(private val service: FeverService) {
|
|||
async {
|
||||
unreadIds = service.getUnreadItemsIds(body)
|
||||
.reversed()
|
||||
.subList(0, MAX_ITEMS_IDS)
|
||||
.take(MAX_ITEMS_IDS)
|
||||
|
||||
var lastId = unreadIds.first()
|
||||
var maxId = unreadIds.first()
|
||||
items = buildList {
|
||||
repeat(INITIAL_SYNC_ITEMS_REQUESTS_COUNT) {
|
||||
val newItems = service.getItems(body, lastId, null)
|
||||
for(index in 0 until INITIAL_SYNC_ITEMS_REQUESTS_COUNT) {
|
||||
val newItems = service.getItems(body, maxId, null)
|
||||
|
||||
lastId = newItems.last().remoteId!!
|
||||
if (newItems.isEmpty()) break
|
||||
// always take the lowest id
|
||||
maxId = newItems.last().remoteId!!
|
||||
addAll(newItems)
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +60,6 @@ class FeverDataSource(private val service: FeverService) {
|
|||
)
|
||||
.awaitAll()
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
return FeverSyncResult().apply {
|
||||
listOf(
|
||||
|
@ -70,18 +70,23 @@ class FeverDataSource(private val service: FeverService) {
|
|||
async { favicons = listOf() },
|
||||
async {
|
||||
items = buildList {
|
||||
var sinceId = syncData.sinceId
|
||||
var localSinceId = lastSinceId
|
||||
|
||||
while (true) {
|
||||
val newItems = service.getItems(body, null, sinceId)
|
||||
val newItems = service.getItems(body, null, localSinceId)
|
||||
|
||||
if (newItems.isEmpty()) break
|
||||
sinceId = newItems.first().remoteId!!
|
||||
// always take the highest id
|
||||
localSinceId = newItems.first().remoteId!!
|
||||
addAll(newItems)
|
||||
}
|
||||
}
|
||||
|
||||
if (items.isNotEmpty()) items.first().remoteId!!.toLong() else sinceId.toLong()
|
||||
sinceId = if (items.isNotEmpty()) {
|
||||
items.first().remoteId!!.toLong()
|
||||
} else {
|
||||
localSinceId.toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.awaitAll()
|
||||
|
@ -105,8 +110,8 @@ class FeverDataSource(private val service: FeverService) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_ITEMS_IDS = 5000
|
||||
private const val INITIAL_SYNC_ITEMS_REQUESTS_COUNT = 10
|
||||
private const val MAX_ITEMS_IDS = 1000
|
||||
private const val INITIAL_SYNC_ITEMS_REQUESTS_COUNT = 20 // (1000 items max)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package com.readrops.api.services.fever
|
||||
|
||||
data class FeverSyncData(
|
||||
val sinceId: String,
|
||||
)
|
|
@ -16,4 +16,10 @@ fun MockWebServer.enqueueStream(stream: InputStream) {
|
|||
enqueue(MockResponse()
|
||||
.setResponseCode(HttpURLConnection.HTTP_OK)
|
||||
.setBody(Buffer().readFrom(stream)))
|
||||
}
|
||||
|
||||
fun MockResponse.Companion.okResponseWithBody(stream: InputStream): MockResponse {
|
||||
return MockResponse()
|
||||
.setResponseCode(HttpURLConnection.HTTP_OK)
|
||||
.setBody(Buffer().readFrom(stream))
|
||||
}
|
|
@ -2,13 +2,17 @@ package com.readrops.api.services.fever
|
|||
|
||||
import com.readrops.api.TestUtils
|
||||
import com.readrops.api.apiModule
|
||||
import com.readrops.api.utils.ApiUtils
|
||||
import com.readrops.api.enqueueOK
|
||||
import com.readrops.api.enqueueStream
|
||||
import com.readrops.api.okResponseWithBody
|
||||
import com.readrops.api.services.SyncType
|
||||
import com.readrops.api.utils.AuthInterceptor
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okio.Buffer
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
|
@ -18,8 +22,8 @@ import org.koin.dsl.module
|
|||
import org.koin.test.KoinTest
|
||||
import org.koin.test.KoinTestRule
|
||||
import org.koin.test.get
|
||||
import java.net.HttpURLConnection
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
|
@ -59,12 +63,7 @@ class FeverDataSourceTest : KoinTest {
|
|||
@Test
|
||||
fun loginSuccessfulTest() = runTest {
|
||||
val stream = TestUtils.loadResource("services/fever/successful_auth.json")
|
||||
|
||||
mockServer.enqueue(
|
||||
MockResponse().setResponseCode(HttpURLConnection.HTTP_OK)
|
||||
.addHeader(ApiUtils.CONTENT_TYPE_HEADER, "application/json")
|
||||
.setBody(Buffer().readFrom(stream))
|
||||
)
|
||||
mockServer.enqueueStream(stream)
|
||||
|
||||
assertTrue { dataSource.login("", "") }
|
||||
}
|
||||
|
@ -72,13 +71,161 @@ class FeverDataSourceTest : KoinTest {
|
|||
@Test
|
||||
fun loginFailedTest() = runTest {
|
||||
val stream = TestUtils.loadResource("services/fever/failed_auth.json")
|
||||
|
||||
mockServer.enqueue(
|
||||
MockResponse().setResponseCode(HttpURLConnection.HTTP_OK)
|
||||
.addHeader(ApiUtils.CONTENT_TYPE_HEADER, "application/json")
|
||||
.setBody(Buffer().readFrom(stream))
|
||||
)
|
||||
mockServer.enqueueStream(stream)
|
||||
|
||||
assertFalse { dataSource.login("", "") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setItemStateTest() = runTest {
|
||||
mockServer.enqueueOK()
|
||||
|
||||
dataSource.setItemState("login", "password", "saved", "itemId")
|
||||
val request = mockServer.takeRequest()
|
||||
val requestBody = request.body.readUtf8()
|
||||
|
||||
assertEquals("saved", request.requestUrl?.queryParameter("as"))
|
||||
assertEquals("itemId", request.requestUrl?.queryParameter("id"))
|
||||
|
||||
assertTrue { requestBody.contains("api_key") }
|
||||
assertTrue { requestBody.contains("fb2f5a9b0eccc1ee95c1d559a2dd797a") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialSyncTest() = runTest {
|
||||
var pageNumber = 0
|
||||
var firstMaxId = ""
|
||||
var secondMaxId = ""
|
||||
var thirdMaxId = ""
|
||||
|
||||
mockServer.dispatcher = object : Dispatcher() {
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
with(request.path!!) {
|
||||
return when {
|
||||
this == "/?feeds" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/feeds.json"))
|
||||
}
|
||||
|
||||
this == "/?groups" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/folders.json"))
|
||||
}
|
||||
|
||||
this == "/?unread_item_ids" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/itemsIds.json"))
|
||||
}
|
||||
|
||||
this == "/?saved_item_ids" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/itemsIds.json"))
|
||||
}
|
||||
|
||||
contains("/?items") -> {
|
||||
when (pageNumber++) {
|
||||
0 -> {
|
||||
firstMaxId = request.requestUrl?.queryParameter("max_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/items_page2.json"))
|
||||
}
|
||||
1 -> {
|
||||
secondMaxId = request.requestUrl?.queryParameter("max_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/items_page1.json"))
|
||||
}
|
||||
2 -> {
|
||||
thirdMaxId = request.requestUrl?.queryParameter("max_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/empty_items.json"))
|
||||
}
|
||||
else -> MockResponse().setResponseCode(404)
|
||||
}
|
||||
}
|
||||
|
||||
else -> MockResponse().setResponseCode(404)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = dataSource.synchronize("login", "password", SyncType.INITIAL_SYNC, "")
|
||||
|
||||
assertEquals(1, result.folders.size)
|
||||
assertEquals(1, result.feverFeeds.feeds.size)
|
||||
assertEquals(6, result.unreadIds.size)
|
||||
assertEquals(6, result.starredIds.size)
|
||||
assertEquals(10, result.items.size)
|
||||
assertEquals(10, result.items.size)
|
||||
|
||||
assertEquals("1564058340320135", firstMaxId)
|
||||
assertEquals("6", secondMaxId)
|
||||
assertEquals("1", thirdMaxId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun classicSyncTest() = runTest {
|
||||
var pageNumber = 0
|
||||
|
||||
var firstLastSinceId = ""
|
||||
var secondLastSinceId = ""
|
||||
var thirdLastSinceId = ""
|
||||
|
||||
mockServer.dispatcher = object : Dispatcher() {
|
||||
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
with(request.path!!) {
|
||||
return when {
|
||||
this == "/?feeds" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/feeds.json"))
|
||||
}
|
||||
|
||||
this == "/?groups" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/folders.json"))
|
||||
}
|
||||
|
||||
this == "/?unread_item_ids" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/itemsIds.json"))
|
||||
}
|
||||
|
||||
this == "/?saved_item_ids" -> {
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/itemsIds.json"))
|
||||
}
|
||||
|
||||
contains("/?items") -> {
|
||||
when (pageNumber++) {
|
||||
0 -> {
|
||||
firstLastSinceId = request.requestUrl?.queryParameter("since_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/items_page1.json"))
|
||||
}
|
||||
1 -> {
|
||||
secondLastSinceId = request.requestUrl?.queryParameter("since_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/items_page2.json"))
|
||||
}
|
||||
2 -> {
|
||||
thirdLastSinceId = request.requestUrl?.queryParameter("since_id").orEmpty()
|
||||
MockResponse.okResponseWithBody(TestUtils.loadResource("services/fever/empty_items.json"))
|
||||
}
|
||||
else -> MockResponse().setResponseCode(404)
|
||||
}
|
||||
}
|
||||
|
||||
else -> MockResponse().setResponseCode(404)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = dataSource.synchronize("login", "password", SyncType.CLASSIC_SYNC, "1")
|
||||
|
||||
assertEquals(1, result.folders.size)
|
||||
assertEquals(1, result.feverFeeds.feeds.size)
|
||||
assertEquals(6, result.unreadIds.size)
|
||||
assertEquals(6, result.starredIds.size)
|
||||
assertEquals(10, result.items.size)
|
||||
assertEquals("5", result.items.first().remoteId)
|
||||
assertEquals("6", result.items.last().remoteId)
|
||||
|
||||
assertEquals("1", firstLastSinceId)
|
||||
assertEquals("5", secondLastSinceId)
|
||||
assertEquals("10", thirdLastSinceId)
|
||||
|
||||
mockServer.dispatcher.shutdown()
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ class FeverItemsAdapterTest {
|
|||
|
||||
@Test
|
||||
fun validItemsTest() {
|
||||
val stream = TestUtils.loadResource("services/fever/items.json")
|
||||
val stream = TestUtils.loadResource("services/fever/items_page2.json")
|
||||
|
||||
val items = adapter.fromJson(Buffer().readFrom(stream))!!
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"api_version": 3,
|
||||
"auth": 1,
|
||||
"last_refreshed_on_time": 1635849601,
|
||||
"total_items": 10814,
|
||||
"items": []
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -3,7 +3,6 @@ package com.readrops.app.repositories
|
|||
import android.util.Log
|
||||
import com.readrops.api.services.SyncType
|
||||
import com.readrops.api.services.fever.FeverDataSource
|
||||
import com.readrops.api.services.fever.FeverSyncData
|
||||
import com.readrops.api.services.fever.ItemAction
|
||||
import com.readrops.api.services.fever.adapters.FeverFeeds
|
||||
import com.readrops.api.utils.exceptions.LoginFailedException
|
||||
|
@ -43,7 +42,7 @@ class FeverRepository(
|
|||
account.login!!,
|
||||
account.password!!,
|
||||
syncType,
|
||||
FeverSyncData(account.lastModified.toString())
|
||||
account.lastModified.toString()
|
||||
).run {
|
||||
insertFolders(folders)
|
||||
val newFeeds = insertFeeds(feverFeeds)
|
||||
|
@ -51,7 +50,7 @@ class FeverRepository(
|
|||
val newItems = insertItems(items)
|
||||
insertItemsIds(unreadIds, starredIds.toMutableList())
|
||||
|
||||
// We store the id to use for the next synchronisation even if it's not a timestamp
|
||||
// We use the most recent item id as lastModified instead of a timestamp
|
||||
database.accountDao().updateLastModified(sinceId, account.id)
|
||||
|
||||
SyncResult(
|
||||
|
@ -66,11 +65,10 @@ class FeverRepository(
|
|||
onUpdate: suspend (Feed) -> Unit
|
||||
): Pair<SyncResult, ErrorResult> = throw NotImplementedError("This method can't be called here")
|
||||
|
||||
// Not supported by Fever API
|
||||
override suspend fun insertNewFeeds(
|
||||
newFeeds: List<Feed>,
|
||||
onUpdate: (Feed) -> Unit
|
||||
): ErrorResult = throw CloneNotSupportedException()
|
||||
): ErrorResult = throw NotImplementedError("Add feed action not supported by Fever API")
|
||||
|
||||
override suspend fun updateFeed(feed: Feed) =
|
||||
throw NotImplementedError("Update feed action not supported by Fever API")
|
||||
|
@ -78,15 +76,12 @@ class FeverRepository(
|
|||
override suspend fun deleteFeed(feed: Feed) =
|
||||
throw NotImplementedError("Delete feed action not supported by Fever API")
|
||||
|
||||
// Not supported by Fever API
|
||||
override suspend fun addFolder(folder: Folder) =
|
||||
throw NotImplementedError("Add folder action not supported by Fever API")
|
||||
|
||||
// Not supported by Fever API
|
||||
override suspend fun updateFolder(folder: Folder) =
|
||||
throw NotImplementedError("Update folder action not supported by Fever API")
|
||||
|
||||
// Not supported by Fever API
|
||||
override suspend fun deleteFolder(folder: Folder) =
|
||||
throw NotImplementedError("Delete folder action not supported by Fever API")
|
||||
|
||||
|
|
Loading…
Reference in New Issue