From 1970e180bf22233b1cd8483d5637bdad7ad9a627 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:07:50 +0100 Subject: [PATCH 01/13] Replacing Instant.now() with timestampService.now() --- .../adaptors/gp2gp/ehr/EhrExtractStatusService.java | 12 ++++++------ .../gp2gp/ehr/SendEhrExtractCoreTaskExecutor.java | 4 ++-- .../gp2gp/ehr/request/EhrExtractAckHandler.java | 4 +++- .../ehr/scheduling/EhrExtractTimeoutScheduler.java | 4 +++- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusService.java b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusService.java index c99c877bf3..02ba1c5e21 100644 --- a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusService.java +++ b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusService.java @@ -202,7 +202,7 @@ public EhrExtractStatus updateEhrExtractStatusAccessStructured(GetGpcStructuredT String structuredRecordJsonFilename) { Query query = createQueryForConversationId(structuredTaskDefinition.getConversationId()); - Instant now = Instant.now(); + Instant now = timestampService.now(); Update update = createUpdateWithUpdatedAt(); update.set(STRUCTURE_ACCESSED_AT_PATH, now); @@ -301,7 +301,7 @@ public Optional updateEhrExtractStatusContinue(String conversa Query query = createQueryForConversationId(conversationId); Update update = createUpdateWithUpdatedAt(); - Instant now = Instant.now(); + Instant now = timestampService.now(); update.set(CONTINUE_RECEIVED_PATH, now); FindAndModifyOptions returningUpdatedRecordOption = getReturningUpdatedRecordOption(); @@ -485,7 +485,7 @@ public EhrExtractStatus updateEhrExtractStatusError( String taskType) { Update update = createUpdateWithUpdatedAt(); - Instant now = Instant.now(); + Instant now = timestampService.now(); update.set(ERROR_OCCURRED_AT_PATH, now); update.set(ERROR_CODE_PATH, errorCode); update.set(ERROR_MESSAGE_PATH, errorMessage); @@ -535,7 +535,7 @@ private EhrExtractStatus updateEhrExtractStatusDocumentSentToMHS(SendDocumentTas var commonMessageId = GPC_DOCUMENTS + DOT + taskDefinition.getDocumentPosition() + DOT + SENT_TO_MHS + DOT + MESSAGE_ID; Update update = createUpdateWithUpdatedAt(); - update.set(commonSentAt, Instant.now()); + update.set(commonSentAt, timestampService.now()); update.set(commonTaskId, taskDefinition.getTaskId()); update.set(commonMessageId, messageIds); @@ -561,7 +561,7 @@ private EhrExtractStatus updateEhrExtractStatusAttachmentSentToMhs(SendDocumentT var commonMessageId = STRUCTURE_OBJECT_AS_ATTACHMENT + DOT + SENT_TO_MHS + DOT + MESSAGE_ID; Update update = createUpdateWithUpdatedAt(); - update.set(commonSentAt, Instant.now()); + update.set(commonSentAt, timestampService.now()); update.set(commonTaskId, taskDefinition.getTaskId()); update.set(commonMessageId, messageIds); @@ -594,7 +594,7 @@ public Query createQueryForConversationId(String conversationId) { } public Update createUpdateWithUpdatedAt() { - Instant now = Instant.now(); + Instant now = timestampService.now(); Update update = new Update(); update.set(UPDATED_AT, now); diff --git a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/SendEhrExtractCoreTaskExecutor.java b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/SendEhrExtractCoreTaskExecutor.java index 5b8ffe430f..374000d814 100644 --- a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/SendEhrExtractCoreTaskExecutor.java +++ b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/SendEhrExtractCoreTaskExecutor.java @@ -83,10 +83,10 @@ public void execute(SendEhrExtractCoreTaskDefinition sendEhrExtractCoreTaskDefin mhsClient.sendMessageToMHS(requestData); - Instant requestSentAtPending = Instant.now(); + Instant requestSentAtPending = timestampService.now(); ehrExtractStatusService.updateEhrExtractStatusCorePending(sendEhrExtractCoreTaskDefinition, requestSentAtPending); - Instant requestSentAt = Instant.now(); + Instant requestSentAt = timestampService.now(); var ehrExtractStatus = ehrExtractStatusService.updateEhrExtractStatusCore(sendEhrExtractCoreTaskDefinition, requestSentAt); if (ehrExtractStatus.getGpcAccessDocument().getDocuments().isEmpty()) { sendAcknowledgementTaskDispatcher.sendPositiveAcknowledgement(ehrExtractStatus); diff --git a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandler.java b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandler.java index 4b12ce7a0d..d4e0b03f66 100644 --- a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandler.java +++ b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandler.java @@ -13,6 +13,7 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import uk.nhs.adaptors.gp2gp.common.service.TimestampService; import uk.nhs.adaptors.gp2gp.common.service.XPathService; import uk.nhs.adaptors.gp2gp.ehr.EhrExtractStatusService; import uk.nhs.adaptors.gp2gp.ehr.exception.EhrExtractException; @@ -38,11 +39,12 @@ public class EhrExtractAckHandler { private final XPathService xPathService; private final EhrExtractStatusService ehrExtractStatusService; + private final TimestampService timestampService; @SneakyThrows public void handle(String conversationId, Document document) { String ackTypeCode = xPathService.getNodeValue(document, ACK_TYPE_CODE_XPATH); - Instant now = Instant.now(); + Instant now = timestampService.now(); String messageRef = xPathService.getNodeValue(document, MESSAGE_REF_XPATH); String rootId = xPathService.getNodeValue(document, MESSAGE_ID_ROOT_XPATH); EhrReceivedAcknowledgementBuilder ackBuilder = EhrReceivedAcknowledgement.builder() diff --git a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java index dc5a6f80a1..67cde2ad80 100644 --- a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java +++ b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java @@ -9,6 +9,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import uk.nhs.adaptors.gp2gp.common.service.TimestampService; import uk.nhs.adaptors.gp2gp.ehr.EhrExtractStatusService; import uk.nhs.adaptors.gp2gp.ehr.exception.EhrExtractException; import uk.nhs.adaptors.gp2gp.ehr.model.EhrExtractStatus; @@ -26,11 +27,12 @@ public class EhrExtractTimeoutScheduler { private static final String ERROR = "error"; private final MongoTemplate mongoTemplate; private final EhrExtractStatusService ehrExtractStatusService; + private TimestampService timestampService; @Scheduled(cron = "${timeout.cronTime}") public void processEhrExtractAckTimeouts() { List inProgressEhrExtractTransfers = findInProgressTransfers(); - var now = Instant.now(); + var now = timestampService.now(); var ehrExtractStatusWithExceededUpdateLimit = inProgressEhrExtractTransfers .stream() .filter(ehrExtractStatus -> Objects.isNull(ehrExtractStatus.getEhrReceivedAcknowledgement()) From 27d5a1b7d1d414e67932fd0c5e0df8222376fac8 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:34:45 +0100 Subject: [PATCH 02/13] checkstyle and spotbugs --- .../gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java index 67cde2ad80..5a4c2535d0 100644 --- a/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java +++ b/service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutScheduler.java @@ -14,8 +14,6 @@ import uk.nhs.adaptors.gp2gp.ehr.exception.EhrExtractException; import uk.nhs.adaptors.gp2gp.ehr.model.EhrExtractStatus; import uk.nhs.adaptors.gp2gp.ehr.utils.ErrorDetail; - -import java.time.Instant; import java.util.List; import java.util.Objects; @@ -27,7 +25,7 @@ public class EhrExtractTimeoutScheduler { private static final String ERROR = "error"; private final MongoTemplate mongoTemplate; private final EhrExtractStatusService ehrExtractStatusService; - private TimestampService timestampService; + private final TimestampService timestampService; @Scheduled(cron = "${timeout.cronTime}") public void processEhrExtractAckTimeouts() { From 668895fd353ae7b8ee471f10cccf00bc128a0da2 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:40:55 +0100 Subject: [PATCH 03/13] checkstyle and spotbugs --- .../gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java index 125ec060a3..fb95b00954 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java @@ -77,7 +77,7 @@ class EhrExtractTimeoutSchedulerTest { void setUp() { ehrExtractStatusService = new EhrExtractStatusService(mongoTemplate, ehrExtractStatusRepository, timestampService); ehrExtractStatusServiceSpy = spy(ehrExtractStatusService); - ehrExtractTimeoutScheduler = new EhrExtractTimeoutScheduler(mongoTemplate, ehrExtractStatusServiceSpy); + ehrExtractTimeoutScheduler = new EhrExtractTimeoutScheduler(mongoTemplate, ehrExtractStatusServiceSpy, timestampService); } @Test From d297a4e4434ec5b8e8969827dfc632ffa5a771be Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:30:08 +0100 Subject: [PATCH 04/13] fixing tests --- .../gp2gp/ehr/request/EhrExtractAckHandlerTest.java | 10 ++++++++++ .../ehr/scheduling/EhrExtractTimeoutSchedulerTest.java | 3 +++ 2 files changed, 13 insertions(+) diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java index 9476b3bfb0..aff8ffd659 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java @@ -13,6 +13,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.time.Instant; import java.util.Optional; import javax.xml.parsers.DocumentBuilderFactory; @@ -32,6 +33,7 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import uk.nhs.adaptors.gp2gp.common.service.TimestampService; import uk.nhs.adaptors.gp2gp.common.service.XPathService; import uk.nhs.adaptors.gp2gp.ehr.EhrExtractStatusService; import uk.nhs.adaptors.gp2gp.ehr.exception.EhrExtractException; @@ -66,6 +68,9 @@ class EhrExtractAckHandlerTest { @Mock private Document document; + @Mock + private TimestampService timestampService; + @Captor private ArgumentCaptor receivedAckField; @@ -141,6 +146,7 @@ void When_Handle_WithAckAndNoEhrExtractMessageIdForConversation_Expect_EhrExtrac when(xPathService.getNodeValue(any(), eq(ACK_TYPE_CODE_XPATH))).thenReturn(ACK_OK_CODE); when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); + when(timestampService.now()).thenReturn(Instant.now()); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.empty()); @@ -159,6 +165,7 @@ void When_Handle_WithNackReferencesEhrExtract_Expect_ConversationClosed() throws when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(xPathService.getNodes(any(), eq(ERROR_CODE_XPATH))).thenReturn(codeNodeList); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(EHR_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); @@ -196,6 +203,7 @@ void When_Handle_WithNackDoesNotReferenceExtract_Expect_ReceivedAckFieldNotUpdat when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(xPathService.getNodes(any(), eq(ERROR_CODE_XPATH))).thenReturn(codeNodeList); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(RANDOM_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); @@ -214,6 +222,7 @@ void When_Handle_WithNackDoesNotReferenceExtract_Expect_AckSaved() throws XPathE when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(xPathService.getNodes(any(), eq(ERROR_CODE_XPATH))).thenReturn(codeNodeList); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(RANDOM_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); @@ -286,6 +295,7 @@ void When_Handle_WithRejectedDoesNotReferenceExtract_Expect_AckSaved() throws XP when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(xPathService.getNodes(any(), eq(ACK_DETAILS_XPATH))).thenReturn(codeNodeList); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(RANDOM_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java index fb95b00954..7111d7453f 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/scheduling/EhrExtractTimeoutSchedulerTest.java @@ -285,6 +285,7 @@ void updateEhrExtractStatusListWithEhrReceivedAcknowledgementError() { doReturn(List.of(ehrExtractStatus)).when(ehrExtractTimeoutSchedulerSpy).findInProgressTransfers(); when(ehrExtractTimeoutSchedulerSpy.logger()).thenReturn(logger); + when(timestampService.now()).thenReturn(Instant.now()); var exception = assertThrows(EhrExtractException.class, ehrExtractTimeoutSchedulerSpy::processEhrExtractAckTimeouts); @@ -313,6 +314,7 @@ void shouldCatchExceptionIfUnexpectedConditionAriseWhileUpdatingEhrExtractStatus var inProgressConversationId = generateRandomUppercaseUUID(); EhrExtractStatus ehrExtractStatus = addInProgressTransfers(inProgressConversationId); doReturn(List.of(ehrExtractStatus)).when(ehrExtractTimeoutSchedulerSpy).findInProgressTransfers(); + when(timestampService.now()).thenReturn(Instant.now()); Exception exception = new RuntimeException("Logger failure"); doThrow(exception).when(logger).info("Scheduler has started processing EhrExtract list with Ack timeouts"); @@ -338,6 +340,7 @@ void whenEhrExtractStatusIsNullInterceptExceptionAndLogErrorMsg() { doReturn(null).when(mongoTemplate).findAndModify(any(Query.class), any(UpdateDefinition.class), any(FindAndModifyOptions.class), any()); when(ehrExtractTimeoutSchedulerSpy.logger()).thenReturn(logger); + when(timestampService.now()).thenReturn(Instant.now()); var exception = assertThrows(EhrExtractException.class, ehrExtractTimeoutSchedulerSpy::processEhrExtractAckTimeouts); From c09384ba65a9d9c4185968179efe5f1b15806d8a Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:39:21 +0100 Subject: [PATCH 05/13] fixing tests --- .../adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java index aff8ffd659..89e071c304 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/request/EhrExtractAckHandlerTest.java @@ -95,6 +95,7 @@ void When_Handle_WithAckAndMessageRefEqualsEhrExtractMessageId_Expect_Conversati when(xPathService.getNodeValue(any(), eq(ACK_TYPE_CODE_XPATH))).thenReturn(ACK_OK_CODE); when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(EHR_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); @@ -241,6 +242,7 @@ void When_Handle_WithRejectedReferencesEhrExtract_Expect_ConversationClosed() th when(xPathService.getNodeValue(any(), eq(MESSAGE_REF_XPATH))).thenReturn(EHR_MESSAGE_REF); when(xPathService.getNodes(any(), eq(ACK_DETAILS_XPATH))).thenReturn(codeNodeList); when(ehrExtractStatusService.fetchEhrExtractMessageId(CONVERSATION_ID)).thenReturn(Optional.of(EHR_MESSAGE_REF)); + when(timestampService.now()).thenReturn(Instant.now()); ehrExtractAckHandler.handle(CONVERSATION_ID, document); From 5e7f6da883596a98ccacbcbaa7cecc2e7dd920a9 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:51:15 +0100 Subject: [PATCH 06/13] addressing pitest mutation complains --- .../ehr/EhrExtractStatusServiceTest.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java index 3e38d26581..1832fd636f 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java @@ -607,8 +607,38 @@ void shouldLogWarningWithMsgIgnoredWhenLateAcknowledgementReceivedAfter8DaysAndE conversationId); } + @Test + void shouldSetSentAtFromTimestampServiceWhenUpdatingDocumentSentToMHS() { + String conversationId = generateRandomUppercaseUUID(); + String taskId = generateRandomUppercaseUUID(); + int documentPosition = 0; + Instant fixedTimestamp = Instant.parse("2026-04-23T10:00:00Z"); + + SendDocumentTaskDefinition taskDefinition = SendDocumentTaskDefinition.builder() + .conversationId(conversationId) + .taskId(taskId) + .documentPosition(documentPosition) + .build(); + + when(timestampService.now()).thenReturn(fixedTimestamp); + + EhrExtractStatus ehrExtractStatus = EhrExtractStatus.builder().build(); + when(mongoTemplate.findAndModify(any(Query.class), any(Update.class), any(FindAndModifyOptions.class), eq(EhrExtractStatus.class))) + .thenReturn(ehrExtractStatus); + + ehrExtractStatusService.updateEhrExtractStatusCommonForDocuments(taskDefinition, List.of("msg-id-1")); + + verify(mongoTemplate).findAndModify(queryCaptor.capture(), updateCaptor.capture(), + any(FindAndModifyOptions.class), eq(EhrExtractStatus.class)); + + Document setDocument = (Document) updateCaptor.getValue().getUpdateObject().get("$set"); + String sentAtPath = "gpcAccessDocument.documents." + documentPosition + ".sentToMhs.sentAt"; + assertEquals(fixedTimestamp, setDocument.get(sentAtPath)); + } + private String generateRandomUppercaseUUID() { return UUID.randomUUID().toString().toUpperCase(); } -} \ No newline at end of file +} + From ea92d5fc9021f2c0b37018d3031bdf01067610de Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:08:28 +0100 Subject: [PATCH 07/13] addressing pitest mutation complains --- .../ehr/EhrExtractStatusServiceTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java index 1832fd636f..a041e5c542 100644 --- a/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java +++ b/service/src/test/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceTest.java @@ -636,6 +636,45 @@ void shouldSetSentAtFromTimestampServiceWhenUpdatingDocumentSentToMHS() { assertEquals(fixedTimestamp, setDocument.get(sentAtPath)); } + @Test + void shouldSetSentAtFromTimestampServiceWhenUpdatingAttachmentSentToMhs() { + String conversationId = generateRandomUppercaseUUID(); + String taskId = generateRandomUppercaseUUID(); + Instant updatedAtTimestamp = Instant.parse("2026-04-23T11:00:00Z"); + Instant sentAtTimestamp = Instant.parse("2026-04-23T12:00:00Z"); + + SendDocumentTaskDefinition taskDefinition = SendDocumentTaskDefinition.builder() + .conversationId(conversationId) + .taskId(taskId) + .documentPosition(0) + .build(); + + when(timestampService.now()) + .thenReturn(updatedAtTimestamp) + .thenReturn(sentAtTimestamp); + + EhrExtractStatus ehrExtractStatus = EhrExtractStatus.builder().build(); + when(mongoTemplate.findAndModify(any(Query.class), any(Update.class), any(FindAndModifyOptions.class), eq(EhrExtractStatus.class))) + .thenReturn(ehrExtractStatus); + + ehrExtractStatusService.updateEhrExtractStatusCommonForExternalEhrExtract(taskDefinition, List.of("msg-id-1")); + + verify(mongoTemplate).findAndModify(queryCaptor.capture(), updateCaptor.capture(), + any(FindAndModifyOptions.class), eq(EhrExtractStatus.class)); + + Document setDocument = (Document) updateCaptor.getValue().getUpdateObject().get("$set"); + + String sentAtPath = "gpcAccessStructured.attachment.sentToMhs.sentAt"; + assertTrue(setDocument.containsKey(sentAtPath), "Update should contain the sentAt field"); + assertEquals(sentAtTimestamp, setDocument.get(sentAtPath)); + + String taskIdPath = "gpcAccessStructured.attachment.sentToMhs.taskId"; + assertEquals(taskId, setDocument.get(taskIdPath)); + + String messageIdPath = "gpcAccessStructured.attachment.sentToMhs.messageId"; + assertEquals(List.of("msg-id-1"), setDocument.get(messageIdPath)); + } + private String generateRandomUppercaseUUID() { return UUID.randomUUID().toString().toUpperCase(); } From 47cc90bb0df7d094ce5b867e9b1ca8379e73d118 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:30:11 +0100 Subject: [PATCH 08/13] fixing integration test failures --- .../uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java | 1 + .../nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java index c862dfe83d..66ffa5fe99 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java @@ -84,6 +84,7 @@ void When_EhrStatusWithExceededTimeout_Expect_EhrStatusShouldNotBeUpdated() { ehrExtractTimeoutScheduler.processEhrExtractAckTimeouts(); when(ehrExtractStatusServiceSpy.logger()).thenReturn(logger); + when(timestampService.now()).thenReturn(NOW); var ehrReceivedAcknowledgement = getEhrReceivedAcknowledgement(inProgressConversationId); ehrReceivedAcknowledgement.setReceived(NOW); diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index e26d0981e6..5eaa651015 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -95,6 +95,7 @@ public void When_AcknowledgementTaskRunsTwice_Expect_DatabaseOverwritesEhrExtrac when(sendAcknowledgementTaskDefinition.getConversationId()).thenReturn(ehrExtractStatus.getConversationId()); when(sendAcknowledgementTaskDefinition.getFromOdsCode()).thenReturn(ehrRequest.getFromOdsCode()); when(mhsClient.sendMessageToMHS(request)).thenReturn("Successful Mhs Outbound Request"); + when(timestampService.now()).thenReturn(Instant.parse(DATE)); sendAcknowledgementExecutor.execute(sendAcknowledgementTaskDefinition); var ehrExtractFirst = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId()).get(); From 169a0009ca4d490e742c3be60edf98b1316ff843 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:39:30 +0100 Subject: [PATCH 09/13] test refactoring --- .../adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index 5eaa651015..6601a07f95 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; @@ -163,7 +164,7 @@ public void When_PositiveAcknowledgementTaskExecuted_Expect_ValidRequestSentToMh assertThat(ackToRequester.getMessageId()).isEqualTo(GENERATED_RANDOM_ID); assertThat(ackToRequester.getTypeCode()).isEqualTo(POSITIVE_ACK_TYPE_CODE); assertThat(ackToRequester.getReasonCode()).isNull(); - assertThat(ackToRequester.getDetail()).isNull(); + assertNotNull(ackToRequester.getDetail()); } @Test From ec4569a61f5f1f700498aabf76912d37f2bb761b Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 16:12:07 +0100 Subject: [PATCH 10/13] test fixes --- .../uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java | 3 ++- .../adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java index 66ffa5fe99..563c97c851 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/EhrExtractStatusServiceIT.java @@ -79,12 +79,13 @@ void When_EhrStatusWithExceededTimeout_Expect_EhrStatusShouldNotBeUpdated() { var inProgressConversationId = generateRandomUppercaseUUID(); var ehrExtractStatusServiceSpy = spy(ehrExtractStatusService); + when(timestampService.now()).thenReturn(NOW); addInProgressTransferWithExceededAckTimeout(inProgressConversationId, List.of()); ehrExtractTimeoutScheduler.processEhrExtractAckTimeouts(); when(ehrExtractStatusServiceSpy.logger()).thenReturn(logger); - when(timestampService.now()).thenReturn(NOW); + var ehrReceivedAcknowledgement = getEhrReceivedAcknowledgement(inProgressConversationId); ehrReceivedAcknowledgement.setReceived(NOW); diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index 6601a07f95..72df94a888 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -96,7 +96,7 @@ public void When_AcknowledgementTaskRunsTwice_Expect_DatabaseOverwritesEhrExtrac when(sendAcknowledgementTaskDefinition.getConversationId()).thenReturn(ehrExtractStatus.getConversationId()); when(sendAcknowledgementTaskDefinition.getFromOdsCode()).thenReturn(ehrRequest.getFromOdsCode()); when(mhsClient.sendMessageToMHS(request)).thenReturn("Successful Mhs Outbound Request"); - when(timestampService.now()).thenReturn(Instant.parse(DATE)); + when(timestampService.now()).thenReturn(Instant.now()); sendAcknowledgementExecutor.execute(sendAcknowledgementTaskDefinition); var ehrExtractFirst = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId()).get(); @@ -164,7 +164,7 @@ public void When_PositiveAcknowledgementTaskExecuted_Expect_ValidRequestSentToMh assertThat(ackToRequester.getMessageId()).isEqualTo(GENERATED_RANDOM_ID); assertThat(ackToRequester.getTypeCode()).isEqualTo(POSITIVE_ACK_TYPE_CODE); assertThat(ackToRequester.getReasonCode()).isNull(); - assertNotNull(ackToRequester.getDetail()); + assertThat(ackToRequester.getDetail()).isNull(); } @Test From bd812c879731ae39908acc6363e48378a5315ad5 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 16:33:37 +0100 Subject: [PATCH 11/13] test fixes --- .../adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index 72df94a888..e26d0981e6 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; @@ -96,7 +95,6 @@ public void When_AcknowledgementTaskRunsTwice_Expect_DatabaseOverwritesEhrExtrac when(sendAcknowledgementTaskDefinition.getConversationId()).thenReturn(ehrExtractStatus.getConversationId()); when(sendAcknowledgementTaskDefinition.getFromOdsCode()).thenReturn(ehrRequest.getFromOdsCode()); when(mhsClient.sendMessageToMHS(request)).thenReturn("Successful Mhs Outbound Request"); - when(timestampService.now()).thenReturn(Instant.now()); sendAcknowledgementExecutor.execute(sendAcknowledgementTaskDefinition); var ehrExtractFirst = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId()).get(); From 62379334e8335f6073d19fabdc2a29ca597fcad2 Mon Sep 17 00:00:00 2001 From: ORybak5 <12736698+ORybak5@users.noreply.github.com> Date: Thu, 23 Apr 2026 16:54:25 +0100 Subject: [PATCH 12/13] integration test fix --- .../adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index e26d0981e6..101a305c02 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -38,6 +38,7 @@ @SpringBootTest(properties = {"command.line.runner.enabled=false"}) public class SendAcknowledgementComponentTest { private static final String GENERATED_RANDOM_ID = "GENERATED-RANDOM-ID"; + private static final String GENERATED_RANDOM_ID_2 = "GENERATED-RANDOM-ID-2"; private static final String FROM_ASID = "0000222-from-asid"; private static final String TO_ASID = "0000333-to-asid"; private static final String FROM_ODS_CODE = "0000222-from-ods-code"; @@ -96,6 +97,10 @@ public void When_AcknowledgementTaskRunsTwice_Expect_DatabaseOverwritesEhrExtrac when(sendAcknowledgementTaskDefinition.getFromOdsCode()).thenReturn(ehrRequest.getFromOdsCode()); when(mhsClient.sendMessageToMHS(request)).thenReturn("Successful Mhs Outbound Request"); + when(randomIdGeneratorService.createNewId()) + .thenReturn(GENERATED_RANDOM_ID) + .thenReturn(GENERATED_RANDOM_ID_2); + sendAcknowledgementExecutor.execute(sendAcknowledgementTaskDefinition); var ehrExtractFirst = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId()).get(); From 0495f7306aaa1ae42a2f8450213f11bcfe027458 Mon Sep 17 00:00:00 2001 From: chiaramapellimt Date: Mon, 27 Apr 2026 11:51:58 +0100 Subject: [PATCH 13/13] Added as many stubs as needed by mockito as per service --- .../ehr/SendAcknowledgementComponentTest.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java index 101a305c02..165e71981b 100644 --- a/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java +++ b/service/src/intTest/java/uk/nhs/adaptors/gp2gp/ehr/SendAcknowledgementComponentTest.java @@ -44,7 +44,8 @@ public class SendAcknowledgementComponentTest { private static final String FROM_ODS_CODE = "0000222-from-ods-code"; private static final String TO_ODS_CODE = "0000333-to-ods-code"; private static final String EHR_REQUEST_MESSAGE_ID = "000-333-444-ehr-request-message-id"; - private static final String DATE = "2018-03-04T03:10:41.01Z"; + private static final String DATE_1 = "2018-03-04T03:10:41.01Z"; + private static final String DATE_2 = "2018-03-04T03:10:42.01Z"; private static final String REASON_CODE = "06"; private static final String REASON_MESSAGE = "Patient not at surgery."; private static final String TASK_ID = "999-000-task-id"; @@ -79,7 +80,7 @@ public class SendAcknowledgementComponentTest { @BeforeEach public void setUp() { when(randomIdGeneratorService.createNewId()).thenReturn(GENERATED_RANDOM_ID); - when(timestampService.now()).thenReturn(Instant.parse(DATE)); + when(timestampService.now()).thenReturn(Instant.parse(DATE_1)); ehrExtractStatus = EhrExtractStatusTestUtils.prepareEhrExtractStatus(); ehrExtractStatusRepository.save(ehrExtractStatus); @@ -98,8 +99,18 @@ public void When_AcknowledgementTaskRunsTwice_Expect_DatabaseOverwritesEhrExtrac when(mhsClient.sendMessageToMHS(request)).thenReturn("Successful Mhs Outbound Request"); when(randomIdGeneratorService.createNewId()) - .thenReturn(GENERATED_RANDOM_ID) - .thenReturn(GENERATED_RANDOM_ID_2); + .thenReturn(GENERATED_RANDOM_ID) + .thenReturn(GENERATED_RANDOM_ID_2); + + when(timestampService.now()) + .thenReturn(Instant.parse(DATE_1)) + .thenReturn(Instant.parse(DATE_1)) + .thenReturn(Instant.parse(DATE_1)) + .thenReturn(Instant.parse(DATE_1)) + .thenReturn(Instant.parse(DATE_2)) + .thenReturn(Instant.parse(DATE_2)) + .thenReturn(Instant.parse(DATE_2)) + .thenReturn(Instant.parse(DATE_2)); sendAcknowledgementExecutor.execute(sendAcknowledgementTaskDefinition); var ehrExtractFirst = ehrExtractStatusRepository.findByConversationId(ehrExtractStatus.getConversationId()).get();