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

PRMT-3413 E2E testing - Unexpected Interaction ID and NHS number #60

Merged
merged 9 commits into from
Aug 15, 2023
1 change: 1 addition & 0 deletions src/main/java/uk/nhs/prm/e2etests/enumeration/Patient.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum Patient {
SUSPENDED_WITH_EHR_AT_TPP("9693642937"),
PATIENT_WITH_LARGE_EHR_AT_EMIS_WITH_MOF_SET_TO_REPO_DEV_ODS("9693643038"),
PATIENT_WITH_LARGE_EHR_AT_EMIS_WITH_MOF_SET_TO_REPO_TEST_ODS("9694181372"),
PATIENT_WITH_SMALL_EHR_IN_REPO_AND_MOF_SET_TO_TPP("9727018440"),
WITH_SINGLE_FRAGMENT_LARGE_EHR("9727018076"),
WITH_MULTIPLE_FRAGMENTS_LARGE_EHR("9693643038"),
WITH_HIGH_FRAGMENT_COUNT_LARGE_EHR("9693796179"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package uk.nhs.prm.e2etests.model.request;

import lombok.EqualsAndHashCode;

public record PdsAdaptorRequest(String previousGp,
String recordETag)
{ }
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package uk.nhs.prm.e2etests.property;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import uk.nhs.prm.e2etests.service.SsmService;

@Getter
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
@Component
public class EhrOutServiceProperties {
@Value("${aws.configuration.serviceUrls.ehrOutService}")
private String ehrOutServiceUrl;

private final SsmService ssmService;

@Autowired
public EhrOutServiceProperties(SsmService ssmService) { this.ssmService = ssmService; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package uk.nhs.prm.e2etests.property;

import com.amazonaws.services.dynamodbv2.xspec.SS;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package uk.nhs.prm.e2etests.queue;

import lombok.extern.log4j.Log4j2;
import org.awaitility.core.ConditionTimeoutException;
import software.amazon.awssdk.services.sqs.model.PurgeQueueInProgressException;
import uk.nhs.prm.e2etests.model.SqsMessage;
import uk.nhs.prm.e2etests.model.nems.NemsResolutionMessage;
Expand All @@ -22,6 +23,8 @@
@Log4j2
public abstract class AbstractMessageQueue {
private static final String CHECKING_QUEUE_LOG_MESSAGE = "Checking if message is present on: {}";

private static final String CHECKING_QUEUE_LOG_MESSAGE_WITH_SUBSTRING = CHECKING_QUEUE_LOG_MESSAGE + ", with substring {}";
private static final String DELETE_ALL_MESSAGES_LOG_MESSAGE = "Attempting to delete all messages on: {}";
private static final String MESSAGE_FOUND_LOG_MESSAGE = "The message has been found on: {}";

Expand Down Expand Up @@ -49,7 +52,7 @@ public void deleteMessage(SqsMessage sqsMessage) {
}

public SqsMessage getMessageContaining(String substring) {
log.info(CHECKING_QUEUE_LOG_MESSAGE, this.queueUri);
log.info(CHECKING_QUEUE_LOG_MESSAGE_WITH_SUBSTRING, this.queueUri, substring);
final SqsMessage foundMessage = await().atMost(120, TimeUnit.SECONDS)
.with()
.pollInterval(100, TimeUnit.MILLISECONDS)
Expand All @@ -74,6 +77,31 @@ public List<SqsMessage> getAllMessageContaining(String substring, int expectedNu
return allMessages;
}




public boolean verifyNoMessageContaining(String substring, int secondsToPoll) {
// Queue the queue repeatedly for {secondsToPoll}, and return true only if a message with given substring NEVER appeared throughout the period.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Queue the queue repeatedly for {secondsToPoll}, and return true only if a message with given substring NEVER appeared throughout the period.
// Poll the queue repeatedly for {secondsToPoll}, and return true only if a message with given substring NEVER appeared throughout the period.

// The reason of using awaitility is to allow for the time taken for communication between micro-services (ehr-out, ehr-repo, gp2gp messenger)
log.info("Verifying that no message on queue {} contains the substring {}", this.queueUri, substring);
try {
await().atMost(secondsToPoll, TimeUnit.SECONDS)
.with()
.pollInterval(100, TimeUnit.MILLISECONDS)
.until(() -> findMessageContaining(substring), Optional::isPresent);
log.info("A message on {} match the given substring {}. Returning false", this.queueUri, substring);
return false;
} catch (ConditionTimeoutException error) {
log.info("Confirmed no message on {} match the given substring {}. Returning true", this.queueUri, substring);
return true;
}
}

public boolean verifyNoMessageContaining(String substring) {
// calls the method with the same name, with secondsToPoll preset to 10 seconds.
return verifyNoMessageContaining(substring, 10);
}

public SqsMessage getMessageContainingAttribute(String attribute, String expectedValue) {
return getMessageContainingAttribute(attribute, expectedValue, 120, TimeUnit.SECONDS);
}
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/uk/nhs/prm/e2etests/service/HealthCheckService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package uk.nhs.prm.e2etests.service;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import uk.nhs.prm.e2etests.exception.ServiceException;
import uk.nhs.prm.e2etests.property.EhrOutServiceProperties;
import uk.nhs.prm.e2etests.property.EhrRepositoryProperties;
import uk.nhs.prm.e2etests.property.Gp2gpMessengerProperties;

import java.util.Map;

@Log4j2
@Service
public class HealthCheckService {
private final RestTemplate restTemplate = new RestTemplate();

private final Map<String, String> listOfServices;

@Autowired
public HealthCheckService(
Gp2gpMessengerProperties gp2gpMessengerProperties,
EhrRepositoryProperties ehrRepositoryProperties,
EhrOutServiceProperties ehrOutServiceProperties
) {
// Ehr Transfer Service doesn't seem to have a health check endpoint for now, so it is not included here.
this.listOfServices = Map.of(
"Gp2gp Messenger", gp2gpMessengerProperties.getGp2gpMessengerUrl(),
"Ehr Repository", ehrRepositoryProperties.getEhrRepositoryUrl(),
"Ehr Out Service", ehrOutServiceProperties.getEhrOutServiceUrl()
);
}

public boolean healthCheckAllPassing() {
return this.listOfServices.entrySet().stream().allMatch(
entry -> runHealthCheck(entry.getKey(), entry.getValue())
);
}

private boolean runHealthCheck(String nameOfService, String baseUrl) {
log.info("Running health check for service: {}", nameOfService);
String ehrRepoHealthCheckUrl = String.format("%s/health", baseUrl);
try {
ResponseEntity<String> exchange = restTemplate.exchange(ehrRepoHealthCheckUrl,
HttpMethod.GET, new HttpEntity<String>(createHeaders()), String.class);
return exchange.getStatusCode().is2xxSuccessful();
} catch (HttpStatusCodeException exception) {
throw new ServiceException(getClass().getName(), exception.getMessage());
}
}

private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}

martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import uk.nhs.prm.e2etests.model.templatecontext.NemsEventTemplateContext;
import uk.nhs.prm.e2etests.model.templatecontext.TemplateContext;
import uk.nhs.prm.e2etests.utility.NhsIdentityUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import uk.nhs.prm.e2etests.enumeration.TemplateVariant;
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ aws:
pdsAdaptor: https://pds-adaptor.${NHS_ENVIRONMENT}.non-prod.patient-deductions.nhs.uk/
gp2GpMessenger: https://gp2gp-messenger.${NHS_ENVIRONMENT}.non-prod.patient-deductions.nhs.uk/
ehrRepository: https://ehr-repo.${NHS_ENVIRONMENT}.non-prod.patient-deductions.nhs.uk/
ehrOutService: https://ehr-out-service.${NHS_ENVIRONMENT}.non-prod.patient-deductions.nhs.uk/

queueNames:
meshForwarder:
Expand Down
92 changes: 86 additions & 6 deletions src/test/java/uk/nhs/prm/e2etests/test/RepositoryE2ETest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package uk.nhs.prm.e2etests.test;

import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.*;
martin-nhs marked this conversation as resolved.
Show resolved Hide resolved
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -19,7 +16,6 @@
import uk.nhs.prm.e2etests.model.RepoIncomingMessage;
import uk.nhs.prm.e2etests.model.RepoIncomingMessageBuilder;
import uk.nhs.prm.e2etests.model.SqsMessage;
import uk.nhs.prm.e2etests.model.database.Acknowledgement;
import uk.nhs.prm.e2etests.model.database.TransferTrackerRecord;
import uk.nhs.prm.e2etests.model.response.PdsAdaptorResponse;
import uk.nhs.prm.e2etests.model.templatecontext.ContinueRequestTemplateContext;
Expand All @@ -41,7 +37,7 @@
import uk.nhs.prm.e2etests.queue.ehrtransfer.observability.EhrTransferServiceTransferCompleteOQ;
import uk.nhs.prm.e2etests.queue.ehrtransfer.observability.EhrTransferServiceUnhandledOQ;
import uk.nhs.prm.e2etests.queue.gp2gpmessenger.observability.Gp2GpMessengerOQ;
import uk.nhs.prm.e2etests.repository.EhrOutDatabaseAcknowledgementRepository;
import uk.nhs.prm.e2etests.service.HealthCheckService;
import uk.nhs.prm.e2etests.service.PdsAdaptorService;
import uk.nhs.prm.e2etests.service.TemplatingService;
import uk.nhs.prm.e2etests.service.TransferTrackerService;
Expand Down Expand Up @@ -76,6 +72,7 @@ class RepositoryE2ETest {
private final TransferTrackerService transferTrackerService;
private final PdsAdaptorService pdsAdaptorService;
private final TemplatingService templatingService;
private final HealthCheckService healthCheckService;
private final SimpleAmqpQueue mhsInboundQueue;

private final Gp2GpMessengerOQ gp2gpMessengerOQ;
Expand All @@ -97,6 +94,7 @@ public RepositoryE2ETest(
TransferTrackerService transferTrackerService,
PdsAdaptorService pdsAdaptorService,
TemplatingService templatingService,
HealthCheckService healthCheckService,
SimpleAmqpQueue mhsInboundQueue,
Gp2GpMessengerOQ gp2gpMessengerOQ,
EhrTransferServiceTransferCompleteOQ ehrTransferServiceTransferCompleteOQ,
Expand All @@ -114,6 +112,7 @@ public RepositoryE2ETest(
this.transferTrackerService = transferTrackerService;
this.pdsAdaptorService = pdsAdaptorService;
this.templatingService = templatingService;
this.healthCheckService = healthCheckService;
this.mhsInboundQueue = mhsInboundQueue;
this.gp2gpMessengerOQ = gp2gpMessengerOQ;
this.ehrTransferServiceTransferCompleteOQ = ehrTransferServiceTransferCompleteOQ;
Expand Down Expand Up @@ -334,6 +333,87 @@ void shouldVerifyThatALargeEhrXMLIsUnchanged() {
});
}

// Test Cases for Erroneous Inbound messages
private Arguments erroneousInboundMessage_UnrecognisedInteractionID() {
String invalidInteractionId = "TEST_XX123456XX01";
String nhsNumber = Patient.PATIENT_WITH_SMALL_EHR_IN_REPO_AND_MOF_SET_TO_TPP.nhsNumber();
String newGpOdsCode = Gp2GpSystem.TPP_PTL_INT.odsCode();

EhrRequestTemplateContext ehrRequestContext = EhrRequestTemplateContext.builder()
.nhsNumber(nhsNumber)
.newGpOdsCode(newGpOdsCode)
.build();

String inboundMessage = this.templatingService.getTemplatedString(TemplateVariant.EHR_REQUEST, ehrRequestContext);

String erroneousInboundMessage = inboundMessage
.replaceAll(MessageType.EHR_REQUEST.interactionId, invalidInteractionId);

return Arguments.of(
Named.of("Message with unrecognised Interaction ID", erroneousInboundMessage),
ehrRequestContext.getOutboundConversationId()
);
}

private Arguments erroneousInboundMessage_EhrRequestWithUnrecognisedNhsNumber() {
String nonExistentNhsNumber = "9729999999";
String newGpOdsCode = Gp2GpSystem.TPP_PTL_INT.odsCode();

EhrRequestTemplateContext ehrRequestContext = EhrRequestTemplateContext.builder()
.nhsNumber(nonExistentNhsNumber)
.newGpOdsCode(newGpOdsCode)
.build();

String erroneousInboundMessage = this.templatingService.getTemplatedString(TemplateVariant.EHR_REQUEST, ehrRequestContext);

return Arguments.of(
Named.of("EHR Request with unrecognised NHS Number", erroneousInboundMessage),
ehrRequestContext.getOutboundConversationId()
);
}

private Arguments erroneousInboundMessage_ContinueRequestWithUnrecognisedConversationId() {
// The builder by default already generates a random conversation ID, which fulfills the test condition
ContinueRequestTemplateContext continueRequestContext = ContinueRequestTemplateContext.builder().build();

String continueRequestMessage = this.templatingService
.getTemplatedString(TemplateVariant.CONTINUE_REQUEST, continueRequestContext);

return Arguments.of(
Named.of("Continue Request with unrecognised Conversation ID", continueRequestMessage),
continueRequestContext.getOutboundConversationId()
);
}

private Stream<Arguments> erroneousInboundMessages() {
return Stream.of(
erroneousInboundMessage_UnrecognisedInteractionID(),
erroneousInboundMessage_EhrRequestWithUnrecognisedNhsNumber(),
erroneousInboundMessage_ContinueRequestWithUnrecognisedConversationId()
);
}

@ParameterizedTest(name = "[{index}] Case of {0}")
@MethodSource("erroneousInboundMessages")
@DisplayName("Test how ORC handles Erroneous inbound messages")
void testsWithErroneousInboundMessages(String inboundMessage, String conversationId) {
// when
mhsInboundQueue.sendMessage(inboundMessage, conversationId.toLowerCase());

// then
log.info("Verify that EHR Transfer Service put the erroneous inbound message to unhandled queue");
SqsMessage unhandledMessage = ehrTransferServiceUnhandledOQ.getMessageContaining(conversationId);
assertThat(unhandledMessage.getBody()).isEqualTo(inboundMessage);

log.info("Verify that no response message with given conversation id is on the gp2gp observability queue");
// later this could be changed to asserting an NACK message on the queue if we do send back NACKs
assertTrue(gp2gpMessengerOQ.verifyNoMessageContaining(conversationId));

assertTrue(healthCheckService.healthCheckAllPassing());
}

// End of tests for Erroneous Inbound messages

@Test
@Disabled("This test was failing before refactoring. The cause seems to be related to EMIS instance not working")
void shouldReceivingAndTrackAllLargeEhrFragments_DevAndTest() {
Expand Down