From b379213e382c6503534e5ed24bd26f569574f8f1 Mon Sep 17 00:00:00 2001 From: spokenbird Date: Fri, 8 Sep 2023 18:21:57 -0700 Subject: [PATCH] Add tests with Spybeans for interceptors --- .../exceptions/LandmarkNotSetException.java | 11 ++ .../interceptors/DataRequiredInterceptor.java | 16 ++- .../DataInterceptorJourneyTest.java | 2 + .../InterceptorOrderMockMvcTest.java | 103 ++++++++++++++++++ .../interceptors/SpyInterceptorConfig.java | 37 +++++++ src/test/resources/application-test.yaml | 2 - 6 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 src/main/java/formflow/library/exceptions/LandmarkNotSetException.java create mode 100644 src/test/java/formflow/library/interceptors/InterceptorOrderMockMvcTest.java create mode 100644 src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java diff --git a/src/main/java/formflow/library/exceptions/LandmarkNotSetException.java b/src/main/java/formflow/library/exceptions/LandmarkNotSetException.java new file mode 100644 index 000000000..d6d35e126 --- /dev/null +++ b/src/main/java/formflow/library/exceptions/LandmarkNotSetException.java @@ -0,0 +1,11 @@ +package formflow.library.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) +public class LandmarkNotSetException extends RuntimeException { + public LandmarkNotSetException(String message) { + super(message); + } +} diff --git a/src/main/java/formflow/library/interceptors/DataRequiredInterceptor.java b/src/main/java/formflow/library/interceptors/DataRequiredInterceptor.java index 8e95a36d7..3f2d15a2b 100644 --- a/src/main/java/formflow/library/interceptors/DataRequiredInterceptor.java +++ b/src/main/java/formflow/library/interceptors/DataRequiredInterceptor.java @@ -2,6 +2,7 @@ import formflow.library.config.FlowConfiguration; import formflow.library.config.ScreenNavigationConfiguration; +import formflow.library.exceptions.LandmarkNotSetException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; @@ -11,6 +12,7 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.servlet.HandlerInterceptor; @@ -18,7 +20,7 @@ @Component @Slf4j @ConditionalOnProperty(name = "form-flow.session-continuity-interceptor.enabled", havingValue = "true") -public class DataRequiredInterceptor implements HandlerInterceptor { +public class DataRequiredInterceptor implements HandlerInterceptor, Ordered { public static final String FLOW_PATH_FORMAT = "/flow/{flow}/{screen}"; public static final String NAVIGATION_FLOW_PATH_FORMAT = "/flow/{flow}/{screen}/navigation"; @@ -50,20 +52,20 @@ public boolean preHandle(HttpServletRequest request, @NotNull HttpServletRespons boolean landmarkNotImplemented = flowConfiguration.getLandmarks() == null; if (landmarkNotImplemented) { - throw new RuntimeException("You have enabled session continuity interception but have not created a landmark section in your applications flow configuration file."); + throw new LandmarkNotSetException("You have enabled session continuity interception but have not created a landmark section in your applications flow configuration file."); } boolean firstScreenNotSet = flowConfiguration.getLandmarks().getFirstScreen() == null; if (firstScreenNotSet) { - throw new RuntimeException("Please make sure to set a firstScreen under your flow configuration files landmark section."); + throw new LandmarkNotSetException("Please make sure to set a firstScreen under your flow configuration files landmark section."); } String firstScreen = flowConfiguration.getLandmarks().getFirstScreen(); boolean firstScreenExists = flowConfiguration.getFlow().containsKey(firstScreen); if (!firstScreenExists) { - throw new RuntimeException(String.format("Please make sure that you have correctly set the firstScreen under your flow configuration files landmark section. Your flow configuration file does not contain a screen with the name %s.", firstScreen)); + throw new LandmarkNotSetException(String.format("Please make sure that you have correctly set the firstScreen under your flow configuration files landmark section. Your flow configuration file does not contain a screen with the name %s.", firstScreen)); } boolean onFirstScreen = screen.equals(firstScreen); @@ -88,4 +90,10 @@ public boolean preHandle(HttpServletRequest request, @NotNull HttpServletRespons return true; } } + + @Override + public int getOrder() { + // Max value ensures that this interceptor is executed last. + return Integer.MAX_VALUE; + } } diff --git a/src/test/java/formflow/library/controllers/DataInterceptorJourneyTest.java b/src/test/java/formflow/library/controllers/DataInterceptorJourneyTest.java index 28c9665a9..293df5737 100644 --- a/src/test/java/formflow/library/controllers/DataInterceptorJourneyTest.java +++ b/src/test/java/formflow/library/controllers/DataInterceptorJourneyTest.java @@ -8,8 +8,10 @@ import org.junit.jupiter.api.Test; import org.openqa.selenium.Cookie; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; @SpringBootTest(properties = {"form-flow.path=flows-config/test-landmark-flow.yaml"}, webEnvironment = RANDOM_PORT) +@TestPropertySource(properties = {"form-flow.session-continuity-interceptor.enabled=true"}) public class DataInterceptorJourneyTest extends AbstractBasePageTest { @Test void interceptorShouldRedirectToLandingPageIfSessionIsNull() { diff --git a/src/test/java/formflow/library/interceptors/InterceptorOrderMockMvcTest.java b/src/test/java/formflow/library/interceptors/InterceptorOrderMockMvcTest.java new file mode 100644 index 000000000..507e71317 --- /dev/null +++ b/src/test/java/formflow/library/interceptors/InterceptorOrderMockMvcTest.java @@ -0,0 +1,103 @@ +package formflow.library.interceptors; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import formflow.library.config.FlowConfiguration; +import formflow.library.config.LandmarkConfiguration; +import formflow.library.config.NextScreen; +import formflow.library.config.ScreenNavigationConfiguration; +import formflow.library.exceptions.LandmarkNotSetException; +import formflow.library.utilities.AbstractMockMvcTest; +import java.util.HashMap; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; + +@SpringBootTest(properties = {"form-flow.path=flows-config/test-landmark-flow.yaml"}) +@TestPropertySource(properties = {"form-flow.session-continuity-interceptor.enabled=true"}) +@Import(SpyInterceptorConfig.class) +class InterceptorOrderMockMvcTest extends AbstractMockMvcTest { + + @Autowired + private LocaleChangeInterceptor localeChangeInterceptor; + + @Autowired + private DataRequiredInterceptor dataRequiredInterceptor; + + @Test + void shouldRunTheDataRequiredInterceptorLast() throws Exception { + mockMvc.perform(get("/flow/testLandmarkFlow/first?lang=es")) + .andExpect(status().isOk()); + + InOrder inOrder = inOrder(localeChangeInterceptor, dataRequiredInterceptor); + inOrder.verify(localeChangeInterceptor).preHandle(any(), any(), any()); + inOrder.verify(dataRequiredInterceptor).preHandle(any(), any(), any()); + } + + @Test + void shouldErrorIfLandmarkIsNotSet() throws Exception { + FlowConfiguration flowConfiguration = new FlowConfiguration(); + flowConfiguration.setName("testLandmarkFlow"); + dataRequiredInterceptor.flowConfigurations = List.of(flowConfiguration); + mockMvc.perform(MockMvcRequestBuilders.get("/flow/testLandmarkFlow/first")) + .andExpect(status().is5xxServerError()) + .andExpect(result -> { + Exception resolvedException = result.getResolvedException(); + assertTrue(resolvedException instanceof LandmarkNotSetException, "Expected RuntimeException to be thrown"); + assertEquals("You have enabled session continuity interception but have not created a landmark section in your applications flow configuration file.", resolvedException.getMessage()); + }); + } + + @Test + void shouldErrorIfFirstScreenIsNotSet() throws Exception { + FlowConfiguration flowConfiguration = new FlowConfiguration(); + flowConfiguration.setName("testLandmarkFlow"); + LandmarkConfiguration landmarkConfiguration = new LandmarkConfiguration(); + landmarkConfiguration.setFirstScreen(null); + flowConfiguration.setLandmarks(landmarkConfiguration); + dataRequiredInterceptor.flowConfigurations = List.of(flowConfiguration); + mockMvc.perform(MockMvcRequestBuilders.get("/flow/testLandmarkFlow/first")) + .andExpect(status().is5xxServerError()) + .andExpect(result -> { + Exception resolvedException = result.getResolvedException(); + assertTrue(resolvedException instanceof LandmarkNotSetException, "Expected RuntimeException to be thrown"); + assertEquals("Please make sure to set a firstScreen under your flow configuration files landmark section.", resolvedException.getMessage()); + }); + } + + @Test + void shouldErrorIfFirstScreenDoesNotExistWithinFlowConfiguration() throws Exception { + FlowConfiguration flowConfiguration = new FlowConfiguration(); + ScreenNavigationConfiguration screenNavigationConfiguration = new ScreenNavigationConfiguration(); + NextScreen nextScreen = new NextScreen(); + nextScreen.setName("first"); + screenNavigationConfiguration.setNextScreens(List.of(nextScreen)); + HashMap screenNavigationConfigurations = new HashMap<>(); + screenNavigationConfigurations.put("first", screenNavigationConfiguration); + flowConfiguration.setFlow(screenNavigationConfigurations); + flowConfiguration.setName("testLandmarkFlow"); + LandmarkConfiguration landmarkConfiguration = new LandmarkConfiguration(); + landmarkConfiguration.setFirstScreen("nonExistentScreen"); + flowConfiguration.setLandmarks(landmarkConfiguration); + dataRequiredInterceptor.flowConfigurations = List.of(flowConfiguration); + + mockMvc.perform(MockMvcRequestBuilders.get("/flow/testLandmarkFlow/first")) + .andExpect(status().is5xxServerError()) + .andExpect(result -> { + Exception resolvedException = result.getResolvedException(); + assertTrue(resolvedException instanceof LandmarkNotSetException, "Expected RuntimeException to be thrown"); + assertEquals("Please make sure that you have correctly set the firstScreen under your flow configuration files landmark section. Your flow configuration file does not contain a screen with the name nonExistentScreen.", resolvedException.getMessage()); + }); + } +} \ No newline at end of file diff --git a/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java b/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java new file mode 100644 index 000000000..128f59245 --- /dev/null +++ b/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java @@ -0,0 +1,37 @@ +package formflow.library.interceptors; + +import formflow.library.config.FlowConfiguration; +import java.util.List; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; + +@TestConfiguration +public class SpyInterceptorConfig implements WebMvcConfigurer { + + @Autowired + private List flowConfigurations; + + @Bean + @Primary + public LocaleChangeInterceptor localeChangeInterceptor() { + return Mockito.spy(new LocaleChangeInterceptor()); + } + + @Bean + @Primary // Ensure this bean takes precedence over the real one + public DataRequiredInterceptor dataRequiredInterceptor() { + return Mockito.spy(new DataRequiredInterceptor(flowConfigurations)); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + registry.addInterceptor(dataRequiredInterceptor()); + } +} diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index 07eb2a4f7..849ba1de8 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -1,8 +1,6 @@ form-flow: path: 'test-flow.yaml' inputs: 'formflow.library.inputs.' - session-continuity-interceptor: - enabled: true uploads: accepted-file-types: '.jpeg, .fake, .heic, .tif, .tiff, .pdf' max-files: '5'