diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..e329e50825
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+continuation_indent_size = 4
+max_line_length = 100
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.adoc]
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/docs/asciidoc/responses.adoc b/docs/asciidoc/responses.adoc
index 5c5e3dd26d..33ae2f6a7f 100644
--- a/docs/asciidoc/responses.adoc
+++ b/docs/asciidoc/responses.adoc
@@ -241,7 +241,7 @@ there is a non-blocking route handler.
<3> Value is provided from *event loop*. No blocking code is permitted
<4> Value is computed/produces from completable future context
-Running your `App` in *worker* mode works identically, except for we are able to do blocking calls:
+Running your `App3508` in *worker* mode works identically, except for we are able to do blocking calls:
.In worker mode
[source,java,role="primary"]
@@ -284,7 +284,7 @@ Running your `App` in *worker* mode works identically, except for we are able to
<3> Value is provided from *worker mode*. Blocking code is permitted
<4> Value is computed/produces from completable future context
-Running your `App` in *default* mode works identically to running in the *event loop* mode:
+Running your `App3508` in *default* mode works identically to running in the *event loop* mode:
.In default mode
[source,java,role="primary"]
diff --git a/docs/asciidoc/routing.adoc b/docs/asciidoc/routing.adoc
index 89c6334395..d258a19c5d 100644
--- a/docs/asciidoc/routing.adoc
+++ b/docs/asciidoc/routing.adoc
@@ -1274,7 +1274,7 @@ class App: Kooby({
<2> Imports all routes, services, callbacks, etc... from `Bar`. Output: `/bar` => `/bar`
-This operator lets you for example to deploy `Foo` as a standalone application or integrate it into a main one called `App`.
+This operator lets you for example to deploy `Foo` as a standalone application or integrate it into a main one called `App3508`.
The install operator shares the state of the main application, so lazy initialization (and therefore _instantiation_) of
any child applications is *mandatory*.
diff --git a/modules/jooby-avaje-validator/pom.xml b/modules/jooby-avaje-validator/pom.xml
index 7d7c8ee089..4da876c1af 100644
--- a/modules/jooby-avaje-validator/pom.xml
+++ b/modules/jooby-avaje-validator/pom.xml
@@ -45,11 +45,6 @@
jooby-apt
test
-
- io.avaje
- avaje-validator-generator
- test
-
io.jooby
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/AvajeValidatorModuleTest.java b/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/AvajeValidatorModuleTest.java
deleted file mode 100644
index c254cede14..0000000000
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/AvajeValidatorModuleTest.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.avaje.validator;
-
-import static io.jooby.StatusCode.UNPROCESSABLE_ENTITY_CODE;
-import static io.jooby.avaje.validator.app.App.DEFAULT_TITLE;
-import static io.restassured.RestAssured.given;
-
-import java.util.List;
-import java.util.Map;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import io.jooby.avaje.validator.app.App;
-import io.jooby.avaje.validator.app.NewAccountRequest;
-import io.jooby.avaje.validator.app.Person;
-import io.jooby.test.JoobyTest;
-import io.jooby.validation.ValidationResult;
-import io.restassured.RestAssured;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.specification.RequestSpecification;
-
-@JoobyTest(value = App.class, port = 8099)
-public class AvajeValidatorModuleTest {
-
- protected static RequestSpecification SPEC =
- new RequestSpecBuilder()
- .setPort(8099)
- .setContentType(ContentType.JSON)
- .setAccept(ContentType.JSON)
- .build();
-
- static {
- RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
- }
-
- @Test
- public void validate_personBean_shouldDetect2Violations() {
- Person person = new Person(null, "Last Name");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(person)
- .post("/create-person")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName", List.of("must not be empty"), ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_arrayOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(new Person[] {person1, person2})
- .post("/create-array-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName", List.of("must not be empty"), ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_listOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(List.of(person1, person2))
- .post("/create-list-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName", List.of("must not be empty"), ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_mapOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(Map.of("1", person1, "2", person2))
- .post("/create-map-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName", List.of("must not be empty"), ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_newAccountBean_shouldDetect6Violations() {
- NewAccountRequest request = new NewAccountRequest();
- request.setLogin("jk");
- request.setPassword("123");
- request.setConfirmPassword("1234");
- request.setPerson(new Person(null, "Last Name"));
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(request)
- .post("/create-new-account")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- List errors =
- List.of(
- new ValidationResult.Error(
- "password",
- List.of("length must be between 8 and 24"),
- ValidationResult.ErrorType.FIELD),
- new ValidationResult.Error(
- "person.firstName", List.of("must not be empty"), ValidationResult.ErrorType.FIELD),
- new ValidationResult.Error(
- "confirmPassword",
- List.of("length must be between 8 and 24"),
- ValidationResult.ErrorType.FIELD),
- new ValidationResult.Error(
- "login",
- List.of("length must be between 3 and 16"),
- ValidationResult.ErrorType.FIELD));
-
- ValidationResult expectedResult = buildResult(errors);
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors")
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- private ValidationResult buildResult(List errors) {
- return new ValidationResult(DEFAULT_TITLE, UNPROCESSABLE_ENTITY_CODE, errors);
- }
-}
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/App.java b/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/App.java
deleted file mode 100644
index 68235e9449..0000000000
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/App.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.avaje.validator.app;
-
-import io.jooby.Jooby;
-import io.jooby.StatusCode;
-import io.jooby.avaje.validator.AvajeValidatorModule;
-import io.jooby.jackson.JacksonModule;
-
-public class App extends Jooby {
-
- private static final StatusCode STATUS_CODE = StatusCode.UNPROCESSABLE_ENTITY;
- public static final String DEFAULT_TITLE = "Validation failed";
-
- {
- install(new JacksonModule());
- install(new AvajeValidatorModule().validationTitle(DEFAULT_TITLE).statusCode(STATUS_CODE));
-
- mvc(new Controller());
- }
-}
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Controller.java b/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Controller.java
deleted file mode 100644
index 7631f7bf5e..0000000000
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Controller.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.avaje.validator.app;
-
-import java.util.List;
-import java.util.Map;
-
-import io.jooby.annotation.POST;
-import io.jooby.annotation.Path;
-import jakarta.validation.Valid;
-
-@Path("")
-public class Controller {
-
- @POST("/create-person")
- public void createPerson(@Valid Person person) {}
-
- @POST("/create-array-of-persons")
- public void createArrayOfPersons(@Valid Person[] persons) {}
-
- @POST("/create-list-of-persons")
- public void createListOfPersons(@Valid List persons) {}
-
- @POST("/create-map-of-persons")
- public void createMapOfPersons(@Valid Map persons) {}
-
- @POST("/create-new-account")
- public void createNewAccount(@Valid NewAccountRequest request) {}
-}
diff --git a/modules/jooby-hibernate-validator/pom.xml b/modules/jooby-hibernate-validator/pom.xml
index e97df77450..ce6b4fce7d 100644
--- a/modules/jooby-hibernate-validator/pom.xml
+++ b/modules/jooby-hibernate-validator/pom.xml
@@ -38,20 +38,6 @@
5.0.0
-
-
- io.jooby
- jooby-netty
- ${jooby.version}
- test
-
-
- io.jooby
- jooby-jackson
- ${jooby.version}
- test
-
-
org.junit.jupiter
junit-jupiter-api
@@ -70,19 +56,6 @@
${jooby.version}
test
-
-
- io.rest-assured
- rest-assured
- test
-
-
-
- org.assertj
- assertj-core
- 3.26.3
- test
-
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/HibernateValidatorModuleTest.java b/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/HibernateValidatorModuleTest.java
deleted file mode 100644
index 9da14767d5..0000000000
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/HibernateValidatorModuleTest.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.hibernate.validator;
-
-import static io.jooby.StatusCode.UNPROCESSABLE_ENTITY_CODE;
-import static io.jooby.hibernate.validator.app.App.DEFAULT_TITLE;
-import static io.restassured.RestAssured.given;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import io.jooby.hibernate.validator.app.App;
-import io.jooby.hibernate.validator.app.NewAccountRequest;
-import io.jooby.hibernate.validator.app.Person;
-import io.jooby.test.JoobyTest;
-import io.jooby.validation.ValidationResult;
-import io.restassured.RestAssured;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.specification.RequestSpecification;
-
-@JoobyTest(value = App.class, port = 8099)
-public class HibernateValidatorModuleTest {
-
- protected static RequestSpecification SPEC =
- new RequestSpecBuilder()
- .setPort(8099)
- .setContentType(ContentType.JSON)
- .setAccept(ContentType.JSON)
- .build();
-
- static {
- RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
- }
-
- @Test
- public void validate_personBean_shouldDetect2Violations() {
- Person person = new Person(null, "Last Name");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(person)
- .post("/create-person")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName",
- List.of("must not be empty", "must not be null"),
- ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_arrayOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(new Person[] {person1, person2})
- .post("/create-array-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName",
- List.of("must not be empty", "must not be null"),
- ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_listOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(List.of(person1, person2))
- .post("/create-list-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName",
- List.of("must not be empty", "must not be null"),
- ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_mapOfPerson_shouldDetect2Violations() {
- Person person1 = new Person("First Name", "Last Name");
- Person person2 = new Person(null, "Last Name 2");
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(Map.of("1", person1, "2", person2))
- .post("/create-map-of-persons")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- var fieldError =
- new ValidationResult.Error(
- "firstName",
- List.of("must not be empty", "must not be null"),
- ValidationResult.ErrorType.FIELD);
- ValidationResult expectedResult = buildResult(List.of(fieldError));
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- @Test
- public void validate_newAccountBean_shouldDetect6Violations() {
- NewAccountRequest request = new NewAccountRequest();
- request.setLogin("jk");
- request.setPassword("123");
- request.setConfirmPassword("1234");
- request.setPerson(new Person(null, "Last Name"));
-
- ValidationResult actualResult =
- given()
- .spec(SPEC)
- .with()
- .body(request)
- .post("/create-new-account")
- .then()
- .assertThat()
- .statusCode(UNPROCESSABLE_ENTITY_CODE)
- .extract()
- .as(ValidationResult.class);
-
- List errors =
- new ArrayList<>() {
- {
- add(
- new ValidationResult.Error(
- null, List.of("Passwords should match"), ValidationResult.ErrorType.GLOBAL));
- add(
- new ValidationResult.Error(
- "person.firstName",
- List.of("must not be empty", "must not be null"),
- ValidationResult.ErrorType.FIELD));
- add(
- new ValidationResult.Error(
- "login",
- List.of("size must be between 3 and 16"),
- ValidationResult.ErrorType.FIELD));
- add(
- new ValidationResult.Error(
- "password",
- List.of("size must be between 8 and 24"),
- ValidationResult.ErrorType.FIELD));
- add(
- new ValidationResult.Error(
- "confirmPassword",
- List.of("size must be between 8 and 24"),
- ValidationResult.ErrorType.FIELD));
- }
- };
-
- ValidationResult expectedResult = buildResult(errors);
-
- Assertions.assertThat(expectedResult)
- .usingRecursiveComparison()
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors")
- .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
- .isEqualTo(actualResult);
- }
-
- private ValidationResult buildResult(List errors) {
- return new ValidationResult(DEFAULT_TITLE, UNPROCESSABLE_ENTITY_CODE, errors);
- }
-}
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/App.java b/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/App.java
deleted file mode 100644
index 7c5f7b9481..0000000000
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/App.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.hibernate.validator.app;
-
-import io.jooby.Jooby;
-import io.jooby.StatusCode;
-import io.jooby.hibernate.validator.HibernateValidatorModule;
-import io.jooby.jackson.JacksonModule;
-
-public class App extends Jooby {
-
- private static final StatusCode STATUS_CODE = StatusCode.UNPROCESSABLE_ENTITY;
- public static final String DEFAULT_TITLE = "Validation failed";
-
- {
- install(new JacksonModule());
- install(new HibernateValidatorModule().validationTitle(DEFAULT_TITLE).statusCode(STATUS_CODE));
-
- mvc(new Controller());
- }
-}
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/NewAccountRequest.java b/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/NewAccountRequest.java
deleted file mode 100644
index 626760cb7b..0000000000
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/NewAccountRequest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.hibernate.validator.app;
-
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-
-@PasswordsShouldMatch
-public class NewAccountRequest {
- @NotNull @NotEmpty
- @Size(min = 3, max = 16)
- private String login;
-
- @NotNull @NotEmpty
- @Size(min = 8, max = 24)
- private String password;
-
- @NotNull @NotEmpty
- @Size(min = 8, max = 24)
- private String confirmPassword;
-
- @Valid private Person person;
-
- public String getLogin() {
- return login;
- }
-
- public void setLogin(String login) {
- this.login = login;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getConfirmPassword() {
- return confirmPassword;
- }
-
- public void setConfirmPassword(String confirmPassword) {
- this.confirmPassword = confirmPassword;
- }
-
- public Person getPerson() {
- return person;
- }
-
- public void setPerson(Person person) {
- this.person = person;
- }
-}
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Person.java b/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Person.java
deleted file mode 100644
index 38c766e1b6..0000000000
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Person.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby.hibernate.validator.app;
-
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-
-public class Person {
-
- @NotEmpty @NotNull private String firstName;
- private String lastName;
-
- public Person(String firstName, String lastName) {
- this.firstName = firstName;
- this.lastName = lastName;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(String lastName) {
- this.lastName = lastName;
- }
-}
diff --git a/tests/pom.xml b/tests/pom.xml
index c4880310cf..e99b33a9de 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -108,6 +108,11 @@
jooby-mutiny
${jooby.version}
+
+ io.jooby
+ jooby-avaje-validator
+ ${jooby.version}
+
io.jooby
jooby-test
@@ -125,6 +130,11 @@
commons-io
commons-io
+
+ io.avaje
+ avaje-validator-generator
+ test
+
org.jetbrains.kotlin
diff --git a/tests/src/test/java/io/jooby/i3508/App3508.java b/tests/src/test/java/io/jooby/i3508/App3508.java
new file mode 100644
index 0000000000..c11b3b7591
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3508/App3508.java
@@ -0,0 +1,19 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3508;
+
+import io.jooby.Extension;
+import io.jooby.Jooby;
+import io.jooby.jackson.JacksonModule;
+
+public class App3508 extends Jooby {
+ public App3508(Extension validator) {
+ install(new JacksonModule());
+ install(validator);
+
+ mvc(new Controller3508_());
+ }
+}
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Controller.java b/tests/src/test/java/io/jooby/i3508/Controller3508.java
similarity index 85%
rename from modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Controller.java
rename to tests/src/test/java/io/jooby/i3508/Controller3508.java
index b90d57f3ff..86a68ec838 100644
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/Controller.java
+++ b/tests/src/test/java/io/jooby/i3508/Controller3508.java
@@ -3,17 +3,19 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.hibernate.validator.app;
+package io.jooby.i3508;
import java.util.List;
import java.util.Map;
import io.jooby.annotation.POST;
import io.jooby.annotation.Path;
+import io.jooby.i3508.data.NewAccountRequest;
+import io.jooby.i3508.data.Person;
import jakarta.validation.Valid;
@Path("")
-public class Controller {
+public class Controller3508 {
@POST("/create-person")
public void createPerson(@Valid Person person) {}
diff --git a/tests/src/test/java/io/jooby/i3508/Issue3508.java b/tests/src/test/java/io/jooby/i3508/Issue3508.java
index 182a8168ea..866036021d 100644
--- a/tests/src/test/java/io/jooby/i3508/Issue3508.java
+++ b/tests/src/test/java/io/jooby/i3508/Issue3508.java
@@ -5,19 +5,37 @@
*/
package io.jooby.i3508;
+import static io.jooby.StatusCode.UNPROCESSABLE_ENTITY_CODE;
import static io.jooby.validation.BeanValidator.validate;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.util.List;
+import java.util.Map;
+
+import org.assertj.core.api.Assertions;
+
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import io.jooby.Extension;
import io.jooby.StatusCode;
+import io.jooby.avaje.validator.AvajeValidatorModule;
import io.jooby.hibernate.validator.HibernateValidatorModule;
+import io.jooby.i3508.data.AvajeNewAccountRequest;
+import io.jooby.i3508.data.HbvNewAccountRequest;
+import io.jooby.i3508.data.NewAccountRequest;
+import io.jooby.i3508.data.Person;
import io.jooby.jackson.JacksonModule;
import io.jooby.junit.ServerTest;
import io.jooby.junit.ServerTestRunner;
import io.jooby.validation.BeanValidator;
+import io.jooby.validation.ValidationResult;
import okhttp3.MediaType;
import okhttp3.RequestBody;
public class Issue3508 {
+ private static final StatusCode STATUS_CODE = StatusCode.UNPROCESSABLE_ENTITY;
+ public static final String DEFAULT_TITLE = "Validation failed";
+
@ServerTest
public void shouldValidateUsingProxy(ServerTestRunner runner) {
runner
@@ -53,4 +71,167 @@ public void shouldValidateUsingProxy(ServerTestRunner runner) {
});
});
}
+
+ @ServerTest
+ public void avajeValidatorTest(ServerTestRunner runner) {
+ validatorTest(
+ runner,
+ new AvajeValidatorModule().validationTitle(DEFAULT_TITLE).statusCode(STATUS_CODE),
+ new AvajeNewAccountRequest());
+ }
+
+ @ServerTest
+ public void hibernateValidatorTest(ServerTestRunner runner) {
+ validatorTest(
+ runner,
+ new HibernateValidatorModule().validationTitle(DEFAULT_TITLE).statusCode(STATUS_CODE),
+ new HbvNewAccountRequest());
+ }
+
+ private void validatorTest(
+ ServerTestRunner runner, Extension extension, NewAccountRequest request) {
+ var sizeLabel = extension instanceof HibernateValidatorModule ? "size" : "length";
+ var json = JsonMapper.builder().build();
+ runner
+ .use(() -> new App3508(extension))
+ .ready(
+ http -> {
+ http.post(
+ "/create-person",
+ RequestBody.create(
+ json.writeValueAsBytes(new Person(null, "Last Name")),
+ MediaType.get("application/json")),
+ rsp -> {
+ assertEquals(UNPROCESSABLE_ENTITY_CODE, rsp.code());
+ var actualResult = json.readValue(rsp.body().string(), ValidationResult.class);
+ var fieldError =
+ new ValidationResult.Error(
+ "firstName",
+ List.of("must not be empty"),
+ ValidationResult.ErrorType.FIELD);
+ var expectedResult = buildResult(List.of(fieldError));
+
+ assertThat(expectedResult)
+ .usingRecursiveComparison()
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
+ .isEqualTo(actualResult);
+ });
+
+ http.post(
+ "/create-array-of-persons",
+ RequestBody.create(
+ json.writeValueAsBytes(
+ List.of(
+ new Person("First Name", "Last Name"),
+ new Person(null, "Last Name 2"))),
+ MediaType.get("application/json")),
+ rsp -> {
+ assertEquals(UNPROCESSABLE_ENTITY_CODE, rsp.code());
+ var actualResult = json.readValue(rsp.body().string(), ValidationResult.class);
+
+ var fieldError =
+ new ValidationResult.Error(
+ "firstName",
+ List.of("must not be empty"),
+ ValidationResult.ErrorType.FIELD);
+ var expectedResult = buildResult(List.of(fieldError));
+
+ assertThat(expectedResult)
+ .usingRecursiveComparison()
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
+ .isEqualTo(actualResult);
+ });
+
+ http.post(
+ "/create-list-of-persons",
+ RequestBody.create(
+ json.writeValueAsBytes(
+ List.of(
+ new Person("First Name", "Last Name"),
+ new Person(null, "Last Name 2"))),
+ MediaType.get("application/json")),
+ rsp -> {
+ assertEquals(UNPROCESSABLE_ENTITY_CODE, rsp.code());
+ var actualResult = json.readValue(rsp.body().string(), ValidationResult.class);
+ var fieldError =
+ new ValidationResult.Error(
+ "firstName",
+ List.of("must not be empty"),
+ ValidationResult.ErrorType.FIELD);
+ var expectedResult = buildResult(List.of(fieldError));
+
+ assertThat(expectedResult)
+ .usingRecursiveComparison()
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
+ .isEqualTo(actualResult);
+ });
+
+ http.post(
+ "/create-map-of-persons",
+ RequestBody.create(
+ json.writeValueAsBytes(
+ Map.of(
+ new Person("First Name", "Last Name"),
+ new Person(null, "Last Name 2"))),
+ MediaType.get("application/json")),
+ rsp -> {
+ assertEquals(UNPROCESSABLE_ENTITY_CODE, rsp.code());
+ var actualResult = json.readValue(rsp.body().string(), ValidationResult.class);
+ var fieldError =
+ new ValidationResult.Error(
+ "firstName",
+ List.of("must not be empty"),
+ ValidationResult.ErrorType.FIELD);
+ ValidationResult expectedResult = buildResult(List.of(fieldError));
+
+ Assertions.assertThat(expectedResult)
+ .usingRecursiveComparison()
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
+ .isEqualTo(actualResult);
+ });
+
+ request.setLogin("jk");
+ request.setPassword("123");
+ request.setConfirmPassword("1234");
+ request.setPerson(new Person(null, "Last Name"));
+ http.post(
+ "/create-new-account",
+ RequestBody.create(
+ json.writeValueAsBytes(request), MediaType.get("application/json")),
+ rsp -> {
+ assertEquals(UNPROCESSABLE_ENTITY_CODE, rsp.code());
+ var actualResult = json.readValue(rsp.body().string(), ValidationResult.class);
+ var errors =
+ List.of(
+ new ValidationResult.Error(
+ "password",
+ List.of(sizeLabel + " must be between 8 and 24"),
+ ValidationResult.ErrorType.FIELD),
+ new ValidationResult.Error(
+ "person.firstName",
+ List.of("must not be empty"),
+ ValidationResult.ErrorType.FIELD),
+ new ValidationResult.Error(
+ "confirmPassword",
+ List.of(sizeLabel + " must be between 8 and 24"),
+ ValidationResult.ErrorType.FIELD),
+ new ValidationResult.Error(
+ "login",
+ List.of(sizeLabel + " must be between 3 and 16"),
+ ValidationResult.ErrorType.FIELD));
+
+ ValidationResult expectedResult = buildResult(errors);
+
+ Assertions.assertThat(expectedResult)
+ .usingRecursiveComparison()
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors")
+ .ignoringCollectionOrderInFieldsMatchingRegexes("errors\\.messages")
+ .isEqualTo(actualResult);
+ });
+ });
+ }
+
+ private ValidationResult buildResult(List errors) {
+ return new ValidationResult(DEFAULT_TITLE, UNPROCESSABLE_ENTITY_CODE, errors);
+ }
}
diff --git a/tests/src/test/java/io/jooby/i3508/data/AvajeNewAccountRequest.java b/tests/src/test/java/io/jooby/i3508/data/AvajeNewAccountRequest.java
new file mode 100644
index 0000000000..87882d2763
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3508/data/AvajeNewAccountRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3508.data;
+
+import jakarta.validation.Valid;
+
+@Valid
+@AvajePasswordsShouldMatch
+public class AvajeNewAccountRequest extends NewAccountRequest {}
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatch.java b/tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatch.java
similarity index 88%
rename from modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatch.java
rename to tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatch.java
index dcb06956c8..4f0a85d374 100644
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatch.java
+++ b/tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatch.java
@@ -3,7 +3,7 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.avaje.validator.app;
+package io.jooby.i3508.data;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
@@ -17,7 +17,7 @@
@Constraint(validatedBy = {})
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
-public @interface PasswordsShouldMatch {
+public @interface AvajePasswordsShouldMatch {
String message() default "Passwords should match";
Class>[] groups() default {};
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatchValidator.java b/tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatchValidator.java
similarity index 69%
rename from modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatchValidator.java
rename to tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatchValidator.java
index 11d43f61c6..81a466f315 100644
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/PasswordsShouldMatchValidator.java
+++ b/tests/src/test/java/io/jooby/i3508/data/AvajePasswordsShouldMatchValidator.java
@@ -3,16 +3,17 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.avaje.validator.app;
+package io.jooby.i3508.data;
import io.avaje.validation.adapter.AbstractConstraintAdapter;
import io.avaje.validation.adapter.ConstraintAdapter;
import io.avaje.validation.adapter.ValidationContext.AdapterCreateRequest;
-@ConstraintAdapter(PasswordsShouldMatch.class)
-public class PasswordsShouldMatchValidator extends AbstractConstraintAdapter {
+@ConstraintAdapter(AvajePasswordsShouldMatch.class)
+public class AvajePasswordsShouldMatchValidator
+ extends AbstractConstraintAdapter {
- public PasswordsShouldMatchValidator(AdapterCreateRequest request) {
+ public AvajePasswordsShouldMatchValidator(AdapterCreateRequest request) {
super(request);
}
diff --git a/tests/src/test/java/io/jooby/i3508/data/HbvNewAccountRequest.java b/tests/src/test/java/io/jooby/i3508/data/HbvNewAccountRequest.java
new file mode 100644
index 0000000000..af804929dc
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3508/data/HbvNewAccountRequest.java
@@ -0,0 +1,12 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3508.data;
+
+import jakarta.validation.Valid;
+
+@Valid
+@HbvPasswordsShouldMatch
+public class HbvNewAccountRequest extends NewAccountRequest {}
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatch.java b/tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatch.java
similarity index 81%
rename from modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatch.java
rename to tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatch.java
index b2cb9aee5b..7f75ab3f5e 100644
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatch.java
+++ b/tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatch.java
@@ -3,7 +3,7 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.hibernate.validator.app;
+package io.jooby.i3508.data;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
@@ -15,10 +15,10 @@
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
-@Constraint(validatedBy = PasswordsShouldMatchValidator.class)
+@Constraint(validatedBy = HbvPasswordsShouldMatchValidator.class)
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
-public @interface PasswordsShouldMatch {
+public @interface HbvPasswordsShouldMatch {
String message() default "Passwords should match";
Class>[] groups() default {};
diff --git a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatchValidator.java b/tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatchValidator.java
similarity index 76%
rename from modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatchValidator.java
rename to tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatchValidator.java
index 594659cf32..05dc3f7334 100644
--- a/modules/jooby-hibernate-validator/src/test/java/io/jooby/hibernate/validator/app/PasswordsShouldMatchValidator.java
+++ b/tests/src/test/java/io/jooby/i3508/data/HbvPasswordsShouldMatchValidator.java
@@ -3,13 +3,13 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.hibernate.validator.app;
+package io.jooby.i3508.data;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
-public class PasswordsShouldMatchValidator
- implements ConstraintValidator {
+public class HbvPasswordsShouldMatchValidator
+ implements ConstraintValidator {
@Override
public boolean isValid(NewAccountRequest request, ConstraintValidatorContext constraintContext) {
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/NewAccountRequest.java b/tests/src/test/java/io/jooby/i3508/data/NewAccountRequest.java
similarity index 95%
rename from modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/NewAccountRequest.java
rename to tests/src/test/java/io/jooby/i3508/data/NewAccountRequest.java
index 435d7656df..64fbd18a20 100644
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/NewAccountRequest.java
+++ b/tests/src/test/java/io/jooby/i3508/data/NewAccountRequest.java
@@ -3,7 +3,7 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.avaje.validator.app;
+package io.jooby.i3508.data;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
@@ -11,7 +11,6 @@
import jakarta.validation.constraints.Size;
@Valid
-@PasswordsShouldMatch
public class NewAccountRequest {
@NotNull @NotEmpty
@Size(min = 3, max = 16)
diff --git a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Person.java b/tests/src/test/java/io/jooby/i3508/data/Person.java
similarity index 94%
rename from modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Person.java
rename to tests/src/test/java/io/jooby/i3508/data/Person.java
index 260d67587d..3e1896a7cd 100644
--- a/modules/jooby-avaje-validator/src/test/java/io/jooby/avaje/validator/app/Person.java
+++ b/tests/src/test/java/io/jooby/i3508/data/Person.java
@@ -3,7 +3,7 @@
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
-package io.jooby.avaje.validator.app;
+package io.jooby.i3508.data;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;