/*
 * Jooby https://jooby.io
 * Apache License Version 2.0 https://jooby.io/LICENSE.txt
 * Copyright 2014 Edgar Espina
 */
package io.jooby.openapi;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.Callable;

import com.fasterxml.jackson.databind.JavaType;
import examples.ABean;
import examples.Letter;
import examples.MvcApp;
import examples.MvcAppWithRoutes;
import examples.MvcInstanceApp;
import examples.NoAppClass;
import examples.RouteBodyArgs;
import examples.RouteFormArgs;
import examples.RouteIdioms;
import examples.RouteImport;
import examples.RouteImportReferences;
import examples.RouteInline;
import examples.RoutePathArgs;
import examples.RoutePatternIdioms;
import examples.RouteQueryArgs;
import examples.RouteReturnTypeApp;
import examples.RouterProduceConsume;
import io.jooby.internal.openapi.RequestBodyExt;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.BooleanSchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.NumberSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import kt.KtAppWithMainKt;
import kt.KtCoroutineRouteIdioms;
import kt.KtMvcApp;
import kt.KtMvcAppWithRoutes;
import kt.KtMvcInstanceApp;
import kt.KtMvcObjectApp;
import kt.KtNoAppClassKt;
import kt.KtRouteIdioms;
import kt.KtRouteImport;
import kt.KtRouteRef;
import kt.KtRouteReturnType;

