package io.github.resilience4j.retry;
import io.vavr.collection.List;
import io.vavr.control.Try;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.time.Duration;
public class IntervalFunctionTest {
@Test
public void shouldRejectNonPositiveDuration() {
// Given
final Duration negativeDuration = Duration.ofMillis(0);
final Duration zeroDuration = Duration.ofMillis(0);
final Duration smallDuration = Duration.ofMillis(9);
final long negativeInterval = -1;
final long zeroInterval = 0;
final long smallInterval = 9;
// When
List<Try> tries = List.of(
Try.of(() -> IntervalFunction.of(negativeDuration)),
Try.of(() -> IntervalFunction.of(zeroDuration)),
Try.of(() -> IntervalFunction.of(smallDuration)),
Try.of(() -> IntervalFunction.of(negativeInterval)),
Try.of(() -> IntervalFunction.of(zeroInterval)),
Try.of(() -> IntervalFunction.of(smallInterval))
);
// Then
Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue();
Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue();
}
@Test
public void shouldPassPositiveDuration() {
// Given
final List<Long> positiveIntervals = List.of(10L, 99L, 981L);
final List<Duration> positiveDurations = positiveIntervals.map(Duration::ofMillis);
// When
positiveDurations.forEach(IntervalFunction::of);
positiveIntervals.forEach(IntervalFunction::of);
positiveDurations.forEach(IntervalFunction::ofRandomized);
positiveIntervals.forEach(IntervalFunction::ofRandomized);
positiveDurations.forEach(IntervalFunction::ofExponentialBackoff);
positiveIntervals.forEach(IntervalFunction::ofExponentialBackoff);
positiveDurations.forEach(IntervalFunction::ofExponentialRandomBackoff);
positiveIntervals.forEach(IntervalFunction::ofExponentialRandomBackoff);
// Then should pass
}
@Test
public void shouldRejectAttemptLessThenOne() {
// Given
final List<IntervalFunction> fns = List.of(
IntervalFunction.ofDefaults(),
IntervalFunction.ofRandomized(),
IntervalFunction.ofExponentialBackoff(),
IntervalFunction.ofExponentialRandomBackoff()
);
// When
final List<Try> tries = fns.map(fn -> Try.of(() -> fn.apply(0)));
// Then
Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue();
Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue();
}
@Test
public void shouldPassAttemptGreaterThenZero() {
// Given
final List<IntervalFunction> fns = List.of(
IntervalFunction.ofDefaults(),
IntervalFunction.ofRandomized(),
IntervalFunction.ofExponentialBackoff(),
IntervalFunction.ofExponentialRandomBackoff()
);
// When
final List<Try> tries1 = fns.map(fn -> Try.of(() -> fn.apply(1)));
final List<Try> tries2 = fns.map(fn -> Try.of(() -> fn.apply(2)));
// Then
Assertions.assertThat(tries1.forAll(Try::isFailure)).isFalse();
Assertions.assertThat(tries2.forAll(Try::isFailure)).isFalse();
}
@Test
public void shouldRejectOutOfBoundsRandomizationFactor() {
// Given
final Duration duration = Duration.ofMillis(100);
final float negativeFactor = -0.0001f;
final float greaterThanOneFactor = 1.0001f;
// When
final List<Try> tries = List.of(
Try.of(() -> IntervalFunction.ofRandomized(duration, negativeFactor)),
Try.of(() -> IntervalFunction.ofRandomized(duration, greaterThanOneFactor))
);
// Then
Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue();
Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue();
}
@Test
public void shouldPassPositiveRandomizationFactor() {
// Given
final Duration duration = Duration.ofMillis(100);
final float multiplier = 1.5f;
final List<Float> correctFactors = List.of(0.0f, 0.25f, 0.5f, 0.75f, 0.1f);
// When
correctFactors.forEach(v -> IntervalFunction.ofRandomized(duration, v));
correctFactors.forEach(v -> IntervalFunction.ofExponentialRandomBackoff(duration, multiplier, v));
}
@Test
public void shouldRejectOutOfBoundsMultiplier() {
// Given
final Duration duration = Duration.ofMillis(100);
final float lessThenOneMultiplier = 0.9999f;
// When
final List<Try> tries = List.of(
Try.of(() -> IntervalFunction.ofExponentialBackoff(duration, lessThenOneMultiplier)),
Try.of(() -> IntervalFunction.ofExponentialRandomBackoff(duration, lessThenOneMultiplier))
);
// Then
Assertions.assertThat(tries.forAll(Try::isFailure)).isTrue();
Assertions.assertThat(tries.map(Try::getCause).forAll(t -> t instanceof IllegalArgumentException)).isTrue();
}
@Test
public void shouldPassPositiveMultiplier() {
// Given
final Duration duration = Duration.ofMillis(100);
final float greaterThanOneMultiplier = 1.0001f;
// When
IntervalFunction.ofExponentialBackoff(duration, greaterThanOneMultiplier);
IntervalFunction.ofExponentialRandomBackoff(duration, greaterThanOneMultiplier);
}
@Test
public void generatesRandomizedIntervals() {
final IntervalFunction f = IntervalFunction.ofRandomized(100, 0.5);
for (int i = 1; i < 50; i++) {
//When
final long delay = f.apply(i);
// Then
Assertions.assertThat(delay).isGreaterThanOrEqualTo(50).isLessThanOrEqualTo(150);
}
}
@Test
public void generatesExponentialIntervals() {
final IntervalFunction f = IntervalFunction.ofExponentialBackoff(100, 1.5);
long prevV = f.apply(1);
for (int i = 2; i < 50; i++) {
//When
final long v = f.apply(i);
// Then
Assertions.assertThat(v).isGreaterThan(prevV);
prevV = v;
}
}
@Test
public void generatesExponentialRandomIntervals() {
final IntervalFunction f = IntervalFunction.ofExponentialRandomBackoff(100, 1.5, 0.5);
long expectedV = 100;
for (int i = 1; i < 50; i++) {
//When
final long v = f.apply(i);
// Then
Assertions.assertThat(v)
.isGreaterThanOrEqualTo( (long)(expectedV * 0.5) - 1)
.isLessThanOrEqualTo((long)(expectedV * 1.5) + 1);
expectedV = (long) (expectedV * 1.5);
}
}
}