#57 added validation checks onto the SimpleTrigger

This commit is contained in:
Fabio Formosa
2022-10-28 01:14:53 +02:00
parent ad3eec4abe
commit 33b4d88d52
9 changed files with 205 additions and 44 deletions

View File

@@ -0,0 +1,11 @@
package it.fabioformosa.quartzmanager.api.dto;
public interface RepetitionDTO {
Integer getRepeatCount();
Long getRepeatInterval();
void setRepeatCount(Integer repeatCount);
void setRepeatInterval(Long repeatInterval);
}

View File

@@ -1,21 +1,19 @@
package it.fabioformosa.quartzmanager.api.dto;
import it.fabioformosa.quartzmanager.api.validators.ValidRepetition;
import lombok.*;
import lombok.experimental.SuperBuilder;
import javax.validation.constraints.NotNull;
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class SimpleTriggerInputDTO extends TriggerCommandDTO {
@ValidRepetition
public class SimpleTriggerInputDTO extends TriggerCommandDTO implements RepetitionDTO {
@NotNull
private Integer repeatCount;
@NotNull
private Long repeatInterval;
}

View File

@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.*;
import lombok.experimental.SuperBuilder;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotBlank;
import java.util.Date;
@SuperBuilder
@@ -15,7 +15,7 @@ import java.util.Date;
@Data
public class TriggerCommandDTO {
@NotNull
@NotBlank
private String jobClass;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

View File

@@ -0,0 +1,19 @@
package it.fabioformosa.quartzmanager.api.validators;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Constraint(validatedBy = ValidRepetitionValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidRepetition {
String message() default "Invalid repetition values";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,15 @@
package it.fabioformosa.quartzmanager.api.validators;
import it.fabioformosa.quartzmanager.api.dto.RepetitionDTO;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ValidRepetitionValidator implements ConstraintValidator<ValidRepetition, RepetitionDTO> {
@Override
public boolean isValid(RepetitionDTO repetitionDTO, ConstraintValidatorContext constraintValidatorContext) {
return (repetitionDTO.getRepeatCount() == null && repetitionDTO.getRepeatInterval() == null) ||
(repetitionDTO.getRepeatCount() != null && repetitionDTO.getRepeatInterval() != null);
}
}

View File

@@ -1,18 +1,16 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.QuartManagerApplicationTests;
import it.fabioformosa.quartzmanager.api.controllers.utils.InvalidSimpleTriggerCommandDTOProvider;
import it.fabioformosa.quartzmanager.api.common.utils.DateUtils;
import it.fabioformosa.quartzmanager.api.controllers.utils.TestUtils;
import it.fabioformosa.quartzmanager.api.controllers.utils.TriggerUtils;
import it.fabioformosa.quartzmanager.api.dto.MisfireInstruction;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerCommandDTO;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerDTO;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerInputDTO;
import it.fabioformosa.quartzmanager.api.services.SimpleTriggerService;
import it.fabioformosa.quartzmanager.api.exceptions.TriggerNotFoundException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@@ -55,16 +53,8 @@ class SimpleTriggerControllerTest {
}
@Test
void givenAnExistingTrigger_whenGetIsCalled_then404IsReturned() throws Exception {
Mockito.when(simpleTriggerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name"));
mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/not_existing_trigger_name")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isNotFound());
}
@Test
void givenASimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception {
SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO();
void givenACompleteSimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception {
SimpleTriggerInputDTO simpleTriggerInputDTO = buildACompleteSimpleTriggerCommandDTO();
SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO);
Mockito.when(simpleTriggerService.scheduleSimpleTrigger(any())).thenReturn(expectedSimpleTriggerDTO);
mockMvc.perform(
@@ -77,27 +67,20 @@ class SimpleTriggerControllerTest {
;
}
private SimpleTriggerInputDTO buildSimpleTriggerCommandDTO() {
private SimpleTriggerInputDTO buildACompleteSimpleTriggerCommandDTO() {
return SimpleTriggerInputDTO.builder()
.jobClass("it.fabioformosa.quartzmanager.api.jobs.SampleJob")
.startDate(new Date())
.repeatCount(20)
.repeatInterval(20000L)
.endDate(DateUtils.addHoursToNow(6))
.misfireInstruction(MisfireInstruction.MISFIRE_INSTRUCTION_FIRE_NOW)
.repeatCount(5)
.repeatInterval(1000L * 60 * 60)
.build();
}
@ParameterizedTest
@ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class)
void givenAnInvalidSimpleTriggerCommandDTO_whenPostedANewTrigger_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerComandDTO) throws Exception {
mockMvc.perform(post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtils.toJson(invalidSimpleTriggerComandDTO)))
.andExpect(MockMvcResultMatchers.status().is4xxClientError());
}
@Test
void givenATriggerName_whenPutSimpleTriggerCommandDTO_thenTheSimpleTriggerIsRescheduled() throws Exception {
SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO();
SimpleTriggerInputDTO simpleTriggerInputDTO = buildACompleteSimpleTriggerCommandDTO();
SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO);
SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder()
.triggerName("mytrigger")
@@ -112,13 +95,4 @@ class SimpleTriggerControllerTest {
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO)));
}
@ParameterizedTest
@ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class)
void givenAnInvalidSimpleTriggerCommandDTO_whenATriggerIsRescheduled_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerCommandTO) throws Exception {
mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtils.toJson(invalidSimpleTriggerCommandTO)))
.andExpect(MockMvcResultMatchers.status().is4xxClientError());
}
}

