Skip to content

Commit 6eeecca

Browse files
Add unit tests for Jitter strategies and context (#1675)
Jitter is currently covered only through the error handler tests, which exercise it indirectly and never reach its validation paths. Cover the three strategies directly, including the lower bound for HALF with odd timeouts and the minimum value of 1 that all of them enforce, along with Jitter.Context construction and its argument validation.
1 parent 15846c8 commit 6eeecca

2 files changed

Lines changed: 247 additions & 0 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.sqs.listener.errorhandler;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
20+
21+
import java.util.Random;
22+
import java.util.concurrent.ThreadLocalRandom;
23+
import java.util.function.Supplier;
24+
import org.junit.jupiter.api.Test;
25+
26+
/**
27+
* Tests for {@link Jitter.Context}.
28+
*
29+
* @author Tomaz Fernandes
30+
*/
31+
class JitterContextTest {
32+
33+
@Test
34+
void shouldCreateContextWithValidParameters() {
35+
// Given
36+
int timeout = 10;
37+
Supplier<Random> randomSupplier = ThreadLocalRandom::current;
38+
39+
// When
40+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
41+
42+
// Then
43+
assertThat(context.getTimeout()).isEqualTo(timeout);
44+
assertThat(context.getRandomSupplier()).isEqualTo(randomSupplier);
45+
}
46+
47+
@Test
48+
void shouldThrowExceptionWhenTimeoutIsZero() {
49+
// Given
50+
int timeout = 0;
51+
Supplier<Random> randomSupplier = ThreadLocalRandom::current;
52+
53+
// When & Then
54+
assertThatThrownBy(() -> new Jitter.Context(timeout, randomSupplier))
55+
.isInstanceOf(IllegalArgumentException.class).hasMessage("Timeout must be >= 1, but was 0");
56+
}
57+
58+
@Test
59+
void shouldThrowExceptionWhenTimeoutIsNegative() {
60+
// Given
61+
int timeout = -1;
62+
Supplier<Random> randomSupplier = ThreadLocalRandom::current;
63+
64+
// When & Then
65+
assertThatThrownBy(() -> new Jitter.Context(timeout, randomSupplier))
66+
.isInstanceOf(IllegalArgumentException.class).hasMessage("Timeout must be >= 1, but was -1");
67+
}
68+
69+
@Test
70+
@SuppressWarnings("null")
71+
void shouldThrowExceptionWhenRandomSupplierIsNull() {
72+
// Given
73+
int timeout = 10;
74+
Supplier<Random> nullSupplier = null;
75+
76+
// When & Then
77+
assertThatThrownBy(() -> new Jitter.Context(timeout, nullSupplier)).isInstanceOf(IllegalArgumentException.class)
78+
.hasMessage("Random supplier cannot be null");
79+
}
80+
81+
@Test
82+
void shouldAcceptMinimumValidTimeout() {
83+
// Given
84+
int timeout = 1;
85+
Supplier<Random> randomSupplier = ThreadLocalRandom::current;
86+
87+
// When
88+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
89+
90+
// Then
91+
assertThat(context.getTimeout()).isEqualTo(1);
92+
assertThat(context.getRandomSupplier()).isEqualTo(randomSupplier);
93+
}
94+
95+
@Test
96+
void shouldAcceptLargeTimeout() {
97+
// Given
98+
int timeout = Integer.MAX_VALUE;
99+
Supplier<Random> randomSupplier = ThreadLocalRandom::current;
100+
101+
// When
102+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
103+
104+
// Then
105+
assertThat(context.getTimeout()).isEqualTo(Integer.MAX_VALUE);
106+
assertThat(context.getRandomSupplier()).isEqualTo(randomSupplier);
107+
}
108+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.sqs.listener.errorhandler;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.Random;
21+
import java.util.concurrent.ThreadLocalRandom;
22+
import java.util.function.Supplier;
23+
import org.junit.jupiter.api.Test;
24+
25+
/**
26+
* Tests for {@link Jitter} strategies.
27+
*
28+
* @author Tomaz Fernandes
29+
*/
30+
class JitterStrategiesTest {
31+
32+
private final Supplier<Random> randomSupplier = ThreadLocalRandom::current;
33+
34+
@Test
35+
void shouldReturnOriginalTimeoutForNoneJitter() {
36+
// Given
37+
int timeout = 100;
38+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
39+
40+
// When
41+
int result = Jitter.NONE.applyJitter(context);
42+
43+
// Then
44+
assertThat(result).isEqualTo(timeout);
45+
}
46+
47+
@Test
48+
void shouldReturnValueBetweenZeroAndTimeoutForFullJitter() {
49+
// Given
50+
int timeout = 100;
51+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
52+
53+
// When
54+
int result = Jitter.FULL.applyJitter(context);
55+
56+
// Then
57+
assertThat(result).isGreaterThanOrEqualTo(1);
58+
assertThat(result).isLessThanOrEqualTo(timeout);
59+
}
60+
61+
@Test
62+
void shouldReturnValueBetweenHalfAndTimeoutForHalfJitter() {
63+
// Given
64+
int timeout = 100;
65+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
66+
67+
// When
68+
int result = Jitter.HALF.applyJitter(context);
69+
70+
// Then
71+
int expectedLowerBound = (int) Math.ceil(timeout / 2.0);
72+
assertThat(result).isGreaterThanOrEqualTo(expectedLowerBound);
73+
assertThat(result).isLessThanOrEqualTo(timeout);
74+
}
75+
76+
@Test
77+
void shouldHandleOddTimeoutForHalfJitter() {
78+
// Given
79+
int timeout = 101; // Odd number
80+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
81+
82+
// When
83+
int result = Jitter.HALF.applyJitter(context);
84+
85+
// Then
86+
int expectedLowerBound = (int) Math.ceil(timeout / 2.0); // Should be 51
87+
assertThat(result).isGreaterThanOrEqualTo(expectedLowerBound);
88+
assertThat(result).isLessThanOrEqualTo(timeout);
89+
}
90+
91+
@Test
92+
void shouldHandleMinimumTimeoutForAllStrategies() {
93+
// Given
94+
int timeout = 1;
95+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
96+
97+
// When & Then
98+
assertThat(Jitter.NONE.applyJitter(context)).isEqualTo(1);
99+
assertThat(Jitter.FULL.applyJitter(context)).isEqualTo(1);
100+
assertThat(Jitter.HALF.applyJitter(context)).isEqualTo(1);
101+
}
102+
103+
@Test
104+
void shouldEnsureMinimumValueOfOneForAllStrategies() {
105+
// Given
106+
int timeout = 2;
107+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
108+
109+
// When
110+
int noneResult = Jitter.NONE.applyJitter(context);
111+
int fullResult = Jitter.FULL.applyJitter(context);
112+
int halfResult = Jitter.HALF.applyJitter(context);
113+
114+
// Then
115+
assertThat(noneResult).isGreaterThanOrEqualTo(1);
116+
assertThat(fullResult).isGreaterThanOrEqualTo(1);
117+
assertThat(halfResult).isGreaterThanOrEqualTo(1);
118+
}
119+
120+
@Test
121+
void shouldProduceDifferentResultsForMultipleCalls() {
122+
// Given
123+
int timeout = 100;
124+
Jitter.Context context = new Jitter.Context(timeout, randomSupplier);
125+
126+
// When
127+
int result1 = Jitter.FULL.applyJitter(context);
128+
int result2 = Jitter.FULL.applyJitter(context);
129+
int result3 = Jitter.FULL.applyJitter(context);
130+
131+
// Then
132+
// While it's theoretically possible for all results to be the same,
133+
// it's extremely unlikely with a good random number generator
134+
// We'll just verify they're all within the expected range
135+
assertThat(result1).isBetween(1, timeout);
136+
assertThat(result2).isBetween(1, timeout);
137+
assertThat(result3).isBetween(1, timeout);
138+
}
139+
}

0 commit comments

Comments
 (0)