Merge pull request #95 from fabioformosa/develop

v4.0.5
This commit is contained in:
Fabio Formosa
2022-11-20 14:00:34 +01:00
committed by GitHub
22 changed files with 219 additions and 110 deletions

View File

@@ -0,0 +1,2 @@
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

View File

@@ -10,7 +10,7 @@
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
<packaging>pom</packaging>
@@ -69,27 +69,27 @@
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-common</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-api</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-security</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-persistence</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-ui</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -179,17 +179,6 @@
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>${nexus-staging-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
@@ -233,6 +222,17 @@
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>${nexus-staging-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-common</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-starter-api</artifactId>
@@ -106,6 +106,10 @@
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- QUARTZ -->
<dependency>
@@ -118,34 +122,6 @@
<version>1.3.2</version>
</dependency>
<!-- Reactor -->
<!-- <dependency>-->
<!-- <groupId>io.projectreactor</groupId>-->
<!-- <artifactId>reactor-core</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.projectreactor</groupId>-->
<!-- <artifactId>reactor-net</artifactId>-->
<!-- <version>2.0.8.RELEASE</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.projectreactor.spring</groupId>-->
<!-- <artifactId>reactor-spring-context</artifactId>-->
<!-- <version>2.0.7.RELEASE</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>io.netty</groupId>-->
<!-- <artifactId>netty-all</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-aop</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- OAS -->
<dependency>
<groupId>org.springdoc</groupId>

View File

@@ -23,6 +23,5 @@ public class JobKeyToJobDetailDTO extends AbstractBaseConverterToDTO<JobKey, Job
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
jobDetailDTO.setJobClassName(jobDetail.getJobClass().getName());
jobDetailDTO.setDescription(jobDetail.getDescription());
//jobDetail.getJobDataMap();
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-starter-persistence</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-starter-security</artifactId>

View File

@@ -34,9 +34,6 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.ArrayList;
import java.util.List;
@@ -101,7 +98,9 @@ public class QuartzManagerSecurityConfig {
@Order(Ordered.HIGHEST_PRECEDENCE)
@Bean(name = "quartzManagerFilterChain")
public SecurityFilterChain filterChain(HttpSecurity http, @Qualifier("quartzManagerInMemoryAuthentication") InMemoryUserDetailsManager userDetailsService, AuthenticationManager authenticationManager) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http,
@Qualifier("quartzManagerInMemoryAuthentication") InMemoryUserDetailsManager userDetailsService,
AuthenticationManager authenticationManager) throws Exception {
http.antMatcher(QUARTZ_MANAGER_API_ANT_MATCHER).csrf().disable() //
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
.exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() //
@@ -128,14 +127,6 @@ public class QuartzManagerSecurityConfig {
};
}
@Bean(name = "quartzManagerCorsConfigurationSource")
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(QUARTZ_MANAGER_API_ANT_MATCHER, new CorsConfiguration().applyPermitDefaultValues());
source.registerCorsConfiguration(QUARTZ_MANAGER_UI_ANT_MATCHER, new CorsConfiguration().applyPermitDefaultValues());
return source;
}
public LoginConfigurer formLoginConfigurer() {
JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler();
AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler);

View File

@@ -20,13 +20,14 @@ import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths
@RequestMapping(value = QUARTZ_MANAGER_AUTH_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
public static final String WHOAMI_URL = "/whoami";
@GetMapping("/whoami")
@GetMapping(WHOAMI_URL)
public ResponseEntity<Object> getLoggedUser() {
SecurityContext context = SecurityContextHolder.getContext();
if (context != null && context.getAuthentication() != null)
return new ResponseEntity<>(context.getAuthentication().getPrincipal(), HttpStatus.OK);
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}

View File

@@ -1,24 +1,38 @@
package it.fabioformosa.quartzmanager.api.security.properties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.security.SecureRandom;
import java.util.Base64;
@Configuration
@ConfigurationProperties(prefix = "quartz-manager.security.jwt")
@Data
@AllArgsConstructor
@Getter
@Setter
public class JwtSecurityProperties {
private String secret = RandomStringUtils.randomAlphabetic(10);
private String secret;
private long expirationInSec = 28800;
private CookieStrategy cookieStrategy = new CookieStrategy();
private HeaderStrategy headerStrategy = new HeaderStrategy();
public JwtSecurityProperties() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
secret = encoder.encodeToString(bytes);
}
@Data
public static class CookieStrategy {
private boolean enabled = false;

View File

@@ -2,6 +2,8 @@ package it.fabioformosa.quartzmanager.api.security;
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
import it.fabioformosa.quartzmanager.api.security.controllers.TestController;
import it.fabioformosa.quartzmanager.api.security.properties.JwtSecurityProperties;
import org.assertj.core.api.Assertions;
import org.hamcrest.core.IsNot;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -19,9 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {
"quartz-manager.security.jwt.enabled=true",
"quartz-manager.security.jwt.secret=bibidibobidiboo",
"quartz-manager.security.jwt.expiration-in-sec=28800",
"quartz-manager.security.jwt.expiration-in-sec=36000",
"quartz-manager.security.jwt.header-strategy.enabled=false",
"quartz-manager.security.jwt.header-strategy.header=Authorization",
"quartz-manager.security.jwt.cookie-strategy.enabled=true",
@@ -31,11 +32,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
"quartz-manager.security.accounts.in-memory.users[0].password=bar",
"quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin",
})
public class SecurityControllerTest {
class SecurityControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private JwtSecurityProperties jwtSecurityProperties;
@Test
void givenAnAnonymousUser_whenCalledADMZController_thenShouldRaiseForbidden() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/dmz"))
@@ -72,4 +76,10 @@ public class SecurityControllerTest {
.andExpect(status().isOk());
}
@Test
void givenSecurityProps_whenTheBootstrapHasCompleted_thenJWTPropertiesShouldBeSetAccordingly() throws Exception {
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(36000);
Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo("bibidibobidiboo");
}
}

View File

@@ -13,7 +13,6 @@ import org.springframework.test.context.TestPropertySource;
@TestPropertySource(properties = {
"quartz-manager.security.login-model.form-login-enabled = false",
"quartz-manager.security.login-model.userpwd-filter-enabled = true",
"quartz-manager.security.jwt.enabled=true",
"quartz-manager.security.jwt.secret=bibidibobidiboo",
"quartz-manager.security.jwt.expiration-in-sec=28800",
"quartz-manager.security.jwt.header-strategy.enabled=true",

View File

@@ -12,7 +12,6 @@ import org.springframework.test.context.TestPropertySource;
@TestPropertySource(properties = {
"quartz-manager.security.login-model.form-login-enabled = true",
"quartz-manager.security.login-model.userpwd-filter-enabled = false",
"quartz-manager.security.jwt.enabled=true",
"quartz-manager.security.jwt.secret=bibidibobidiboo",
"quartz-manager.security.jwt.expiration-in-sec=28800",
"quartz-manager.security.jwt.header-strategy.enabled=true",

View File

@@ -0,0 +1,41 @@
package it.fabioformosa.quartzmanager.api.security.controllers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths.QUARTZ_MANAGER_AUTH_PATH;
import static it.fabioformosa.quartzmanager.api.security.controllers.UserController.WHOAMI_URL;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {
"quartz-manager.security.accounts.in-memory.enabled=true",
"quartz-manager.security.accounts.in-memory.users[0].username=admin",
"quartz-manager.security.accounts.in-memory.users[0].password=admin",
"quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin",
})
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser("admin")
void givenAnUser_whenCalledTheWhoamiEndpoint_thenShouldReturn2xx() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get(QUARTZ_MANAGER_AUTH_PATH + WHOAMI_URL))
.andExpect(status().isOk());
}
@Test
void givenAnAnonymousUser_whenCalledTheWhoamiEndpoint_thenShouldReturnNotFound() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get(QUARTZ_MANAGER_AUTH_PATH + WHOAMI_URL))
.andExpect(status().isUnauthorized());
}
}