View File

@@ -0,0 +1,93 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.QuartManagerApplicationTests;
import it.fabioformosa.quartzmanager.api.controllers.utils.InvalidSimpleTriggerCommandDTOProvider;
import it.fabioformosa.quartzmanager.api.controllers.utils.TestUtils;
import it.fabioformosa.quartzmanager.api.controllers.utils.TriggerUtils;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerDTO;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerInputDTO;
import it.fabioformosa.quartzmanager.api.exceptions.TriggerNotFoundException;
import it.fabioformosa.quartzmanager.api.services.SimpleTriggerService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@ContextConfiguration(classes = {QuartManagerApplicationTests.class})
@WebMvcTest(controllers = SimpleTriggerController.class, properties = {
"quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs"
})
class SimpleTriggerControllerValidationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SimpleTriggerService simpleTriggerService;
@AfterEach
void cleanUp(){
Mockito.reset(simpleTriggerService);
}
@Test
void givenANotExistingTrigger_whenGetIsCalled_then404IsReturned() throws Exception {
Mockito.when(simpleTriggerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name"));
mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/not_existing_trigger_name")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isNotFound());
}
@Test
void givenAMinimalSimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception {
SimpleTriggerInputDTO simpleTriggerInputDTO = buildAMinimalSimpleTriggerCommandDTO();
SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("my-minimal-trigger");
Mockito.when(simpleTriggerService.scheduleSimpleTrigger(any())).thenReturn(expectedSimpleTriggerDTO);
mockMvc.perform(
post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/my-minimal-trigger")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtils.toJson(simpleTriggerInputDTO))
)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO)))
;
}
private SimpleTriggerInputDTO buildAMinimalSimpleTriggerCommandDTO() {
return SimpleTriggerInputDTO.builder()
.jobClass("it.fabioformosa.quartzmanager.api.jobs.SampleJob")
.build();
}
@ParameterizedTest
@ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class)
void givenAnInvalidSimpleTriggerCommandDTO_whenPostedANewTrigger_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerComandDTO) throws Exception {
mockMvc.perform(post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtils.toJson(invalidSimpleTriggerComandDTO)))
.andExpect(MockMvcResultMatchers.status().is4xxClientError());
}
@ParameterizedTest
@ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class)
void givenAnInvalidSimpleTriggerCommandDTO_whenATriggerIsRescheduled_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerCommandTO) throws Exception {
mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtils.toJson(invalidSimpleTriggerCommandTO)))
.andExpect(MockMvcResultMatchers.status().is4xxClientError());
}
}

View File

@@ -9,7 +9,7 @@ import java.util.stream.Stream;
public class InvalidSimpleTriggerCommandDTOProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
return Stream.of(
Arguments.of(SimpleTriggerInputDTO.builder().build()),
Arguments.of(SimpleTriggerInputDTO.builder().repeatCount(1).build()),

View File

@@ -0,0 +1,51 @@
package it.fabioformosa.quartzmanager.api.validators;
import it.fabioformosa.quartzmanager.api.dto.RepetitionDTO;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerInputDTO;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class ValidRepetitionValidatorTest {
private ValidRepetitionValidator validRepetitionValidator = new ValidRepetitionValidator();
@Test
public void givenACountAndIntervalSet_whenTheValidatorIsCalled_shouldReturnValid() {
RepetitionDTO repetitionDTO = new SimpleTriggerInputDTO();
repetitionDTO.setRepeatCount(10);
repetitionDTO.setRepeatInterval(1000L);
boolean valid = validRepetitionValidator.isValid(repetitionDTO, null);
Assertions.assertThat(valid).isTrue();
}
@Test
public void givenACountAndIntervalUnSet_whenTheValidatorIsCalled_shouldReturnInValid() {
RepetitionDTO repetitionDTO = new SimpleTriggerInputDTO();
boolean valid = validRepetitionValidator.isValid(repetitionDTO, null);
Assertions.assertThat(valid).isTrue();
}
@ParameterizedTest
@CsvSource({"10, ", ",1000"})
public void givenACountAndIntervalNotSet_whenTheValidatorIsCalled_shouldReturnInValid(String repeatCountStr, String repeatIntervalStr) {
Integer repeatCount = null;
if (StringUtils.isNotBlank(repeatCountStr))
repeatCount = Integer.valueOf(repeatCountStr);
Long repeatInterval = null;
if (StringUtils.isNotBlank(repeatIntervalStr))
repeatInterval = Long.valueOf(repeatIntervalStr);
RepetitionDTO repetitionDTO = new SimpleTriggerInputDTO();
repetitionDTO.setRepeatInterval(repeatInterval);
repetitionDTO.setRepeatCount(repeatCount);
boolean valid = validRepetitionValidator.isValid(repetitionDTO, null);
Assertions.assertThat(valid).isFalse();
}
}