Skip to content

Commit

Permalink
Merge pull request #81 from Parsely/api_connection_unit_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
wzieba authored Oct 23, 2023
2 parents 22a8241 + 0f21123 commit b2882f5
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 26 deletions.
1 change: 1 addition & 0 deletions parsely/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ dependencies {
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'org.assertj:assertj-core:3.24.2'
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
}

apply from: "${rootProject.projectDir}/publication.gradle"
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,24 @@

import android.os.AsyncTask;

import androidx.annotation.NonNull;

import java.io.OutputStream;
import java.net.URL;
import java.net.HttpURLConnection;

public class ParselyAPIConnection extends AsyncTask<String, Exception, HttpURLConnection> {
class ParselyAPIConnection extends AsyncTask<String, Exception, Void> {

@NonNull
private final ParselyTracker tracker;
private Exception exception;

public Exception exception;
ParselyAPIConnection(@NonNull ParselyTracker tracker) {
this.tracker = tracker;
}

@Override
protected HttpURLConnection doInBackground(String... data) {
protected Void doInBackground(String... data) {
HttpURLConnection connection = null;
try {
if (data.length == 1) { // non-batched (since no post data is included)
Expand All @@ -46,35 +54,23 @@ protected HttpURLConnection doInBackground(String... data) {

} catch (Exception ex) {
this.exception = ex;
return null;
}
return connection;
return null;
}

protected void onPostExecute(HttpURLConnection conn) {
@Override
protected void onPostExecute(Void result) {
if (this.exception != null) {
ParselyTracker.PLog("Pixel request exception");
ParselyTracker.PLog(this.exception.toString());
} else {
ParselyTracker.PLog("Pixel request success");

ParselyTracker instance = null;
try {
instance = ParselyTracker.sharedInstance();
} catch (NullPointerException ex) {
ParselyTracker.PLog("ParselyTracker is null");
}

if (instance != null) {
// only purge the queue if the request was successful
instance.eventQueue.clear();
instance.purgeStoredQueue();
// only purge the queue if the request was successful
tracker.purgeEventsQueue();

if (instance.queueSize() == 0 && instance.storedEventsCount() == 0) {
ParselyTracker.PLog("Event queue empty, flush timer cleared.");
instance.stopFlushTimer();
}
}
ParselyTracker.PLog("Event queue empty, flush timer cleared.");
tracker.stopFlushTimer();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,9 @@ private void sendBatchRequest(ArrayList<Map<String, Object>> events) {

if (isDebug) {
PLog("Debug mode on. Not sending to Parse.ly");
eventQueue.clear();
purgeStoredQueue();
purgeEventsQueue();
} else {
new ParselyAPIConnection().execute(ROOT_URL + "mobileproxy", JsonEncode(batchMap));
new ParselyAPIConnection(this).execute(ROOT_URL + "mobileproxy", JsonEncode(batchMap));
PLog("Requested %s", ROOT_URL);
}
PLog("POST Data %s", JsonEncode(batchMap));
Expand Down Expand Up @@ -518,10 +517,15 @@ private ArrayList<Map<String, Object>> getStoredQueue() {
return storedQueue;
}

void purgeEventsQueue() {
eventQueue.clear();
purgeStoredQueue();
}

/**
* Delete the stored queue from persistent storage.
*/
protected void purgeStoredQueue() {
private void purgeStoredQueue() {
persistObject(new ArrayList<Map<String, Object>>());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.parsely.parselyandroid

import androidx.test.core.app.ApplicationProvider
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.LooperMode
import org.robolectric.shadows.ShadowLooper.shadowMainLooper

@RunWith(RobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
class ParselyAPIConnectionTest {

private lateinit var sut: ParselyAPIConnection
private val mockServer = MockWebServer()
private val url = mockServer.url("").toString()
private val tracker = FakeTracker()

@Before
fun setUp() {
sut = ParselyAPIConnection(tracker)
}

@After
fun tearDown() {
mockServer.shutdown()
}

@Test
fun `given successful response, when making connection without any events, then make GET request`() {
// given
mockServer.enqueue(MockResponse().setResponseCode(200))

// when
sut.execute(url).get()
shadowMainLooper().idle();

// then
val request = mockServer.takeRequest()
assertThat(request).satisfies({
assertThat(it.method).isEqualTo("GET")
assertThat(it.failure).isNull()
})
}

@Test
fun `given successful response, when making connection with events, then make POST request with JSON Content-Type header`() {
// given
mockServer.enqueue(MockResponse().setResponseCode(200))

// when
sut.execute(url, pixelPayload).get()
shadowMainLooper().idle();

// then
assertThat(mockServer.takeRequest()).satisfies({
assertThat(it.method).isEqualTo("POST")
assertThat(it.headers["Content-Type"]).isEqualTo("application/json")
assertThat(it.body.readUtf8()).isEqualTo(pixelPayload)
})
}

@Test
fun `given successful response, when request is made, then purge events queue and stop flush timer`() {
// given
mockServer.enqueue(MockResponse().setResponseCode(200))
tracker.events.add(mapOf("idsite" to "example.com"))

// when
sut.execute(url).get()
shadowMainLooper().idle();

// then
assertThat(tracker.events).isEmpty()
assertThat(tracker.flushTimerStopped).isTrue
}

@Test
fun `given unsuccessful response, when request is made, then do not purge events queue and do not stop flush timer`() {
// given
mockServer.enqueue(MockResponse().setResponseCode(400))
val sampleEvents = mapOf("idsite" to "example.com")
tracker.events.add(sampleEvents)

// when
sut.execute(url).get()
shadowMainLooper().idle();

// then
assertThat(tracker.events).containsExactly(sampleEvents)
assertThat(tracker.flushTimerStopped).isFalse
}

companion object {
val pixelPayload: String =
this::class.java.getResource("pixel_payload.json")?.readText().orEmpty()
}

private class FakeTracker : ParselyTracker(
"siteId", 10, ApplicationProvider.getApplicationContext()
) {

var flushTimerStopped = false
val events = mutableListOf<Map<String, Any>>()

override fun purgeEventsQueue() {
events.clear()
}

override fun stopFlushTimer() {
flushTimerStopped = true
}
}
}
10 changes: 10 additions & 0 deletions parsely/src/test/resources/pixel_payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"events": [
{
"idsite": "example.com"
},
{
"idsite": "example2.com"
}
]
}

0 comments on commit b2882f5

Please sign in to comment.