From 681d46578875e83f0e33210809c7511bca45a7c7 Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:20:46 -0300 Subject: [PATCH 1/6] test: add integration tests for SignUpResource --- .../presentation/SignUpResourceTest.java | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java diff --git a/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java b/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java new file mode 100644 index 0000000..1b517c6 --- /dev/null +++ b/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java @@ -0,0 +1,162 @@ +package dev.matheuscruz.presentation; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import dev.matheuscruz.domain.User; +import dev.matheuscruz.domain.UserRepository; +import dev.matheuscruz.presentation.data.Problem; +import io.quarkus.narayana.jta.QuarkusTransaction; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class SignUpResourceTest { + + @Inject + UserRepository userRepository; + + @BeforeEach + @Transactional + void setUp() { + userRepository.deleteAll(); + } + + @Test + @DisplayName("should return Created when valid SignUpRequest is provided") + void should_returnCreated_when_validSignUpRequestIsProvided() { + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", "Doe"); + + var response = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() + .ifValidationFails().statusCode(201).extract().as(SignUpResource.SignUpResponse.class); + + assertThat(response.id()).isNotNull().isNotBlank(); + assertThat(response.email()).isEqualTo(request.email()); + } + + @Test + @DisplayName("should return Conflict when email already exists") + void should_returnConflict_when_emailAlreadyExists() { + var existingEmail = "existing.user@email.com"; + var existingUser = User.create(existingEmail, "some-hashed-password", "Existing", "User"); + + QuarkusTransaction.requiringNew().run(() -> { + userRepository.persist(existingUser); + }); + + var request = new SignUpResource.SignUpRequest(existingEmail, "a-different-password", "New", "Person"); + + var response = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() + .ifValidationFails().statusCode(409).extract().as(Problem.class); + + assertThat(response.message()).isEqualTo("Este nome de usuário já foi usado. Tente outro."); + assertThat(userRepository.count()).isEqualTo(1); + assertThat(userRepository.existsByEmail(existingEmail)).isTrue(); + } + + @Test + @DisplayName("should return Bad Request when email is not valid") + void should_returnBadRequest_when_emailIsNotValid() { + var request = new SignUpResource.SignUpRequest("invalid-email.com", "password-1234", "John", "Doe"); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.email", "must be a well-formed email address")); + } + + @Test + @DisplayName("should return Bad Request when password is too short") + void should_returnBadRequest_when_passwordIsTooShort() { + var request = new SignUpResource.SignUpRequest("test@email.com", "1234", "John", "Doe"); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.password", "size must be between 8 and 32")); + } + + @Test + @DisplayName("should return Bad Request when password is too long") + void should_returnBadRequest_when_passwordIsTooLong() { + var request = new SignUpResource.SignUpRequest("test@email.com", "12345678910111213141516171819202122", "John", + "Doe"); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.password", "size must be between 8 and 32")); + } + + @Test + @DisplayName("should return Bad Request when password is blank") + void should_returnBadRequest_when_passwordIsBlank() { + var request = new SignUpResource.SignUpRequest("test@email.com", "", "John", "Doe"); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(2).extracting(Violation::field, Violation::message) + .containsExactlyInAnyOrder(tuple("signUp.req.password", "size must be between 8 and 32"), + tuple("signUp.req.password", "must not be blank") + + ); + } + + @Test + @DisplayName("should return Bad Request when first name is blank") + void should_returnBadRequest_when_firstNameIsBlank() { + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "", "Doe"); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.firstName", "must not be blank")); + } + + @Test + @DisplayName("should return Bad Request when last name is blank") + void should_returnBadRequest_when_lastNameIsBlank() { + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", ""); + + var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.lastName", "must not be blank")); + } + + @Test + @DisplayName("should return Bad Request when e-mail is blank") + void should_returnBadRequest_when_emailIsBlank() { + var request = new SignUpResource.SignUpRequest("", "password-1234", "John", "Doe"); + + var problem = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() + .ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + + assertThat(problem.title()).isEqualTo("Constraint Violation"); + assertThat(problem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactlyInAnyOrder(tuple("signUp.req.email", "must not be blank")); + } + + public record Violation(String field, String message) { + } + + public record ValidationProblem(String title, int status, java.util.List violations) { + } +} From 92f60c90476372611524e927b4bbf8a73d32dce9 Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:20:55 -0300 Subject: [PATCH 2/6] fix: reorder validation annotations in SignUpRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java diff --git c/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java i/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java index 224eec6..8b42abb 100644 --- c/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java +++ i/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java @@ -4,10 +4,9 @@ import dev.matheuscruz.domain.User; import dev.matheuscruz.domain.UserRepository; import dev.matheuscruz.infra.security.AESAdapter; import dev.matheuscruz.infra.security.BCryptAdapter; -import dev.matheuscruz.presentation.data.ProblemResponse; +import dev.matheuscruz.presentation.data.Problem; import io.quarkus.logging.Log; import io.quarkus.narayana.jta.QuarkusTransaction; -import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; @@ -17,7 +16,6 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.core.Response; @Path("/api/sign-up") -@PermitAll public class SignUpResource { UserRepository userRepository; @@ -35,11 +33,10 @@ public class SignUpResource { boolean exists = this.userRepository.existsByEmail(req.email()); if (exists) { return Response.status(Response.Status.CONFLICT) - .entity(new ProblemResponse("Este nome de usuário já foi usado. Tente outro.")).build(); + .entity(new Problem("Este nome de usuário já foi usado. Tente outro.")).build(); } - User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName(), - req.phoneNumber()); + User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName()); QuarkusTransaction.requiringNew().run(() -> { this.userRepository.persist(user); @@ -49,8 +46,8 @@ public class SignUpResource { .build(); } - public record SignUpRequest(@Email String email, @NotBlank @Size(min = 8, max = 32) String password, - @NotBlank String firstName, @NotBlank String lastName, @NotBlank String phoneNumber) { + public record SignUpRequest(@NotBlank @Email String email, @NotBlank @Size(min = 8, max = 32) String password, + @NotBlank String firstName, @NotBlank String lastName) { } public record SignUpResponse(String id, String email) { --- .../matheuscruz/presentation/SignUpResource.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java b/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java index 224eec6..8b42abb 100644 --- a/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java +++ b/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java @@ -4,10 +4,9 @@ import dev.matheuscruz.domain.UserRepository; import dev.matheuscruz.infra.security.AESAdapter; import dev.matheuscruz.infra.security.BCryptAdapter; -import dev.matheuscruz.presentation.data.ProblemResponse; +import dev.matheuscruz.presentation.data.Problem; import io.quarkus.logging.Log; import io.quarkus.narayana.jta.QuarkusTransaction; -import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; @@ -17,7 +16,6 @@ import jakarta.ws.rs.core.Response; @Path("/api/sign-up") -@PermitAll public class SignUpResource { UserRepository userRepository; @@ -35,11 +33,10 @@ public Response signUp(@Valid SignUpRequest req) { boolean exists = this.userRepository.existsByEmail(req.email()); if (exists) { return Response.status(Response.Status.CONFLICT) - .entity(new ProblemResponse("Este nome de usuário já foi usado. Tente outro.")).build(); + .entity(new Problem("Este nome de usuário já foi usado. Tente outro.")).build(); } - User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName(), - req.phoneNumber()); + User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName()); QuarkusTransaction.requiringNew().run(() -> { this.userRepository.persist(user); @@ -49,8 +46,8 @@ public Response signUp(@Valid SignUpRequest req) { .build(); } - public record SignUpRequest(@Email String email, @NotBlank @Size(min = 8, max = 32) String password, - @NotBlank String firstName, @NotBlank String lastName, @NotBlank String phoneNumber) { + public record SignUpRequest(@NotBlank @Email String email, @NotBlank @Size(min = 8, max = 32) String password, + @NotBlank String firstName, @NotBlank String lastName) { } public record SignUpResponse(String id, String email) { From bb974c5bc493ec0191414f8998bdd2ebe873eabd Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:21:07 -0300 Subject: [PATCH 3/6] test: add test profile configurations in application.properties diff --git c/timeless-api/src/main/resources/application.properties i/timeless-api/src/main/resources/application.properties index f7cc93b..ab0eae2 100644 --- c/timeless-api/src/main/resources/application.properties +++ i/timeless-api/src/main/resources/application.properties @@ -33,4 +33,19 @@ quarkus.quinoa.build-dir=dist/timeless/browser # jwt mp.jwt.verify.publickey=${JWT_PUBLIC_KEY} mp.jwt.verify.issuer=https://timelessapp.platformoon.com/issuer -smallrye.jwt.sign.key=${JWT_PRIVATE_KEY} \ No newline at end of file +smallrye.jwt.sign.key=${JWT_PRIVATE_KEY} + +# Test Profile +%test.security.sensible.secret=YS0xNi1ieXRlLXNlY3JldA== +%test.whatsapp.incoming-messages.queue-url=test-incoming-queue +%test.whatsapp.messages-processed.queue-url=test-processed-queue + +%test.quarkus.sqs.aws.credentials.static-provider.access-key-id=test +%test.quarkus.sqs.aws.credentials.static-provider.secret-access-key=test +%test.quarkus.sqs.aws.credentials.type=STATIC +%test.quarkus.sqs.aws.region=us-east-1 +%test.quarkus.sqs.endpoint-override=http://127.0.0.1:4566 + +%quarkus.test.arg-line=--add-opens java.base/java.lang=ALL-UNNAMED + +%test.quarkus.scheduler.enabled=false --- .../src/main/resources/application.properties | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/timeless-api/src/main/resources/application.properties b/timeless-api/src/main/resources/application.properties index f7cc93b..ab0eae2 100644 --- a/timeless-api/src/main/resources/application.properties +++ b/timeless-api/src/main/resources/application.properties @@ -33,4 +33,19 @@ quarkus.quinoa.build-dir=dist/timeless/browser # jwt mp.jwt.verify.publickey=${JWT_PUBLIC_KEY} mp.jwt.verify.issuer=https://timelessapp.platformoon.com/issuer -smallrye.jwt.sign.key=${JWT_PRIVATE_KEY} \ No newline at end of file +smallrye.jwt.sign.key=${JWT_PRIVATE_KEY} + +# Test Profile +%test.security.sensible.secret=YS0xNi1ieXRlLXNlY3JldA== +%test.whatsapp.incoming-messages.queue-url=test-incoming-queue +%test.whatsapp.messages-processed.queue-url=test-processed-queue + +%test.quarkus.sqs.aws.credentials.static-provider.access-key-id=test +%test.quarkus.sqs.aws.credentials.static-provider.secret-access-key=test +%test.quarkus.sqs.aws.credentials.type=STATIC +%test.quarkus.sqs.aws.region=us-east-1 +%test.quarkus.sqs.endpoint-override=http://127.0.0.1:4566 + +%quarkus.test.arg-line=--add-opens java.base/java.lang=ALL-UNNAMED + +%test.quarkus.scheduler.enabled=false From 3f0fc24d59589c499c26219dfa0e249fcd5167cd Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:40:34 -0300 Subject: [PATCH 4/6] fix: update SignUpResource to use ProblemResponse and include phone number in SignUpRequest --- .../dev/matheuscruz/presentation/SignUpResource.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java b/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java index 8b42abb..202617f 100644 --- a/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java +++ b/timeless-api/src/main/java/dev/matheuscruz/presentation/SignUpResource.java @@ -4,7 +4,7 @@ import dev.matheuscruz.domain.UserRepository; import dev.matheuscruz.infra.security.AESAdapter; import dev.matheuscruz.infra.security.BCryptAdapter; -import dev.matheuscruz.presentation.data.Problem; +import dev.matheuscruz.presentation.data.ProblemResponse; import io.quarkus.logging.Log; import io.quarkus.narayana.jta.QuarkusTransaction; import jakarta.validation.Valid; @@ -33,10 +33,11 @@ public Response signUp(@Valid SignUpRequest req) { boolean exists = this.userRepository.existsByEmail(req.email()); if (exists) { return Response.status(Response.Status.CONFLICT) - .entity(new Problem("Este nome de usuário já foi usado. Tente outro.")).build(); + .entity(new ProblemResponse("Este nome de usuário já foi usado. Tente outro.")).build(); } - User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName()); + User user = User.create(req.email(), BCryptAdapter.encrypt(req.password()), req.firstName(), req.lastName(), + req.phoneNumber()); QuarkusTransaction.requiringNew().run(() -> { this.userRepository.persist(user); @@ -47,7 +48,7 @@ public Response signUp(@Valid SignUpRequest req) { } public record SignUpRequest(@NotBlank @Email String email, @NotBlank @Size(min = 8, max = 32) String password, - @NotBlank String firstName, @NotBlank String lastName) { + @NotBlank String firstName, @NotBlank String lastName, @NotBlank String phoneNumber) { } public record SignUpResponse(String id, String email) { From cb48f7ce5d69e1a44963f9ed29c49895a2e00b9e Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:54:50 -0300 Subject: [PATCH 5/6] fix: correct property names for WhatsApp message queues in application.properties --- timeless-api/src/main/resources/application.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/timeless-api/src/main/resources/application.properties b/timeless-api/src/main/resources/application.properties index ab0eae2..b4b2f97 100644 --- a/timeless-api/src/main/resources/application.properties +++ b/timeless-api/src/main/resources/application.properties @@ -37,8 +37,9 @@ smallrye.jwt.sign.key=${JWT_PRIVATE_KEY} # Test Profile %test.security.sensible.secret=YS0xNi1ieXRlLXNlY3JldA== -%test.whatsapp.incoming-messages.queue-url=test-incoming-queue -%test.whatsapp.messages-processed.queue-url=test-processed-queue +%test.whatsapp.incoming-message.queue-url=test-incoming-queue +%test.whatsapp.recognized-message.queue-url=test-processed-queue +%test.mp.jwt.verify.publickey=test-public-key %test.quarkus.sqs.aws.credentials.static-provider.access-key-id=test %test.quarkus.sqs.aws.credentials.static-provider.secret-access-key=test From 83c334bb8e1af408ed3d8f69f40466105905256f Mon Sep 17 00:00:00 2001 From: Matheus Oliveira Date: Sun, 4 Jan 2026 08:54:54 -0300 Subject: [PATCH 6/6] test: update SignUpResourceTest to include phone number in requests and responses --- .../presentation/SignUpResourceTest.java | 104 +++++++++++------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java b/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java index 1b517c6..f3fd450 100644 --- a/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java +++ b/timeless-api/src/test/java/dev/matheuscruz/presentation/SignUpResourceTest.java @@ -6,7 +6,7 @@ import dev.matheuscruz.domain.User; import dev.matheuscruz.domain.UserRepository; -import dev.matheuscruz.presentation.data.Problem; +import dev.matheuscruz.presentation.data.ProblemResponse; import io.quarkus.narayana.jta.QuarkusTransaction; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; @@ -30,7 +30,7 @@ void setUp() { @Test @DisplayName("should return Created when valid SignUpRequest is provided") void should_returnCreated_when_validSignUpRequestIsProvided() { - var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", "Doe"); + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", "Doe", "+1234567890"); var response = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() .ifValidationFails().statusCode(201).extract().as(SignUpResource.SignUpResponse.class); @@ -43,16 +43,17 @@ void should_returnCreated_when_validSignUpRequestIsProvided() { @DisplayName("should return Conflict when email already exists") void should_returnConflict_when_emailAlreadyExists() { var existingEmail = "existing.user@email.com"; - var existingUser = User.create(existingEmail, "some-hashed-password", "Existing", "User"); + var existingUser = User.create(existingEmail, "some-hashed-password", "Existing", "User", "+0987654321"); QuarkusTransaction.requiringNew().run(() -> { userRepository.persist(existingUser); }); - var request = new SignUpResource.SignUpRequest(existingEmail, "a-different-password", "New", "Person"); + var request = new SignUpResource.SignUpRequest(existingEmail, "a-different-password", "New", "Person", + "+0987654321"); var response = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() - .ifValidationFails().statusCode(409).extract().as(Problem.class); + .ifValidationFails().statusCode(409).extract().as(ProblemResponse.class); assertThat(response.message()).isEqualTo("Este nome de usuário já foi usado. Tente outro."); assertThat(userRepository.count()).isEqualTo(1); @@ -62,26 +63,29 @@ void should_returnConflict_when_emailAlreadyExists() { @Test @DisplayName("should return Bad Request when email is not valid") void should_returnBadRequest_when_emailIsNotValid() { - var request = new SignUpResource.SignUpRequest("invalid-email.com", "password-1234", "John", "Doe"); + var request = new SignUpResource.SignUpRequest("invalid-email.com", "password-1234", "John", "Doe", + "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactly(tuple("signUp.req.email", "must be a well-formed email address")); } @Test @DisplayName("should return Bad Request when password is too short") void should_returnBadRequest_when_passwordIsTooShort() { - var request = new SignUpResource.SignUpRequest("test@email.com", "1234", "John", "Doe"); + var request = new SignUpResource.SignUpRequest("test@email.com", "1234", "John", "Doe", "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactly(tuple("signUp.req.password", "size must be between 8 and 32")); } @@ -89,26 +93,28 @@ void should_returnBadRequest_when_passwordIsTooShort() { @DisplayName("should return Bad Request when password is too long") void should_returnBadRequest_when_passwordIsTooLong() { var request = new SignUpResource.SignUpRequest("test@email.com", "12345678910111213141516171819202122", "John", - "Doe"); + "Doe", "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactly(tuple("signUp.req.password", "size must be between 8 and 32")); } @Test @DisplayName("should return Bad Request when password is blank") void should_returnBadRequest_when_passwordIsBlank() { - var request = new SignUpResource.SignUpRequest("test@email.com", "", "John", "Doe"); + var request = new SignUpResource.SignUpRequest("test@email.com", "", "John", "Doe", "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(2).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(2).extracting(Violation::field, Violation::message) .containsExactlyInAnyOrder(tuple("signUp.req.password", "size must be between 8 and 32"), tuple("signUp.req.password", "must not be blank") @@ -118,45 +124,61 @@ void should_returnBadRequest_when_passwordIsBlank() { @Test @DisplayName("should return Bad Request when first name is blank") void should_returnBadRequest_when_firstNameIsBlank() { - var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "", "Doe"); + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "", "Doe", "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactly(tuple("signUp.req.firstName", "must not be blank")); } @Test @DisplayName("should return Bad Request when last name is blank") void should_returnBadRequest_when_lastNameIsBlank() { - var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", ""); + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", "", "+1234567890"); - var validationProblem = given().contentType("application/json").body(request).when().post("/api/sign-up").then() - .log().ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); - assertThat(validationProblem.title()).isEqualTo("Constraint Violation"); - assertThat(validationProblem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactly(tuple("signUp.req.lastName", "must not be blank")); } @Test @DisplayName("should return Bad Request when e-mail is blank") void should_returnBadRequest_when_emailIsBlank() { - var request = new SignUpResource.SignUpRequest("", "password-1234", "John", "Doe"); + var request = new SignUpResource.SignUpRequest("", "password-1234", "John", "Doe", "+1234567890"); - var problem = given().contentType("application/json").body(request).when().post("/api/sign-up").then().log() - .ifValidationFails().statusCode(400).extract().as(ValidationProblem.class); + var ProblemResponse = given().contentType("application/json").body(request).when().post("/api/sign-up").then() + .log().ifValidationFails().statusCode(400).extract().as(ValidationProblemResponse.class); - assertThat(problem.title()).isEqualTo("Constraint Violation"); - assertThat(problem.violations()).hasSize(1).extracting(Violation::field, Violation::message) + assertThat(ProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(ProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) .containsExactlyInAnyOrder(tuple("signUp.req.email", "must not be blank")); } + @Test + @DisplayName("should return Bad Request when phone number is blank") + void should_returnBadRequest_when_phoneNumberIsBlank() { + var request = new SignUpResource.SignUpRequest("test@email.com", "password-1234", "John", "Doe", ""); + + var validationProblemResponse = given().contentType("application/json").body(request).when() + .post("/api/sign-up").then().log().ifValidationFails().statusCode(400).extract() + .as(ValidationProblemResponse.class); + + assertThat(validationProblemResponse.title()).isEqualTo("Constraint Violation"); + assertThat(validationProblemResponse.violations()).hasSize(1).extracting(Violation::field, Violation::message) + .containsExactly(tuple("signUp.req.phoneNumber", "must not be blank")); + } + public record Violation(String field, String message) { } - public record ValidationProblem(String title, int status, java.util.List violations) { + public record ValidationProblemResponse(String title, int status, java.util.List violations) { } }