|
29 | 29 | import static org.mockito.ArgumentMatchers.eq; |
30 | 30 | import static org.mockito.Mockito.when; |
31 | 31 |
|
| 32 | +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; |
32 | 33 | import com.github.tomakehurst.wiremock.stubbing.Scenario; |
33 | 34 | import com.graviteesource.entrypoint.http.get.HttpGetEntrypointConnectorFactory; |
34 | 35 | import com.graviteesource.reactor.message.MessageApiReactorFactory; |
|
63 | 64 | import io.gravitee.policy.apikey.ApiKeyPolicy; |
64 | 65 | import io.gravitee.policy.apikey.ApiKeyPolicyInitializer; |
65 | 66 | import io.gravitee.policy.apikey.configuration.ApiKeyPolicyConfiguration; |
| 67 | +import io.gravitee.policy.dynamicrouting.DynamicRoutingPolicy; |
| 68 | +import io.gravitee.policy.dynamicrouting.configuration.DynamicRoutingPolicyConfiguration; |
66 | 69 | import io.gravitee.reporter.api.v4.metric.Metrics; |
67 | 70 | import io.reactivex.rxjava3.annotations.NonNull; |
68 | 71 | import io.reactivex.rxjava3.core.Flowable; |
@@ -1087,6 +1090,114 @@ void should_success_on_second_retry(HttpClient client) { |
1087 | 1090 | } |
1088 | 1091 | } |
1089 | 1092 |
|
| 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 | + |
1090 | 1201 | @NonNull |
1091 | 1202 | private static Flowable<JsonObject> extractPlainTextMessages(Buffer body) { |
1092 | 1203 | final List<JsonObject> messages = new ArrayList<>(); |
|
0 commit comments