Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more meaningful error messages in EventDtoRequestValidator #8058

Merged
merged 3 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Expand Up @@ -479,21 +479,6 @@ public final ResponseEntity<Object> handleUnsupportedSortException(
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionResponse);
}

/**
* Customize the response for EventDtoValidationException.
*
* @param ex the exception
* @param request the current request
* @return a {@code ResponseEntity} message
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There are the same handler for parent class (ValidationException) of EventDtoValidationException, so I think there are not need in this code

*/
@ExceptionHandler(EventDtoValidationException.class)
public final ResponseEntity<Object> handleEventDtoValidationException(
EventDtoValidationException ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(getErrorAttributes(request));
log.warn(ex.getMessage(), ex);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionResponse);
}

/**
* Customize the response for WrongIdException.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,30 @@ public class EventDtoRequestValidator
*/
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value instanceof AddEventDtoRequest addEventDtoRequest) {
validateDateLocations(addEventDtoRequest.getDatesLocations());
convertToUTC(addEventDtoRequest.getDatesLocations());
validateEventDateLocations(addEventDtoRequest.getDatesLocations());
validateTags(addEventDtoRequest.getTags());
} else if (value instanceof UpdateEventRequestDto updateEventDto) {
validateDateLocations(updateEventDto.getDatesLocations());
convertToUTC(updateEventDto.getDatesLocations());
validateEventDateLocations(updateEventDto.getDatesLocations());
validateTags(updateEventDto.getTags());
} else {
try {
switch (value) {
case AddEventDtoRequest addEventDtoRequest -> {
validateDateLocations(addEventDtoRequest.getDatesLocations());
convertToUTC(addEventDtoRequest.getDatesLocations());
validateEventDateLocations(addEventDtoRequest.getDatesLocations());
validateTags(addEventDtoRequest.getTags());
}
case UpdateEventRequestDto updateEventDto -> {
validateDateLocations(updateEventDto.getDatesLocations());
convertToUTC(updateEventDto.getDatesLocations());
validateEventDateLocations(updateEventDto.getDatesLocations());
validateTags(updateEventDto.getTags());
}
default -> {
return false;
}
}
return true;
} catch (EventDtoValidationException ex) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(ex.getMessage()).addConstraintViolation();
return false;
}
return true;
}

private <T extends AbstractEventDateLocationDto> void validateDateLocations(List<T> dates) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,103 @@
import greencity.dto.event.AddEventDtoRequest;
import greencity.dto.event.UpdateEventDateLocationDto;
import greencity.dto.event.UpdateEventRequestDto;
import greencity.exception.exceptions.EventDtoValidationException;
import greencity.exception.exceptions.InvalidURLException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import jakarta.validation.ConstraintValidatorContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

@ExtendWith(SpringExtension.class)
class EventDtoRequestValidatorTest {
@InjectMocks
private EventDtoRequestValidator validator;

@Mock
private ConstraintValidatorContext constraintValidatorContext;

@Mock
private ConstraintValidatorContext.ConstraintViolationBuilder violationBuilder;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);

Choose a reason for hiding this comment

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

Ideally, the mocks should be closed, so that Mockito will function properly.

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/MockitoAnnotations.html

when(constraintValidatorContext.buildConstraintViolationWithTemplate(anyString()))
.thenReturn(violationBuilder);
when(violationBuilder.addConstraintViolation()).thenReturn(constraintValidatorContext);
}

@Test
void withoutDatesException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventDtoWithoutDates();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for empty date locations");
}

@Test
void withZeroDatesException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventDtoWithZeroDates();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for zero date locations");
}

@Test
void withTooManyDatesException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventDtoWithTooManyDates();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for too many date locations");
}

@Test
void withStartDateInPastException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventWithPastStartDate();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for past start date");
}

@Test
void withStartDateAfterFinishDateException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventWithStartDateAfterFinishDate();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for start date after finish date");
}

@Test
void withoutAddressAndLinkException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventWithoutAddressAndLink();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for event without address and link");
}

@Test
void withInvalidLinkException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventWithInvalidLink();
assertThrows(InvalidURLException.class, () -> validator.isValid(addEventDtoRequest, null));
assertThrows(InvalidURLException.class,
() -> validator.isValid(addEventDtoRequest, constraintValidatorContext));
}

@Test
void withTooManyTagsException() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getEventWithTooManyTags();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDtoRequest, null));
boolean isValid = validator.isValid(addEventDtoRequest, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for too many tags");
}

@Test
void validEvent() {
AddEventDtoRequest addEventDtoRequest = ModelUtils.getAddEventDtoRequest();
assertTrue(validator.isValid(addEventDtoRequest, null));
assertTrue(validator.isValid(addEventDtoRequest, constraintValidatorContext));
}

@Test
Expand All @@ -82,46 +109,51 @@ void saveEventWithSameDates() {
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC).plusHours(2L);
addEventDto.getDatesLocations().forEach(e -> e.setStartDate(zonedDateTime));
addEventDto.getDatesLocations().forEach(e -> e.setFinishDate(zonedDateTime));
assertThrows(EventDtoValidationException.class, () -> validator.isValid(addEventDto, null));
boolean isValid = validator.isValid(addEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false same event with same dates");
}

@Test
void updateWithTooManyTagsException() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventDtoWithTooManyDates();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));
boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for too many tags");
}

@Test
void updateWithEmptyDateLocations() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventDtoWithEmptyDateLocations();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));
boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for empty date locations");
}

@Test
void updateEventDtoWithoutDates() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventDtoWithoutDates();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));

boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false event without dates");
}

@Test
void updateWithInvalidLinkException() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventWithoutAddressAndLink();
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));
boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for invalid link");
}

@Test
void updateWithoutLinkAndCoordinates() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventDto();
updateEventDto.getDatesLocations().forEach(e -> e.setOnlineLink(null));
updateEventDto.getDatesLocations().forEach(e -> e.setCoordinates(null));
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));
boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for dto without link and coordinates");
}

@Test
void validEventUpdate() {
UpdateEventRequestDto updateEventDto = ModelUtils.getUpdateEventDto();
assertTrue(validator.isValid(updateEventDto, null));
assertTrue(validator.isValid(updateEventDto, constraintValidatorContext));
}

@Test
Expand All @@ -130,13 +162,14 @@ void updateEventWithSameDates() {
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneOffset.UTC).plusHours(2L);
updateEventDto.getDatesLocations().forEach(e -> e.setStartDate(zonedDateTime));
updateEventDto.getDatesLocations().forEach(e -> e.setFinishDate(zonedDateTime));
assertThrows(EventDtoValidationException.class, () -> validator.isValid(updateEventDto, null));
boolean isValid = validator.isValid(updateEventDto, constraintValidatorContext);
assertFalse(isValid, "Validation should fail and return false for event with same dates");
}

@Test
void invalidObjectType() {
Object value = new Object();
assertFalse(validator.isValid(value, null));
assertFalse(validator.isValid(value, constraintValidatorContext));
}

@Test
Expand All @@ -148,7 +181,7 @@ void invalidDates() {
.onlineLink("http://localhost:8060/swagger-ui.html#/")
.build()))
.tags(List.of("first", "second", "third")).build();
assertThrows(EventDtoValidationException.class,
() -> validator.isValid(updateEventRequestDto, null));
boolean isValid = validator.isValid(updateEventRequestDto, constraintValidatorContext);
assertFalse(isValid);
}
}
Loading