Skip to content

Commit 098ccfd

Browse files
test(gateway): add integration test for dynamic routing with failover
1 parent 48d2b05 commit 098ccfd

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed

gravitee-apim-integration-tests/src/test/java/io/gravitee/apim/integration/tests/http/failover/FailoverV4IntegrationTest.java

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static org.mockito.ArgumentMatchers.eq;
3030
import static org.mockito.Mockito.when;
3131

32+
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
3233
import com.github.tomakehurst.wiremock.stubbing.Scenario;
3334
import com.graviteesource.entrypoint.http.get.HttpGetEntrypointConnectorFactory;
3435
import com.graviteesource.reactor.message.MessageApiReactorFactory;
@@ -63,6 +64,8 @@
6364
import io.gravitee.policy.apikey.ApiKeyPolicy;
6465
import io.gravitee.policy.apikey.ApiKeyPolicyInitializer;
6566
import io.gravitee.policy.apikey.configuration.ApiKeyPolicyConfiguration;
67+
import io.gravitee.policy.dynamicrouting.DynamicRoutingPolicy;
68+
import io.gravitee.policy.dynamicrouting.configuration.DynamicRoutingPolicyConfiguration;
6669
import io.gravitee.reporter.api.v4.metric.Metrics;
6770
import io.reactivex.rxjava3.annotations.NonNull;
6871
import io.reactivex.rxjava3.core.Flowable;
@@ -1087,6 +1090,114 @@ void should_success_on_second_retry(HttpClient client) {
10871090
}
10881091
}
10891092

