-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* tests for failsafe * tests for withRetry * tests for failsafe * apply format * tests for failsafe * tests for multy instance execution
- Loading branch information
1 parent
5243658
commit caa35af
Showing
4 changed files
with
293 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
...lsar-core/src/test/kotlin/io/redpulsar/core/locks/excecutors/MultyInstanceExecutorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package io.redpulsar.core.locks.excecutors | ||
|
||
import TestTags | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import io.mockk.verify | ||
import io.redpulsar.core.locks.abstracts.Backend | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Tag | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.ValueSource | ||
import kotlin.random.Random | ||
import kotlin.time.Duration.Companion.milliseconds | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
@Tag(TestTags.UNIT) | ||
class MultyInstanceExecutorTest { | ||
private var backends: List<TestBackend> = emptyList() | ||
private lateinit var scope: CoroutineScope | ||
|
||
@BeforeEach | ||
fun setUp() { | ||
backends = createBackends(1) | ||
scope = CoroutineScope(Dispatchers.Default) | ||
} | ||
|
||
@ParameterizedTest(name = "all {0} instances are ok") | ||
@ValueSource(ints = [1, 2, 3, 4, 5, 7, 10]) | ||
fun `all instances are ok`(number: Int) { | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns "OK" } | ||
|
||
val result = | ||
multyInstanceExecute( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
waiter = ::waitAllJobs, | ||
) { backend -> | ||
backend.test() | ||
} | ||
|
||
assertEquals(createResponses(number), result) | ||
verify(exactly = 1) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
@ParameterizedTest(name = "quorum instance count is down {0} instances") | ||
@ValueSource(ints = [2, 3, 4, 5, 7, 10]) | ||
fun `quorum instance count is down`(number: Int) { | ||
val quorum = number / 2 + 1 | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns "OK" } | ||
repeat(quorum) { i -> | ||
every { backends[i].test() } returns null | ||
} | ||
|
||
val result = | ||
multyInstanceExecute( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
waiter = ::waitAllJobs, | ||
) { backend -> | ||
backend.test() | ||
} | ||
|
||
assertEquals(emptyList<String>(), result) | ||
verify(exactly = 1) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
@ParameterizedTest(name = "non quorum instance count is down {0} instances") | ||
@ValueSource(ints = [2, 3, 4, 5, 7, 10]) | ||
fun `non quorum instance count is down`(number: Int) { | ||
val quorum = number / 2 + 1 | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns "OK" } | ||
repeat(number - quorum) { i -> | ||
every { backends[i].test() } returns null | ||
} | ||
|
||
val result = | ||
multyInstanceExecute( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
waiter = ::waitAllJobs, | ||
) { backend -> | ||
backend.test() | ||
} | ||
|
||
assertEquals(createResponses(quorum), result) | ||
verify(exactly = 1) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
@ParameterizedTest(name = "all instances are ok, wait any with {0} instances") | ||
@ValueSource(ints = [1, 2, 3, 4, 5, 7, 10]) | ||
fun `all instances are ok, wait any`(number: Int) { | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns "OK" } | ||
|
||
val result = | ||
multyInstanceExecute( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
waiter = ::waitAnyJobs, | ||
) { backend -> backend.test() } | ||
|
||
assertEquals(createResponses(number), result) | ||
verify(exactly = 1) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
@ParameterizedTest(name = "all instances are ok, wait any with {0} instances") | ||
@ValueSource(ints = [1, 2, 3, 4, 5, 7, 10]) | ||
fun `one instance is up, wait any`(number: Int) { | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns null } | ||
every { backends[Random.nextInt(0, number)].test() } returns "OK" | ||
|
||
val result = | ||
multyInstanceExecute( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
waiter = ::waitAnyJobs, | ||
) { backend -> backend.test() } | ||
|
||
assertEquals(createResponses(number), result) | ||
verify(exactly = 1) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
@ParameterizedTest(name = "retry on non quorum instance count is down {0} instances") | ||
@ValueSource(ints = [2, 3, 4, 5, 7, 10]) | ||
fun `retry on non quorum instance count is down`(number: Int) { | ||
val quorum = number / 2 + 1 | ||
backends = createBackends(number) | ||
backends.forEach { backend -> every { backend.test() } returns "OK" } | ||
repeat(quorum) { i -> | ||
every { backends[i].test() } returns null | ||
} | ||
|
||
val result = | ||
multyInstanceExecuteWithRetry( | ||
backends = backends, | ||
scope = scope, | ||
releaseTime = 1.seconds, | ||
retryCount = 3, | ||
retryDelay = 1.milliseconds, | ||
waiter = ::waitAllJobs, | ||
) { backend -> | ||
backend.test() | ||
} | ||
|
||
assertEquals(emptyList<String>(), result) | ||
verify(exactly = 3) { backends.forEach { backend -> backend.test() } } | ||
} | ||
|
||
private abstract inner class TestBackend : Backend() { | ||
abstract fun test(): String? | ||
} | ||
|
||
private fun createBackends(number: Int): List<TestBackend> { | ||
val backends = mutableListOf<TestBackend>() | ||
repeat(number) { | ||
backends.add(mockk<TestBackend>()) | ||
} | ||
return backends | ||
} | ||
|
||
private fun createResponses(number: Int): List<String> { | ||
val responses = mutableListOf<String>() | ||
repeat(number) { | ||
responses.add("OK") | ||
} | ||
return responses | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
redpulsar-core/src/test/kotlin/io/redpulsar/core/utils/FailsafeTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package io.redpulsar.core.utils | ||
|
||
import io.mockk.mockk | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.Tag | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertDoesNotThrow | ||
import org.junit.jupiter.api.assertThrows | ||
|
||
@Tag(TestTags.UNIT) | ||
class FailsafeTest { | ||
@Test | ||
fun `returned value correctly`() { | ||
val returnValue = | ||
failsafe(0) { | ||
"OK" | ||
} | ||
assertEquals("OK", returnValue) | ||
} | ||
|
||
@Test | ||
fun `supress top level exception`() { | ||
assertDoesNotThrow { | ||
failsafe(0) { | ||
throw Exception("test") | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
fun `throwable is not captured`() { | ||
assertThrows<Throwable> { | ||
failsafe(0) { | ||
throw mockk<Throwable>() | ||
} | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
redpulsar-core/src/test/kotlin/io/redpulsar/core/utils/WithRetryTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package io.redpulsar.core.utils | ||
|
||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.Assertions.assertTrue | ||
import org.junit.jupiter.api.Tag | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.ValueSource | ||
import kotlin.system.measureTimeMillis | ||
import kotlin.time.Duration.Companion.microseconds | ||
import kotlin.time.Duration.Companion.milliseconds | ||
|
||
@Tag(TestTags.UNIT) | ||
class WithRetryTest { | ||
@Test | ||
fun `doesn't retry`() { | ||
var counter = 0 | ||
val returnVal = | ||
withRetry(3, 1.milliseconds) { | ||
counter++ | ||
listOf("OK") | ||
} | ||
|
||
assertEquals(1, counter) | ||
assertEquals(listOf("OK"), returnVal) | ||
} | ||
|
||
@ParameterizedTest(name = "retry count with ttl - {0}") | ||
@ValueSource(ints = [-123, -1, 0, 1, 2, 5, 10, 11, 12, 20, 40]) | ||
fun `check retry count`(withCount: Int) { | ||
var counter = 0 | ||
val returnVal = | ||
withRetry(withCount, 1.microseconds) { | ||
counter++ | ||
emptyList<Int>() | ||
} | ||
if (withCount > 0) { | ||
assertEquals(withCount, counter) | ||
} else { | ||
assertEquals(1, counter) | ||
} | ||
assertEquals(emptyList<Int>(), returnVal) | ||
} | ||
|
||
@Test | ||
fun `retry with negative delay is ignored`() { | ||
var counter = 0 | ||
val returnVal = | ||
withRetry(3, (-1).milliseconds) { | ||
counter++ | ||
emptyList<Int>() | ||
} | ||
assertEquals(3, counter) | ||
assertEquals(emptyList<Int>(), returnVal) | ||
} | ||
|
||
@Test | ||
fun `check exponential delay`() { | ||
var counter = 0 | ||
val time = | ||
measureTimeMillis { | ||
val returnVal = | ||
withRetry(4, 50.milliseconds) { | ||
counter++ | ||
emptyList<Int>() | ||
} | ||
assertEquals(emptyList<Int>(), returnVal) | ||
} | ||
|
||
assertEquals(4, counter) | ||
// 50 + 100 + 200 + 400 = 750, 45 - is allowed clock error | ||
assertTrue(time in 750 - 45..750 + 45) | ||
} | ||
} |