Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit bfee025

Browse files
authored
Merge pull request #1352 from wordpress-mobile/feature/file_downloads_endpoint
Stats - File downloads endpoint
2 parents fa2bc56 + ba3fd2e commit bfee025

File tree

13 files changed

+552
-38
lines changed

13 files changed

+552
-38
lines changed

example/src/androidTest/java/org/wordpress/android/fluxc/release/ReleaseStack_TimeStatsTestJetpack.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.wordpress.android.fluxc.store.SiteStore.SiteErrorType
2626
import org.wordpress.android.fluxc.store.stats.time.AuthorsStore
2727
import org.wordpress.android.fluxc.store.stats.time.ClicksStore
2828
import org.wordpress.android.fluxc.store.stats.time.CountryViewsStore
29+
import org.wordpress.android.fluxc.store.stats.time.FileDownloadsStore
2930
import org.wordpress.android.fluxc.store.stats.time.PostAndPageViewsStore
3031
import org.wordpress.android.fluxc.store.stats.time.ReferrersStore
3132
import org.wordpress.android.fluxc.store.stats.time.SearchTermsStore
@@ -55,6 +56,7 @@ class ReleaseStack_TimeStatsTestJetpack : ReleaseStack_Base() {
5556
@Inject lateinit var authorsStore: AuthorsStore
5657
@Inject lateinit var searchTermsStore: SearchTermsStore
5758
@Inject lateinit var videoPlaysStore: VideoPlaysStore
59+
@Inject lateinit var fileDownloadsStore: FileDownloadsStore
5860
@Inject internal lateinit var siteStore: SiteStore
5961
@Inject internal lateinit var accountStore: AccountStore
6062

@@ -265,6 +267,30 @@ class ReleaseStack_TimeStatsTestJetpack : ReleaseStack_Base() {
265267
}
266268
}
267269

