Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions src/test/java/io/spring/api/CurrentUserApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> 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<String, Object> 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<String, Object> param =
new HashMap<String, Object>() {
{
put(
"user",
new HashMap<String, Object>() {
{
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<String, Object> param =
new HashMap<String, Object>() {
{
put(
"user",
new HashMap<String, Object>() {
{
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<String, Object> param =
new HashMap<String, Object>() {
{
put(
"user",
new HashMap<String, Object>() {
{
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<String, Object> param =
new HashMap<String, Object>() {
{
put(
"user",
new HashMap<String, Object>() {
{
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<String, Object> param =
new HashMap<String, Object>() {
{
put(
"user",
new HashMap<String, Object>() {
{
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<String, Object> 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))
Comment on lines +416 to +417
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Test asserts stale pre-update values instead of the updated email and username

The test should_successfully_update_user_profile_with_all_fields sends newEmail = "updated@example.com" and newUsername = "updateduser" in the update request, but then asserts the original email ("john@jacob.com") and username ("johnjacob") in the response body. This happens because userQueryService.findById is mocked to return the pre-update userData object (set up in TestWithCurrentUser.userFixture() at src/test/java/io/spring/api/TestWithCurrentUser.java:38).

Why this is a real test bug

The test name claims to verify a successful update with all fields, but the assertions at lines 416-417 check the old values:

.body("user.email", equalTo(email))       // email = "john@jacob.com"
.body("user.username", equalTo(username))  // username = "johnjacob"

These assertions would pass even if the update endpoint was completely broken (e.g., if userService.updateUser() was never called), because the mock always returns the same stale userData. The test provides false confidence that the update works correctly.

To properly test a successful update with response body verification, the mock for userQueryService.findById should return a UserData object reflecting the new values, or the test should verify that userService.updateUser() was called with the correct parameters.

Impact: The test gives a false sense of coverage — it will pass regardless of whether the update logic is correct.

Prompt for agents
In src/test/java/io/spring/api/CurrentUserApiTest.java, the test should_successfully_update_user_profile_with_all_fields (starting at line 397) needs to be fixed so that the mock for userQueryService.findById returns a UserData with the updated values (newEmail, newUsername, newBio), and the assertions at lines 416-417 should check against newEmail and newUsername instead of the original email and username. Specifically:

1. Around line 406, change the mock to return updated data:
   UserData updatedUserData = new UserData(user.getId(), newEmail, newUsername, newBio, defaultAvatar);
   when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(updatedUserData));

2. At lines 416-417, change the assertions:
   .body("user.email", equalTo(newEmail))
   .body("user.username", equalTo(newUsername))

Alternatively, add a verify call to confirm userService.updateUser() was invoked with the correct parameters.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

.body("user.token", equalTo(token));
}
}
Loading