diff --git a/src/test/java/io/spring/api/CurrentUserApiTest.java b/src/test/java/io/spring/api/CurrentUserApiTest.java index 08e8ece2e..66beb9c75 100644 --- a/src/test/java/io/spring/api/CurrentUserApiTest.java +++ b/src/test/java/io/spring/api/CurrentUserApiTest.java @@ -1,6 +1,7 @@ package io.spring.api; import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -176,4 +177,275 @@ public void should_get_401_if_not_login() throws Exception { .then() .statusCode(401); } + + @Test + public void should_get_error_if_username_exists_when_update_user_profile() throws Exception { + String newEmail = "newemail@example.com"; + String newBio = "updated"; + String newUsername = "existinguser"; + + Map param = prepareUpdateParam(newEmail, newBio, newUsername); + + when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.empty()); + when(userRepository.findByUsername(eq(newUsername))) + .thenReturn(Optional.of(new User("other@example.com", newUsername, "123", "", ""))); + + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(422) + .body("errors.username[0]", equalTo("username already exist")); + } + + @Test + public void should_get_error_if_both_email_and_username_exist_when_update() throws Exception { + String newEmail = "taken@example.com"; + String newBio = "updated"; + String newUsername = "takenuser"; + + Map param = prepareUpdateParam(newEmail, newBio, newUsername); + + when(userRepository.findByEmail(eq(newEmail))) + .thenReturn(Optional.of(new User(newEmail, "someone", "123", "", ""))); + when(userRepository.findByUsername(eq(newUsername))) + .thenReturn(Optional.of(new User("other@example.com", newUsername, "123", "", ""))); + + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(422) + .body("errors", hasKey("email")) + .body("errors", hasKey("username")); + } + + @Test + public void should_update_user_profile_with_only_bio() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("bio", "new bio content"); + } + }); + } + }; + + when(userRepository.findByUsername(eq(""))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq(""))).thenReturn(Optional.empty()); + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(200); + } + + @Test + public void should_update_user_profile_with_only_image() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("image", "https://example.com/newimage.jpg"); + } + }); + } + }; + + when(userRepository.findByUsername(eq(""))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq(""))).thenReturn(Optional.empty()); + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(200); + } + + @Test + public void should_get_401_with_invalid_token_on_update() throws Exception { + String invalidToken = "invalidtoken"; + when(jwtService.getSubFromToken(eq(invalidToken))).thenReturn(Optional.empty()); + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("bio", "updated bio"); + } + }); + } + }; + + given() + .contentType("application/json") + .header("Authorization", "Token " + invalidToken) + .body(param) + .when() + .put("/user") + .then() + .statusCode(401); + } + + @Test + public void should_get_error_for_invalid_email_format_on_update() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "not-an-email"); + } + }); + } + }; + + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(422) + .body("errors", hasKey("email")); + } + + @Test + public void should_allow_update_with_own_existing_email() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", email); + put("bio", "updated bio"); + } + }); + } + }; + + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user)); + when(userRepository.findByUsername(eq(""))).thenReturn(Optional.empty()); + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(200); + } + + @Test + public void should_allow_update_with_own_existing_username() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("username", username); + put("bio", "updated bio"); + } + }); + } + }; + + when(userRepository.findByUsername(eq(username))).thenReturn(Optional.of(user)); + when(userRepository.findByEmail(eq(""))).thenReturn(Optional.empty()); + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(200); + } + + @Test + public void should_return_correct_response_structure_on_get_current_user() throws Exception { + when(userQueryService.findById(any())).thenReturn(Optional.of(userData)); + + given() + .header("Authorization", "Token " + token) + .contentType("application/json") + .when() + .get("/user") + .then() + .statusCode(200) + .body("user", hasKey("email")) + .body("user", hasKey("username")) + .body("user", hasKey("bio")) + .body("user", hasKey("image")) + .body("user", hasKey("token")); + } + + @Test + public void should_return_correct_response_structure_on_update() throws Exception { + String newEmail = "newemail@example.com"; + String newBio = "updated"; + String newUsername = "newusernamee"; + + Map param = prepareUpdateParam(newEmail, newBio, newUsername); + + when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.empty()); + when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); + + given() + .contentType("application/json") + .header("Authorization", "Token " + token) + .body(param) + .when() + .put("/user") + .then() + .statusCode(200) + .body("user", hasKey("email")) + .body("user", hasKey("username")) + .body("user", hasKey("bio")) + .body("user", hasKey("image")) + .body("user", hasKey("token")); + } } diff --git a/src/test/java/io/spring/api/UsersApiTest.java b/src/test/java/io/spring/api/UsersApiTest.java index 9074f2edc..c558a7cbd 100644 --- a/src/test/java/io/spring/api/UsersApiTest.java +++ b/src/test/java/io/spring/api/UsersApiTest.java @@ -1,6 +1,8 @@ package io.spring.api; import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -268,4 +270,388 @@ public void should_fail_login_with_wrong_password() throws Exception { .statusCode(422) .body("message", equalTo("invalid email or password")); } + + @Test + public void should_show_error_message_for_blank_email() throws Exception { + String email = ""; + String username = "johnjacob"; + + Map param = prepareRegisterParameter(email, username); + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors.email[0]", notNullValue()); + } + + @Test + public void should_show_error_message_for_blank_password() throws Exception { + String email = "john@jacob.com"; + String username = "johnjacob"; + + when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty()); + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", email); + put("password", ""); + put("username", username); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors.password[0]", equalTo("can't be empty")); + } + + @Test + public void should_show_error_for_missing_username_field() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "john@jacob.com"); + put("password", "johnnyjacob"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors.username[0]", equalTo("can't be empty")); + } + + @Test + public void should_show_error_for_missing_email_field() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("password", "johnnyjacob"); + put("username", "johnjacob"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors.email[0]", notNullValue()); + } + + @Test + public void should_show_error_for_missing_password_field() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "john@jacob.com"); + put("username", "johnjacob"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors.password[0]", equalTo("can't be empty")); + } + + @Test + public void should_show_errors_for_all_blank_fields() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", ""); + put("password", ""); + put("username", ""); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors", hasKey("email")) + .body("errors", hasKey("password")) + .body("errors", hasKey("username")); + } + + @Test + public void should_show_error_for_duplicated_email_and_username() throws Exception { + String email = "john@jacob.com"; + String username = "johnjacob"; + + when(userRepository.findByUsername(eq(username))) + .thenReturn(Optional.of(new User(email, username, "123", "bio", ""))); + when(userRepository.findByEmail(eq(email))) + .thenReturn(Optional.of(new User(email, username, "123", "bio", ""))); + + Map param = prepareRegisterParameter(email, username); + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422) + .body("errors", hasKey("email")) + .body("errors", hasKey("username")); + } + + @Test + public void should_fail_login_with_nonexistent_email() throws Exception { + when(userRepository.findByEmail(eq("nonexistent@example.com"))).thenReturn(Optional.empty()); + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "nonexistent@example.com"); + put("password", "password123"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("message", equalTo("invalid email or password")); + } + + @Test + public void should_fail_login_with_blank_email() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", ""); + put("password", "password123"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors", hasKey("email")); + } + + @Test + public void should_fail_login_with_blank_password() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "john@jacob.com"); + put("password", ""); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors", hasKey("password")); + } + + @Test + public void should_fail_login_with_all_blank_fields() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", ""); + put("password", ""); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors", hasKey("email")) + .body("errors", hasKey("password")); + } + + @Test + public void should_fail_login_with_invalid_email_format() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "notanemail"); + put("password", "password123"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors", hasKey("email")); + } + + @Test + public void should_return_correct_response_structure_on_successful_registration() + throws Exception { + String email = "structure@test.com"; + String username = "structuretest"; + + when(jwtService.toToken(any())).thenReturn("jwt-token"); + User user = new User(email, username, "123", "", defaultAvatar); + UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar); + when(userReadService.findById(any())).thenReturn(userData); + when(userService.createUser(any())).thenReturn(user); + when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty()); + + Map param = prepareRegisterParameter(email, username); + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(201) + .body("user", hasKey("email")) + .body("user", hasKey("username")) + .body("user", hasKey("bio")) + .body("user", hasKey("image")) + .body("user", hasKey("token")); + } + + @Test + public void should_return_correct_response_structure_on_successful_login() throws Exception { + String email = "john@jacob.com"; + String username = "johnjacob2"; + String password = "123"; + + User user = new User(email, username, passwordEncoder.encode(password), "", defaultAvatar); + UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar); + + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user)); + when(userReadService.findById(eq(user.getId()))).thenReturn(userData); + when(jwtService.toToken(any())).thenReturn("jwt-token"); + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", email); + put("password", password); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(200) + .body("user", hasKey("email")) + .body("user", hasKey("username")) + .body("user", hasKey("bio")) + .body("user", hasKey("image")) + .body("user", hasKey("token")); + } } diff --git a/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java b/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java index 12929118a..b2261705e 100644 --- a/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java +++ b/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java @@ -13,7 +13,8 @@ public class DefaultJwtServiceTest { @BeforeEach public void setUp() { - jwtService = new DefaultJwtService("123123123123123123123123123123123123123123123123123123123123", 3600); + jwtService = + new DefaultJwtService("123123123123123123123123123123123123123123123123123123123123", 3600); } @Test