1093+
@Nested
1094+
@GatewayTest
1095+
class DynamicRoutingGroupCaptureWithFailover extends AbstractGatewayTest {
1096+
1097+
protected final int wiremockPort = getAvailablePort();
1098+
1099+
@Override
1100+
protected void configureWireMock(WireMockConfiguration configuration) {
1101+
configuration.port(wiremockPort);
1102+
}
1103+
1104+
@Override
1105+
public void configurePolicies(Map<String, PolicyPlugin> policies) {
1106+
policies.put(
1107+
"dynamic-routing",
1108+
PolicyBuilder.build("dynamic-routing", DynamicRoutingPolicy.class, DynamicRoutingPolicyConfiguration.class)
1109+
);
1110+
}
1111+
1112+
@Override
1113+
public void configureEntrypoints(Map<String, EntrypointConnectorPlugin<?, ?>> entrypoints) {
1114+
entrypoints.putIfAbsent("http-proxy", EntrypointBuilder.build("http-proxy", HttpProxyEntrypointConnectorFactory.class));
1115+
}
1116+
1117+
@Override
1118+
public void configureEndpoints(Map<String, EndpointConnectorPlugin<?, ?>> endpoints) {
1119+
endpoints.putIfAbsent("http-proxy", EndpointBuilder.build("http-proxy", HttpProxyEndpointConnectorFactory.class));
1120+
}
1121+
1122+
@Override
1123+
public void configureApi(ReactableApi<?> api, Class<?> definitionClass) {
1124+
if (isLegacyApi(definitionClass)) {
1125+
throw new IllegalStateException("should be testing a v4 API");
1126+
}
1127+
final Api definition = (Api) api.getDefinition();
1128+
definition
1129+
.getEndpointGroups()
1130+
.stream()
1131+
.filter(group -> group.getName().equals("second"))
1132+
.flatMap(group -> group.getEndpoints().stream())
1133+
.forEach(endpoint ->
1134+
endpoint.setConfiguration(endpoint.getConfiguration().replace("8080", Integer.toString(wiremockPort)))
1135+
);
1136+
var manager = applicationContext.getBean(ApiManager.class);
1137+
manager.unregister(api.getId());
1138+
manager.register(api);
1139+
}
1140+
1141+
@Test
1142+
@DeployApi("/apis/v4/http/failover/api-failover-dynamic-routing-group-capture.json")
1143+
void should_route_to_second_group_when_group_capture_produces_double_slash(HttpClient client) {
1144+
wiremock.stubFor(get("/second-group//hello").willReturn(ok("ok from backend")));
1145+
1146+
client
1147+
.rxRequest(HttpMethod.GET, "/test/plan/hello")
1148+
.flatMap(HttpClientRequest::rxSend)
1149+
.flatMap(response -> {
1150+
assertThat(response.statusCode()).isEqualTo(200);
1151+
return response.body();
1152+
})
1153+
.test()
1154+
.awaitDone(30, TimeUnit.SECONDS)
1155+
.assertComplete()
1156+
.assertValue(response -> {
1157+
assertThat(response).hasToString("ok from backend");
1158+
return true;
1159+
});
1160+
1161+
wiremock.verify(getRequestedFor(urlPathEqualTo("/second-group//hello")));
1162+
}
1163+
1164+
@Test
1165+
@DeployApi("/apis/v4/http/failover/api-failover-dynamic-routing-group-capture.json")
1166+
void should_retry_to_second_group_on_slow_first_call(HttpClient client) {
1167+
wiremock.stubFor(
1168+
get("/second-group//hello")
1169+
.inScenario("Slow first call")
1170+
.whenScenarioStateIs(Scenario.STARTED)
1171+
.willReturn(ok("ok from backend").withFixedDelay(750))
1172+
.willSetStateTo("First Retry")
1173+
);
1174+
1175+
wiremock.stubFor(
1176+
get("/second-group//hello")
1177+
.inScenario("Slow first call")
1178+
.whenScenarioStateIs("First Retry")
1179+
.willReturn(ok("ok from backend"))
1180+
);
1181+
1182+
client
1183+
.rxRequest(HttpMethod.GET, "/test/plan/hello")
1184+
.flatMap(HttpClientRequest::rxSend)
1185+
.flatMap(response -> {
1186+
assertThat(response.statusCode()).isEqualTo(200);
1187+
return response.body();
1188+
})
1189+
.test()
1190+
.awaitDone(30, TimeUnit.SECONDS)
1191+
.assertComplete()
1192+
.assertValue(response -> {
1193+
assertThat(response).hasToString("ok from backend");
1194+
return true;
1195+
});
1196+
1197+
wiremock.verify(2, getRequestedFor(urlPathEqualTo("/second-group//hello")));
1198+
}
1199+
}
1200+
10901201
@NonNull
10911202
private static Flowable<JsonObject> extractPlainTextMessages(Buffer body) {
10921203
final List<JsonObject> messages = new ArrayList<>();
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"id": "failover-dynamic-routing-group-capture",
3+
"name": "failover-dynamic-routing-group-capture",
4+
"gravitee": "4.0.0",
5+
"type": "proxy",
6+
"listeners": [
7+
{
8+
"type": "http",
9+
"paths": [
10+
{
11+
"path": "/test"
12+
}
13+
],
14+
"entrypoints": [
15+
{
16+
"type": "http-proxy"
17+
}
18+
]
19+
}
20+
],
21+
"failover": {
22+
"enabled": true,
23+
"maxRetries": 2,
24+
"slowCallDuration": 500,
25+
"perSubscription": false
26+
},
27+
"endpointGroups": [
28+
{
29+
"name": "default-group",
30+
"type": "http-proxy",
31+
"endpoints": [
32+
{
33+
"name": "default-endpoint",
34+
"type": "http-proxy",
35+
"weight": 1,
36+
"inheritConfiguration": false,
37+
"configuration": {
38+
"target": "http://localhost:8080/default-group"
39+
},
40+
"sharedConfigurationOverride": {
41+
"http": {
42+
"connectTimeout": 3000,
43+
"readTimeout": 60000
44+
}
45+
}
46+
}
47+
]
48+
},
49+
{
50+
"name": "second",
51+
"type": "http-proxy",
52+
"endpoints": [
53+
{
54+
"name": "second-endpoint",
55+
"type": "http-proxy",
56+
"weight": 1,
57+
"inheritConfiguration": false,
58+
"configuration": {
59+
"target": "http://localhost:8080/second-group"
60+
},
61+
"sharedConfigurationOverride": {
62+
"http": {
63+
"connectTimeout": 3000,
64+
"readTimeout": 60000
65+
}
66+
}
67+
}
68+
]
69+
}
70+
],
71+
"flows": [
72+
{
73+
"name": "dynamic-routing-flow",
74+
"enabled": true,
75+
"selectors": [
76+
{
77+
"type": "http",
78+
"path": "/",
79+
"pathOperator": "START_WITH",
80+
"methods": ["GET"]
81+
}
82+
],
83+
"request": [
84+
{
85+
"name": "Dynamic routing",
86+
"enabled": true,
87+
"policy": "dynamic-routing",
88+
"configuration": {
89+
"rules": [
90+
{
91+
"pattern": "/plan(.*)",
92+
"url": "{#endpoints['second']}/{#group[0]}"
93+
}
94+
]
95+
}
96+
}
97+
]
98+
}
99+
],
100+
"analytics": {
101+
"enabled": false
102+
}
103+
}

0 commit comments

Comments
 (0)