View File

@@ -0,0 +1,39 @@
package it.fabioformosa.quartzmanager.api.security.properties;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
public abstract class AbstractPropertyValidatorTest {
protected static Validator propertyValidator;
@BeforeAll
public static void setup() {
propertyValidator = Validation.buildDefaultValidatorFactory().getValidator();
}
protected static <T> T inflateConfigurationPropertyFromAMap(Map<String, String> properties, String configurationPropName, Class<T> propClass) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(source);
BindResult<T> result = binder.bind(configurationPropName, propClass);
if (properties != null && !properties.isEmpty()) {
Assertions.assertThat(result.isBound()).isTrue();
T configPropObject = result.get();
return configPropObject;
} else {
try {
return propClass.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -1,25 +1,16 @@
package it.fabioformosa.quartzmanager.api.security.properties;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class InMemoryUsersValidationControllerTest {
private static Validator propertyValidator;
class InMemoryUsersValidationControllerTest extends AbstractPropertyValidatorTest {
static Stream<Arguments> notValidInMemoryProps = Stream.of(
Arguments.of(
@@ -34,26 +25,15 @@ public class InMemoryUsersValidationControllerTest {
);
@BeforeAll
public static void setup() {
propertyValidator = Validation.buildDefaultValidatorFactory().getValidator();
}
static Stream<Arguments> getNotValidInMemoryProps(){
static Stream<Arguments> getNotValidInMemoryProps() {
return notValidInMemoryProps;
}
@ParameterizedTest
@MethodSource("it.fabioformosa.quartzmanager.api.security.properties.InMemoryUsersValidationControllerTest#getNotValidInMemoryProps")
void givenAMissingUsername_whenThePropertyValidationIsApplied_thenShouldRaiseValidationError(Map<String, String> properties) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(source);
BindResult<InMemoryAccountProperties> result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
Assertions.assertThat(result.isBound()).isTrue();
InMemoryAccountProperties inMemoryAccountProperties = result.get();
InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties,
"quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isNotEmpty();
}
@@ -65,14 +45,9 @@ public class InMemoryUsersValidationControllerTest {
properties.put("quartz-manager.security.accounts.in-memory.users[0].password", "bar");
properties.put("quartz-manager.security.accounts.in-memory.users[0].roles[0]", "admin");
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties,
"quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
Binder binder = new Binder(source);
BindResult<InMemoryAccountProperties> result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
Assertions.assertThat(result.isBound()).isTrue();
InMemoryAccountProperties inMemoryAccountProperties = result.get();
Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isEmpty();
}

View File

@@ -0,0 +1,39 @@
package it.fabioformosa.quartzmanager.api.security.properties;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
class JwtSecurityPropertiesTest extends AbstractPropertyValidatorTest {
@Test
void givenAllJWTSecurityPropSet_whenThePropertyValidationIsApplied_thenShouldBeValid() {
Map<String, String> properties = new HashMap<>();
String secret = "helloworld";
properties.put("quartz-manager.security.jwt.secret", secret);
String expirationInSec = "36000";
properties.put("quartz-manager.security.jwt.expirationInSec", expirationInSec);
JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties,
"quartz-manager.security.jwt", JwtSecurityProperties.class);
Assertions.assertThat(propertyValidator.validate(jwtSecurityProperties)).isEmpty();
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(Long.valueOf(expirationInSec));
Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo(secret);
}
@Test
void givenTheMandatoryJWTSecurityPropUnset_whenThePropertyValidationIsApplied_thenShouldBeSetWithDefault() {
Map<String, String> properties = new HashMap<>();
JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties,
"quartz-manager.security.jwt", JwtSecurityProperties.class);
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(28800L);
Assertions.assertThat(jwtSecurityProperties.getSecret()).isNotBlank();
}
}

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-starter-ui</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</parent>
<artifactId>quartz-manager-web-showcase</artifactId>

View File

@@ -2,9 +2,9 @@ package it.fabioformosa.quartzmanager.jobs.tests;
import it.fabioformosa.quartzmanager.api.jobs.AbstractQuartzManagerJob;
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This job can be used to test the misfire policy. It pretends to be a long
@@ -13,20 +13,26 @@ import org.slf4j.LoggerFactory;
* @author Fabio.Formosa
*
*/
@Slf4j
@NoArgsConstructor
public class MisfireTestJob extends AbstractQuartzManagerJob {
private Logger log = LoggerFactory.getLogger(MisfireTestJob.class);
private long sleepPeriodInMs = 10 * 1000L;
public MisfireTestJob(long sleepPeriodInMs) {
this.sleepPeriodInMs = sleepPeriodInMs;
}
@Override
public LogRecord doIt(JobExecutionContext jobExecutionContext) {
try {
log.info("{} is going to sleep...", Thread.currentThread().getName());
Thread.sleep(10 * 1000);
Thread.sleep(sleepPeriodInMs);
log.info("{} woke up!", Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
return new LogRecord(LogRecord.LogType.INFO, "Hello!");

View File

@@ -9,7 +9,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
public class QuartManagerApplicationTests {
@Test
public void contextLoads() {
void contextLoads() {
}
}

View File

@@ -0,0 +1,18 @@
package it.fabioformosa.quartzmanager.jobs.tests;
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
class MisfireTestJobTest {
@Test
void givenAMisfireTestJob_whenIsExecuted_shoulReturnALogRecord() {
MisfireTestJob misfireTestJob = new MisfireTestJob(10L);
LogRecord logRecord = misfireTestJob.doIt(null);
Assertions.assertThat(logRecord.getMessage()).isEqualTo("Hello!");
}
}