Skip to content

Commit

Permalink
add tests for failure and not returning network requests
Browse files Browse the repository at this point in the history
  • Loading branch information
vahidlazio committed Jul 31, 2023
1 parent 998eb85 commit b9f392a
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ enum class EventStatus {
data class ApplyInstance(
@Contextual
val time: Date,
val sent: EventStatus
val eventStatus: EventStatus
)

internal typealias FlagsAppliedMap =
Expand All @@ -67,8 +67,9 @@ class FlagApplierWithRetries(
onInitialised = {
val data: FlagsAppliedMap = mutableMapOf()
readFile(data)
setSendingEventsCreating(data)
data
// we consider all the sending events as not sent to not miss them
// there is a chance that they are sending status because the network response is dropped
changeSendingEventsToCreate(data)
},
onApply = { flagApplierInput, mutableData ->
val data = internalApply(
Expand Down Expand Up @@ -111,20 +112,6 @@ class FlagApplierWithRetries(
)
}

// we consider all the sending events as not sent to not miss them
// there is a chance that they are sending status because the network response is dropped
private fun setSendingEventsCreating(data: FlagsAppliedMap) {
data.entries.forEach {
it.value.mapValues { entry ->
if (entry.value.sent == EventStatus.SENDING) {
EventStatus.CREATED
} else {
entry.value.sent
}
}
}
}

init {
eventProcessor.start()
}
Expand Down Expand Up @@ -152,7 +139,7 @@ class FlagApplierWithRetries(
data.entries.forEach { (token, flagsForToken) ->
val appliedFlagsKeyed: List<AppliedFlag> = flagsForToken.entries
.filter { e ->
e.value.sent == EventStatus.CREATED
e.value.eventStatus == EventStatus.CREATED
}
.map { e -> AppliedFlag(e.key, e.value.time) }
// TODO chunk size 20 is an arbitrary value, replace with appropriate size
Expand Down Expand Up @@ -181,19 +168,31 @@ class FlagApplierWithRetries(
appliedFlag.forEach { applied ->
data[token]?.let { map ->
computeIfPresent(map, applied.flag) { _, v ->
v.copy(sent = eventStatus)
v.copy(eventStatus = eventStatus)
}
}
}
}

private fun changeSendingEventsToCreate(data: FlagsAppliedMap) = data.mapValues { entryValue ->
entryValue.value
.mapValues { entry ->
if (entry.value.eventStatus == EventStatus.SENDING) {
entry.value.copy(eventStatus = EventStatus.CREATED)
} else {
entry.value
}
}
.toMutableMap()
}.toMutableMap()

private fun writeToFile(
data: Map<String, MutableMap<String, ApplyInstance>>
) {
// All apply events have been sent for this token, don't add this token to the file
val toStoreData = data
.filter {
!it.value.values.all { applyInstance -> applyInstance.sent == EventStatus.SENT }
!it.value.values.all { applyInstance -> applyInstance.eventStatus == EventStatus.SENT }
}
file.writeText(json.encodeToString(toStoreData))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,23 @@ private const val cacheFileData = "{\n" +
" \"token1\": {\n" +
" \"fdema-kotlin-flag-0\": {\n" +
" \"time\": \"2023-06-26T11:55:33.443Z\",\n" +
" \"sent\": \"SENT\"\n" +
" \"eventStatus\": \"SENT\"\n" +
" }\n" +
" },\n" +
" \"token2\": {\n" +
" \"fdema-kotlin-flag-2\": {\n" +
" \"time\": \"2023-06-26T11:55:33.444Z\",\n" +
" \"sent\": \"SENT\"\n" +
" \"eventStatus\": \"SENT\"\n" +
" },\n" +
" \"fdema-kotlin-flag-3\": {\n" +
" \"time\": \"2023-06-26T11:55:33.445Z\",\n" +
" \"sent\": \"CREATED\"\n" +
" \"eventStatus\": \"CREATED\"\n" +
" }\n" +
" },\n" +
" \"token3\": {\n" +
" \"fdema-kotlin-flag-4\": {\n" +
" \"time\": \"2023-06-26T11:55:33.446Z\",\n" +
" \"sent\": \"CREATED\"\n" +
" \"eventStatus\": \"CREATED\"\n" +
" }\n" +
" }\n" +
"}\n"
Expand Down Expand Up @@ -251,7 +251,7 @@ internal class ConfidenceFeatureProviderTests {
advanceUntilIdle()
verify(mockClient, times(8)).apply(any(), eq("token1"))
val expectedStatus = json.decodeFromString<FlagsAppliedMap>(cacheFile.readText())["token1"]
?.get("fdema-kotlin-flag-1")?.sent
?.get("fdema-kotlin-flag-1")?.eventStatus
assertEquals(EventStatus.CREATED, expectedStatus)
whenever(mockClient.apply(any(), any())).thenReturn(Result.Success)

Expand Down Expand Up @@ -399,7 +399,7 @@ internal class ConfidenceFeatureProviderTests {
fun testApplyFromStoredCache() = runTest {
val cacheFile = File(mockContext.filesDir, APPLY_FILE_NAME)
cacheFile.writeText(
"{\"token1\":{\"fdema-kotlin-flag-1\":{\"time\":\"2023-06-26T11:55:33.184774Z\",\"sent\":\"CREATED\"}}}"
"{\"token1\":{\"fdema-kotlin-flag-1\":{\"time\":\"2023-06-26T11:55:33.184774Z\",\"eventStatus\":\"CREATED\"}}}"
)

val testDispatcher = UnconfinedTestDispatcher(testScheduler)
Expand Down Expand Up @@ -430,6 +430,113 @@ internal class ConfidenceFeatureProviderTests {
verify(mockClient, times(1)).apply(any(), eq("token1"))
}

@Test
fun testApplyFromStoredCacheSendingStatus() = runTest {
val cacheFile = File(mockContext.filesDir, APPLY_FILE_NAME)
cacheFile.writeText(
"{\"token1\":{\"fdema-kotlin-flag-1\":{\"time\":\"2023-06-26T11:55:33.184774Z\",\"eventStatus\":\"SENDING\"}}}"
)
whenever(mockClient.apply(any(), any())).thenReturn(Result.Success)
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
val confidenceFeatureProvider = ConfidenceFeatureProvider.create(
context = mockContext,
clientSecret = "",
cache = InMemoryCache(),
client = mockClient,
eventsPublisher = EventHandler.eventsPublisher(testDispatcher),
dispatcher = testDispatcher
)

whenever(mockClient.resolve(eq(listOf()), any())).thenReturn(
ResolveResponse.Resolved(
ResolveFlags(resolvedFlags, "token1")
)
)

val evaluationContext = ImmutableContext("foo")
confidenceFeatureProvider.initialize(evaluationContext)
advanceUntilIdle()

verify(mockClient, times(1)).resolve(any(), eq(evaluationContext))

confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "empty", evaluationContext)
advanceUntilIdle()
verify(mockClient, times(1)).apply(any(), eq("token1"))
}

@Test
fun testNotSendDuplicateWhileSending() = runTest {
val cacheFile = File(mockContext.filesDir, APPLY_FILE_NAME)
cacheFile.writeText(
"{\"token1\":{\"fdema-kotlin-flag-1\":{\"time\":\"2023-06-26T11:55:33.184774Z\",\"eventStatus\":\"CREATED\"}}}"
)
whenever(mockClient.apply(any(), any())).then { }
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
val confidenceFeatureProvider = ConfidenceFeatureProvider.create(
context = mockContext,
clientSecret = "",
cache = InMemoryCache(),
client = mockClient,
eventsPublisher = EventHandler.eventsPublisher(testDispatcher),
dispatcher = testDispatcher
)

whenever(mockClient.resolve(eq(listOf()), any())).thenReturn(
ResolveResponse.Resolved(
ResolveFlags(resolvedFlags, "token1")
)
)

val evaluationContext = ImmutableContext("foo")
confidenceFeatureProvider.initialize(evaluationContext)
advanceUntilIdle()

verify(mockClient, times(1)).resolve(any(), eq(evaluationContext))

confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "empty", evaluationContext)
advanceUntilIdle()
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.myboolean", "false", evaluationContext)
advanceUntilIdle()
verify(mockClient, times(1)).apply(any(), eq("token1"))
}

@Test
fun testDoSendAgainWhenNetworkRequestFailed() = runTest {
val cacheFile = File(mockContext.filesDir, APPLY_FILE_NAME)
cacheFile.writeText(
"{\"token1\":{\"fdema-kotlin-flag-1\":{\"time\":\"2023-06-26T11:55:33.184774Z\",\"eventStatus\":\"CREATED\"}}}"
)

whenever(mockClient.apply(any(), any())).thenReturn(Result.Failure)
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
val confidenceFeatureProvider = ConfidenceFeatureProvider.create(
context = mockContext,
clientSecret = "",
cache = InMemoryCache(),
client = mockClient,
eventsPublisher = EventHandler.eventsPublisher(testDispatcher),
dispatcher = testDispatcher
)

whenever(mockClient.resolve(eq(listOf()), any())).thenReturn(
ResolveResponse.Resolved(
ResolveFlags(resolvedFlags, "token1")
)
)

val evaluationContext = ImmutableContext("foo")
confidenceFeatureProvider.initialize(evaluationContext)
advanceUntilIdle()

verify(mockClient, times(1)).resolve(any(), eq(evaluationContext))

confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "empty", evaluationContext)
advanceUntilIdle()
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.myboolean", "false", evaluationContext)
advanceUntilIdle()
verify(mockClient, times(3)).apply(any(), eq("token1"))
}

@Test
fun testOnProcessBatchOnInitAndEval() = runTest {
val cacheFile = File(mockContext.filesDir, APPLY_FILE_NAME)
Expand Down Expand Up @@ -474,7 +581,7 @@ internal class ConfidenceFeatureProviderTests {
cacheFile.writeText(cacheFileData)
whenever(mockClient.apply(any(), any())).thenReturn(Result.Success)
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
val test = ConfidenceFeatureProvider.create(
ConfidenceFeatureProvider.create(
context = mockContext,
clientSecret = "",
cache = InMemoryCache(),
Expand Down

0 comments on commit b9f392a

Please sign in to comment.