Как провести тестирование шлюза пружины? - PullRequest
2 голосов
/ 20 января 2020

Мой шлюз будет перенаправлять трафик c на множество разных сервисов (под разными доменными именами). Как я могу проверить конфигурацию шлюза? только с одним сервисом я могу просто установить фиктивный сервер (например, httpbin) и проверить ответ. с несколькими службами я бы предпочел избегать запуска всей сети docker или изменения псевдонимов locak dns. Spring предлагает какой-нибудь легкий способ тестирования шлюза?

Ответы [ 2 ]

1 голос
/ 20 января 2020

Вот как добиться того, что вы хотите, с помощью API Simulator :

package my.package;

import static com.apisimulator.embedded.SuchThat.isEqualTo;
import static com.apisimulator.embedded.SuchThat.startsWith;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpApiSimulation;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpRequest;
import static com.apisimulator.embedded.http.HttpApiSimulation.httpResponse;
import static com.apisimulator.embedded.http.HttpApiSimulation.simlet;
import static com.apisimulator.http.Http1Header.CONTENT_TYPE;
import static com.apisimulator.http.HttpMethod.CONNECT;
import static com.apisimulator.http.HttpMethod.GET;
import static com.apisimulator.http.HttpStatus.OK;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

import java.time.Duration;
import java.util.Map;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.SocketUtils;

import com.apisimulator.embedded.http.JUnitHttpApiSimulation;

@RunWith(SpringRunner.class)
@SpringBootTest(
   webEnvironment = RANDOM_PORT,
   properties = { 
      "management.server.port=${test.port}", "logging.level.root=info",
      // Configure the Gateway to use HTTP proxy - the API Simulator 
      // instance running at localhost:6090
      "spring.cloud.gateway.httpclient.proxy.host=localhost",
      "spring.cloud.gateway.httpclient.proxy.port=6090"
      //"logging.level.reactor.netty.http.server=debug",
      //"spring.cloud.gateway.httpserver.wiretap=true" 
   }
)
@Import(ServiceGatewayApplication.class)
public class ServiceGatewayApplicationTest
{

   // Configure an API simulation. This starts up an instance 
   // of API Simulator on localhost, default port 6090
   @ClassRule
   public static final JUnitHttpApiSimulation clApiSimulation = JUnitHttpApiSimulation
            .as(httpApiSimulation("svc-gateway-backends"));

   protected static int managementPort;

   @LocalServerPort
   protected int port = 0;

   protected String baseUri;
   protected WebTestClient webClient;

   @BeforeClass
   public static void beforeClass()
   {
      managementPort = SocketUtils.findAvailableTcpPort();
      System.setProperty("test.port", String.valueOf(managementPort));

      // Configure simlets for the API simulation
      // @formatter:off
      clApiSimulation.add(simlet("http-proxy")
         .when(httpRequest(CONNECT))
         .then(httpResponse(OK))
      );

      clApiSimulation.add(simlet("test-domain-1")
         .when(httpRequest()
               .whereMethod(GET)
               .whereUriPath(isEqualTo("/static"))
               // The `host` header is used to determine the actual destination 
               .whereHeader("host", startsWith("domain-1.com"))
          )
         .then(httpResponse()
               .withStatus(OK)
               .withHeader(CONTENT_TYPE, "application/text")
               .withBody("{ \"domain\": \"1\" }")
          )
      );

      clApiSimulation.add(simlet("test-domain-2")
         .when(httpRequest()
               .whereMethod(GET)
               .whereUriPath(isEqualTo("/v1/api/foo"))
               .whereHeader("host", startsWith("domain-2.com"))
          )
         .then(httpResponse()
               .withStatus(OK)
               .withHeader(CONTENT_TYPE, "application/json; charset=UTF-8")
               .withBody(
                  "{\n" +
                  "   \"domain\": \"2\"\n" + 
                  "}"
                )
          )
      );
      // @formatter:on
   }

   @AfterClass
   public static void afterClass()
   {
      System.clearProperty("test.port");
   }

   @Before
   public void setup()
   {
      // @formatter:off
      baseUri = "http://localhost:" + port;
      webClient = WebTestClient.bindToServer()
         .baseUrl(baseUri)
         .responseTimeout(Duration.ofSeconds(2))
         .build();
      // @formatter:on
   }

   @Test
   public void test_domain1()
   {
      // @formatter:off
      webClient.get()
         .uri("/static")
         .exchange()
         .expectStatus().isOk()
         .expectBody(String.class).consumeWith(result -> 
             assertThat(result.getResponseBody()).isEqualTo("{ \"domain\": \"1\" }")
          );
      // @formatter:on
   }

   @Test
   public void test_domain2()
   {
      // @formatter:off
      webClient.get()
         .uri("/v1/api/foo")
         .exchange()
         .expectStatus().isOk()
         .expectHeader()
            .contentType("application/json; charset=UTF-8")
         .expectBody(Map.class).consumeWith(result -> 
             assertThat(result.getResponseBody()).containsEntry("domain", "2")
          );
      // @formatter:on
   }

}

Большая часть кода основана на этом GatewaySampleApplicationTests классе Spring Проект Cloud Gateway.

Выше предполагается, что у шлюза есть маршруты, подобные этим (только фрагменты):

    ...
    uri: "http://domain-1.com"
    predicates:
      - Path=/static
    ...
    uri: "http://domain-2.com"
    predicates:
      - Path=/v1/api/foo
    ...
0 голосов
/ 28 января 2020

@ apsisim предоставили отличную идею для использования веб-прокси. но инструмент, который он предлагает, отсутствует в репозитории Maven и имеет коммерческую лицензию что сработало для меня:

запустите шлюз, чтобы он использовал прокси-сервер (вы можете потренироваться и найти свободный порт):

private const val proxyPort = 1080

@SpringBootTest(
    properties = [
        //"logging.level.reactor.netty.http.server=debug",
        //"spring.cloud.gateway.httpserver.wiretap=true",
        //"spring.cloud.gateway.httpclient.wiretap=true",
        "spring.cloud.gateway.httpclient.proxy.host=localhost",
        "spring.cloud.gateway.httpclient.proxy.port=$proxyPort"
    ]
)

, затем использовать mockwebserver в качестве прокси

testImplementation("com.squareup.okhttp3:mockwebserver:4.2.1")
testImplementation("com.squareup.okhttp3:okhttp:4.2.1")

, и тогда все ваши запросы будут go на ваш прокси. просто помните, что протокол http указывает, что первый запрос к новому серверу требует туннелирования через прокси, поэтому, когда вы делаете первый запрос к шлюзу, шлюз отправит 2 запроса к прокси:

testClient.get()
            .uri(path)
            .header("host", gatewayDns)
            .exchange()

nextRequestFromGateway {
    method `should equal` "CONNECT"
    headers[HOST] `should equal` "$realUrlBehindGateway:80"
}

nextRequestFromGateway {
    path `should equal` "/api/v1/whatever"
    headers[HOST] `should equal` realUrlBehindGateway
}

...
fun nextRequestFromGateway(block : RecordedRequest.() -> Unit) {
    mockWebServer.takeRequest().apply (block)
}
...