Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.xpeho.spring_boot_java_random_user.domain.usecases;

import com.xpeho.spring_boot_java_random_user.domain.services.LocalUserService;
import com.xpeho.spring_boot_java_random_user.domain.exceptions.UserNotFoundException;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -12,6 +13,7 @@ public DeleteUserByIdUseCase(LocalUserService userService) {
}

public void execute(long id) {
userService.getById(id).orElseThrow(() -> new UserNotFoundException(id));
userService.deleteById(id);
Comment on lines 15 to 17
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

This use case now calls userService.getById(id).orElseThrow(...) before deleting. Existing unit tests for DeleteUserByIdUseCase that don't stub getById will start failing with a NullPointerException (Mockito returns null by default for unstubbed calls returning Optional). Update the tests to stub getById and/or adjust interaction verifications accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 17
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

execute() does a separate getById() check followed by deleteById(). With JpaRepository.deleteById, there is still a race where the row can be deleted between those calls, causing EmptyResultDataAccessException and a 500 instead of 404. Consider wrapping userService.deleteById(id) in a try/catch that translates EmptyResultDataAccessException (or equivalent) into UserNotFoundException, or exposing a service-level delete that is idempotent / returns whether a row was deleted.

Copilot uses AI. Check for mistakes.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ ResponseEntity<List<UserEntity>> filterUsers(
@ApiResponse(responseCode = "204", description = "User successfully deleted")
@ApiResponse(responseCode = "404", description = "The requested user does not exist")
@ApiResponse(responseCode = "500", description = "Internal server error")
void deleteUserById(
ResponseEntity<Void> deleteUserById(
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

The deleteUserById method signature is indented differently than the other endpoints in this interface. Align its indentation with the surrounding methods to keep formatting consistent (and avoid potential style/lint failures if enforced).

Suggested change
ResponseEntity<Void> deleteUserById(
ResponseEntity<Void> deleteUserById(

Copilot uses AI. Check for mistakes.
@PathVariable
int id
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,13 @@ public ResponseEntity<List<UserEntity>> filterUsers(
}

@Override
public void deleteUserById(int id) {
public ResponseEntity<Void> deleteUserById(int id) {
try {
deleteUserUseCase.execute(id);
return ResponseEntity.noContent().build();
} catch (UserNotFoundException e) {
logUserNotFound(e);
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,12 @@ void shouldReturnCreatedWhenCreateUserSucceeds() {
@DisplayName("Should return 204 when deleteUserById succeeds")
void shouldReturnNoContentWhenDeleteUserByIdSucceeds() {
int userId = 42;
doNothing().when(deleteUserUseCase).execute(userId);

userHandler.deleteUserById(userId);
ResponseEntity<Void> response = userHandler.deleteUserById(userId);

assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
assertNull(response.getBody());
verify(deleteUserUseCase, times(1)).execute(userId);
}

Expand All @@ -161,8 +164,10 @@ void shouldLogWarningWhenDeleteUserByIdFails() {
int userId = 123;
doThrow(new UserNotFoundException(userId)).when(deleteUserUseCase).execute(userId);

userHandler.deleteUserById(userId);
ResponseEntity<Void> response = userHandler.deleteUserById(userId);

assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
assertNull(response.getBody());
verify(deleteUserUseCase, times(1)).execute(userId);
}

Expand Down
6 changes: 6 additions & 0 deletions src/test/java/feature/SpringIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -46,4 +47,9 @@ protected void executePost(String path, Object payload) {
HttpEntity<Object> request = new HttpEntity<>(payload, headers);
latestResponse = restTemplate.postForEntity(url, request, String.class);
}

protected void executeDelete(String path) {
String url = "http://localhost:" + port + path;
latestResponse = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);
}
Comment thread
Theo-lbg marked this conversation as resolved.
Comment thread
Theo-lbg marked this conversation as resolved.
}
17 changes: 17 additions & 0 deletions src/test/resources/features/delete_user.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Feature: Delete user endpoint
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

The file starts with extra indentation before the Feature: keyword. Other feature files in this repo (e.g., get_user.feature) start Feature: at column 1; aligning to that keeps the Gherkin style consistent and avoids tooling/parser edge cases.

Copilot uses AI. Check for mistakes.

Scenario: Delete a user successfully after creation
Given a valid user payload for creation
When the client call to POST /random-users
Then the response status should be 201
And the user profile
| id | <generated_id> |
| firstname | Emma |
When the client call to DELETE the created user
Then the response status should be 204
When the client call to GET the deleted user
Comment on lines +10 to +12
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

The feature introduces steps ("DELETE the created user" / "GET the deleted user") but there are no matching step definitions in src/test/java/feature/StepDefinition.java, so Cucumber will fail with undefined steps. Add corresponding @When bindings that use createdUserId and call executeDelete("/random-users/" + createdUserId) and executeGet("/random-users/" + createdUserId) (with an assertion that createdUserId is set).

Suggested change
When the client call to DELETE the created user
Then the response status should be 204
When the client call to GET the deleted user
When the client call to DELETE /random-users/<generated_id>
Then the response status should be 204
When the client call to GET /random-users/<generated_id>

Copilot uses AI. Check for mistakes.
Then the response status should be 404

Scenario: Delete a user that does not exist
When the client call to DELETE /random-users/999
Then the response status should be 404
Comment on lines +15 to +17
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

This scenario uses the step text "When the client call to DELETE /random-users/999", but there is no matching step definition in StepDefinition.java. Add a parameterized @When step for DELETE /random-users/{int} that calls executeDelete("/random-users/" + id).

Copilot uses AI. Check for mistakes.
Loading