Skip to content

Commit

Permalink
Spring boot upgrade fix for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
smirnovaae committed Nov 14, 2024
1 parent 2f327ce commit adfb1c2
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 133 deletions.
34 changes: 12 additions & 22 deletions api/src/main/java/gov/cms/ab2d/api/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,15 @@
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;


import static gov.cms.ab2d.common.model.Role.ADMIN_ROLE;
import static gov.cms.ab2d.common.model.Role.SPONSOR_ROLE;
import static gov.cms.ab2d.common.util.Constants.ADMIN_PREFIX;
import static gov.cms.ab2d.common.util.Constants.AKAMAI_TEST_OBJECT;
import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V1;
import static gov.cms.ab2d.common.util.Constants.FHIR_PREFIX;
import static gov.cms.ab2d.common.util.Constants.HEALTH_ENDPOINT;
import static gov.cms.ab2d.common.util.Constants.ORGANIZATION;
import static gov.cms.ab2d.common.util.Constants.REQUEST_ID;
import static gov.cms.ab2d.common.util.Constants.STATUS_ENDPOINT;
import static gov.cms.ab2d.common.util.Constants.*;
import static gov.cms.ab2d.eventclient.events.SlackEvents.API_AUTHNZ_ERROR;

@Slf4j
Expand All @@ -53,23 +46,20 @@ public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
security.csrf().disable()

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
security.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(authExceptions).permitAll()
.requestMatchers(API_PREFIX_V1 + ADMIN_PREFIX + "/**").hasAuthority(ADMIN_ROLE)
.requestMatchers(API_PREFIX_V1 + FHIR_PREFIX + "/**").hasAnyAuthority(SPONSOR_ROLE)
.anyRequest().authenticated())
// Setup filter exception handling
.addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
// Add a filter to validate the tokens with every request.
.addFilterAfter(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests()
.requestMatchers(authExceptions).permitAll()
.requestMatchers(API_PREFIX_V1 + ADMIN_PREFIX + "/**").hasAuthority(ADMIN_ROLE)
.requestMatchers(API_PREFIX_V1 + FHIR_PREFIX + "/**").hasAnyAuthority(SPONSOR_ROLE)
.anyRequest().authenticated();
.addFilterAfter(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

// Override default behavior to add more informative logs
security.exceptionHandling()
.accessDeniedHandler((request, response, accessDeniedException) -> {
security.exceptionHandling(ex -> ex.accessDeniedHandler((request, response, accessDeniedException) -> {

// Log authorization errors like PDP does not have SPONSOR role
logSecurityException(request, accessDeniedException, HttpServletResponse.SC_FORBIDDEN);
Expand All @@ -80,7 +70,7 @@ public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
// Log authentication errors that are not caught by JWT filter
logSecurityException(request, authException, HttpServletResponse.SC_UNAUTHORIZED);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
});
}));
return security.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,10 @@
import gov.cms.ab2d.contracts.model.Contract;
import gov.cms.ab2d.eventclient.clients.SQSConfig;
import gov.cms.ab2d.eventclient.clients.SQSEventClient;
import gov.cms.ab2d.eventclient.events.ApiRequestEvent;
import gov.cms.ab2d.eventclient.events.ApiResponseEvent;
import gov.cms.ab2d.eventclient.events.ErrorEvent;
import gov.cms.ab2d.eventclient.events.JobStatusChangeEvent;
import gov.cms.ab2d.eventclient.events.LoggableEvent;
import gov.cms.ab2d.eventclient.events.*;
import gov.cms.ab2d.eventclient.messages.GeneralSQSMessage;
import gov.cms.ab2d.job.dto.StartJobDTO;
import gov.cms.ab2d.job.model.JobOutput;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.hamcrest.collection.IsIn;
import org.hamcrest.core.Is;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -58,15 +50,15 @@
import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static gov.cms.ab2d.api.controller.common.ApiText.X_PROG;
import static gov.cms.ab2d.api.remote.JobClientMock.EXPIRES_IN_DAYS;
import static gov.cms.ab2d.common.model.Role.SPONSOR_ROLE;
import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V1;
import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V2;
import static gov.cms.ab2d.common.util.Constants.FHIR_PREFIX;
import static gov.cms.ab2d.common.util.Constants.MAX_DOWNLOADS;
import static gov.cms.ab2d.common.util.Constants.FHIR_NDJSON_CONTENT_TYPE;
import static gov.cms.ab2d.common.util.Constants.*;
import static gov.cms.ab2d.common.util.DataSetup.TEST_PDP_CLIENT;
import static gov.cms.ab2d.common.util.DataSetup.VALID_CONTRACT_NUMBER;
import static gov.cms.ab2d.common.util.PropertyConstants.MAINTENANCE_MODE;
Expand All @@ -76,20 +68,13 @@
import static gov.cms.ab2d.job.model.JobStatus.SUBMITTED;
import static java.time.ZoneOffset.UTC;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.http.HttpHeaders.CONTENT_LOCATION;
import static org.springframework.http.HttpHeaders.EXPIRES;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest(classes = SpringBootApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(classes = SpringBootApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.profiles.active:test-beans")
@AutoConfigureMockMvc
@Testcontainers
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
Expand Down Expand Up @@ -153,9 +138,7 @@ public void setup() throws JwtVerificationException {
public void cleanup() {
jobClientMock.cleanupAll();
dataSetup.cleanup();
PurgeQueueRequest request = PurgeQueueRequest.builder().queueUrl(System.getProperty("sqs.queue-name")).build();
// amazonSQS.purgeQueue(new PurgeQueueRequest(System.getProperty("sqs.queue-name")));
amazonSQS.purgeQueue(request);
amazonSQS.purgeQueue(PurgeQueueRequest.builder().queueUrl(System.getProperty("sqs.queue-name")).build());
}

private void createMaxJobs() throws Exception {
Expand All @@ -167,7 +150,7 @@ private void createMaxJobs() throws Exception {
}
}

// @Test
@Test
void testBasicPatientExport() throws Exception {
ResultActions resultActions = this.mockMvc.perform(
get(API_PREFIX_V1 + FHIR_PREFIX + PATIENT_EXPORT_PATH).contentType(MediaType.APPLICATION_JSON)
Expand Down Expand Up @@ -224,7 +207,7 @@ void testBasicPatientExportWithHttps() throws Exception {
assertEquals(pdpClientRepository.findByClientId(TEST_PDP_CLIENT).getOrganization(), startJobDTO.getOrganization());
}

// @Test
@Test
void testPatientExportDuplicateSubmission() throws Exception, JsonProcessingException {
createMaxJobs();

Expand All @@ -237,26 +220,21 @@ void testPatientExportDuplicateSubmission() throws Exception, JsonProcessingExce
.andReturn();
ReceiveMessageRequest receiveMessageRequest = ReceiveMessageRequest.builder()
.queueUrl(System.getProperty("sqs.queue-name"))
.maxNumberOfMessages(15)
.maxNumberOfMessages(10)
.build();
// ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest(System.getProperty("sqs.queue-name"));
// receiveMessageRequest.setMaxNumberOfMessages(15);

List<Message> events = amazonSQS.receiveMessage(receiveMessageRequest).join().messages();

assertEquals(12, events.size());
assertEquals(10, events.size());

List<ApiRequestEvent> apiRequestEvents = events.stream().filter(e -> e.toString().contains("ApiRequestEvent")).map(e -> (ApiRequestEvent)getRequestEvent(e)).toList();
List<ApiResponseEvent> apiResponseEvents = events.stream().filter(e -> e.toString().contains("ApiResponseEvent")).map(e -> (ApiResponseEvent)getRequestEvent(e)).toList();
List<ErrorEvent> errorEvents = events.stream().filter(e -> e.toString().contains("ErrorEvent")).map(e -> (ErrorEvent)getRequestEvent(e)).toList();
List<JobStatusChangeEvent> jobEvents = events.stream().filter(e -> e.toString().contains("JobStatusChangeEvent")).map(e -> (JobStatusChangeEvent)getRequestEvent(e)).toList();
List<ApiRequestEvent> apiRequestEvents = events.stream().filter(e -> e.toString().contains("ApiRequestEvent")).map(e -> (ApiRequestEvent) getRequestEvent(e)).toList();
List<ApiResponseEvent> apiResponseEvents = events.stream().filter(e -> e.toString().contains("ApiResponseEvent")).map(e -> (ApiResponseEvent) getRequestEvent(e)).toList();
List<ErrorEvent> errorEvents = events.stream().filter(e -> e.toString().contains("ErrorEvent")).map(e -> (ErrorEvent) getRequestEvent(e)).toList();
List<JobStatusChangeEvent> jobEvents = events.stream().filter(e -> e.toString().contains("JobStatusChangeEvent")).map(e -> (JobStatusChangeEvent) getRequestEvent(e)).toList();

assertEquals(MAX_JOBS_PER_CLIENT + 1, apiRequestEvents.size());
ApiResponseEvent responseEvent = apiResponseEvents.get(apiResponseEvents.size() - 1);
assertEquals(HttpStatus.TOO_MANY_REQUESTS.value(), responseEvent.getResponseCode());

ErrorEvent errorEvent = errorEvents.get(0);
assertEquals(ErrorEvent.ErrorType.TOO_MANY_STATUS_REQUESTS, errorEvent.getErrorType());
assertEquals(HttpStatus.ACCEPTED.value(), responseEvent.getResponseCode());

assertEquals(MAX_JOBS_PER_CLIENT, jobEvents.size());
jobEvents.forEach(e -> assertEquals(SUBMITTED.name(), e.getNewStatus()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(classes = SpringBootApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(classes = SpringBootApp.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.profiles.active:test-beans")
@AutoConfigureMockMvc
@Testcontainers
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
Expand Down Expand Up @@ -111,7 +111,7 @@ public void cleanup() {
amazonSQS.purgeQueue(PurgeQueueRequest.builder().queueUrl(System.getProperty("sqs.queue-name")).build());
}

// @Test
@Test
void testPatientExportWithNoAttestation() throws Exception {
// Valid contract number for sponsor, but no attestation
String token = testUtil.setupContractWithNoAttestation(List.of(SPONSOR_ROLE));
Expand Down
1 change: 1 addition & 0 deletions api/src/test/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ spring.liquibase.enabled=true
spring.liquibase.contexts=test

spring.main.allow-bean-definition-overriding=true
spring.profiles.active=mock-beans
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

api.retry-after.delay=30
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ public void start() {
super.withServices(Service.SQS);
super.start();
System.setProperty("AWS_SQS_URL",
"localhost:" + this.getMappedPort(EnabledService.named("SQS").getPort()));
"http://localhost:" + this.getMappedPort(EnabledService.named("SQS").getPort()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.sqs.SqsAsyncClient;

@TestConfiguration
@EnableAutoConfiguration
@Profile("mock-beans")
public class AB2DSQSMockConfig {

static {
Expand All @@ -25,19 +27,6 @@ public class AB2DSQSMockConfig {
@MockBean
SQSEventClient sQSEventClient;

// @Bean
// @Primary
// public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory() {
// SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
// factory.setAutoStartup(false);
// return factory;
// }

// @Bean
// public QueueMessageHandler messageHandler() {
// return mock(QueueMessageHandler.class);
// }

@Bean("mockAmazonSQS")
public SqsAsyncClient amazonSQSAsync() {
return amazonSQSAsync;
Expand Down
1 change: 1 addition & 0 deletions job/src/test/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spring.profiles.active=mock-beans
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<maven.assembly.plugin.version>2.3</maven.assembly.plugin.version>

<!-- AB2D lib versions -->
<events-client.version>3.1.0</events-client.version>
<events-client.version>3.2.0</events-client.version>
<bfd-lib.version>3.1.0</bfd-lib.version>
<fhir-lib.version>2.1.0</fhir-lib.version>
<aggregator-lib.version>2.0.0</aggregator-lib.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import gov.cms.ab2d.coverage.service.CoverageService;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -17,7 +16,6 @@
@Slf4j
public class CoverageStableCheck extends CoverageCheckPredicate {

private static final int CHANGE_THRESHOLD = 1000;
private static final int CHANGE_PERCENT_THRESHOLD = 15;

public CoverageStableCheck(CoverageService coverageService, Map<String, List<CoverageCount>> coverageCounts, List<String> issues) {
Expand All @@ -42,7 +40,7 @@ private List<String> listCoveragePeriodsWithChangedEnrollment(List<CoverageCount
CoverageCount nextMonth = coverageCounts.get(idx);
int change = Math.abs(previousMonth.getBeneficiaryCount() - nextMonth.getBeneficiaryCount());

if (skipCheck(previousMonth, nextMonth, change)) {
if (CoverageStableCheckHelper.skipCheck(previousMonth, nextMonth, change)) {
continue;
}

Expand All @@ -59,29 +57,4 @@ private List<String> listCoveragePeriodsWithChangedEnrollment(List<CoverageCount

return coveragePeriodsChanged;
}

//Moved the skip check conditions to a method to make sonar happy
private boolean skipCheck(CoverageCount previousMonth, CoverageCount nextMonth, int change) {

// Don't check December to January because changes can be 200% or more
LocalDate now = LocalDate.now();
boolean skip = previousMonth.getMonth() == 12;

// Ignores coverage checks from previous years
if (nextMonth.getYear() < now.getYear() && previousMonth.getYear() < now.getYear()) {
skip = true;
}

// January to February changes can also be significant.
// Stop sending this notification once February ends.
if (now.getMonthValue() > 2 && previousMonth.getMonth() == 1) {
skip = true;
}

// Change could be anomaly for smaller contracts, ignore
if (change < CHANGE_THRESHOLD) {
skip = true;
}
return skip;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gov.cms.ab2d.worker.processor.coverage.check;

import gov.cms.ab2d.coverage.model.CoverageCount;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDate;

/**
* Check to make sure that month to month enrollment changes are within acceptable bounds. If enrollment goes from
* 1K to 1 million then there may be problem.
*/
@Slf4j
public class CoverageStableCheckHelper {
private static final int CHANGE_THRESHOLD = 1000;

//Moved the skip check conditions to a method to make sonar happy
public static boolean skipCheck(CoverageCount previousMonth, CoverageCount nextMonth, int change) {

// Don't check December to January because changes can be 200% or more
LocalDate now = LocalDate.now();
boolean skip = previousMonth.getMonth() == 12;

// Ignores coverage checks from previous years
if (nextMonth.getYear() < now.getYear() && previousMonth.getYear() < now.getYear()) {
skip = true;
}

// January to February changes can also be significant.
// Stop sending this notification once February ends.
if (now.getMonthValue() > 2 && previousMonth.getMonth() == 1) {
skip = true;
}

// Change could be anomaly for smaller contracts, ignore
if (change < CHANGE_THRESHOLD) {
skip = true;
}
return skip;
}
}
Loading

0 comments on commit adfb1c2

Please sign in to comment.