diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt index 6bf0bc0ed..9f94e8592 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt @@ -7,8 +7,8 @@ import com.google.gson.annotations.SerializedName */ class CampaignConfig { @SerializedName("showOnlyLiveCampaigns") - private val showOnlyLiveCampaigns = false + var showOnlyLiveCampaigns = false @SerializedName("sortBy") - private val sortBy: String? = null -} + var sortBy: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt index 767732eb7..1656109e7 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt @@ -8,8 +8,8 @@ import fr.free.nrw.commons.campaigns.models.Campaign */ class CampaignResponseDTO { @SerializedName("config") - val campaignConfig: CampaignConfig? = null + var campaignConfig: CampaignConfig? = null @SerializedName("campaigns") - val campaigns: List? = null -} + var campaigns: List? = null +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt index 9e7891560..1a9d1500b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt @@ -3,6 +3,10 @@ package fr.free.nrw.commons import com.google.gson.Gson import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.times +import fr.free.nrw.commons.campaigns.CampaignResponseDTO +import fr.free.nrw.commons.campaigns.CampaignConfig +import fr.free.nrw.commons.campaigns.models.Campaign import fr.free.nrw.commons.explore.depictions.DepictsClient import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient @@ -10,13 +14,22 @@ import fr.free.nrw.commons.nearby.model.NearbyQueryParams import okhttp3.Call import okhttp3.HttpUrl import okhttp3.OkHttpClient +import okhttp3.Request import okhttp3.Response +import okhttp3.ResponseBody +import okhttp3.mockwebserver.MockResponse +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.times +import org.mockito.Mockito.eq import org.mockito.MockitoAnnotations +import java.io.BufferedReader +import java.io.InputStreamReader import java.lang.Exception class OkHttpJsonApiClientTests { @@ -45,34 +58,43 @@ class OkHttpJsonApiClientTests { @Mock lateinit var response: Response + @Mock + lateinit var responseBody: ResponseBody + + private lateinit var mockWebServer: TestWebServer + @Before fun setUp() { MockitoAnnotations.openMocks(this) - okHttpJsonApiClient = - OkHttpJsonApiClient( - okhttpClient, - depictsClient, - wikiMediaToolforgeUrl, - sparqlQueryUrl, - campaignsUrl, - gson, - ) + mockWebServer = TestWebServer() + mockWebServer.setUp() + okHttpJsonApiClient = OkHttpJsonApiClient( + okhttpClient, + depictsClient, + wikiMediaToolforgeUrl, + sparqlQueryUrl, + mockWebServer.getUrl(), //use the mock server for the campaignsUrl + gson + ) Mockito.`when`(okhttpClient.newCall(any())).thenReturn(call) Mockito.`when`(call.execute()).thenReturn(response) + Mockito.`when`(response.isSuccessful).thenReturn(false) + Mockito.`when`(response.message).thenReturn("test") + Mockito.`when`(response.body).thenReturn(responseBody) + Mockito.`when`(responseBody.string()).thenReturn("{\"error\": \"test\"}") } @Test fun testGetNearbyPlacesCustomQuery() { - Mockito.`when`(response.message).thenReturn("test") try { okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, "test") } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } try { okHttpJsonApiClient.getNearbyPlaces(NearbyQueryParams.Rectangular(latLng, latLng), "test", true, "test") } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } verify(okhttpClient, times(2)).newCall(any()) verify(call, times(2)).execute() @@ -80,11 +102,10 @@ class OkHttpJsonApiClientTests { @Test fun testGetNearbyPlaces() { - Mockito.`when`(response.message).thenReturn("test") try { okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, null) } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } try { okHttpJsonApiClient.getNearbyPlaces( @@ -93,9 +114,8 @@ class OkHttpJsonApiClientTests { true, null ) - } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } try { okHttpJsonApiClient.getNearbyPlaces( @@ -105,7 +125,7 @@ class OkHttpJsonApiClientTests { null ) } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } verify(okhttpClient, times(3)).newCall(any()) verify(call, times(3)).execute() @@ -113,18 +133,121 @@ class OkHttpJsonApiClientTests { @Test fun testGetNearbyItemCount() { - Mockito.`when`(response.message).thenReturn("test") try { okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Radial(latLng, 10f)) } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } try { okHttpJsonApiClient.getNearbyItemCount(NearbyQueryParams.Rectangular(latLng, latLng)) } catch (e: Exception) { - assert(e.message.equals("test")) + assertEquals("test", e.message) } verify(okhttpClient, times(2)).newCall(any()) verify(call, times(2)).execute() } -} + + @Test + fun testGetCampaignsWithData() { + //loads the json response from resources + val jsonResponse = loadJsonFromResource("campaigns_response_with_data.json") + + //mocks the succesfull response chain + Mockito.`when`(response.isSuccessful).thenReturn(true) + Mockito.`when`(response.message).thenReturn("OK") + Mockito.`when`(response.body).thenReturn(responseBody) + Mockito.`when`(responseBody.string()).thenReturn(jsonResponse) + + val campaignResponse = CampaignResponseDTO().apply { + campaignConfig = CampaignConfig().apply { + showOnlyLiveCampaigns = false + sortBy = "startDate" + } + campaigns = listOf( + Campaign().apply { + title = "Wiki Loves Monuments" + isWLMCampaign = true + }, + Campaign().apply { + title = "Wiki Loves Nature" + isWLMCampaign = false + } + ) + } + + //any() for the string argument and eq() for the class argument. + Mockito.`when`( + gson.fromJson( + any(), + eq(CampaignResponseDTO::class.java) + ) + ).thenReturn(campaignResponse) + + //call the getCampaigns + val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet() + + //verify the results + assertNotNull(result) + assertNotNull(result?.campaigns) + assertEquals(2, result?.campaigns!!.size) + assertEquals("Wiki Loves Monuments", result.campaigns!![0].title) + assertTrue(result.campaigns!![0].isWLMCampaign) + assertEquals("Wiki Loves Nature", result.campaigns!![1].title) + assertEquals(false, result.campaigns!![1].isWLMCampaign) + assertNotNull(result.campaignConfig) + assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns) + assertEquals("startDate", result.campaignConfig!!.sortBy) + } + + @Test + fun testGetCampaignsEmpty() { + //loads the empty json response + val jsonResponse = loadJsonFromResource("campaigns_response_empty.json") + + //mocks the successful response chain + Mockito.`when`(response.isSuccessful).thenReturn(true) + Mockito.`when`(response.message).thenReturn("OK") + Mockito.`when`(response.body).thenReturn(responseBody) + Mockito.`when`(responseBody.string()).thenReturn(jsonResponse) + + val campaignResponse = CampaignResponseDTO().apply { + campaignConfig = CampaignConfig().apply { + showOnlyLiveCampaigns = false + sortBy = "startDate" + } + campaigns = emptyList() + } + + //use any() for the string argument and eq() for the class argument. + Mockito.`when`( + gson.fromJson( + any(), + eq(CampaignResponseDTO::class.java) + ) + ).thenReturn(campaignResponse) + + //calls getCampaigns + val result: CampaignResponseDTO? = okHttpJsonApiClient.getCampaigns().blockingGet() + + //verify the results + assertNotNull(result) + assertNotNull(result?.campaigns) + assertTrue(result?.campaigns!!.isEmpty()) + assertNotNull(result.campaignConfig) + assertFalse(result.campaignConfig!!.showOnlyLiveCampaigns) + assertEquals("startDate", result.campaignConfig!!.sortBy) + } + + fun loadJsonFromResource(fileName: String): String { + val resourcePath = "raw/$fileName" + //uses the classloader to find the resource in the test environment + val inputStream = javaClass.classLoader?.getResourceAsStream(resourcePath) + + if (inputStream != null) { + //reads the entire stream content + return BufferedReader(InputStreamReader(inputStream)).use { it.readText() } + } + //throws an exception with the correct expected path + throw IllegalArgumentException("Resource $fileName not found. Please ensure the file is located in app/src/test/resources/raw/") + } +} \ No newline at end of file diff --git a/app/src/test/resources/raw/campaigns_response_empty.json b/app/src/test/resources/raw/campaigns_response_empty.json new file mode 100644 index 000000000..61903e24c --- /dev/null +++ b/app/src/test/resources/raw/campaigns_response_empty.json @@ -0,0 +1,7 @@ +{ + "config": { + "showOnlyLiveCampaigns": false, + "sortBy": "startDate" + }, + "campaigns": [] +} \ No newline at end of file diff --git a/app/src/test/resources/raw/campaigns_response_with_data.json b/app/src/test/resources/raw/campaigns_response_with_data.json new file mode 100644 index 000000000..dab818e2a --- /dev/null +++ b/app/src/test/resources/raw/campaigns_response_with_data.json @@ -0,0 +1,24 @@ +{ + "config": { + "showOnlyLiveCampaigns": false, + "sortBy": "startDate" + }, + "campaigns": [ + { + "title": "Wiki Loves Monuments", + "description": "A campaign to photograph monuments", + "startDate": "2025-09-01", + "endDate": "2025-09-30", + "link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Monuments", + "isWLMCampaign": true + }, + { + "title": "Wiki Loves Nature", + "description": "A campaign to photograph nature", + "startDate": "2025-06-01", + "endDate": "2025-06-30", + "link": "https://commons.wikimedia.org/wiki/Campaign:Wiki_Loves_Nature", + "isWLMCampaign": false + } + ] +} \ No newline at end of file