#3841 Unit Test DepictsClient (#3842)

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadCategoryDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - update BaseAdapter to be easier to use

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchImagesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchCategoriesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace NotificationRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadDepictsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace PlaceRenderer

* #3756 Convert SearchDepictionsFragment to use Pagination - convert SearchDepictionsFragment

* #3756 Convert SearchDepictionsFragment to use Pagination - fix presenter unit tests now that view is not nullable - fix Category prefix imports

* #3756 Convert SearchDepictionsFragment to use Pagination - test DataSource related classes

* #3756 Convert SearchDepictionsFragment to use Pagination - reset rx scheduler - ignore failing test

* #3760 Convert SearchCategoriesFragment to use Pagination - extract functionality of pagination to base classes - add category pagination

* #3772 Convert SearchImagesFragment to use Pagination  - convert SearchImagesFragment - tidy up showing the empty view - make search fragments show snackbar with appropriate text

* #3772 Convert SearchImagesFragment to use Pagination  - allow viewpager to load more data

* #3760 remove test that got re-added by merge

* #3760 remove duplicate dependency

* #3772 fix compilation

* #3780 Create media using a combination of Entities & MwQueryResult - construct media with an entity - move fields from media down to contribution - move dynamic fields outside of media - remove unused constructors - remove all unnecessary fetching of captions/descriptions - bump database version

* #3808 Construct media objects that depict an item id correctly - use generator to construct media for DepictedImages

* #3810 Convert DepictedImagesFragment to use Pagination - extract common media paging methods - convert to DepictedImages to use pagination

* #3810 Convert DepictedImagesFragment to use Pagination - rename base classes to better reflect usage

* #3810 Convert DepictedImagesFragment to use Pagination - map to empty result with no pages

* #3810 Convert DepictedImagesFragment to use Pagination - align test with returned values

* #3780 Create media using a combination of Entities & MwQueryResult - update wikicode to align with expected behaviour

* #3780 Create media using a combination of Entities & MwQueryResult - replace old site of thumbnail title with most relevant caption

* #3818 Convert SubDepictionListFragment to use Pagination - replace SubDepictionList with Child and Parent Fragments - replace contracts with simple presenter declarations - move classes to appropriate packages - delete unused network models - delete duplicated paging classes

* #3820 Convert CategoryImagesListFragment to use Pagination - replace CategoryImagesListFragment with CategoriesMediaFragment - disallow the construction of media objects without imageinfo

* #3822 Convert SubCategoryImagesListFragment to use Pagination - convert subcategories - add continuation support in category client - rely on interfaces for callbacks of PageableMediaFragments

* #3822 Convert SubCategoryImagesListFragment to use Pagination - convert parent categories - delete list fragment - creat base class to support continuation requests in clients

* #3822 Convert SubCategoryImagesListFragment to use Pagination - add tests for ParentCategoriesDataSource

* #3822 Convert SubCategoryImagesListFragment to use Pagination - remove no longer applicable test

* #3841 Unit Test DepictsClient - add tests

* #3841 Unit Test DepictsClient - fix return types
This commit is contained in:
Seán Mac Gillicuddy 2020-06-26 08:54:26 +01:00 committed by GitHub
parent 9d59915459
commit 542d321acc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 100 additions and 25 deletions

View file

