Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSD - Stats Card - Handle Success Response #2244

Merged
merged 10 commits into from
Feb 18, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.mockito.junit.MockitoJUnitRunner
import org.wordpress.android.fluxc.Dispatcher
import org.wordpress.android.fluxc.UnitTestUtils
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.dashboard.CardModel
import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError
import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType
import org.wordpress.android.fluxc.network.UserAgent
Expand All @@ -31,6 +32,7 @@ import org.wordpress.android.fluxc.network.rest.wpcom.auth.AccessToken
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.CardsResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.PostResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.PostsResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.TodaysStatsResponse
import org.wordpress.android.fluxc.store.dashboard.CardsStore.CardsErrorType
import org.wordpress.android.fluxc.store.dashboard.CardsStore.CardsPayload
import org.wordpress.android.fluxc.test
Expand All @@ -39,8 +41,19 @@ import org.wordpress.android.fluxc.test

private const val DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss"

/* CARD TYPES */

private val CARD_TYPES = listOf(CardModel.Type.TODAYS_STATS, CardModel.Type.POSTS)

/* RESPONSE */

private val TODAYS_STATS_RESPONSE = TodaysStatsResponse(
views = 100,
visitors = 30,
likes = 50,
comments = 10
)

private val DRAFT_POST_RESPONSE_TWO = PostResponse(
id = 708,
title = "",
Expand Down Expand Up @@ -77,6 +90,7 @@ private val POSTS_RESPONSE = PostsResponse(
)

private val CARDS_RESPONSE = CardsResponse(
todaysStats = TODAYS_STATS_RESPONSE,
posts = POSTS_RESPONSE
)

Expand All @@ -90,13 +104,15 @@ class CardsRestClientTest {
@Mock private lateinit var site: SiteModel

private lateinit var urlCaptor: KArgumentCaptor<String>
private lateinit var paramsCaptor: KArgumentCaptor<Map<String, String>>
private lateinit var restClient: CardsRestClient

private val siteId: Long = 1

@Before
fun setUp() {
urlCaptor = argumentCaptor()
paramsCaptor = argumentCaptor()
restClient = CardsRestClient(
wpComGsonRequestBuilder,
dispatcher,
Expand All @@ -112,7 +128,7 @@ class CardsRestClientTest {
val json = UnitTestUtils.getStringFromResourceFile(javaClass, DASHBOARD_CARDS_JSON)
initFetchCards(data = getCardsResponseFromJsonString(json))

restClient.fetchCards(site)
restClient.fetchCards(site, CARD_TYPES)

assertEquals(urlCaptor.firstValue, "$API_SITE_PATH/${site.siteId}/$API_DASHBOARD_CARDS_PATH")
}
Expand All @@ -122,7 +138,7 @@ class CardsRestClientTest {
val json = UnitTestUtils.getStringFromResourceFile(javaClass, DASHBOARD_CARDS_JSON)
initFetchCards(data = getCardsResponseFromJsonString(json))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertSuccess(CARDS_RESPONSE, result)
}
Expand All @@ -131,7 +147,7 @@ class CardsRestClientTest {
fun `given timeout, when fetch cards gets triggered, then return cards timeout error`() = test {
initFetchCards(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.TIMEOUT)))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertError(CardsErrorType.TIMEOUT, result)
}
Expand All @@ -140,7 +156,7 @@ class CardsRestClientTest {
fun `given network error, when fetch cards gets triggered, then return cards api error`() = test {
initFetchCards(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.NETWORK_ERROR)))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertError(CardsErrorType.API_ERROR, result)
}
Expand All @@ -149,7 +165,7 @@ class CardsRestClientTest {
fun `given invalid response, when fetch cards gets triggered, then return cards invalid response error`() = test {
initFetchCards(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.INVALID_RESPONSE)))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertError(CardsErrorType.INVALID_RESPONSE, result)
}
Expand All @@ -158,7 +174,7 @@ class CardsRestClientTest {
fun `given not authenticated, when fetch cards gets triggered, then return cards auth required error`() = test {
initFetchCards(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.NOT_AUTHENTICATED)))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertError(CardsErrorType.AUTHORIZATION_REQUIRED, result)
}
Expand All @@ -167,7 +183,7 @@ class CardsRestClientTest {
fun `given unknown error, when fetch cards gets triggered, then return cards generic error`() = test {
initFetchCards(error = WPComGsonNetworkError(BaseNetworkError(GenericErrorType.UNKNOWN)))

val result = restClient.fetchCards(site)
val result = restClient.fetchCards(site, CARD_TYPES)

assertError(CardsErrorType.GENERIC_ERROR, result)
}
Expand All @@ -188,7 +204,7 @@ class CardsRestClientTest {
wpComGsonRequestBuilder.syncGetRequest(
eq(restClient),
urlCaptor.capture(),
eq(mapOf()),
paramsCaptor.capture(),
eq(CardsResponse::class.java),
eq(false),
any(),
Expand Down Expand Up @@ -226,7 +242,7 @@ class CardsRestClientTest {
companion object {
private const val API_BASE_PATH = "https://public-api.wordpress.com/wpcom/v2"
private const val API_SITE_PATH = "$API_BASE_PATH/sites"
private const val API_DASHBOARD_CARDS_PATH = "dashboard/cards/"
private const val API_DASHBOARD_CARDS_PATH = "dashboard/cards-data/"

private const val DASHBOARD_CARDS_JSON = "wp/dashboard/cards.json"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.dashboard.CardModel
import org.wordpress.android.fluxc.model.dashboard.CardModel.PostsCardModel
import org.wordpress.android.fluxc.model.dashboard.CardModel.PostsCardModel.PostCardModel
import org.wordpress.android.fluxc.model.dashboard.CardModel.TodaysStatsCardModel
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.CardsResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.PostResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.PostsResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsRestClient.TodaysStatsResponse
import org.wordpress.android.fluxc.network.rest.wpcom.dashboard.CardsUtils
import org.wordpress.android.fluxc.persistence.dashboard.CardsDao
import org.wordpress.android.fluxc.persistence.dashboard.CardsDao.CardEntity
Expand All @@ -34,6 +36,13 @@ import kotlin.test.assertNull

const val SITE_LOCAL_ID = 1

/* TODAY'S STATS */

const val TODAYS_STATS_VIEWS = 100
const val TODAYS_STATS_VISITORS = 30
const val TODAYS_STATS_LIKES = 50
const val TODAYS_STATS_COMMENTS = 10

/* POST */

const val POST_ID = 1
Expand All @@ -42,8 +51,19 @@ const val POST_CONTENT = "content"
const val POST_FEATURED_IMAGE = "featuredImage"
const val POST_DATE = "2021-12-27 11:33:55"

/* CARD TYPES */

private val CARD_TYPES = listOf(CardModel.Type.TODAYS_STATS, CardModel.Type.POSTS)

/* RESPONSE */

private val TODAYS_STATS_RESPONSE = TodaysStatsResponse(
views = TODAYS_STATS_VIEWS,
visitors = TODAYS_STATS_VISITORS,
likes = TODAYS_STATS_LIKES,
comments = TODAYS_STATS_COMMENTS
)

private val POST_RESPONSE = PostResponse(
id = POST_ID,
title = POST_TITLE,
Expand All @@ -59,10 +79,17 @@ private val POSTS_RESPONSE = PostsResponse(
)

private val CARDS_RESPONSE = CardsResponse(
todaysStats = TODAYS_STATS_RESPONSE,
posts = POSTS_RESPONSE
)

/* MODEL */
private val TODAYS_STATS_MODEL = TodaysStatsCardModel(
views = TODAYS_STATS_VIEWS,
visitors = TODAYS_STATS_VISITORS,
likes = TODAYS_STATS_LIKES,
comments = TODAYS_STATS_COMMENTS
)

private val POST_MODEL = PostCardModel(
id = POST_ID,
Expand All @@ -79,10 +106,17 @@ private val POSTS_MODEL = PostsCardModel(
)

private val CARDS_MODEL = listOf(
TODAYS_STATS_MODEL,
POSTS_MODEL
)

/* ENTITY */
private val TODAYS_STATS_ENTITY = CardEntity(
siteLocalId = SITE_LOCAL_ID,
type = CardModel.Type.TODAYS_STATS.name,
date = CardsUtils.getInsertDate(),
json = CardsUtils.GSON.toJson(TODAYS_STATS_MODEL)
)

private val POSTS_ENTITY = CardEntity(
siteLocalId = SITE_LOCAL_ID,
Expand All @@ -92,6 +126,7 @@ private val POSTS_ENTITY = CardEntity(
)

private val CARDS_ENTITY = listOf(
TODAYS_STATS_ENTITY,
POSTS_ENTITY
)

Expand All @@ -118,21 +153,41 @@ class CardsStoreTest {
}

@Test
fun `given cards response, when fetch cards gets triggered, then cards model is inserted into db`() = test {
fun `given all card types, when fetch cards triggered, then all cards model is inserted into db`() = test {
val payload = CardsPayload(CARDS_RESPONSE)
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

cardsStore.fetchCards(siteModel)
cardsStore.fetchCards(siteModel, CARD_TYPES)

verify(dao).insertWithDate(siteModel.id, CARDS_MODEL)
}

@Test
fun `given todays stats type, when fetch cards triggered, then today's stats card model inserted into db`() = test {
val payload = CardsPayload(CardsResponse(todaysStats = TODAYS_STATS_RESPONSE))
whenever(restClient.fetchCards(siteModel, listOf(CardModel.Type.TODAYS_STATS))).thenReturn(payload)

cardsStore.fetchCards(siteModel, listOf(CardModel.Type.TODAYS_STATS))

verify(dao).insertWithDate(siteModel.id, listOf(TODAYS_STATS_MODEL))
}

@Test
fun `given posts type, when fetch cards triggered, then post card model inserted into db`() = test {
val payload = CardsPayload(CardsResponse(posts = POSTS_RESPONSE))
whenever(restClient.fetchCards(siteModel, listOf(CardModel.Type.POSTS))).thenReturn(payload)

cardsStore.fetchCards(siteModel, listOf(CardModel.Type.POSTS))

verify(dao).insertWithDate(siteModel.id, listOf(POSTS_MODEL))
}

@Test
fun `given cards response, when fetch cards gets triggered, then empty cards model is returned`() = test {
val payload = CardsPayload(CARDS_RESPONSE)
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

val result = cardsStore.fetchCards(siteModel)
val result = cardsStore.fetchCards(siteModel, CARD_TYPES)

assertThat(result.model).isNull()
assertThat(result.error).isNull()
Expand All @@ -141,10 +196,10 @@ class CardsStoreTest {
@Test
fun `given card response with exception, when fetch cards gets triggered, then cards error is returned`() = test {
val payload = CardsPayload(CARDS_RESPONSE)
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)
whenever(dao.insertWithDate(siteModel.id, CARDS_MODEL)).thenThrow(IllegalStateException("Error"))

val result = cardsStore.fetchCards(siteModel)
val result = cardsStore.fetchCards(siteModel, CARD_TYPES)

assertThat(result.model).isNull()
assertEquals(CardsErrorType.GENERIC_ERROR, result.error.type)
Expand All @@ -155,9 +210,9 @@ class CardsStoreTest {
fun `given cards error, when fetch cards gets triggered, then cards error is returned`() = test {
val errorType = CardsErrorType.API_ERROR
val payload = CardsPayload<CardsResponse>(CardsError(errorType))
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

val result = cardsStore.fetchCards(siteModel)
val result = cardsStore.fetchCards(siteModel, CARD_TYPES)

assertThat(result.model).isNull()
assertEquals(errorType, result.error.type)
Expand All @@ -168,9 +223,9 @@ class CardsStoreTest {
fun `given authorization required, when fetch cards gets triggered, then db is cleared of cards model`() = test {
val errorType = CardsErrorType.AUTHORIZATION_REQUIRED
val payload = CardsPayload<CardsResponse>(CardsError(errorType))
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

cardsStore.fetchCards(siteModel)
cardsStore.fetchCards(siteModel, CARD_TYPES)

verify(dao).clear()
}
Expand All @@ -179,9 +234,9 @@ class CardsStoreTest {
fun `given authorization required, when fetch cards gets triggered, then empty cards model is returned`() = test {
val errorType = CardsErrorType.AUTHORIZATION_REQUIRED
val payload = CardsPayload<CardsResponse>(CardsError(errorType))
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

val result = cardsStore.fetchCards(siteModel)
val result = cardsStore.fetchCards(siteModel, CARD_TYPES)

assertThat(result.model).isNull()
assertThat(result.error).isNull()
Expand All @@ -190,9 +245,9 @@ class CardsStoreTest {
@Test
fun `given empty cards payload, when fetch cards gets triggered, then cards error is returned`() = test {
val payload = CardsPayload<CardsResponse>()
whenever(restClient.fetchCards(siteModel)).thenReturn(payload)
whenever(restClient.fetchCards(siteModel, CARD_TYPES)).thenReturn(payload)

val result = cardsStore.fetchCards(siteModel)
val result = cardsStore.fetchCards(siteModel, CARD_TYPES)

assertThat(result.model).isNull()
assertEquals(CardsErrorType.INVALID_RESPONSE, result.error.type)
Expand All @@ -201,10 +256,30 @@ class CardsStoreTest {

@Test
fun `when get cards gets triggered, then a flow of cards model is returned`() = test {
whenever(dao.get(SITE_LOCAL_ID)).thenReturn(flowOf(CARDS_ENTITY))
whenever(dao.get(SITE_LOCAL_ID, CARD_TYPES)).thenReturn(flowOf(CARDS_ENTITY))

val result = cardsStore.getCards(siteModel).single()
val result = cardsStore.getCards(siteModel, CARD_TYPES).single()

assertThat(result).isEqualTo(CardsResult(CARDS_MODEL))
}

@Test
fun `when get cards gets triggered for today's stats only, then a flow of today's stats card model is returned`() =
test {
whenever(dao.get(SITE_LOCAL_ID, listOf(CardModel.Type.TODAYS_STATS)))
.thenReturn(flowOf(listOf(TODAYS_STATS_ENTITY)))

val result = cardsStore.getCards(siteModel, listOf(CardModel.Type.TODAYS_STATS)).single()

assertThat(result).isEqualTo(CardsResult(listOf(TODAYS_STATS_MODEL)))
}

@Test
fun `when get cards gets triggered for posts only, then a flow of post card model is returned`() = test {
whenever(dao.get(SITE_LOCAL_ID, listOf(CardModel.Type.POSTS))).thenReturn(flowOf(listOf(POSTS_ENTITY)))

val result = cardsStore.getCards(siteModel, listOf(CardModel.Type.POSTS)).single()

assertThat(result).isEqualTo(CardsResult(listOf(POSTS_MODEL)))
}
}
6 changes: 6 additions & 0 deletions example/src/test/resources/wp/dashboard/cards.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"todays_stats": {
"views": 100,
"visitors": 30,
"likes": 50,
"comments": 10
},
"posts": {
"has_published": true,
"draft": [
Expand Down
2 changes: 1 addition & 1 deletion fluxc-processor/src/main/resources/wp-com-v2-endpoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/read/sites/$site#String/notification-subscriptions/$action#String

/sites/$site/dashboard/cards
/sites/$site/dashboard/cards-data

/sites/$site/activity
/sites/$site/activity/count/group
Expand Down
Loading