package com.exponea import androidx.test.core.app.ApplicationProvider import com.exponea.sdk.Exponea import com.exponea.sdk.models.Consent import com.exponea.sdk.models.ConsentSources import com.exponea.sdk.models.CustomerRecommendation import com.exponea.sdk.models.CustomerRecommendationOptions import com.exponea.sdk.models.FetchError import com.exponea.sdk.models.Result import com.exponea.sdk.util.ExponeaGson import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.BridgeReactContext import com.facebook.react.bridge.JavaOnlyArray import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.ReadableArray import com.google.gson.JsonPrimitive import io.mockk.Runs import io.mockk.every import io.mockk.just import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.runs import io.mockk.slot import io.mockk.unmockkAll import io.mockk.verify import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) internal class ExponeaModuleFetchingTest { lateinit var module: ExponeaModule @Before fun before() { mockkObject(Exponea) mockkStatic(Arguments::class) every { Arguments.createArray() } answers { JavaOnlyArray() } every { Arguments.createMap() } answers { JavaOnlyMap() } module = ExponeaModule(BridgeReactContext(ApplicationProvider.getApplicationContext())) } @After fun after() { unmockkAll() } @Test fun `fetch consents should not reject when Exponea not initialized`() { every { Exponea.isInitialized } returns false every { Exponea.getConsents(any(), any()) } just runs module.fetchConsents( MockResolvingPromise { verify { Exponea.getConsents(any(), any()) } } ) } @Test fun `fetch consents should reject on fetch error`() { every { Exponea.isInitialized } returns true every { Exponea.getConsents(any(), any()) } answers { secondArg<(Result) -> Unit>().invoke(Result(false, FetchError("{}", "mock error"))) } module.fetchConsents( MockRejectingPromise { assertEquals(ExponeaModule.ExponeaFetchException::class, it.errorThrowable!!::class) assertEquals("mock error", it.errorThrowable!!.message) } ) } @Test fun `fetch consents should resolve with response from server`() { every { Exponea.isInitialized } returns true every { Exponea.getConsents(any(), any()) } answers { firstArg<(Result>) -> Unit>().invoke(Result(true, arrayListOf( Consent( "mock-id", true, ConsentSources( createdFromCRM = true, imported = false, fromConsentPage = true, privateAPI = false, publicAPI = true, trackedFromScenario = false ), hashMapOf( "it" to hashMapOf("Antonio" to "Margheriti"), "de" to hashMapOf("Hugo" to "Stiglitz") ) ) ))) } val expectedResponse = """ [ { "sources":{ "imported":false, "privateAPI":false, "publicAPI":true, "createdFromCRM":true, "trackedFromScenario":false }, "translations":{"de":{"Hugo":"Stiglitz"},"it":{"Antonio":"Margheriti"}}, "legitimateInterest":true, "id":"mock-id" } ]""" module.fetchConsents( MockResolvingPromise { val resultArray = it.result as ReadableArray val resultList = (0 until resultArray.size()).map { i -> resultArray.getMap(i)!!.toHashMapRecursively() } assertEquals( ExponeaGson.instance.fromJson(expectedResponse, Object::class.java), ExponeaGson.instance.fromJson(ExponeaGson.instance.toJson(resultList), Object::class.java) ) } ) } @Test fun `fetch recommendations should not reject when Exponea is not initialized`() { every { Exponea.isInitialized } returns false every { Exponea.fetchRecommendation(any(), any(), any()) } just Runs module.fetchRecommendations( JavaOnlyMap.of( "id", "mock-id", "fillWithRandom", false, "size", 100, "items", JavaOnlyMap.of("item1", "value1", "item2", "value2"), "noTrack", true, "catalogAttributesWhitelist", JavaOnlyArray.of("item3", "item4") ), MockResolvingPromise { verify { Exponea.fetchRecommendation(any(), any(), any()) } } ) } @Test fun `fetch recommendations should reject without required fields`() { every { Exponea.isInitialized } returns true module.fetchRecommendations( JavaOnlyMap.of(), MockRejectingPromise { assertEquals(ExponeaModule.ExponeaDataException::class, it.errorThrowable!!::class) assertEquals("Property 'id' cannot be null.", it.errorThrowable!!.message) } ) } @Test fun `fetch recommendations should properly parse options`() { every { Exponea.isInitialized } returns true val slot = slot() every { Exponea.fetchRecommendation(capture(slot), any(), any()) } answers { thirdArg<(Result) -> Unit>().invoke(Result(false, FetchError("{}", "mock error"))) } module.fetchRecommendations( JavaOnlyMap.of( "id", "mock-id", "fillWithRandom", false, "size", 100, "items", JavaOnlyMap.of("item1", "value1", "item2", "value2"), "noTrack", true, "catalogAttributesWhitelist", JavaOnlyArray.of("item3", "item4") ), MockRejectingPromise { assertEquals( slot.captured, CustomerRecommendationOptions( id = "mock-id", fillWithRandom = false, size = 100, items = hashMapOf("item1" to "value1", "item2" to "value2"), noTrack = true, catalogAttributesWhitelist = arrayListOf("item3", "item4") ) ) } ) } @Test fun `fetch recommendations should reject on fetch error`() { every { Exponea.isInitialized } returns true val slot = slot() every { Exponea.fetchRecommendation(capture(slot), any(), any()) } answers { thirdArg<(Result) -> Unit>().invoke(Result(false, FetchError("{}", "mock error"))) } module.fetchRecommendations( JavaOnlyMap.of("id", "mock-id", "fillWithRandom", true), MockRejectingPromise { assertEquals(ExponeaModule.ExponeaFetchException::class, it.errorThrowable!!::class) assertEquals("mock error", it.errorThrowable!!.message) } ) } @Test fun `fetch recommendations should resolve with response from server`() { every { Exponea.isInitialized } returns true val slot = slot() every { Exponea.fetchRecommendation(capture(slot), any(), any()) } answers { secondArg<(Result>) -> Unit>().invoke( Result( true, arrayListOf( CustomerRecommendation( engineName = "mock engine name", itemId = "mock item id", recommendationId = "mock recommendation id", recommendationVariantId = "mock variant id", data = hashMapOf( "string" to JsonPrimitive("string"), "number" to JsonPrimitive(123), "boolean" to JsonPrimitive(false) ) ) ) ) ) } val expectedResponse = """ [ { "itemId": "mock item id", "recommendationVariantId": "mock variant id", "data": { "string": "string", "number": 123.0, "boolean": false }, "recommendationId": "mock recommendation id", "engineName": "mock engine name" } ] """.trimIndent() module.fetchRecommendations( JavaOnlyMap.of("id", "mock-id", "fillWithRandom", true), MockResolvingPromise { val resultArray = it.result as ReadableArray val resultList = (0 until resultArray.size()).map { i -> resultArray.getMap(i)!!.toHashMapRecursively() } assertEquals( ExponeaGson.instance.fromJson(expectedResponse, Object::class.java), ExponeaGson.instance.fromJson(ExponeaGson.instance.toJson(resultList), Object::class.java) ) } ) } }