From bf59571268143fa068f1debe6605252bb3d9375c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:36:17 +0000 Subject: [PATCH] Add comprehensive edge case tests for User API controllers - UsersApiTest: Add 14 new tests covering blank/null field registration, duplicate email+username combo, non-existent email login, blank/invalid login fields, validation-prevents-service-call verification, token in login response, and 201 status verification - CurrentUserApiTest: Add 10 new tests covering duplicate username on update, both email+username duplicate, partial field updates (bio-only, image-only), invalid email format on update, malformed auth headers, empty token, own-email reuse allowed, missing auth header scenarios, and full update response body verification Co-Authored-By: mason.batchelor@cognition.ai --- .../io/spring/api/CurrentUserApiTest.java | 241 +++++++++++ src/test/java/io/spring/api/UsersApiTest.java | 399 ++++++++++++++++++ 2 files changed, 640 insertions(+) diff --git a/src/test/java/io/spring/api/CurrentUserApiTest.java b/src/test/java/io/spring/api/CurrentUserApiTest.java index 08e8ece2e..be31a2028 100644 --- a/src/test/java/io/spring/api/CurrentUserApiTest.java +++ b/src/test/java/io/spring/api/CurrentUserApiTest.java @@ -176,4 +176,245 @@ 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@test.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@test.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.email[0]", equalTo("email already exist")) + .body("errors.username[0]", equalTo("username already exist")); + } + + @Test + public void should_update_with_only_bio_field() 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_with_only_image_field() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("image", "https://example.com/new-avatar.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_error_for_invalid_email_format_on_update() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "not-a-valid-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.email[0]", equalTo("should be an email")); + } + + @Test + public void should_get_401_with_only_token_prefix_no_value() throws Exception { + given() + .contentType("application/json") + .header("Authorization", "Token") + .when() + .get("/user") + .then() + .statusCode(401); + } + + @Test + public void should_get_401_with_empty_token_value() throws Exception { + given() + .contentType("application/json") + .header("Authorization", "Token ") + .when() + .get("/user") + .then() + .statusCode(401); + } + + @Test + public void should_allow_update_with_own_existing_email() throws Exception { + String newBio = "updated bio"; + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", email); + put("bio", newBio); + put("username", username); + } + }); + } + }; + + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user)); + when(userRepository.findByUsername(eq(username))).thenReturn(Optional.of(user)); + 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_for_get_user_without_authorization_header() throws Exception { + given().contentType("application/json").when().get("/user").then().statusCode(401); + } + + @Test + public void should_get_401_for_update_without_authorization_header() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("bio", "new bio"); + } + }); + } + }; + + given().contentType("application/json").body(param).when().put("/user").then().statusCode(401); + } + + @Test + public void should_successfully_update_user_profile_with_all_fields() throws Exception { + String newEmail = "updated@example.com"; + String newBio = "updated bio"; + String newUsername = "updateduser"; + + 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.email", equalTo(email)) + .body("user.username", equalTo(username)) + .body("user.token", equalTo(token)); + } } diff --git a/src/test/java/io/spring/api/UsersApiTest.java b/src/test/java/io/spring/api/UsersApiTest.java index 9074f2edc..363367d34 100644 --- a/src/test/java/io/spring/api/UsersApiTest.java +++ b/src/test/java/io/spring/api/UsersApiTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -268,4 +269,402 @@ 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_on_register() 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]", equalTo("can't be empty")); + } + + @Test + public void should_show_error_message_for_blank_password_on_register() 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_all_blank_fields_on_register() 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); + } + + @Test + public void should_show_error_for_null_email_on_register() 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]", equalTo("can't be empty")); + } + + @Test + public void should_show_error_for_null_username_on_register() 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_null_password_on_register() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "john@jacob.com"); + put("username", "johnjacob"); + } + }); + } + }; + + when(userRepository.findByUsername(eq("johnjacob"))).thenReturn(Optional.empty()); + when(userRepository.findByEmail(eq("john@jacob.com"))).thenReturn(Optional.empty()); + + 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_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.username[0]", equalTo("duplicated username")) + .body("errors.email[0]", equalTo("duplicated email")); + } + + @Test + public void should_fail_login_with_nonexistent_email() throws Exception { + String email = "nonexistent@example.com"; + + when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty()); + + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", email); + put("password", "somepassword"); + } + }); + } + }; + + 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", "somepassword"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors.email[0]", equalTo("can't be empty")); + } + + @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.password[0]", equalTo("can't be empty")); + } + + @Test + public void should_fail_login_with_invalid_email_format() throws Exception { + Map param = + new HashMap() { + { + put( + "user", + new HashMap() { + { + put("email", "not-an-email"); + put("password", "somepassword"); + } + }); + } + }; + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users/login") + .then() + .statusCode(422) + .body("errors.email[0]", equalTo("should be an email")); + } + + @Test + public void should_fail_login_with_both_blank_email_and_password() 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); + } + + @Test + public void should_not_call_create_user_when_validation_fails() throws Exception { + String email = "invalid-email"; + String username = ""; + + Map param = prepareRegisterParameter(email, username); + + given() + .contentType("application/json") + .body(param) + .when() + .post("/users") + .then() + .statusCode(422); + + verify(userService, never()).createUser(any()); + } + + @Test + public void should_return_token_in_login_response() throws Exception { + String email = "john@jacob.com"; + String username = "johnjacob2"; + String password = "123"; + String expectedToken = "jwt-token-value"; + + 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(expectedToken); + + 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.token", equalTo(expectedToken)); + } + + @Test + public void should_return_201_status_on_successful_registration() throws Exception { + String email = "new@user.com"; + String username = "newuser"; + + when(jwtService.toToken(any())).thenReturn("token123"); + 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.email", equalTo(email)) + .body("user.username", equalTo(username)) + .body("user.bio", equalTo("")) + .body("user.image", equalTo(defaultAvatar)) + .body("user.token", equalTo("token123")); + } }