@ -1,9 +1,10 @@
package fr.free.nrw.commons.explore.depictions package fr.free.nrw.commons.explore.depictions
import fr.free.nrw.commons.mwapi.Binding
import fr.free.nrw.commons.mwapi.SparqlResponse import fr.free.nrw.commons.mwapi.SparqlResponse
import fr.free.nrw.commons.upload.depicts.DepictsInterface import fr.free.nrw.commons.upload.depicts.DepictsInterface
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
import io.reactivex.Observable import fr.free.nrw.commons.wikidata.model.DepictSearchItem
import io.reactivex.Single import io.reactivex.Single
import org.wikipedia.wikidata.Entities import org.wikipedia.wikidata.Entities
import java.util.* import java.util.*
@ -14,9 +15,7 @@ import javax.inject.Singleton
* Depicts Client to handle custom calls to Commons Wikibase APIs * Depicts Client to handle custom calls to Commons Wikibase APIs
*/ */
@Singleton @Singleton
class DepictsClient @Inject constructor( class DepictsClient @Inject constructor(private val depictsInterface: DepictsInterface) {
private val depictsInterface: DepictsInterface
) {
/** /**
* Search for depictions using the search item * Search for depictions using the search item
@ -25,22 +24,21 @@ class DepictsClient @Inject constructor(
fun searchForDepictions(query: String?, limit: Int, offset: Int): Single<List<DepictedItem>> { fun searchForDepictions(query: String?, limit: Int, offset: Int): Single<List<DepictedItem>> {
val language = Locale.getDefault().language val language = Locale.getDefault().language
return depictsInterface.searchForDepicts(query, "$limit", language, language, "$offset") return depictsInterface.searchForDepicts(query, "$limit", language, language, "$offset")
.map { it.search.joinToString("|") { searchItem -> searchItem.id } } .map { it.search.joinToString("|", transform = DepictSearchItem::id) }
.flatMap(::getEntities) .mapToDepictions()
.map { it.entities().values.map(::DepictedItem) }
} }
fun getEntities(ids: String): Single<Entities> { fun getEntities(ids: String): Single<Entities> {
return depictsInterface.getEntities(ids) return depictsInterface.getEntities(ids)
} }
fun toDepictions(sparqlResponse: Observable<SparqlResponse>): Observable<List<DepictedItem>> { fun toDepictions(sparqlResponse: Single<SparqlResponse>): Single<List<DepictedItem>> {
return sparqlResponse.map { return sparqlResponse.map {
it.results.bindings.joinToString("|") { binding -> it.results.bindings.joinToString("|", transform = Binding::id)
binding.id }.mapToDepictions()
}
}
.flatMap { getEntities(it).toObservable() }
.map { it.entities().values.map(::DepictedItem) }
} }
private fun Single<String>.mapToDepictions() =
flatMap(::getEntities)
.map { it.entities().values.map(::DepictedItem) }
} }

View file

@ -11,7 +11,7 @@ class PageableChildDepictionsDataSource @Inject constructor(
private val okHttpJsonApiClient: OkHttpJsonApiClient private val okHttpJsonApiClient: OkHttpJsonApiClient
) : PageableBaseDataSource<DepictedItem>(liveDataConverter) { ) : PageableBaseDataSource<DepictedItem>(liveDataConverter) {
override val loadFunction = { limit: Int, startPosition: Int -> override val loadFunction = { limit: Int, startPosition: Int ->
okHttpJsonApiClient.getChildDepictions(query, startPosition, limit).blockingFirst() okHttpJsonApiClient.getChildDepictions(query, startPosition, limit).blockingGet()
} }
} }

View file

@ -11,7 +11,7 @@ class PageableParentDepictionsDataSource @Inject constructor(
private val okHttpJsonApiClient: OkHttpJsonApiClient private val okHttpJsonApiClient: OkHttpJsonApiClient
) : PageableBaseDataSource<DepictedItem>(liveDataConverter) { ) : PageableBaseDataSource<DepictedItem>(liveDataConverter) {
override val loadFunction = { limit: Int, startPosition: Int -> override val loadFunction = { limit: Int, startPosition: Int ->
okHttpJsonApiClient.getParentDepictions(query, startPosition, limit).blockingFirst() okHttpJsonApiClient.getParentDepictions(query, startPosition, limit).blockingGet()
} }
} }

View file

@ -209,7 +209,7 @@ public class OkHttpJsonApiClient {
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example: * Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
* bridge -> suspended bridge, aqueduct, etc * bridge -> suspended bridge, aqueduct, etc
*/ */
public Observable<List<DepictedItem>> getChildDepictions(String qid, int startPosition, public Single<List<DepictedItem>> getChildDepictions(String qid, int startPosition,
int limit) throws IOException { int limit) throws IOException {
return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,"/queries/subclasses_query.rq")); return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,"/queries/subclasses_query.rq"));
} }
@ -218,14 +218,14 @@ public class OkHttpJsonApiClient {
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example: * Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
* bridge -> suspended bridge, aqueduct, etc * bridge -> suspended bridge, aqueduct, etc
*/ */
public Observable<List<DepictedItem>> getParentDepictions(String qid, int startPosition, public Single<List<DepictedItem>> getParentDepictions(String qid, int startPosition,
int limit) throws IOException { int limit) throws IOException {
return depictedItemsFrom(sparqlQuery(qid, startPosition, limit, return depictedItemsFrom(sparqlQuery(qid, startPosition, limit,
"/queries/parentclasses_query.rq")); "/queries/parentclasses_query.rq"));
} }
private Observable<List<DepictedItem>> depictedItemsFrom(Request request) { private Single<List<DepictedItem>> depictedItemsFrom(Request request) {
return depictsClient.toDepictions(Observable.fromCallable(() -> { return depictsClient.toDepictions(Single.fromCallable(() -> {
try (ResponseBody body = okHttpClient.newCall(request).execute().body()) { try (ResponseBody body = okHttpClient.newCall(request).execute().body()) {
return gson.fromJson(body.string(), SparqlResponse.class); return gson.fromJson(body.string(), SparqlResponse.class);
} }

View file

@ -7,6 +7,7 @@ import fr.free.nrw.commons.nearby.Label
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.nearby.Sitelinks import fr.free.nrw.commons.nearby.Sitelinks
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
import fr.free.nrw.commons.wikidata.model.DepictSearchItem
import org.wikipedia.wikidata.* import org.wikipedia.wikidata.*
import java.util.* import java.util.*
@ -63,6 +64,14 @@ fun media(
depictionIds depictionIds
) )
fun depictSearchItem(
id: String = "id",
pageId: String = "pageid",
url: String = "url",
label: String = "label",
description: String = "description"
) = DepictSearchItem(id, pageId, url, label, description)
fun place( fun place(
name: String = "name", name: String = "name",
label: Label? = null, label: Label? = null,

View file

@ -0,0 +1,69 @@
package fr.free.nrw.commons.explore.depictions
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import depictSearchItem
import fr.free.nrw.commons.mwapi.Binding
import fr.free.nrw.commons.mwapi.Result
import fr.free.nrw.commons.mwapi.SparqlResponse
import fr.free.nrw.commons.upload.depicts.DepictsInterface
import fr.free.nrw.commons.wikidata.model.DepictSearchResponse
import io.reactivex.Single
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.wikipedia.wikidata.Entities
class DepictsClientTest {
@Mock
private lateinit var depictsInterface: DepictsInterface
private lateinit var depictsClient: DepictsClient
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
depictsClient = DepictsClient(depictsInterface)
}
@Test
fun searchForDepictions() {
val depictSearchResponse = mock<DepictSearchResponse>()
whenever(depictsInterface.searchForDepicts("query", "1", "en", "en", "0"))
.thenReturn(Single.just(depictSearchResponse))
whenever(depictSearchResponse.search).thenReturn(listOf(depictSearchItem("1"),depictSearchItem("2")))
val entities = mock<Entities>()
whenever(depictsInterface.getEntities("1|2")).thenReturn(Single.just(entities))
whenever(entities.entities()).thenReturn(emptyMap())
depictsClient.searchForDepictions("query", 1, 0)
.test()
.assertValue(emptyList())
}
@Test
fun getEntities() {
val entities = mock<Entities>()
whenever(depictsInterface.getEntities("ids")).thenReturn(Single.just(entities))
depictsClient.getEntities("ids").test().assertValue(entities)
}
@Test
fun toDepictions() {
val sparqlResponse = mock<SparqlResponse>()
val result = mock<Result>()
whenever(sparqlResponse.results).thenReturn(result)
val binding1 = mock<Binding>()
val binding2 = mock<Binding>()
whenever(result.bindings).thenReturn(listOf(binding1, binding2))
whenever(binding1.id).thenReturn("1")
whenever(binding2.id).thenReturn("2")
val entities = mock<Entities>()
whenever(depictsInterface.getEntities("1|2")).thenReturn(Single.just(entities))
whenever(entities.entities()).thenReturn(emptyMap())
depictsClient.toDepictions(Single.just(sparqlResponse))
.test()
.assertValue(emptyList())
}
}

View file

@ -1,11 +1,10 @@
package fr.free.nrw.commons.explore.depictions.child package fr.free.nrw.commons.explore.depictions.child
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever import com.nhaarman.mockitokotlin2.whenever
import depictedItem import depictedItem
import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.LiveDataConverter
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import io.reactivex.Observable import io.reactivex.Single
import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before import org.junit.Before
@ -30,7 +29,7 @@ class PageableChildDepictionsDataSourceTest {
PageableChildDepictionsDataSource(liveDataConverter, okHttpJsonApiClient) PageableChildDepictionsDataSource(liveDataConverter, okHttpJsonApiClient)
dataSource.onQueryUpdated("test") dataSource.onQueryUpdated("test")
whenever(okHttpJsonApiClient.getChildDepictions("test", 0, 1)) whenever(okHttpJsonApiClient.getChildDepictions("test", 0, 1))
.thenReturn(Observable.just(listOf(depictedItem()))) .thenReturn(Single.just(listOf(depictedItem())))
assertThat(dataSource.loadFunction(1, 0), `is`(listOf(depictedItem()))) assertThat(dataSource.loadFunction(1, 0), `is`(listOf(depictedItem())))
} }
} }

View file

@ -4,7 +4,7 @@ import com.nhaarman.mockitokotlin2.whenever
import depictedItem import depictedItem
import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.LiveDataConverter
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import io.reactivex.Observable import io.reactivex.Single
import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before import org.junit.Before
@ -30,7 +30,7 @@ class PageableParentDepictionsDataSourceTest {
PageableParentDepictionsDataSource(liveDataConverter, okHttpJsonApiClient) PageableParentDepictionsDataSource(liveDataConverter, okHttpJsonApiClient)
dataSource.onQueryUpdated("test") dataSource.onQueryUpdated("test")
whenever(okHttpJsonApiClient.getParentDepictions("test", 0, 1)) whenever(okHttpJsonApiClient.getParentDepictions("test", 0, 1))
.thenReturn(Observable.just(listOf(depictedItem()))) .thenReturn(Single.just(listOf(depictedItem())))
assertThat(dataSource.loadFunction(1, 0), `is`(listOf(depictedItem()))) assertThat(dataSource.loadFunction(1, 0), `is`(listOf(depictedItem())))
} }
} }