Updates WeightCalculatorWebFilter to be more testable.

Since Random can't be mocked in jdk17+, replace the settable Random with Supplier<Double>. Production should still use ThreadLoacalRandom unless the supplier is set.
This commit is contained in:
sgibb
2023-06-23 23:36:03 -04:00
parent e17509648f
commit fba558aade
4 changed files with 34 additions and 34 deletions

View File

@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -64,7 +65,7 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
private final ConfigurationService configurationService;
private Random random = null;
private Supplier<Double> randomSupplier = null;
private int order = WEIGHT_CALC_FILTER_ORDER;
@@ -98,8 +99,13 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
this.order = order;
}
@Deprecated
public void setRandom(Random random) {
this.random = random;
this.randomSupplier = random::nextDouble;
}
public void setRandomSupplier(Supplier<Double> randomSupplier) {
this.randomSupplier = randomSupplier;
}
@Override
@@ -236,9 +242,13 @@ public class WeightCalculatorWebFilter implements WebFilter, Ordered, SmartAppli
* Usually, multiple threads accessing the same random object will have some
* performance problems, so we can use ThreadLocalRandom by default
*/
Random useRandom = this.random;
useRandom = useRandom == null ? ThreadLocalRandom.current() : useRandom;
double r = useRandom.nextDouble();
double r;
if (this.randomSupplier != null) {
r = randomSupplier.get();
}
else {
r = ThreadLocalRandom.current().nextDouble();
}
List<Double> ranges = config.ranges;

View File

@@ -19,11 +19,9 @@ package org.springframework.cloud.gateway.filter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.springframework.cloud.gateway.event.PredicateArgsEvent;
import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter.GroupWeightConfig;
@@ -95,20 +93,18 @@ public class WeightCalculatorWebFilterTests {
return "route" + i;
}
// TODO: modify implementation for testability on JDK17 for Spring 6
@Test
@DisabledForJreRange(min = JRE.JAVA_17)
public void testChooseRouteWithRandom() {
WeightCalculatorWebFilter filter = createFilter();
filter.addWeightConfig(new WeightConfig("groupa", "route1", 1));
filter.addWeightConfig(new WeightConfig("groupa", "route2", 3));
filter.addWeightConfig(new WeightConfig("groupa", "route3", 6));
Random random = mock(Random.class);
Supplier<Double> random = mock(Supplier.class);
when(random.nextDouble()).thenReturn(0.05).thenReturn(0.2).thenReturn(0.6);
when(random.get()).thenReturn(0.05).thenReturn(0.2).thenReturn(0.6);
filter.setRandom(random);
filter.setRandomSupplier(random);
MockServerWebExchange exchange = MockServerWebExchange
.from(MockServerHttpRequest.get("http://localhost").build());

View File

@@ -16,12 +16,10 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.Random;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -45,21 +43,20 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
@DisabledForJreRange(min = JRE.JAVA_17)
public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTests {
@Autowired
private WeightCalculatorWebFilter filter;
private static Random getRandom(double value) {
Random random = mock(Random.class);
when(random.nextDouble()).thenReturn(value);
private static Supplier<Double> getRandom(double value) {
Supplier<Double> random = mock(Supplier.class);
when(random.get()).thenReturn(value);
return random;
}
@Test
public void highWeight() {
filter.setRandom(getRandom(0.9));
filter.setRandomSupplier(getRandom(0.9));
testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weighthigh.org").exchange().expectStatus().isOk()
.expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_high_test");
@@ -67,7 +64,7 @@ public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTe
@Test
public void lowWeight() {
filter.setRandom(getRandom(0.1));
filter.setRandomSupplier(getRandom(0.1));
testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weightlow.org").exchange().expectStatus().isOk()
.expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_low_test");
@@ -89,9 +86,9 @@ public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTe
private String uri;
public TestConfig(WeightCalculatorWebFilter filter) {
Random random = getRandom(0.4);
Supplier<Double> random = getRandom(0.4);
filter.setRandom(random);
filter.setRandomSupplier(random);
}
@Bean

View File

@@ -16,11 +16,9 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.Random;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
@@ -40,21 +38,20 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("weights-404")
@DirtiesContext
@DisabledForJreRange(min = JRE.JAVA_17)
class WeightRoutePredicateFactoryYaml404Tests extends BaseWebClientTests {
@Autowired
private WeightCalculatorWebFilter filter;
private static Random getRandom(double value) {
Random random = mock(Random.class);
when(random.nextDouble()).thenReturn(value);
private static Supplier<Double> getRandom(double value) {
Supplier<Double> random = mock(Supplier.class);
when(random.get()).thenReturn(value);
return random;
}
@Test
void weightsFromYamlNot404() {
filter.setRandom(getRandom(0.5));
filter.setRandomSupplier(getRandom(0.5));
testClient.get().uri("/get").header(HttpHeaders.HOST, "www.weight4041.org").exchange().expectStatus().isOk()
.expectHeader().valueEquals(ROUTE_ID_HEADER, "weight_first_404_test_1");
@@ -66,9 +63,9 @@ class WeightRoutePredicateFactoryYaml404Tests extends BaseWebClientTests {
static class TestConfig {
TestConfig(WeightCalculatorWebFilter filter) {
Random random = getRandom(0.4);
Supplier<Double> random = getRandom(0.4);
filter.setRandom(random);
filter.setRandomSupplier(random);
}
}