270+
@Test
271+
fun testFetchFileDownloads() {
272+
val site = authenticate()
273+
274+
for (granularity in StatsGranularity.values()) {
275+
val fetchedInsights = runBlocking {
276+
fileDownloadsStore.fetchFileDownloads(
277+
site,
278+
granularity,
279+
LIMIT_MODE,
280+
SELECTED_DATE,
281+
true
282+
)
283+
}
284+
285+
assertNotNull(fetchedInsights)
286+
assertNotNull(fetchedInsights.model)
287+
288+
val insightsFromDb = fileDownloadsStore.getFileDownloads(site, granularity, LIMIT_MODE, SELECTED_DATE)
289+
290+
assertEquals(fetchedInsights.model, insightsFromDb)
291+
}
292+
}
293+
268294
private fun authenticate(): SiteModel {
269295
authenticateWPComAndFetchSites(
270296
BuildConfig.TEST_WPCOM_USERNAME_SINGLE_JETPACK_ONLY,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package org.wordpress.android.fluxc.network.rest.wpcom.stats.time
2+
3+
import com.android.volley.RequestQueue
4+
import com.android.volley.VolleyError
5+
import com.google.gson.Gson
6+
import com.google.gson.GsonBuilder
7+
import com.nhaarman.mockitokotlin2.KArgumentCaptor
8+
import com.nhaarman.mockitokotlin2.any
9+
import com.nhaarman.mockitokotlin2.argumentCaptor
10+
import com.nhaarman.mockitokotlin2.eq
11+
import com.nhaarman.mockitokotlin2.isNull
12+
import com.nhaarman.mockitokotlin2.mock
13+
import com.nhaarman.mockitokotlin2.whenever
14+
import org.assertj.core.api.Assertions.assertThat
15+
import org.junit.Before
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
import org.mockito.Mock
19+
import org.mockito.junit.MockitoJUnitRunner
20+
import org.wordpress.android.fluxc.Dispatcher
21+
import org.wordpress.android.fluxc.model.SiteModel
22+
import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError
23+
import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType.NETWORK_ERROR
24+
import org.wordpress.android.fluxc.network.UserAgent
25+
import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequest.WPComGsonNetworkError
26+
import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder
27+
import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response
28+
import org.wordpress.android.fluxc.network.rest.wpcom.WPComGsonRequestBuilder.Response.Success
29+
import org.wordpress.android.fluxc.network.rest.wpcom.auth.AccessToken
30+
import org.wordpress.android.fluxc.network.rest.wpcom.stats.time.FileDownloadsRestClient.FileDownloadsResponse
31+
import org.wordpress.android.fluxc.network.utils.StatsGranularity
32+
import org.wordpress.android.fluxc.network.utils.StatsGranularity.DAYS
33+
import org.wordpress.android.fluxc.network.utils.StatsGranularity.MONTHS
34+
import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS
35+
import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS
36+
import org.wordpress.android.fluxc.store.StatsStore.StatsErrorType.API_ERROR
37+
import org.wordpress.android.fluxc.test
38+
import java.util.Date
39+
40+
@RunWith(MockitoJUnitRunner::class)
41+
class FileDownloadsRestClientTest {
42+
@Mock private lateinit var dispatcher: Dispatcher
43+
@Mock private lateinit var wpComGsonRequestBuilder: WPComGsonRequestBuilder
44+
@Mock private lateinit var site: SiteModel
45+
@Mock private lateinit var requestQueue: RequestQueue
46+
@Mock private lateinit var accessToken: AccessToken
47+
@Mock private lateinit var userAgent: UserAgent
48+
@Mock private lateinit var statsUtils: StatsUtils
49+
private val gson: Gson = GsonBuilder().create()
50+
private lateinit var urlCaptor: KArgumentCaptor<String>
51+
private lateinit var paramsCaptor: KArgumentCaptor<Map<String, String>>
52+
private lateinit var restClient: FileDownloadsRestClient
53+
private val siteId: Long = 12
54+
private val pageSize = 5
55+
private val stringDate = "2018-10-10"
56+
private val requestedDate = Date(0)
57+
58+
@Before
59+
fun setUp() {
60+
urlCaptor = argumentCaptor()
61+
paramsCaptor = argumentCaptor()
62+
restClient = FileDownloadsRestClient(
63+
dispatcher,
64+
wpComGsonRequestBuilder,
65+
null,
66+
requestQueue,
67+
accessToken,
68+
userAgent,
69+
gson,
70+
statsUtils
71+
)
72+
whenever(statsUtils.getFormattedDate(eq(requestedDate), isNull())).thenReturn(stringDate)
73+
}
74+
75+
@Test
76+
fun `returns authors by day success response`() = test {
77+
testSuccessResponse(DAYS)
78+
}
79+
80+
@Test
81+
fun `returns authors by day error response`() = test {
82+
testErrorResponse(DAYS)
83+
}
84+
85+
@Test
86+
fun `returns authors by week success response`() = test {
87+
testSuccessResponse(WEEKS)
88+
}
89+
90+
@Test
91+
fun `returns authors by week error response`() = test {
92+
testErrorResponse(WEEKS)
93+
}
94+
95+
@Test
96+
fun `returns authors by month success response`() = test {
97+
testSuccessResponse(MONTHS)
98+
}
99+
100+
@Test
101+
fun `returns authors by month error response`() = test {
102+
testErrorResponse(MONTHS)
103+
}
104+
105+
@Test
106+
fun `returns authors by year success response`() = test {
107+
testSuccessResponse(YEARS)
108+
}
109+
110+
@Test
111+
fun `returns authors by year error response`() = test {
112+
testErrorResponse(YEARS)
113+
}
114+
115+
private suspend fun testSuccessResponse(period: StatsGranularity) {
116+
val response = mock<FileDownloadsResponse>()
117+
initFileDownloadsResponse(response)
118+
119+
val responseModel = restClient.fetchFileDownloads(site, period, requestedDate, pageSize, false)
120+
121+
assertThat(responseModel.response).isNotNull()
122+
assertThat(responseModel.response).isEqualTo(response)
123+
assertThat(urlCaptor.lastValue)
124+
.isEqualTo("https://public-api.wordpress.com/rest/v1.1/sites/12/stats/file-downloads/")
125+
assertThat(paramsCaptor.lastValue).isEqualTo(
126+
mapOf(
127+
"num" to pageSize.toString(),
128+
"period" to period.toString(),
129+
"date" to stringDate
130+
)
131+
)
132+
}
133+
134+
private suspend fun testErrorResponse(period: StatsGranularity) {
135+
val errorMessage = "message"
136+
initFileDownloadsResponse(
137+
error = WPComGsonNetworkError(
138+
BaseNetworkError(
139+
NETWORK_ERROR,
140+
errorMessage,
141+
VolleyError(errorMessage)
142+
)
143+
)
144+
)
145+
146+
val responseModel = restClient.fetchFileDownloads(site, period, requestedDate, pageSize, false)
147+
148+
assertThat(responseModel.error).isNotNull()
149+
assertThat(responseModel.error.type).isEqualTo(API_ERROR)
150+
assertThat(responseModel.error.message).isEqualTo(errorMessage)
151+
}
152+
153+
private suspend fun initFileDownloadsResponse(
154+
data: FileDownloadsResponse? = null,
155+
error: WPComGsonNetworkError? = null
156+
): Response<FileDownloadsResponse> {
157+
return initResponse(FileDownloadsResponse::class.java, data ?: mock(), error)
158+
}
159+
160+
private suspend fun <T> initResponse(
161+
kclass: Class<T>,
162+
data: T,
163+
error: WPComGsonNetworkError? = null,
164+
cachingEnabled: Boolean = false
165+
): Response<T> {
166+
val response = if (error != null) Response.Error<T>(error) else Success(data)
167+
whenever(
168+
wpComGsonRequestBuilder.syncGetRequest(
169+
eq(restClient),
170+
urlCaptor.capture(),
171+
paramsCaptor.capture(),
172+
eq(kclass),
173+
eq(cachingEnabled),
174+
any(),
175+
eq(false)
176+
)
177+
).thenReturn(response)
178+
whenever(site.siteId).thenReturn(siteId)
179+
return response
180+
}
181+
}

example/src/test/java/org/wordpress/android/fluxc/store/StatsStoreTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.wordpress.android.fluxc.store.StatsStore.InsightType.FOLLOWERS
2323
import org.wordpress.android.fluxc.store.StatsStore.InsightType.LATEST_POST_SUMMARY
2424
import org.wordpress.android.fluxc.store.StatsStore.InsightType.POSTING_ACTIVITY
2525
import org.wordpress.android.fluxc.store.StatsStore.ManagementType
26+
import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.FILE_DOWNLOADS
2627
import org.wordpress.android.fluxc.test
2728
import org.wordpress.android.fluxc.utils.PreferenceUtils.PreferenceUtilsWrapper
2829

@@ -227,4 +228,24 @@ class StatsStoreTest {
227228

228229
verify(statsSqlUtils).deleteSiteStats(site)
229230
}
231+
232+
@Test
233+
fun `filters out file downloads on Jetpack site`() = test {
234+
val site = SiteModel()
235+
site.setIsJetpackConnected(true)
236+
237+
val timeStatsTypes = store.getTimeStatsTypes(site)
238+
239+
assertThat(timeStatsTypes).doesNotContain(FILE_DOWNLOADS)
240+
}
241+
242+
@Test
243+
fun `does not filter out file downloads on non-Jetpack site`() = test {
244+
val site = SiteModel()
245+
site.setIsJetpackConnected(false)
246+
247+
val timeStatsTypes = store.getTimeStatsTypes(site)
248+
249+
assertThat(timeStatsTypes).contains(FILE_DOWNLOADS)
250+
}
230251
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package org.wordpress.android.fluxc.store.stats.time
2+
3+
import com.nhaarman.mockitokotlin2.any
4+
import com.nhaarman.mockitokotlin2.isNull
5+
import com.nhaarman.mockitokotlin2.mock
6+
import com.nhaarman.mockitokotlin2.never
7+
import com.nhaarman.mockitokotlin2.verify
8+
import com.nhaarman.mockitokotlin2.whenever
9+
import kotlinx.coroutines.Dispatchers.Unconfined
10+
import org.assertj.core.api.Assertions.assertThat
11+
import org.junit.Before
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
import org.mockito.Mock
15+
import org.mockito.junit.MockitoJUnitRunner
16+
import org.wordpress.android.fluxc.model.SiteModel
17+
import org.wordpress.android.fluxc.model.stats.LimitMode
18+
import org.wordpress.android.fluxc.model.stats.time.FileDownloadsModel
19+
import org.wordpress.android.fluxc.model.stats.time.TimeStatsMapper
20+
import org.wordpress.android.fluxc.network.rest.wpcom.stats.time.FileDownloadsRestClient
21+
import org.wordpress.android.fluxc.network.rest.wpcom.stats.time.FileDownloadsRestClient.FileDownloadsResponse
22+
import org.wordpress.android.fluxc.network.utils.StatsGranularity.DAYS
23+
import org.wordpress.android.fluxc.persistence.TimeStatsSqlUtils.FileDownloadsSqlUtils
24+
import org.wordpress.android.fluxc.store.StatsStore.FetchStatsPayload
25+
import org.wordpress.android.fluxc.store.StatsStore.StatsError
26+
import org.wordpress.android.fluxc.store.StatsStore.StatsErrorType.API_ERROR
27+
import org.wordpress.android.fluxc.test
28+
import java.util.Date
29+
import kotlin.test.assertEquals
30+
import kotlin.test.assertNotNull
31+
32+
private const val ITEMS_TO_LOAD = 8
33+
private val LIMIT_MODE = LimitMode.Top(ITEMS_TO_LOAD)
34+
private val DATE = Date(0)
35+
36+
@RunWith(MockitoJUnitRunner::class)
37+
class FileDownloadsStoreTest {
38+
@Mock lateinit var site: SiteModel
39+
@Mock lateinit var restClient: FileDownloadsRestClient
40+
@Mock lateinit var sqlUtils: FileDownloadsSqlUtils
41+
@Mock lateinit var mapper: TimeStatsMapper
42+
private lateinit var store: FileDownloadsStore
43+
@Before
44+
fun setUp() {
45+
store = FileDownloadsStore(
46+
restClient,
47+
sqlUtils,
48+
mapper,
49+
Unconfined
50+
)
51+
}
52+
53+
@Test
54+
fun `returns file downloads per site`() = test {
55+
val fetchInsightsPayload = FetchStatsPayload(
56+
FILE_DOWNLOADS_RESPONSE
57+
)
58+
val forced = true
59+
whenever(restClient.fetchFileDownloads(site, DAYS, DATE, ITEMS_TO_LOAD + 1, forced))
60+
.thenReturn(fetchInsightsPayload)
61+
val model = mock<FileDownloadsModel>()
62+
whenever(mapper.map(FILE_DOWNLOADS_RESPONSE, LIMIT_MODE)).thenReturn(model)
63+
64+
val responseModel = store.fetchFileDownloads(site, DAYS, LIMIT_MODE, DATE, forced)
65+
66+
assertThat(responseModel.model).isEqualTo(model)
67+
verify(sqlUtils).insert(site, FILE_DOWNLOADS_RESPONSE, DAYS, DATE, ITEMS_TO_LOAD)
68+
}
69+
70+
@Test
71+
fun `returns cached data per site`() = test {
72+
whenever(sqlUtils.hasFreshRequest(site, DAYS, DATE, ITEMS_TO_LOAD)).thenReturn(true)
73+
whenever(sqlUtils.select(site, DAYS, DATE)).thenReturn(FILE_DOWNLOADS_RESPONSE)
74+
val model = mock<FileDownloadsModel>()
75+
whenever(mapper.map(FILE_DOWNLOADS_RESPONSE, LIMIT_MODE)).thenReturn(model)
76+
77+
val forced = false
78+
val responseModel = store.fetchFileDownloads(site, DAYS, LIMIT_MODE, DATE, forced)
79+
80+
assertThat(responseModel.model).isEqualTo(model)
81+
assertThat(responseModel.cached).isTrue()
82+
verify(sqlUtils, never()).insert(any(), any(), any(), any<Date>(), isNull())
83+
}
84+
85+
@Test
86+
fun `returns error when file downloads call fail`() = test {
87+
val type = API_ERROR
88+
val message = "message"
89+
val errorPayload = FetchStatsPayload<FileDownloadsResponse>(StatsError(type, message))
90+
val forced = true
91+
whenever(restClient.fetchFileDownloads(site, DAYS, DATE, ITEMS_TO_LOAD + 1, forced)).thenReturn(errorPayload)
92+
93+
val responseModel = store.fetchFileDownloads(site, DAYS, LIMIT_MODE, DATE, forced)
94+
95+
assertNotNull(responseModel.error)
96+
val error = responseModel.error!!
97+
assertEquals(type, error.type)
98+
assertEquals(message, error.message)
99+
}
100+
101+
@Test
102+
fun `returns file downloads from db`() {
103+
whenever(sqlUtils.select(site, DAYS, DATE)).thenReturn(FILE_DOWNLOADS_RESPONSE)
104+
val model = mock<FileDownloadsModel>()
105+
whenever(mapper.map(FILE_DOWNLOADS_RESPONSE, LIMIT_MODE)).thenReturn(model)
106+
107+
val result = store.getFileDownloads(site, DAYS, LIMIT_MODE, DATE)
108+
109+
assertThat(result).isEqualTo(model)
110+
}
111+
}

0 commit comments

Comments
 (0)