Skip to content

Commit

Permalink
Merge pull request #60 from nhsconnect/PRMT-3413-REFACTOR
Browse files Browse the repository at this point in the history
PRMT-3413 E2E testing - Unexpected Interaction ID and NHS number
  • Loading branch information
martin-nhs authored Aug 15, 2023
2 parents e56d958 + 765d4df commit f262033
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 5 deletions.
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,14 @@
package uk.nhs.prm.e2etests.property;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class EhrOutServiceProperties {
@Value("${aws.configuration.serviceUrls.ehrOutService}")
private String ehrOutServiceUrl;

public String getEhrOutServiceUrl() {
return ehrOutServiceUrl;
}
}
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,5 +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.nems.NemsResolutionMessage;
import org.awaitility.core.ConditionTimeoutException;
Expand All @@ -26,6 +28,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 @@ -53,7 +57,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 @@ -78,6 +82,31 @@ public List<SqsMessage> getAllMessagesContaining(String substring, int expectedN
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.
// 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
64 changes: 64 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,64 @@
package uk.nhs.prm.e2etests.service;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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;
}
}
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
87 changes: 87 additions & 0 deletions src/test/java/uk/nhs/prm/e2etests/test/RepositoryE2ETest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
Expand Down Expand Up @@ -41,6 +43,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.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 @@ -78,6 +81,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;
private final EhrTransferServiceTransferCompleteOQ ehrTransferServiceTransferCompleteOQ;
Expand All @@ -98,6 +102,7 @@ public RepositoryE2ETest(
TransferTrackerService transferTrackerService,
PdsAdaptorService pdsAdaptorService,
TemplatingService templatingService,
HealthCheckService healthCheckService,
SimpleAmqpQueue mhsInboundQueue,
Gp2GpMessengerOQ gp2gpMessengerOQ,
EhrTransferServiceTransferCompleteOQ ehrTransferServiceTransferCompleteOQ,
Expand All @@ -115,6 +120,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 @@ -335,6 +341,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

0 comments on commit f262033

Please sign in to comment.