public class OpenAPIGeneratorTest {
  @OpenAPITest(value = RoutePatternIdioms.class)
  public void routePatternIdioms(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /variable", route.toString());
            })
        .next(
            (route, params) -> {
              assertEquals("DELETE /variable/{id}", route.toString());
              params.next(
                  param -> {
                    assertEquals("path", param.getIn());
                    assertEquals("id", param.getName());
                    assertEquals(String.class.getName(), param.getJavaType());
                  });
            })
        .next(
            route -> {
              assertEquals("POST /variable/foo", route.toString());
            })
        .next(
            route -> {
              assertEquals("PUT /variable/variable/foo", route.toString());
            })
        .verify();
  }

  @OpenAPITest(value = RouteInline.class)
  public void routeInline(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /inline", route.toString());
            })
        .verify();
  }

  @OpenAPITest(value = RouteIdioms.class)
  public void routeIdioms(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /aaa/bbb", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /aaa/ccc/ddd", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /aaa/eee", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /inline", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /routeReference", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /staticRouteReference", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /externalReference", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /externalStaticReference", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /alonevar", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /aloneinline", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /lambdaRef", route.toString());
            })
        .verify();
  }

  @OpenAPITest(value = KtRouteIdioms.class)
  public void ktRoute(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /implicitContext", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /explicitContext", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/people", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/version", route.toString());
            })
        .verify();
  }

  @OpenAPITest(value = KtCoroutineRouteIdioms.class)
  public void ktCoroutineRoute(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /version", route.toString());
            })
        .next(
            route -> {
              assertEquals("PATCH /api/version", route.toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/people", route.toString());
            })
        .verify();
  }

  @OpenAPITest(value = RouteQueryArgs.class)
  public void routeQueryArguments(RouteIterator iterator) {
    iterator
        .next(
            (route, args) -> {
              assertEquals("GET /", route.toString());

              args.next(
                      it -> {
                        assertEquals(String.class.getName(), it.getJavaType());
                        assertEquals("str", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("i", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.List<java.lang.String>", it.getJavaType());
                        assertEquals("listStr", it.getName());
                        assertNull(it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.List<java.lang.Double>", it.getJavaType());
                        assertEquals("listType", it.getName());
                        assertNull(it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(String.class.getName(), it.getJavaType());
                        assertEquals("defstr", it.getName());
                        assertEquals("200", it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("defint", it.getName());
                        assertEquals(87, it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("defint0", it.getName());
                        assertEquals(0, it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(boolean.class.getName(), it.getJavaType());
                        assertEquals("defbool", it.getName());
                        assertEquals(true, it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.String>", it.getJavaType());
                        assertEquals("optstr", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.Integer>", it.getJavaType());
                        assertEquals("optint", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.String>", it.getJavaType());
                        assertEquals("optstr2", it.getName());
                        assertEquals("optional", it.getDefaultValue());
                        assertFalse(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.lang.Integer", it.getJavaType());
                        assertEquals("toI", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertNull(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(Letter.class.getName(), it.getJavaType());
                        assertEquals("letter", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(
                            "java.util.Map<java.lang.String,java.lang.String>", it.getJavaType());
                        assertEquals("query", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertFalse(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(
                            "java.util.Map<java.lang.String,java.util.List<java.lang.String>>",
                            it.getJavaType());
                        assertEquals("queryList", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertFalse(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("foo", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertNull(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("query", it.getIn());
                      })
                  .verify();
            })
        .verify();
  }

  @OpenAPITest(value = RoutePathArgs.class)
  public void routePathArguments(RouteIterator iterator) {
    iterator
        .next(
            (route, args) -> {
              assertEquals("GET /", route.toString());
              args.next(
                      it -> {
                        assertEquals(String.class.getName(), it.getJavaType());
                        assertEquals("str", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("i", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.List<java.lang.String>", it.getJavaType());
                        assertEquals("listStr", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.List<java.lang.Double>", it.getJavaType());
                        assertEquals("listType", it.getName());
                        assertNull(it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(String.class.getName(), it.getJavaType());
                        assertEquals("defstr", it.getName());
                        assertEquals("200", it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("defint", it.getName());
                        assertEquals(87, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(int.class.getName(), it.getJavaType());
                        assertEquals("defint0", it.getName());
                        assertEquals(0, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(boolean.class.getName(), it.getJavaType());
                        assertEquals("defbool", it.getName());
                        assertEquals(true, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.String>", it.getJavaType());
                        assertEquals("optstr", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.Integer>", it.getJavaType());
                        assertEquals("optint", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.util.Optional<java.lang.String>", it.getJavaType());
                        assertEquals("optstr2", it.getName());
                        assertEquals("optional", it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals("java.lang.Integer", it.getJavaType());
                        assertEquals("toI", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(Letter.class.getName(), it.getJavaType());
                        assertEquals("letter", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertTrue(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(
                            "java.util.Map<java.lang.String,java.lang.String>", it.getJavaType());
                        assertEquals("path", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertFalse(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(
                            "java.util.Map<java.lang.String,java.util.List<java.lang.String>>",
                            it.getJavaType());
                        assertEquals("pathList", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertFalse(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .next(
                      it -> {
                        assertEquals(ABean.class.getName(), it.getJavaType());
                        assertEquals("path", it.getName());
                        assertEquals(null, it.getDefaultValue());
                        assertTrue(it.getRequired());
                        assertFalse(it.isSingle());
                        assertEquals("path", it.getIn());
                      })
                  .verify();
            })
        .verify();
  }

  @OpenAPITest(value = RouteFormArgs.class)
  public void routeFormArguments(RouteIterator iterator) {
    iterator
        .next(
            (route, args) -> {
              assertEquals("GET /", route.toString());

              RequestBodyExt requestBody = route.getRequestBody();
              assertNotNull(requestBody);
              assertNotNull(requestBody.getContentType());
              assertNotNull(requestBody.getContent());
              assertNotNull(requestBody.getContent().get(requestBody.getContentType()));
              Schema schema =
                  requestBody.getContent().get(requestBody.getContentType()).getSchema();
              assertNotNull(schema);

              new AssertIterator<Map.Entry<String, Schema>>(schema.getProperties().entrySet())
                  .next(
                      e -> {
                        assertEquals("str", e.getKey());
                        assertTrue(e.getValue() instanceof StringSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("i", e.getKey());
                        assertTrue(e.getValue() instanceof IntegerSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("listStr", e.getKey());
                        assertTrue(e.getValue() instanceof ArraySchema);
                        ArraySchema array = (ArraySchema) e.getValue();
                        assertTrue(array.getItems() instanceof StringSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("listType", e.getKey());
                        assertTrue(e.getValue() instanceof ArraySchema);
                        ArraySchema array = (ArraySchema) e.getValue();
                        assertTrue(array.getItems() instanceof NumberSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("defstr", e.getKey());
                        assertTrue(e.getValue() instanceof StringSchema);
                        assertEquals("200", e.getValue().getDefault());
                      })
                  .next(
                      e -> {
                        assertEquals("defint", e.getKey());
                        assertTrue(e.getValue() instanceof IntegerSchema);
                        assertEquals(87, e.getValue().getDefault());
                      })
                  .next(
                      e -> {
                        assertEquals("defint0", e.getKey());
                        assertTrue(e.getValue() instanceof IntegerSchema);
                        assertEquals(0, e.getValue().getDefault());
                      })
                  .next(
                      e -> {
                        assertEquals("defbool", e.getKey());
                        assertTrue(e.getValue() instanceof BooleanSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("optstr", e.getKey());
                        assertTrue(e.getValue() instanceof StringSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("optint", e.getKey());
                        assertTrue(e.getValue() instanceof IntegerSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("optstr2", e.getKey());
                        assertTrue(e.getValue() instanceof StringSchema);
                        assertEquals("optional", e.getValue().getDefault());
                      })
                  .next(
                      e -> {
                        assertEquals("toI", e.getKey());
                        assertTrue(e.getValue() instanceof IntegerSchema);
                      })
                  .next(
                      e -> {
                        assertEquals("letter", e.getKey());
                        assertTrue(
                            e.getValue() instanceof StringSchema,
                            e.getValue().getClass().getName());
                        StringSchema ss = (StringSchema) e.getValue();
                        assertEquals(Arrays.asList("A", "B"), ss.getEnum());
                      })
                  .verify();
            })
        .verify();
  }

  @OpenAPITest(value = RouteBodyArgs.class)
  public void routeBodyArg(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /str", route.toString());
              assertEquals(String.class.getName(), route.getRequestBody().getJavaType());
              assertEquals(Boolean.TRUE, route.getRequestBody().getRequired());
            })
        .next(
            route -> {
              assertEquals("GET /int", route.toString());
              assertEquals(int.class.getName(), route.getRequestBody().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /listOfStr", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getRequestBody().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /listOfDouble", route.toString());
              assertEquals(
                  "java.util.List<java.lang.Double>", route.getRequestBody().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /defstr", route.toString());
              assertEquals(String.class.getName(), route.getRequestBody().getJavaType());
              assertEquals(Boolean.FALSE, route.getRequestBody().getRequired());
            })
        .next(
            route -> {
              assertEquals("GET /opt-int", route.toString());
              assertEquals(
                  "java.util.Optional<java.lang.Integer>", route.getRequestBody().getJavaType());
              assertEquals(Boolean.FALSE, route.getRequestBody().getRequired());
            })
        .next(
            route -> {
              assertEquals("GET /body-bean", route.toString());
              assertEquals(ABean.class.getName(), route.getRequestBody().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /body-bean2", route.toString());
              assertEquals(ABean.class.getName(), route.getRequestBody().getJavaType());
            })
        .verify();
  }

  public static class Java {
    private JavaType type;

    public JavaType getType() {
      return type;
    }

    public void setType(JavaType type) {
      this.type = type;
    }

    @Override
    public String toString() {
      return type.toString();
    }
  }

  @OpenAPITest(value = RouteReturnTypeApp.class, ignoreArguments = true)
  public void routeReturnType(RouteIterator iterator) {
    //    ObjectMapper mapper = new ObjectMapper();
    //    Java java = new Java();
    //    java.type = mapper.constructType(int[].class);
    //    String json = mapper.writeValueAsString(java);
    //    System.out.println(json);
    //    Java j = mapper.readValue(json, Java.class);
    //    System.out.println(j);
    iterator
        .next(
            route -> {
              assertEquals("GET /literal/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/2", route.toString());
              assertEquals(Integer.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/3", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/4", route.toString());
              assertEquals(Boolean.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/1", route.toString());
              assertEquals(
                  RouteReturnTypeApp.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/2", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/3", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/4", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/5", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/6", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/1", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/2", route.toString());
              assertEquals(Integer.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/3", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/4", route.toString());
              assertEquals(
                  Callable.class.getName() + "<" + Byte.class.getName() + ">",
                  route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/5", route.toString());
              assertEquals(
                  Callable.class.getName() + "<" + Character.class.getName() + ">",
                  route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/2", route.toString());
              assertEquals(Integer.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/3", route.toString());
              assertEquals("[Ljava.lang.String;", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/4", route.toString());
              assertEquals("[F", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/1", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/2", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/3", route.toString());
              assertEquals(
                  "java.util.List<java.util.List<java.lang.String>>",
                  route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/1", route.toString());
              assertEquals("java.util.ArrayList", route.getDefaultResponse().getJavaTypes().get(0));
              assertEquals(
                  "java.util.LinkedList", route.getDefaultResponse().getJavaTypes().get(1));
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/2", route.toString());
              assertEquals("examples.ABean", route.getDefaultResponse().getJavaTypes().get(0));
              assertEquals("examples.BBean", route.getDefaultResponse().getJavaTypes().get(1));
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/3", route.toString());
              assertEquals("examples.Bean", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/1", route.toString());
              assertEquals("[Z", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/2", route.toString());
              assertEquals(
                  "[Lexamples.RouteReturnTypeApp;", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/3", route.toString());
              assertEquals("[I", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/4", route.toString());
              assertEquals("[Ljava.lang.String;", route.getDefaultResponse().getJavaType());
            })
        .verify();
  }

  @OpenAPITest(value = RouteImport.class)
  public void routeImport(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /main/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /main/submain/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /require/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /subroute/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .verify();
  }

  @OpenAPITest(value = RouteImportReferences.class)
  public void routeImportReferences(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /require/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /prefix/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .verify();
  }

  @OpenAPITest(value = KtRouteImport.class)
  public void ktRouteImport(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /main/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /main/submain/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /require/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /subroute/a/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .verify();
  }

  @OpenAPITest(value = KtRouteReturnType.class, ignoreArguments = true)
  public void ktRouteReturnType(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /literal/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/2", route.toString());
              assertEquals(Integer.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/3", route.toString());
              assertEquals(void.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /literal/4", route.toString());
              assertEquals(Boolean.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/1", route.toString());
              assertEquals(
                  KtRouteReturnType.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/2", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/3", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/4", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/5", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /call/6", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/1", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/2", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/3", route.toString());
              assertEquals(Object.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /generic/4", route.toString());
              //          assertEquals(Callable.class.getName() + "<" + Byte.class.getName() + ">",
              //              route.getDefaultResponse().getJavaType());
              assertEquals(Callable.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/1", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/2", route.toString());
              assertEquals(Integer.class.getName(), route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/3", route.toString());
              assertEquals("[Ljava.lang.String;", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /localvar/4", route.toString());
              assertEquals("[F", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/1", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/2", route.toString());
              assertEquals(
                  "java.util.List<java.lang.String>", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /complexType/3", route.toString());
              assertEquals("java.util.List", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/1", route.toString());
              assertEquals("java.util.ArrayList", route.getDefaultResponse().getJavaTypes().get(0));
              assertEquals(
                  "java.util.LinkedList", route.getDefaultResponse().getJavaTypes().get(1));
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/2", route.toString());
              assertEquals("examples.ABean", route.getDefaultResponse().getJavaTypes().get(0));
              assertEquals("examples.BBean", route.getDefaultResponse().getJavaTypes().get(1));
            })
        .next(
            route -> {
              assertEquals("GET /multipleTypes/3", route.toString());
              assertEquals("examples.Bean", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/1", route.toString());
              assertEquals("[Z", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/2", route.toString());
              assertEquals("java.util.List", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/3", route.toString());
              assertEquals("[I", route.getDefaultResponse().getJavaType());
            })
        .next(
            route -> {
              assertEquals("GET /array/4", route.toString());
              assertEquals("[Ljava.lang.String;", route.getDefaultResponse().getJavaType());
            })
        .verify();
  }

  @OpenAPITest(value = RouterProduceConsume.class)
  public void routeProduceConsume(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /", route.toString());
              assertEquals(
                  Arrays.asList("text/html", "text/plain", "some/type"), route.getProduces());
              assertEquals(
                  Arrays.asList("application/json", "application/javascript"), route.getConsumes());
            })
        .next(
            route -> {
              assertEquals("GET /json", route.toString());
              assertEquals(Arrays.asList("application/json"), route.getProduces());
              assertEquals(Arrays.asList("application/json"), route.getConsumes());
            })
        .next(
            route -> {
              assertEquals("GET /api/people", route.toString());
              assertEquals(Arrays.asList("text/yaml"), route.getProduces());
              assertEquals(Arrays.asList("text/yaml"), route.getConsumes());
            })
        .verify();
  }

  private void assertController(RouteIterator iterator) {
    iterator
        .next(
            (route, args) -> {
              assertEquals("GET /api/foo", route.toString());
              args.next(
                      arg -> {
                        assertEquals("q", arg.getName());
                        assertEquals("java.util.Optional<java.lang.String>", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api/bar", route.toString());
              args.next(
                      arg -> {
                        assertEquals("q", arg.getName());
                        assertEquals("java.util.Optional<java.lang.String>", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api", route.toString());

              args.next(
                      arg -> {
                        assertEquals("bool", arg.getName());
                        assertEquals("boolean", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("s", arg.getName());
                        assertEquals("short", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("i", arg.getName());
                        assertEquals("int", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("c", arg.getName());
                        assertEquals("char", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("l", arg.getName());
                        assertEquals("long", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("f", arg.getName());
                        assertEquals("float", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("d", arg.getName());
                        assertEquals("double", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("POST /api/post", route.toString());
              args.next(
                      arg -> {
                        assertEquals("bool", arg.getName());
                        assertEquals("boolean", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("s", arg.getName());
                        assertEquals("short", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("i", arg.getName());
                        assertEquals("int", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("c", arg.getName());
                        assertEquals("char", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("l", arg.getName());
                        assertEquals("long", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("f", arg.getName());
                        assertEquals("float", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("d", arg.getName());
                        assertEquals("double", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api/path", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/path-only", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/session", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/returnList", route.toString());
              assertEquals(
                  "java.util.List<" + String.class.getName() + ">",
                  route.getDefaultResponse().toString());
              assertTrue(route.getDeprecated());
            })
        .next(
            route -> {
              assertEquals("POST /api/bean", route.toString());
              assertEquals(ABean.class.getName(), route.getDefaultResponse().toString());
              assertEquals(ABean.class.getName(), route.getRequestBody().getJavaType());
            })
        .verify();
  }

  @OpenAPITest(value = MvcApp.class)
  public void routeMvc(RouteIterator iterator) {
    assertController(iterator);
  }

  @OpenAPITest(value = MvcAppWithRoutes.class)
  public void routeMvcWithRoutes(RouteIterator iterator) {
    assertController(iterator);
  }

  @OpenAPITest(value = MvcInstanceApp.class)
  public void routeMvcInstance(RouteIterator iterator) {
    iterator
        .next(
            (route, args) -> {
              assertEquals("GET /api/foo", route.toString());
              args.next(
                      arg -> {
                        assertEquals("q", arg.getName());
                        assertEquals("java.util.Optional<java.lang.String>", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api/bar", route.toString());
              args.next(
                      arg -> {
                        assertEquals("q", arg.getName());
                        assertEquals("java.util.Optional<java.lang.String>", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api", route.toString());

              args.next(
                      arg -> {
                        assertEquals("bool", arg.getName());
                        assertEquals("boolean", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("s", arg.getName());
                        assertEquals("short", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("i", arg.getName());
                        assertEquals("int", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("c", arg.getName());
                        assertEquals("char", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("l", arg.getName());
                        assertEquals("long", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("f", arg.getName());
                        assertEquals("float", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("d", arg.getName());
                        assertEquals("double", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("POST /api/post", route.toString());
              args.next(
                      arg -> {
                        assertEquals("bool", arg.getName());
                        assertEquals("boolean", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("s", arg.getName());
                        assertEquals("short", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("i", arg.getName());
                        assertEquals("int", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("c", arg.getName());
                        assertEquals("char", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("l", arg.getName());
                        assertEquals("long", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("f", arg.getName());
                        assertEquals("float", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("d", arg.getName());
                        assertEquals("double", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .verify();
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /api/path", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/path-only", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/session", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /api/returnList", route.toString());
              assertEquals(
                  "java.util.List<" + String.class.getName() + ">",
                  route.getDefaultResponse().toString());
              assertTrue(route.getDeprecated());
            })
        .next(
            route -> {
              assertEquals("POST /api/bean", route.toString());
              assertEquals(ABean.class.getName(), route.getDefaultResponse().toString());
              assertEquals(ABean.class.getName(), route.getRequestBody().getJavaType());
            })
        .verify();
  }

  private void assertKtController(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("PUT /entity/{id}", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().toString());
              args.next(
                  arg -> {
                    assertEquals("id", arg.getName());
                    assertEquals("java.lang.String", arg.getJavaType());
                    assertEquals("path", arg.getIn());
                    assertTrue(arg.getRequired());
                  });
            })
        .next(
            route -> {
              assertEquals("DELETE /unit", route.toString());
              assertEquals(void.class.getName(), route.getDefaultResponse().toString());
            })
        .next(
            route -> {
              assertEquals("GET /doMap", route.toString());
              assertEquals(
                  "java.util.Map<java.lang.String,java.lang.Object>",
                  route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /doParams", route.toString());
              assertEquals(ABean.class.getName(), route.getDefaultResponse().toString());
              args.next(
                      arg -> {
                        assertEquals("I", arg.getName());
                        assertEquals("int", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("oI", arg.getName());
                        assertEquals("java.lang.Integer", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .next(
                      arg -> {
                        assertEquals("q", arg.getName());
                        assertEquals("java.lang.String", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("nullq", arg.getName());
                        assertEquals("java.lang.String", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                      })
                  .verify();
            })
        .next(
            (route, args) -> {
              assertEquals("GET /coroutine", route.toString());
              assertEquals(
                  "java.util.List<" + String.class.getName() + ">",
                  route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /future", route.toString());
              assertEquals("java.lang.String", route.getDefaultResponse().toString());
            })
        .next(
            (route, args) -> {
              assertEquals("GET /httpNames", route.toString());
              assertEquals("java.lang.String", route.getDefaultResponse().toString());
              args.next(
                      arg -> {
                        assertEquals("Last-Modified-Since", arg.getName());
                        assertEquals("java.lang.String", arg.getJavaType());
                        assertEquals("header", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .next(
                      arg -> {
                        assertEquals("x-search", arg.getName());
                        assertEquals("java.lang.String", arg.getJavaType());
                        assertEquals("query", arg.getIn());
                        assertTrue(arg.getRequired());
                      })
                  .verify();
            })
        .verify();
  }

  @OpenAPITest(value = KtMvcApp.class)
  public void ktMvc(RouteIterator iterator) {
    assertKtController(iterator);
  }

  @OpenAPITest(value = KtMvcInstanceApp.class)
  public void ktMvcInstance(RouteIterator iterator) {
    assertKtController(iterator);
  }

  @OpenAPITest(value = KtMvcObjectApp.class)
  public void ktMvcObject(RouteIterator iterator) {
    assertKtController(iterator);
  }

  @OpenAPITest(value = KtMvcAppWithRoutes.class)
  public void ktMvcWithRoutes(RouteIterator iterator) {
    assertKtController(iterator);
  }

  @OpenAPITest(value = KtRouteRef.class)
  public void ktRouteRef(RouteIterator iterator) {
    iterator.next(
        route -> {
          assertEquals("Create a Pet", route.getSummary());
          assertEquals("createPetRef", route.getOperationId());
        });
  }

  @OpenAPITest(value = KtNoAppClassKt.class)
  public void ktNoApp(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /path", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
              assertEquals("getPath", route.getOperationId());
            })
        .next(
            route -> {
              assertEquals("GET /fn", route.toString());
              assertEquals(int.class.getName(), route.getDefaultResponse().getJavaType());
              assertEquals("fnRef", route.getOperationId());
              assertEquals("function reference", route.getSummary());
            })
        .verify();
  }

  @OpenAPITest(value = NoAppClass.class)
  public void noApp(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /path", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
              assertEquals("getPath", route.getOperationId());
            })
        .next(
            route -> {
              assertEquals("GET /fn", route.toString());
              assertEquals(int.class.getName(), route.getDefaultResponse().getJavaType());
              assertEquals("fnRef", route.getOperationId());
              assertEquals("function reference", route.getSummary());
            })
        .verify();
  }

  @OpenAPITest(value = KtAppWithMainKt.class)
  public void ktAppWithMain(RouteIterator iterator) {
    iterator
        .next(
            route -> {
              assertEquals("GET /welcome", route.toString());
              assertEquals(String.class.getName(), route.getDefaultResponse().getJavaType());
              assertEquals("getWelcome", route.getOperationId());
            })
        .verify();
  }
}
