diff --git a/.gitignore b/.gitignore index e524c39686..675d01fb0a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ dev production uuid_results -libs/pact-test-helpers/bin +libs/test-helpers/bin local-running/bin/ # User-specific secrets diff --git a/lambdas/build-client-oauth-response/build.gradle b/lambdas/build-client-oauth-response/build.gradle index 13679f57c7..5752b98666 100644 --- a/lambdas/build-client-oauth-response/build.gradle +++ b/lambdas/build-client-oauth-response/build.gradle @@ -17,7 +17,9 @@ dependencies { libs.aspectj testImplementation libs.junitJupiter, - libs.mockitoJunit + libs.mockitoJunit, + libs.hamcrest, + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/build-client-oauth-response/src/main/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandler.java b/lambdas/build-client-oauth-response/src/main/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandler.java index c446f6908d..60ac76f51d 100644 --- a/lambdas/build-client-oauth-response/src/main/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandler.java +++ b/lambdas/build-client-oauth-response/src/main/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandler.java @@ -223,6 +223,9 @@ public Map handleRequest(JourneyRequest input, Context context) } catch (IpvSessionNotFoundException e) { return buildJourneyErrorResponse( HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorResponse.IPV_SESSION_NOT_FOUND); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/build-client-oauth-response/src/test/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandlerTest.java b/lambdas/build-client-oauth-response/src/test/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandlerTest.java index 0daf5742db..e7809fe185 100644 --- a/lambdas/build-client-oauth-response/src/test/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandlerTest.java +++ b/lambdas/build-client-oauth-response/src/test/java/uk/gov/di/ipv/core/buildclientoauthresponse/BuildClientOauthResponseHandlerTest.java @@ -38,6 +38,7 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.validation.ValidationResult; import java.net.URI; @@ -47,8 +48,11 @@ import java.util.Map; import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; @@ -414,6 +418,34 @@ void shouldReturn200OnSuccessfulOauthRequestForJsonRequest() throws Exception { assertEquals("test-state", params.get(1).getValue()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + JourneyRequest event = + JourneyRequest.builder() + .ipvSessionId(TEST_SESSION_ID) + .ipAddress(TEST_IP_ADDRESS) + .clientOAuthSessionId(TEST_CLIENT_OAUTH_SESSION_ID) + .build(); + + var logCollector = LogCollector.getLogCollectorFor(BuildClientOauthResponseHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> handler.handleRequest(event, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private IpvSessionItem generateIpvSessionItem() { IpvSessionItem item = new IpvSessionItem(); item.setIpvSessionId(SecureTokenHelper.getInstance().generate()); diff --git a/lambdas/build-cri-oauth-request/build.gradle b/lambdas/build-cri-oauth-request/build.gradle index 79d2f9f1ca..712742b7af 100644 --- a/lambdas/build-cri-oauth-request/build.gradle +++ b/lambdas/build-cri-oauth-request/build.gradle @@ -22,9 +22,12 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') + testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/build-cri-oauth-request/src/main/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandler.java b/lambdas/build-cri-oauth-request/src/main/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandler.java index ca8d1dfa37..6b87ef5c03 100644 --- a/lambdas/build-cri-oauth-request/src/main/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandler.java +++ b/lambdas/build-cri-oauth-request/src/main/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandler.java @@ -257,6 +257,9 @@ public Map handleRequest(CriJourneyRequest input, Context contex e, HttpStatus.SC_INTERNAL_SERVER_ERROR, IPV_SESSION_NOT_FOUND); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/build-cri-oauth-request/src/test/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandlerTest.java b/lambdas/build-cri-oauth-request/src/test/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandlerTest.java index 249eec6aac..cf32606b4c 100644 --- a/lambdas/build-cri-oauth-request/src/test/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandlerTest.java +++ b/lambdas/build-cri-oauth-request/src/test/java/uk/gov/di/ipv/core/buildcrioauthrequest/BuildCriOauthRequestHandlerTest.java @@ -56,6 +56,7 @@ import uk.gov.di.ipv.core.library.service.UserIdentityService; import uk.gov.di.ipv.core.library.signing.LocalECDSASigner; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.helpers.VcHelper; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; @@ -73,8 +74,11 @@ import java.util.Optional; import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -1094,6 +1098,35 @@ void shouldIncludeGivenParametersIntoCriResponseIfInJourneyUri( } } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(SESSION_ID)) + .thenThrow(new RuntimeException("Test error")); + CriJourneyRequest input = + CriJourneyRequest.builder() + .ipvSessionId(SESSION_ID) + .ipAddress(TEST_IP_ADDRESS) + .language(TEST_LANGUAGE) + .journey(String.format(JOURNEY_BASE_URL, HMRC_KBV.getId())) + .build(); + + var logCollector = LogCollector.getLogCollectorFor(BuildCriOauthRequestHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> handleRequest(input, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private static Stream journeyUriParameters() { return Stream.of( Arguments.of(CRI_WITH_CONTEXT, Map.of(CONTEXT, TEST_CONTEXT)), diff --git a/lambdas/build-proven-user-identity-details/build.gradle b/lambdas/build-proven-user-identity-details/build.gradle index 2d7ca81dd2..8495af5e02 100644 --- a/lambdas/build-proven-user-identity-details/build.gradle +++ b/lambdas/build-proven-user-identity-details/build.gradle @@ -18,9 +18,12 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') + testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/build-proven-user-identity-details/src/main/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandler.java b/lambdas/build-proven-user-identity-details/src/main/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandler.java index 9827834bb2..0535906d46 100644 --- a/lambdas/build-proven-user-identity-details/src/main/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandler.java +++ b/lambdas/build-proven-user-identity-details/src/main/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandler.java @@ -119,6 +119,9 @@ public APIGatewayProxyResponseEvent handleRequest( ErrorResponse.FAILED_TO_GENERATE_PROVEN_USER_IDENTITY_DETAILS); } catch (IpvSessionNotFoundException e) { return buildJourneyErrorResponse(ErrorResponse.IPV_SESSION_NOT_FOUND); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/build-proven-user-identity-details/src/test/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandlerTest.java b/lambdas/build-proven-user-identity-details/src/test/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandlerTest.java index 22d37a6274..4ea87aa20d 100644 --- a/lambdas/build-proven-user-identity-details/src/test/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandlerTest.java +++ b/lambdas/build-proven-user-identity-details/src/test/java/uk/gov/di/ipv/core/buildprovenuseridentitydetails/BuildProvenUserIdentityDetailsHandlerTest.java @@ -27,15 +27,20 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.helpers.VcHelper; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import java.util.List; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -89,7 +94,9 @@ void setUp() { .userId(TEST_USER_ID) .build(); - when(mockIpvSessionItem.getClientOAuthSessionId()).thenReturn(TEST_CLIENT_OAUTH_SESSION_ID); + Mockito.lenient() + .when(mockIpvSessionItem.getClientOAuthSessionId()) + .thenReturn(TEST_CLIENT_OAUTH_SESSION_ID); Mockito.lenient().when(mockIpvSessionItem.getVot()).thenReturn(Vot.P2); } @@ -428,6 +435,30 @@ private APIGatewayProxyRequestEvent createRequestEvent() { Map.of(IPV_SESSION_ID_HEADER, SESSION_ID, IP_ADDRESS_HEADER, "10.10.10.1")); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + var input = createRequestEvent(); + + var logCollector = + LogCollector.getLogCollectorFor(BuildProvenUserIdentityDetailsHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> handler.handleRequest(input, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private T toResponseClass( APIGatewayProxyResponseEvent handlerOutput, Class responseClass) throws JsonProcessingException { diff --git a/lambdas/build-user-identity/build.gradle b/lambdas/build-user-identity/build.gradle index 6c3f2e15ae..c616218054 100644 --- a/lambdas/build-user-identity/build.gradle +++ b/lambdas/build-user-identity/build.gradle @@ -18,11 +18,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactProviderJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/build-user-identity/src/main/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandler.java b/lambdas/build-user-identity/src/main/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandler.java index c6a84be8cc..91cbee2186 100644 --- a/lambdas/build-user-identity/src/main/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandler.java +++ b/lambdas/build-user-identity/src/main/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandler.java @@ -167,6 +167,9 @@ public APIGatewayProxyResponseEvent handleRequest( return getAccessDeniedApiGatewayProxyResponseEvent(); } catch (IpvSessionNotFoundException e) { return getUnknownAccessTokenApiGatewayProxyResponseEvent(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandlerTest.java b/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandlerTest.java index 3ecc95cf2f..e254c82229 100644 --- a/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandlerTest.java +++ b/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/BuildUserIdentityHandlerTest.java @@ -50,6 +50,7 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.model.BirthDate; import uk.gov.di.model.ContraIndicator; @@ -70,10 +71,14 @@ import java.util.Map; import static java.lang.String.valueOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; @@ -986,6 +991,28 @@ void shouldReturnErrorResponseWhenIpvSessionIsNull() throws Exception { verify(mockSessionCredentialsService, never()).deleteSessionCredentials(any()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSessionByAccessToken(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(BuildUserIdentityHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> buildUserIdentityHandler.handleRequest(testEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private static APIGatewayProxyRequestEvent getEventWithAuthAndIpHeaders() { APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); AccessToken accessToken = new BearerAccessToken(TEST_ACCESS_TOKEN); diff --git a/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/pact/BuildUserIdentityHandlerTest.java b/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/pact/BuildUserIdentityHandlerTest.java index 2b335f653a..6e320b6d13 100644 --- a/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/pact/BuildUserIdentityHandlerTest.java +++ b/lambdas/build-user-identity/src/test/java/uk/gov/di/ipv/core/builduseridentity/pact/BuildUserIdentityHandlerTest.java @@ -25,8 +25,6 @@ import uk.gov.di.ipv.core.library.dto.AccessTokenMetadata; import uk.gov.di.ipv.core.library.enums.Vot; import uk.gov.di.ipv.core.library.exceptions.CredentialParseException; -import uk.gov.di.ipv.core.library.pacttesthelpers.LambdaHttpServer; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.persistence.DataStore; import uk.gov.di.ipv.core.library.persistence.item.ClientOAuthSessionItem; import uk.gov.di.ipv.core.library.persistence.item.IpvSessionItem; @@ -39,6 +37,8 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.pact.LambdaHttpServer; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import java.io.IOException; diff --git a/lambdas/call-dcmaw-async-cri/build.gradle b/lambdas/call-dcmaw-async-cri/build.gradle index 831e7b064e..330fccde88 100644 --- a/lambdas/call-dcmaw-async-cri/build.gradle +++ b/lambdas/call-dcmaw-async-cri/build.gradle @@ -20,11 +20,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/call-dcmaw-async-cri/src/test/java/uk/gov/di/ipv/core/calldcmawasynccri/CallDcmawAsyncCriHandlerTest.java b/lambdas/call-dcmaw-async-cri/src/test/java/uk/gov/di/ipv/core/calldcmawasynccri/CallDcmawAsyncCriHandlerTest.java index 6464be7080..4efbcc0efb 100644 --- a/lambdas/call-dcmaw-async-cri/src/test/java/uk/gov/di/ipv/core/calldcmawasynccri/CallDcmawAsyncCriHandlerTest.java +++ b/lambdas/call-dcmaw-async-cri/src/test/java/uk/gov/di/ipv/core/calldcmawasynccri/CallDcmawAsyncCriHandlerTest.java @@ -21,14 +21,18 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.domain.VerifiableCredentialResponse; import uk.gov.di.ipv.core.library.verifiablecredential.domain.VerifiableCredentialStatus; import java.util.Collections; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verify; @@ -196,4 +200,21 @@ void handleRequestShouldReturnJourneyErrorIfResponseIsUserIdDoesntMatch() throws ErrorResponse.ERROR_CALLING_DCMAW_ASYNC_CRI.getMessage(), lambdaResult.get("message")); } + + @Test + void shouldLogRuntimeExceptions() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(CallDcmawAsyncCriHandler.class); + + // Act + callDcmawAsyncCriHandler.handleRequest(input, mockContext); + + // Assert + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Error calling DCMAW Async CRI")); + assertThat(logMessage, containsString("Test error")); + } } diff --git a/lambdas/call-ticf-cri/build.gradle b/lambdas/call-ticf-cri/build.gradle index 165ddc5d76..a55052e663 100644 --- a/lambdas/call-ticf-cri/build.gradle +++ b/lambdas/call-ticf-cri/build.gradle @@ -19,11 +19,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/call-ticf-cri/src/main/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandler.java b/lambdas/call-ticf-cri/src/main/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandler.java index 1ee50bc57d..b0a4a7d1e2 100644 --- a/lambdas/call-ticf-cri/src/main/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandler.java +++ b/lambdas/call-ticf-cri/src/main/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandler.java @@ -135,6 +135,9 @@ public Map handleRequest(ProcessRequest request, Context context HttpStatus.SC_INTERNAL_SERVER_ERROR, ERROR_PROCESSING_TICF_CRI_RESPONSE) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { if (ipvSessionItem != null) { ipvSessionService.updateIpvSession(ipvSessionItem); diff --git a/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandlerTest.java b/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandlerTest.java index d709a58b30..e995432f03 100644 --- a/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandlerTest.java +++ b/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/CallTicfCriHandlerTest.java @@ -32,14 +32,19 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; @@ -273,4 +278,26 @@ void handleRequestShouldReturnJourneyErrorResponseIfCimitServiceThrows() throws ErrorResponse.ERROR_PROCESSING_TICF_CRI_RESPONSE.getMessage(), lambdaResult.get("message")); } + + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(CallTicfCriHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> callTicfCriHandler.handleRequest(input, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } } diff --git a/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/pact/ticfCri/ContractTest.java b/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/pact/ticfCri/ContractTest.java index 8236ccee31..8c811c9bb5 100644 --- a/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/pact/ticfCri/ContractTest.java +++ b/lambdas/call-ticf-cri/src/test/java/uk/gov/di/ipv/core/callticfcri/pact/ticfCri/ContractTest.java @@ -22,10 +22,10 @@ import uk.gov.di.ipv.core.library.dto.RestCriConfig; import uk.gov.di.ipv.core.library.enums.Vot; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.persistence.item.ClientOAuthSessionItem; import uk.gov.di.ipv.core.library.persistence.item.IpvSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; diff --git a/lambdas/check-coi/build.gradle b/lambdas/check-coi/build.gradle index 54b03cbf8b..105804e806 100644 --- a/lambdas/check-coi/build.gradle +++ b/lambdas/check-coi/build.gradle @@ -18,9 +18,11 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/check-coi/src/main/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandler.java b/lambdas/check-coi/src/main/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandler.java index 0c7ef558bc..45576757f5 100644 --- a/lambdas/check-coi/src/main/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandler.java +++ b/lambdas/check-coi/src/main/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandler.java @@ -213,6 +213,9 @@ public Map handleRequest(ProcessRequest request, Context context return new JourneyErrorResponse( JOURNEY_ERROR_PATH, SC_INTERNAL_SERVER_ERROR, IPV_SESSION_NOT_FOUND) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/check-coi/src/test/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandlerTest.java b/lambdas/check-coi/src/test/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandlerTest.java index 823dc51851..e7048db524 100644 --- a/lambdas/check-coi/src/test/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandlerTest.java +++ b/lambdas/check-coi/src/test/java/uk/gov/di/ipv/core/checkcoi/CheckCoiHandlerTest.java @@ -40,6 +40,7 @@ import uk.gov.di.ipv.core.library.service.EvcsService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; import uk.gov.di.model.NamePart; @@ -50,8 +51,11 @@ import java.util.Optional; import static com.nimbusds.oauth2.sdk.http.HTTPResponse.SC_SERVER_ERROR; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; @@ -726,6 +730,35 @@ void shouldReturnIfFindIdentityClaimThrowsHttpResponseException() throws Excepti FAILED_TO_GENERATE_IDENTITY_CLAIM.getMessage(), responseMap.get("message")); verify(mockAuditService, times(1)).sendAuditEvent(auditEventCaptor.capture()); } + + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockUserIdentityService.areGivenNamesAndDobCorrelated( + List.of(M1A_ADDRESS_VC, M1A_EXPERIAN_FRAUD_VC))) + .thenThrow(new RuntimeException("Test error")); + + var request = + ProcessRequest.processRequestBuilder() + .ipvSessionId(IPV_SESSION_ID) + .lambdaInput(Map.of("checkType", GIVEN_NAMES_AND_DOB.name())) + .build(); + + var logCollector = LogCollector.getLogCollectorFor(CheckCoiHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> checkCoiHandler.handleRequest(request, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } } private JsonNode getJsonNodeForAuditEvent(AuditEvent object) throws Exception { diff --git a/lambdas/check-existing-identity/build.gradle b/lambdas/check-existing-identity/build.gradle index 37eba4c746..ffdcbb4ea4 100644 --- a/lambdas/check-existing-identity/build.gradle +++ b/lambdas/check-existing-identity/build.gradle @@ -21,10 +21,13 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.jacksonDatabind, + testImplementation libs.hamcrest, + libs.jacksonDatabind, libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') + testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/check-existing-identity/src/main/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandler.java b/lambdas/check-existing-identity/src/main/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandler.java index d1f7d3d871..af5e74ba81 100644 --- a/lambdas/check-existing-identity/src/main/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandler.java +++ b/lambdas/check-existing-identity/src/main/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandler.java @@ -240,6 +240,9 @@ public Map handleRequest(JourneyRequest event, Context context) return new JourneyErrorResponse( JOURNEY_ERROR_PATH, SC_NOT_FOUND, ErrorResponse.IPV_SESSION_NOT_FOUND) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/check-existing-identity/src/test/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandlerTest.java b/lambdas/check-existing-identity/src/test/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandlerTest.java index 4c4f3f2323..8065bb3cfe 100644 --- a/lambdas/check-existing-identity/src/test/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandlerTest.java +++ b/lambdas/check-existing-identity/src/test/java/uk/gov/di/ipv/core/checkexistingidentity/CheckExistingIdentityHandlerTest.java @@ -63,6 +63,7 @@ import uk.gov.di.ipv.core.library.service.EvcsService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; import uk.gov.di.model.ContraIndicator; @@ -76,9 +77,12 @@ import java.util.Optional; import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -1533,6 +1537,28 @@ void shouldNotReturnJourneyRepeatFraudCheckResponseIfNotExpiredFraudAndFlagIsTru assertEquals(P2, ipvSessionItem.getTargetVot()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(ipvSessionService.getIpvSessionWithRetry(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(CheckExistingIdentityHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> checkExistingIdentityHandler.handleRequest(event, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + @MockitoSettings(strictness = LENIENT) @Nested class BulkMigrationRollback { diff --git a/lambdas/check-gpg45-score/build.gradle b/lambdas/check-gpg45-score/build.gradle index f911efdea1..1f7e33c062 100644 --- a/lambdas/check-gpg45-score/build.gradle +++ b/lambdas/check-gpg45-score/build.gradle @@ -18,10 +18,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.jacksonDatabind, + testImplementation libs.hamcrest, + libs.jacksonDatabind, libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/check-gpg45-score/src/main/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandler.java b/lambdas/check-gpg45-score/src/main/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandler.java index c92d7df966..918f496d35 100644 --- a/lambdas/check-gpg45-score/src/main/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandler.java +++ b/lambdas/check-gpg45-score/src/main/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandler.java @@ -127,6 +127,9 @@ public Map handleRequest(ProcessRequest event, Context context) return new JourneyErrorResponse( JOURNEY_ERROR_PATH, SC_INTERNAL_SERVER_ERROR, IPV_SESSION_NOT_FOUND) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/check-gpg45-score/src/test/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandlerTest.java b/lambdas/check-gpg45-score/src/test/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandlerTest.java index 831a519905..cfe345971b 100644 --- a/lambdas/check-gpg45-score/src/test/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandlerTest.java +++ b/lambdas/check-gpg45-score/src/test/java/uk/gov/di/ipv/core/checkgpg45score/CheckGpg45ScoreHandlerTest.java @@ -23,13 +23,18 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import java.util.HashMap; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -186,6 +191,28 @@ void shouldReturn500IfUnknownScoreType() throws Exception { verify(clientOAuthSessionDetailsService).getClientOAuthSession(any()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(ipvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(CheckGpg45ScoreHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> checkGpg45ScoreHandler.handleRequest(request, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private T toResponseClass(Map handlerOutput, Class responseClass) { return OBJECT_MAPPER.convertValue(handlerOutput, responseClass); } diff --git a/lambdas/check-mobile-app-vc-receipt/build.gradle b/lambdas/check-mobile-app-vc-receipt/build.gradle index 7b73fd7905..c272ac44d9 100644 --- a/lambdas/check-mobile-app-vc-receipt/build.gradle +++ b/lambdas/check-mobile-app-vc-receipt/build.gradle @@ -20,9 +20,11 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java b/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java index 273c514e8c..c7a64c9f39 100644 --- a/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java +++ b/lambdas/check-mobile-app-vc-receipt/src/main/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandler.java @@ -85,6 +85,9 @@ public APIGatewayProxyResponseEvent handleRequest( } catch (CredentialParseException e) { LOGGER.info(buildErrorMessage(ErrorResponse.FAILED_TO_PARSE_ISSUED_CREDENTIALS)); return ApiGatewayResponseGenerator.proxyResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandlerTest.java b/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandlerTest.java index 361624401c..8b3d805947 100644 --- a/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandlerTest.java +++ b/lambdas/check-mobile-app-vc-receipt/src/test/java/uk/gov/di/ipv/core/checkmobileappvcreceipt/CheckMobileAppVcReceiptHandlerTest.java @@ -20,12 +20,17 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; import java.util.List; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -150,6 +155,31 @@ void shouldReturn404WhenCriResponseStatusPendingAndVcNotFound() throws Exception assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(ipvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + var requestEvent = buildValidRequestEventWithState(); + + var logCollector = LogCollector.getLogCollectorFor(CheckMobileAppVcReceiptHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> + checkMobileAppVcReceiptHandler.handleRequest( + requestEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private APIGatewayProxyRequestEvent buildValidRequestEventWithState() { var event = new APIGatewayProxyRequestEvent(); event.setHeaders(Map.of("ipv-session-id", TEST_IPV_SESSION_ID)); diff --git a/lambdas/evaluate-gpg45-scores/build.gradle b/lambdas/evaluate-gpg45-scores/build.gradle index 7329aab4dd..c81eb1c251 100644 --- a/lambdas/evaluate-gpg45-scores/build.gradle +++ b/lambdas/evaluate-gpg45-scores/build.gradle @@ -20,9 +20,11 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/evaluate-gpg45-scores/src/main/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandler.java b/lambdas/evaluate-gpg45-scores/src/main/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandler.java index 456eb69a06..de8858f6f4 100644 --- a/lambdas/evaluate-gpg45-scores/src/main/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandler.java +++ b/lambdas/evaluate-gpg45-scores/src/main/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandler.java @@ -190,6 +190,9 @@ public Map handleRequest(JourneyRequest event, Context context) return new JourneyErrorResponse( JOURNEY_ERROR_PATH, SC_INTERNAL_SERVER_ERROR, IPV_SESSION_NOT_FOUND) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/evaluate-gpg45-scores/src/test/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandlerTest.java b/lambdas/evaluate-gpg45-scores/src/test/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandlerTest.java index 4acc876455..54702924fe 100644 --- a/lambdas/evaluate-gpg45-scores/src/test/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandlerTest.java +++ b/lambdas/evaluate-gpg45-scores/src/test/java/uk/gov/di/ipv/core/evaluategpg45scores/EvaluateGpg45ScoresHandlerTest.java @@ -39,6 +39,7 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.model.ContraIndicator; @@ -47,9 +48,13 @@ import java.util.Optional; import static com.nimbusds.oauth2.sdk.http.HTTPResponse.SC_SERVER_ERROR; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; @@ -511,6 +516,28 @@ void shouldReturnJourneyMetForMeetingLowConfidencesWhenMediumConfidenceBreachesC verify(ipvSessionItem).setVot(Vot.P1); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(ipvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(EvaluateGpg45ScoresHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> evaluateGpg45ScoresHandler.handleRequest(request, context), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private T toResponseClass(Map handlerOutput, Class responseClass) { return OBJECT_MAPPER.convertValue(handlerOutput, responseClass); } diff --git a/lambdas/initialise-ipv-session/build.gradle b/lambdas/initialise-ipv-session/build.gradle index 77422183c4..deb9df1850 100644 --- a/lambdas/initialise-ipv-session/build.gradle +++ b/lambdas/initialise-ipv-session/build.gradle @@ -24,9 +24,11 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.jacksonDatabind, + testImplementation libs.hamcrest, + libs.jacksonDatabind, libs.junitJupiter, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly(libs.junitPlatform) } diff --git a/lambdas/initialise-ipv-session/src/main/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandler.java b/lambdas/initialise-ipv-session/src/main/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandler.java index dff7611939..e3de29e5df 100644 --- a/lambdas/initialise-ipv-session/src/main/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandler.java +++ b/lambdas/initialise-ipv-session/src/main/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandler.java @@ -299,6 +299,9 @@ public APIGatewayProxyResponseEvent handleRequest( LogHelper.buildErrorMessage("Failed to check if stronger vot vc present.", e)); return ApiGatewayResponseGenerator.proxyJsonResponse( HttpStatus.SC_BAD_REQUEST, ErrorResponse.FAILED_TO_PARSE_ISSUED_CREDENTIALS); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/initialise-ipv-session/src/test/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandlerTest.java b/lambdas/initialise-ipv-session/src/test/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandlerTest.java index 95868462f7..0b27b248cf 100644 --- a/lambdas/initialise-ipv-session/src/test/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandlerTest.java +++ b/lambdas/initialise-ipv-session/src/test/java/uk/gov/di/ipv/core/initialiseipvsession/InitialiseIpvSessionHandlerTest.java @@ -66,6 +66,7 @@ import uk.gov.di.ipv.core.library.service.EvcsService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.helpers.VcHelper; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import uk.gov.di.model.NamePart; @@ -89,8 +90,11 @@ import static com.nimbusds.oauth2.sdk.OAuth2Error.INVALID_REQUEST_OBJECT_CODE; import static com.nimbusds.oauth2.sdk.http.HTTPResponse.SC_SERVER_ERROR; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -640,6 +644,27 @@ void shouldReturnIpvSessionIdWhenRecoverableErrorFound() assertEquals(ipvSessionItem.getIpvSessionId(), responseBody.get("ipvSessionId")); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() { + // Arrange + doThrow(new RuntimeException("Test error")).when(mockConfigService).setFeatureSet(any()); + + var logCollector = LogCollector.getLogCollectorFor(InitialiseIpvSessionHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> initialiseIpvSessionHandler.handleRequest(validEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + @Nested @DisplayName("inherited identity tests") class InheritedIdentityTests { diff --git a/lambdas/issue-client-access-token/build.gradle b/lambdas/issue-client-access-token/build.gradle index 06b9d569b5..7a7b58e9fd 100644 --- a/lambdas/issue-client-access-token/build.gradle +++ b/lambdas/issue-client-access-token/build.gradle @@ -20,11 +20,12 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactProviderJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/issue-client-access-token/src/main/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandler.java b/lambdas/issue-client-access-token/src/main/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandler.java index 500ec2915e..e006a2b937 100644 --- a/lambdas/issue-client-access-token/src/main/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandler.java +++ b/lambdas/issue-client-access-token/src/main/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandler.java @@ -225,6 +225,9 @@ public APIGatewayProxyResponseEvent handleRequest( return ApiGatewayResponseGenerator.proxyJsonResponse( error.getHTTPStatusCode(), error.toJSONObject()); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandlerTest.java b/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandlerTest.java index bcafcb900c..26d6a551bc 100644 --- a/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandlerTest.java +++ b/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/IssueClientAccessTokenHandlerTest.java @@ -19,6 +19,9 @@ import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.di.ipv.core.issueclientaccesstoken.exception.ClientAuthenticationException; import uk.gov.di.ipv.core.issueclientaccesstoken.service.AccessTokenService; import uk.gov.di.ipv.core.issueclientaccesstoken.validation.TokenRequestValidator; @@ -30,33 +33,38 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.validation.ValidationResult; import java.time.Instant; import java.util.List; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static uk.gov.di.ipv.core.library.config.ConfigurationVariable.AUTH_CODE_EXPIRY_SECONDS; +@ExtendWith(MockitoExtension.class) class IssueClientAccessTokenHandlerTest { private final String TEST_AUTHORIZATION_CODE = "12345"; private final String TEST_ACCESS_TOKEN = "98765"; private final String TEST_REDIRECT_URL = "https://callback.example.com"; private final String TEST_SESSION_ID = "test-session-id"; private final ObjectMapper objectMapper = new ObjectMapper(); - private IpvSessionItem mockSessionItem; - private Context context; - private ConfigService mockConfigService; - private AccessTokenService mockAccessTokenService; - private IpvSessionService mockSessionService; - private ClientOAuthSessionDetailsService mockClientOAuthSessionService; - private TokenRequestValidator mockTokenRequestValidator; + private IpvSessionItem sessionItem; + @Mock private Context mockContext; + @Mock private ConfigService mockConfigService; + @Mock private AccessTokenService mockAccessTokenService; + @Mock private IpvSessionService mockSessionService; + @Mock private ClientOAuthSessionDetailsService mockClientOAuthSessionService; + @Mock private TokenRequestValidator mockTokenRequestValidator; private IssueClientAccessTokenHandler handler; private TokenResponse tokenResponse; @@ -66,18 +74,10 @@ void setUp() { AccessToken accessToken = new BearerAccessToken(); tokenResponse = new AccessTokenResponse(new Tokens(accessToken, null)); - mockAccessTokenService = mock(AccessTokenService.class); - when(mockAccessTokenService.generateAccessToken()).thenReturn(tokenResponse); - - mockConfigService = mock(ConfigService.class); - when(mockConfigService.getLongParameter(AUTH_CODE_EXPIRY_SECONDS)).thenReturn(3600L); - - mockSessionService = mock(IpvSessionService.class); - mockClientOAuthSessionService = mock(ClientOAuthSessionDetailsService.class); - - mockTokenRequestValidator = mock(TokenRequestValidator.class); - - context = mock(Context.class); + lenient().when(mockAccessTokenService.generateAccessToken()).thenReturn(tokenResponse); + lenient() + .when(mockConfigService.getLongParameter(AUTH_CODE_EXPIRY_SECONDS)) + .thenReturn(3600L); handler = new IssueClientAccessTokenHandler( @@ -91,11 +91,11 @@ void setUp() { mockAuthorizationCodeMetadata.setCreationDateTime(Instant.now().toString()); mockAuthorizationCodeMetadata.setRedirectUrl(TEST_REDIRECT_URL); - mockSessionItem = new IpvSessionItem(); - mockSessionItem.setIpvSessionId(TEST_SESSION_ID); - mockSessionItem.setAuthorizationCode(TEST_AUTHORIZATION_CODE); - mockSessionItem.setAuthorizationCodeMetadata(mockAuthorizationCodeMetadata); - mockSessionItem.setFeatureSet("someCoolNewThing"); + sessionItem = new IpvSessionItem(); + sessionItem.setIpvSessionId(TEST_SESSION_ID); + sessionItem.setAuthorizationCode(TEST_AUTHORIZATION_CODE); + sessionItem.setAuthorizationCodeMetadata(mockAuthorizationCodeMetadata); + sessionItem.setFeatureSet("someCoolNewThing"); } @Test @@ -112,13 +112,13 @@ void shouldReturnAccessTokenOnSuccessfulExchange() throws Exception { when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(ValidationResult.createValidResult()); when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); + .thenReturn(sessionItem); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); Map responseBody = objectMapper.readValue(response.getBody(), new TypeReference<>() {}); @@ -138,7 +138,7 @@ void shouldReturn400WhenInvalidTokenRequestProvided() throws Exception { String invalidTokenRequest = "invalid-token-request"; event.setBody(invalidTokenRequest); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -157,7 +157,7 @@ void shouldReturn400WhenInvalidGrantTypeProvided() throws Exception { + "&client_id=test_client_id"; event.setBody(tokenRequestBody); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -177,7 +177,7 @@ void shouldReturn400IfAccessTokenServiceDeemsAuthGrantInvalid() throws ParseExce when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(new ValidationResult<>(false, OAuth2Error.UNSUPPORTED_GRANT_TYPE)); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -203,7 +203,7 @@ void shouldReturn400OWhenInvalidAuthorisationCodeProvided() throws Exception { new IpvSessionNotFoundException( "The supplied authorization code was not found in the database")); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -227,7 +227,7 @@ void shouldReturn400WhenIpvSessionNotFoundForAuthCode() throws Exception { when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) .thenThrow(new IpvSessionNotFoundException("Error")); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -248,11 +248,11 @@ void shouldReturn400WhenAuthorisationCodeHasExpired() throws Exception { when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(ValidationResult.createValidResult()); when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); + .thenReturn(sessionItem); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); @@ -269,15 +269,11 @@ void shouldReturn401WhenInvalidJwtProvided() throws Exception { + "&client_assertion=eyJzdWIiOiIxMjM0IiwiYXVkIjoiYWRtaW4iLCJpc3MiOiJtYXNvbi5tZXRhbXVnLm5ldCIsImV4cCI6MTU3NDUxMjc2NSwiaWF0IjoxNTY2NzM2NzY1LCJqdGkiOiJmN2JmZTMzZi03YmY3LTRlYjQtOGU1OS05OTE3OTliNWViOGEifQ==.EVcCaSqrSNVs3cWdLt-qkoqUk7rPHEOsDHS8yejwxMw&redirect_uri=http://test.com&grant_type=authorization_code&client_id=test_client_id"; // pragma: allowlist secret event.setBody(tokenRequestBody); - when(mockAccessTokenService.validateAuthorizationGrant(any())) - .thenReturn(ValidationResult.createValidResult()); - when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); doThrow(new ClientAuthenticationException("error")) .when(mockTokenRequestValidator) .authenticateClient(any()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusCode()); @@ -292,13 +288,11 @@ void shouldReturn401WhenJwtMissingFromRequestProvided() throws Exception { "code=12345&redirect_uri=http://test.com&grant_type=authorization_code&client_id=test_client_id"; event.setBody(tokenRequestBody); - when(mockAccessTokenService.validateAuthorizationGrant(any())) - .thenReturn(ValidationResult.createValidResult()); doThrow(new ClientAuthenticationException("error")) .when(mockTokenRequestValidator) .authenticateClient(any()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HTTPResponse.SC_UNAUTHORIZED, response.getStatusCode()); @@ -317,15 +311,15 @@ void shouldReturn400WhenAuthCodeIsUsedMoreThanOnce() throws Exception { when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(ValidationResult.createValidResult()); - mockSessionItem.setAccessToken(TEST_ACCESS_TOKEN); + sessionItem.setAccessToken(TEST_ACCESS_TOKEN); when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); + .thenReturn(sessionItem); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); - verify(mockSessionService).revokeAccessToken(mockSessionItem); + verify(mockSessionService).revokeAccessToken(sessionItem); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HTTPResponse.SC_BAD_REQUEST, response.getStatusCode()); @@ -344,18 +338,18 @@ void shouldReturn400WhenRevokingAccessTokenFails() throws Exception { when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(ValidationResult.createValidResult()); - mockSessionItem.setAccessToken(TEST_ACCESS_TOKEN); + sessionItem.setAccessToken(TEST_ACCESS_TOKEN); when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); + .thenReturn(sessionItem); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); String errorMessage = "Failed to revoke access token"; doThrow(new IllegalArgumentException(errorMessage)) .when(mockSessionService) - .revokeAccessToken(mockSessionItem); + .revokeAccessToken(sessionItem); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HTTPResponse.SC_BAD_REQUEST, response.getStatusCode()); @@ -373,13 +367,13 @@ void shouldReturn400WhenRedirectURLsDoNotMatch() throws Exception { event.setBody(tokenRequestBody); when(mockSessionService.getIpvSessionByAuthorizationCode(TEST_AUTHORIZATION_CODE)) - .thenReturn(mockSessionItem); + .thenReturn(sessionItem); when(mockAccessTokenService.validateAuthorizationGrant(any())) .thenReturn(ValidationResult.createValidResult()); when(mockClientOAuthSessionService.getClientOAuthSession(any())) .thenReturn(getClientOAuthSessionItem()); - APIGatewayProxyResponseEvent response = handler.handleRequest(event, context); + APIGatewayProxyResponseEvent response = handler.handleRequest(event, mockContext); ErrorObject errorResponse = createErrorObjectFromResponse(response.getBody()); assertEquals(HTTPResponse.SC_BAD_REQUEST, response.getStatusCode()); @@ -389,6 +383,30 @@ void shouldReturn400WhenRedirectURLsDoNotMatch() throws Exception { errorResponse.getDescription()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + doThrow(new RuntimeException("Test error")) + .when(mockTokenRequestValidator) + .authenticateClient(any()); + APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); + + var logCollector = LogCollector.getLogCollectorFor(IssueClientAccessTokenHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> handler.handleRequest(event, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private ErrorObject createErrorObjectFromResponse(String responseBody) throws ParseException { HTTPResponse httpErrorResponse = new HTTPResponse(HttpStatus.SC_BAD_REQUEST); httpErrorResponse.setContentType(ContentType.APPLICATION_JSON.getType()); diff --git a/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/pact/IssueClientAccessTokenHandlerTest.java b/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/pact/IssueClientAccessTokenHandlerTest.java index d07d99cfdd..5bb3414761 100644 --- a/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/pact/IssueClientAccessTokenHandlerTest.java +++ b/lambdas/issue-client-access-token/src/test/java/uk/gov/di/ipv/core/issueclientaccesstoken/pact/IssueClientAccessTokenHandlerTest.java @@ -28,7 +28,6 @@ import uk.gov.di.ipv.core.library.config.ConfigurationVariable; import uk.gov.di.ipv.core.library.dto.AuthorizationCodeMetadata; import uk.gov.di.ipv.core.library.oauthkeyservice.OAuthKeyService; -import uk.gov.di.ipv.core.library.pacttesthelpers.LambdaHttpServer; import uk.gov.di.ipv.core.library.persistence.DataStore; import uk.gov.di.ipv.core.library.persistence.item.ClientOAuthSessionItem; import uk.gov.di.ipv.core.library.persistence.item.IpvSessionItem; @@ -36,6 +35,7 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.pact.LambdaHttpServer; import java.io.IOException; diff --git a/lambdas/process-async-cri-credential/build.gradle b/lambdas/process-async-cri-credential/build.gradle index 0f5fee282f..c9ca44772d 100644 --- a/lambdas/process-async-cri-credential/build.gradle +++ b/lambdas/process-async-cri-credential/build.gradle @@ -21,11 +21,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/process-async-cri-credential/src/main/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandler.java b/lambdas/process-async-cri-credential/src/main/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandler.java index fd44c22eed..f940ee4011 100644 --- a/lambdas/process-async-cri-credential/src/main/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandler.java +++ b/lambdas/process-async-cri-credential/src/main/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandler.java @@ -112,6 +112,9 @@ public SQSBatchResponse handleRequest(SQSEvent event, Context context) { } return SQSBatchResponse.builder().withBatchItemFailures(failedRecords).build(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandlerTest.java b/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandlerTest.java index 3ab3c07b59..25add1d2af 100644 --- a/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandlerTest.java +++ b/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/ProcessAsyncCriCredentialHandlerTest.java @@ -31,6 +31,7 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.EvcsService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import uk.gov.di.ipv.core.processasynccricredential.dto.CriResponseMessageDto; @@ -42,8 +43,11 @@ import java.util.Optional; import java.util.UUID; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; @@ -307,6 +311,30 @@ void willNotPersistVerifiableCredentialIfFailsToPostMitigatingCredentialToCIMIT( verifyBatchResponseFailures(testEvent, batchResponse); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + final SQSEvent testEvent = createSuccessTestEvent(TEST_OAUTH_STATE); + + when(criResponseService.getCriResponseItemWithState(any(), any())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(ProcessAsyncCriCredentialHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> handler.handleRequest(testEvent, null), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private SQSEvent createErrorTestEvent(String errorType) throws JsonProcessingException { final SQSEvent sqsEvent = new SQSEvent(); final CriResponseMessageDto criResponseMessageDto = diff --git a/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/pact/f2fCri/ContractTest.java b/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/pact/f2fCri/ContractTest.java index ddd6f63e0d..c29367fb72 100644 --- a/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/pact/f2fCri/ContractTest.java +++ b/lambdas/process-async-cri-credential/src/test/java/uk/gov/di/ipv/core/processasynccricredential/pact/f2fCri/ContractTest.java @@ -23,8 +23,8 @@ import uk.gov.di.ipv.core.library.dto.OauthCriConfig; import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.service.ConfigService; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import uk.gov.di.ipv.core.processasynccricredential.domain.SuccessAsyncCriResponse; diff --git a/lambdas/process-cri-callback/build.gradle b/lambdas/process-cri-callback/build.gradle index 3fd14a0a4b..ff5792bb0f 100644 --- a/lambdas/process-cri-callback/build.gradle +++ b/lambdas/process-cri-callback/build.gradle @@ -26,11 +26,12 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(":libs:pact-test-helpers") + project(':libs:test-helpers',) testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java index 0b69f74faf..79c104ae64 100644 --- a/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java +++ b/lambdas/process-cri-callback/src/main/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandler.java @@ -215,6 +215,9 @@ public APIGatewayProxyResponseEvent handleRequest( } catch (IpvSessionNotFoundException e) { return buildErrorResponse( e, HttpStatus.SC_INTERNAL_SERVER_ERROR, ErrorResponse.IPV_SESSION_NOT_FOUND); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java index 54bd1725c5..df8b94137b 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/ProcessCriCallbackHandlerTest.java @@ -32,6 +32,7 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.CriOAuthSessionService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.domain.VerifiableCredentialResponse; import uk.gov.di.ipv.core.library.verifiablecredential.domain.VerifiableCredentialStatus; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; @@ -41,8 +42,12 @@ import java.util.List; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; @@ -385,6 +390,30 @@ void shouldReturnTimeoutRecoverablePageForCriOAuthSessionException() throws Exce assertEquals("pyi-timeout-recoverable", errorPageResponse.get("page")); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + var callbackRequest = buildValidCallbackRequest(); + var requestEvent = buildValidRequestEvent(callbackRequest); + + var logCollector = LogCollector.getLogCollectorFor(ProcessCriCallbackHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> processCriCallbackHandler.handleRequest(requestEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private CriCallbackRequest buildValidCallbackRequest() { return CriCallbackRequest.builder() .credentialIssuerId(ADDRESS.getId()) diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/addressCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/addressCri/CredentialTests.java index 18b59950d7..3ef22087b8 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/addressCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/addressCri/CredentialTests.java @@ -24,10 +24,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/bavCri/ContractTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/bavCri/ContractTest.java index ddad69c0b2..42c02f4da9 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/bavCri/ContractTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/bavCri/ContractTest.java @@ -33,11 +33,11 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.CoreSigner; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/cicCri/ContractTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/cicCri/ContractTest.java index 367bf3ad46..db92ad4b09 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/cicCri/ContractTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/cicCri/ContractTest.java @@ -33,11 +33,11 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.CoreSigner; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/dcmawCri/ContractTest.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/dcmawCri/ContractTest.java index b3e83754a1..8c69ba5f85 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/dcmawCri/ContractTest.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/dcmawCri/ContractTest.java @@ -35,11 +35,11 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.CoreSigner; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/drivingLicenceCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/drivingLicenceCri/CredentialTests.java index 37e72cae31..f9067808cb 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/drivingLicenceCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/drivingLicenceCri/CredentialTests.java @@ -26,10 +26,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/experianKbvCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/experianKbvCri/CredentialTests.java index d43def51e8..10d0f5a38f 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/experianKbvCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/experianKbvCri/CredentialTests.java @@ -26,10 +26,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/fraudCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/fraudCri/CredentialTests.java index b1e0a61e7f..df105df4a6 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/fraudCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/fraudCri/CredentialTests.java @@ -26,10 +26,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/hmrcKbvCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/hmrcKbvCri/CredentialTests.java index 5b7c7673e5..398728fea7 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/hmrcKbvCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/hmrcKbvCri/CredentialTests.java @@ -26,10 +26,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/ninoCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/ninoCri/CredentialTests.java index 2a35109926..4a32abca62 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/ninoCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/ninoCri/CredentialTests.java @@ -27,10 +27,10 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/passportCri/CredentialTests.java b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/passportCri/CredentialTests.java index 0a7ff929af..46c7e8a964 100644 --- a/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/passportCri/CredentialTests.java +++ b/lambdas/process-cri-callback/src/test/java/uk/gov/di/ipv/core/processcricallback/pact/passportCri/CredentialTests.java @@ -26,11 +26,11 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.helpers.SecureTokenHelper; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.persistence.item.CriOAuthSessionItem; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.signing.CoreSigner; import uk.gov.di.ipv.core.library.signing.SignerFactory; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtIgnoreSignatureBodyBuilder; import uk.gov.di.ipv.core.library.verifiablecredential.validator.VerifiableCredentialValidator; import java.net.URI; diff --git a/lambdas/process-journey-event/build.gradle b/lambdas/process-journey-event/build.gradle index e2d13622f0..ba9024d8ca 100644 --- a/lambdas/process-journey-event/build.gradle +++ b/lambdas/process-journey-event/build.gradle @@ -18,8 +18,10 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, - libs.mockitoJunit + testImplementation libs.hamcrest, + libs.junitJupiter, + libs.mockitoJunit, + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/process-journey-event/src/main/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandler.java b/lambdas/process-journey-event/src/main/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandler.java index 45951f5f2a..3af98cf7fc 100644 --- a/lambdas/process-journey-event/src/main/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandler.java +++ b/lambdas/process-journey-event/src/main/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandler.java @@ -187,6 +187,9 @@ public Map handleRequest(JourneyRequest journeyRequest, Context } catch (IpvSessionNotFoundException e) { return StepFunctionHelpers.generateErrorOutputMap( HttpStatus.SC_BAD_REQUEST, ErrorResponse.IPV_SESSION_NOT_FOUND); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/process-journey-event/src/test/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandlerTest.java b/lambdas/process-journey-event/src/test/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandlerTest.java index 337f6d9fbe..06c577ac8e 100644 --- a/lambdas/process-journey-event/src/test/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandlerTest.java +++ b/lambdas/process-journey-event/src/test/java/uk/gov/di/ipv/core/processjourneyevent/ProcessJourneyEventHandlerTest.java @@ -36,6 +36,7 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.processjourneyevent.statemachine.NestedJourneyTypes; import uk.gov.di.ipv.core.processjourneyevent.statemachine.StateMachineInitializerMode; @@ -45,8 +46,11 @@ import java.util.Map; import java.util.stream.Stream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.inOrder; @@ -1001,6 +1005,44 @@ void shouldSkipTicfCriOnlyIfDisabled( assertEquals(expectedPage, output.get("page")); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + var input = + JourneyRequest.builder() + .ipAddress(TEST_IP) + .journey("eventSix") + .ipvSessionId(TEST_IP) + .build(); + + ProcessJourneyEventHandler processJourneyEventHandler = + new ProcessJourneyEventHandler( + mockAuditService, + mockIpvSessionService, + mockConfigService, + mockClientOAuthSessionService, + List.of(INITIAL_JOURNEY_SELECTION, TECHNICAL_ERROR), + StateMachineInitializerMode.TEST, + TEST_NESTED_JOURNEY_TYPES); + + var logCollector = LogCollector.getLogCollectorFor(ProcessJourneyEventHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> processJourneyEventHandler.handleRequest(input, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private void mockIpvSessionItemAndTimeout(String userState) throws Exception { IpvSessionItem ipvSessionItem = spy(IpvSessionItem.class); ipvSessionItem.setIpvSessionId(SecureTokenHelper.getInstance().generate()); diff --git a/lambdas/process-mobile-app-callback/build.gradle b/lambdas/process-mobile-app-callback/build.gradle index 18a3c53c8a..de19c8374b 100644 --- a/lambdas/process-mobile-app-callback/build.gradle +++ b/lambdas/process-mobile-app-callback/build.gradle @@ -19,9 +19,11 @@ dependencies { compileOnly libs.lombok annotationProcessor libs.lombok - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java b/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java index 44d8419279..dc8c4a9928 100644 --- a/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java +++ b/lambdas/process-mobile-app-callback/src/main/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandler.java @@ -83,6 +83,9 @@ public APIGatewayProxyResponseEvent handleRequest( e, HttpStatus.SC_BAD_REQUEST, ErrorResponse.IPV_SESSION_NOT_FOUND); } catch (InvalidCriResponseException e) { return buildErrorResponse(e, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getErrorResponse()); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java b/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java index 6aa3f8a01d..0c9f4a73e0 100644 --- a/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java +++ b/lambdas/process-mobile-app-callback/src/test/java/uk/gov/di/ipv/core/processmobileappcallback/ProcessMobileAppCallbackHandlerTest.java @@ -22,11 +22,16 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.processmobileappcallback.dto.MobileAppCallbackRequest; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_ERROR_PATH; import static uk.gov.di.ipv.core.library.journeyuris.JourneyUris.JOURNEY_NEXT_PATH; @@ -185,6 +190,31 @@ void shouldReturnErrorWhenCriResponseStatusError() throws Exception { journeyResponse); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(ipvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + var requestEvent = buildValidRequestEventWithState(TEST_OAUTH_STATE); + + var logCollector = LogCollector.getLogCollectorFor(ProcessMobileAppCallbackHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> + processMobileAppCallbackHandler.handleRequest( + requestEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private APIGatewayProxyRequestEvent buildValidRequestEventWithState(String state) throws JsonProcessingException { var event = new APIGatewayProxyRequestEvent(); diff --git a/lambdas/reset-session-identity/build.gradle b/lambdas/reset-session-identity/build.gradle index 661f90fbb6..e6404e3310 100644 --- a/lambdas/reset-session-identity/build.gradle +++ b/lambdas/reset-session-identity/build.gradle @@ -18,9 +18,11 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(":libs:common-services").sourceSets.test.output + project(":libs:common-services").sourceSets.test.output, + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/reset-session-identity/src/main/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandler.java b/lambdas/reset-session-identity/src/main/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandler.java index e40f111ee9..52ba202e60 100644 --- a/lambdas/reset-session-identity/src/main/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandler.java +++ b/lambdas/reset-session-identity/src/main/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandler.java @@ -164,6 +164,9 @@ public Map handleRequest(ProcessRequest input, Context context) SC_INTERNAL_SERVER_ERROR, FAILED_TO_PARSE_ISSUED_CREDENTIALS) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } diff --git a/lambdas/reset-session-identity/src/test/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandlerTest.java b/lambdas/reset-session-identity/src/test/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandlerTest.java index a5c49264e1..ae3202b723 100644 --- a/lambdas/reset-session-identity/src/test/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandlerTest.java +++ b/lambdas/reset-session-identity/src/test/java/uk/gov/di/ipv/core/resetsessionidentity/ResetSessionIdentityHandlerTest.java @@ -29,6 +29,7 @@ import uk.gov.di.ipv.core.library.service.CriResponseService; import uk.gov.di.ipv.core.library.service.EvcsService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; @@ -36,8 +37,12 @@ import java.util.Map; import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.spy; @@ -398,6 +403,34 @@ void shouldReturnErrorJourneyIfUnknownResetType() throws Exception { assertEquals(UNKNOWN_RESET_TYPE.getMessage(), journeyResponse.get("message")); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + ProcessRequest event = + ProcessRequest.processRequestBuilder() + .ipvSessionId(TEST_SESSION_ID) + .featureSet(TEST_FEATURE_SET) + .lambdaInput(Map.of("resetType", "SAUSAGES")) + .build(); + + var logCollector = LogCollector.getLogCollectorFor(ResetSessionIdentityHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> resetSessionIdentityHandler.handleRequest(event, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private void verifyVotSetToP0() { InOrder inOrder = inOrder(ipvSessionItem, mockIpvSessionService); inOrder.verify(ipvSessionItem).setVot(P0); diff --git a/lambdas/store-identity/build.gradle b/lambdas/store-identity/build.gradle index d9f1e22f03..ab5ada6ad2 100644 --- a/lambdas/store-identity/build.gradle +++ b/lambdas/store-identity/build.gradle @@ -17,9 +17,11 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, - project(path: ':libs:common-services', configuration: 'tests') + project(path: ':libs:common-services', configuration: 'tests'), + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/store-identity/src/main/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandler.java b/lambdas/store-identity/src/main/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandler.java index 3f4a25f20c..b2cf980abd 100644 --- a/lambdas/store-identity/src/main/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandler.java +++ b/lambdas/store-identity/src/main/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandler.java @@ -135,6 +135,9 @@ public Map handleRequest(ProcessRequest input, Context context) HttpStatus.SC_INTERNAL_SERVER_ERROR, IPV_SESSION_NOT_FOUND) .toObjectMap(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } finally { auditService.awaitAuditEvents(); } diff --git a/lambdas/store-identity/src/test/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandlerTest.java b/lambdas/store-identity/src/test/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandlerTest.java index 6e3e9f91ec..f219e75516 100644 --- a/lambdas/store-identity/src/test/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandlerTest.java +++ b/lambdas/store-identity/src/test/java/uk/gov/di/ipv/core/storeidentity/StoreIdentityHandlerTest.java @@ -31,6 +31,7 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.EvcsService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.library.verifiablecredential.service.VerifiableCredentialService; @@ -40,9 +41,12 @@ import java.util.Map; import static org.apache.http.HttpStatus.SC_BAD_REQUEST; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; 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.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; @@ -128,13 +132,17 @@ void setUpBeforeEach() throws Exception { ipvSessionItem.setClientOAuthSessionId(CLIENT_SESSION_ID); ipvSessionItem.setVot(P2); - when(mockIpvSessionService.getIpvSession(SESSION_ID)).thenReturn(ipvSessionItem); - when(mockClientOauthSessionDetailsService.getClientOAuthSession(CLIENT_SESSION_ID)) + Mockito.lenient() + .when(mockIpvSessionService.getIpvSession(SESSION_ID)) + .thenReturn(ipvSessionItem); + Mockito.lenient() + .when(mockClientOauthSessionDetailsService.getClientOAuthSession(CLIENT_SESSION_ID)) .thenReturn(clientOAuthSessionItem); Mockito.lenient() .when(mockSessionCredentialService.getCredentials(SESSION_ID, USER_ID)) .thenReturn(VCS); - when(mockConfigService.getParameter(ConfigurationVariable.COMPONENT_ID)) + Mockito.lenient() + .when(mockConfigService.getParameter(ConfigurationVariable.COMPONENT_ID)) .thenReturn(COMPONENT_ID); } @@ -415,4 +423,28 @@ void shouldReturnAnErrorJourneyIfFailedAtEvcsIdentityStore_whenEvcsReadEnabled_f assertEquals(JOURNEY_IDENTITY_STORED_PATH, response.get(JOURNEY)); } + + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSession(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(StoreIdentityHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> + storeIdentityHandler.handleRequest( + PROCESS_REQUEST_FOR_PENDING_IDENTITY, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } } diff --git a/lambdas/user-reverification/build.gradle b/lambdas/user-reverification/build.gradle index 143548d16d..d406bf0fc7 100644 --- a/lambdas/user-reverification/build.gradle +++ b/lambdas/user-reverification/build.gradle @@ -17,11 +17,12 @@ dependencies { libs.powertoolsTracing, libs.aspectj - testImplementation libs.junitJupiter, + testImplementation libs.hamcrest, + libs.junitJupiter, libs.mockitoJunit, libs.pactProviderJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/lambdas/user-reverification/src/main/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandler.java b/lambdas/user-reverification/src/main/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandler.java index fb7d020904..ebe2193472 100644 --- a/lambdas/user-reverification/src/main/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandler.java +++ b/lambdas/user-reverification/src/main/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandler.java @@ -94,6 +94,9 @@ public APIGatewayProxyResponseEvent handleRequest( return getAccessDeniedApiGatewayProxyResponseEvent(); } catch (IpvSessionNotFoundException | ClientOauthSessionNotFoundException e) { return getUnknownAccessTokenApiGatewayProxyResponseEvent(); + } catch (Exception e) { + LOGGER.error(LogHelper.buildErrorMessage("Unhandled lambda exception", e)); + throw e; } } } diff --git a/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandlerTest.java b/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandlerTest.java index 2ec1bf2f56..d9fb6c39ce 100644 --- a/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandlerTest.java +++ b/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/UserReverificationHandlerTest.java @@ -25,13 +25,18 @@ import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; import uk.gov.di.ipv.core.library.service.UserIdentityService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import java.time.Instant; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -253,6 +258,28 @@ void shouldReturnErrorResponseWhenScopeIsInvalid() throws Exception { verify(mockSessionCredentialsService, never()).deleteSessionCredentials(any()); } + @Test + void shouldLogRuntimeExceptionsAndRethrow() throws Exception { + // Arrange + when(mockIpvSessionService.getIpvSessionByAccessToken(anyString())) + .thenThrow(new RuntimeException("Test error")); + + var logCollector = LogCollector.getLogCollectorFor(UserReverificationHandler.class); + + // Act + var thrown = + assertThrows( + Exception.class, + () -> userReverificationHandler.handleRequest(testEvent, mockContext), + "Expected handleRequest() to throw, but it didn't"); + + // Assert + assertEquals("Test error", thrown.getMessage()); + var logMessage = logCollector.getLogMessages().get(0); + assertThat(logMessage, containsString("Unhandled lambda exception")); + assertThat(logMessage, containsString("Test error")); + } + private static APIGatewayProxyRequestEvent getEventWithAuthAndIpHeaders() { APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); AccessToken accessToken = new BearerAccessToken(TEST_ACCESS_TOKEN); diff --git a/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/pact/UserReverificationHandlerTest.java b/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/pact/UserReverificationHandlerTest.java index 78c2f7ecee..c81e021fcf 100644 --- a/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/pact/UserReverificationHandlerTest.java +++ b/lambdas/user-reverification/src/test/java/uk/gov/di/ipv/core/userreverification/pact/UserReverificationHandlerTest.java @@ -20,7 +20,6 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import uk.gov.di.ipv.core.library.dto.AccessTokenMetadata; -import uk.gov.di.ipv.core.library.pacttesthelpers.LambdaHttpServer; import uk.gov.di.ipv.core.library.persistence.DataStore; import uk.gov.di.ipv.core.library.persistence.item.ClientOAuthSessionItem; import uk.gov.di.ipv.core.library.persistence.item.IpvSessionItem; @@ -28,6 +27,7 @@ import uk.gov.di.ipv.core.library.service.ClientOAuthSessionDetailsService; import uk.gov.di.ipv.core.library.service.ConfigService; import uk.gov.di.ipv.core.library.service.IpvSessionService; +import uk.gov.di.ipv.core.library.testhelpers.pact.LambdaHttpServer; import uk.gov.di.ipv.core.library.verifiablecredential.service.SessionCredentialsService; import uk.gov.di.ipv.core.userreverification.UserReverificationHandler; diff --git a/libs/cimit-service/build.gradle b/libs/cimit-service/build.gradle index 4f8ab1fb11..286ec42541 100644 --- a/libs/cimit-service/build.gradle +++ b/libs/cimit-service/build.gradle @@ -22,7 +22,7 @@ dependencies { libs.mockitoJunit, libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/libs/cimit-service/src/test/java/uk/gov/di/ipv/core/library/pact/ContractTest.java b/libs/cimit-service/src/test/java/uk/gov/di/ipv/core/library/pact/ContractTest.java index 7cff96ccda..10361a759f 100644 --- a/libs/cimit-service/src/test/java/uk/gov/di/ipv/core/library/pact/ContractTest.java +++ b/libs/cimit-service/src/test/java/uk/gov/di/ipv/core/library/pact/ContractTest.java @@ -21,9 +21,9 @@ import uk.gov.di.ipv.core.library.cimit.exception.CiRetrievalException; import uk.gov.di.ipv.core.library.domain.VerifiableCredential; import uk.gov.di.ipv.core.library.exceptions.CredentialParseException; -import uk.gov.di.ipv.core.library.pacttesthelpers.PactJwtBuilder; import uk.gov.di.ipv.core.library.service.CimitService; import uk.gov.di.ipv.core.library.service.ConfigService; +import uk.gov.di.ipv.core.library.testhelpers.pact.PactJwtBuilder; import uk.gov.di.model.SecurityCheck; import uk.gov.di.model.SecurityCheckCredential; diff --git a/libs/evcs-service/build.gradle b/libs/evcs-service/build.gradle index cd2a27b916..51a6604202 100644 --- a/libs/evcs-service/build.gradle +++ b/libs/evcs-service/build.gradle @@ -27,7 +27,7 @@ dependencies { project(path: ':libs:evcs-migration'), libs.pactConsumerJunit, project(path: ':libs:common-services', configuration: 'tests'), - project(path: ':libs:pact-test-helpers') + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/libs/pact-test-helpers/build.gradle b/libs/test-helpers/build.gradle similarity index 89% rename from libs/pact-test-helpers/build.gradle rename to libs/test-helpers/build.gradle index 4dfb9b67e2..6474b83c00 100644 --- a/libs/pact-test-helpers/build.gradle +++ b/libs/test-helpers/build.gradle @@ -10,9 +10,14 @@ dependencies { libs.jacksonDatabind, libs.mockitoJunit, libs.pactConsumerJunit, + libs.powertoolsLogging, project(path: ':libs:common-services') + compileOnly libs.lombok + annotationProcessor libs.lombok + testImplementation libs.junitJupiter, + libs.hamcrest, libs.mockitoJunit testRuntimeOnly libs.junitPlatform diff --git a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/LambdaHttpServer.java b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/LambdaHttpServer.java similarity index 99% rename from libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/LambdaHttpServer.java rename to libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/LambdaHttpServer.java index 5efc55c082..f7fd678778 100644 --- a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/LambdaHttpServer.java +++ b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/LambdaHttpServer.java @@ -1,4 +1,4 @@ -package uk.gov.di.ipv.core.library.pacttesthelpers; +package uk.gov.di.ipv.core.library.testhelpers.pact; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; diff --git a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilder.java b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilder.java similarity index 97% rename from libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilder.java rename to libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilder.java index e46d486113..1a6ad69a61 100644 --- a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilder.java +++ b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilder.java @@ -1,4 +1,4 @@ -package uk.gov.di.ipv.core.library.pacttesthelpers; +package uk.gov.di.ipv.core.library.testhelpers.pact; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; diff --git a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilder.java b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilder.java similarity index 97% rename from libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilder.java rename to libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilder.java index f1049e9820..c2996f124e 100644 --- a/libs/pact-test-helpers/src/main/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilder.java +++ b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilder.java @@ -1,4 +1,4 @@ -package uk.gov.di.ipv.core.library.pacttesthelpers; +package uk.gov.di.ipv.core.library.testhelpers.pact; import au.com.dius.pact.consumer.dsl.BodyBuilder; import au.com.dius.pact.core.model.ContentType; diff --git a/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollector.java b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollector.java new file mode 100644 index 0000000000..28d0ac1ace --- /dev/null +++ b/libs/test-helpers/src/main/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollector.java @@ -0,0 +1,32 @@ +package uk.gov.di.ipv.core.library.testhelpers.unit; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.appender.AbstractAppender; + +import java.util.ArrayList; +import java.util.List; + +@lombok.Getter +public class LogCollector extends AbstractAppender { + private final List logMessages = new ArrayList<>(); + + private LogCollector() { + super("LogCollector", null, null, true, null); + } + + @Override + public void append(LogEvent event) { + logMessages.add(event.getMessage().getFormattedMessage()); + } + + public static LogCollector getLogCollectorFor(Class clazz) { + var logCollector = new LogCollector(); + var logger = (Logger) LogManager.getLogger(clazz); + logger.get().addAppender(logCollector, Level.ALL, null); + + return logCollector; + } +} diff --git a/libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilderTest.java b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilderTest.java similarity index 85% rename from libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilderTest.java rename to libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilderTest.java index 45ec9d263f..22759fd762 100644 --- a/libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtBuilderTest.java +++ b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtBuilderTest.java @@ -1,4 +1,4 @@ -package uk.gov.di.ipv.core.library.pacttesthelpers; +package uk.gov.di.ipv.core.library.testhelpers.pact; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -11,7 +11,7 @@ @ExtendWith(MockitoExtension.class) class PactJwtBuilderTest { - private final String JWT_HEADER = + private static final String JWT_HEADER = """ { "alg": "ES256", @@ -19,9 +19,9 @@ class PactJwtBuilderTest { } """; - private final String JWT_HEADER_BASE64 = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; + private static final String JWT_HEADER_BASE64 = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; - private final String JWT_BODY = + private static final String JWT_BODY = """ { "sub": "1234567890", @@ -29,7 +29,7 @@ class PactJwtBuilderTest { } """; - private final String JWT_BODY_BASE64 = + private static final String JWT_BODY_BASE64 = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0"; // pragma: allowlist secret @Test diff --git a/libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilderTest.java b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilderTest.java similarity index 89% rename from libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilderTest.java rename to libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilderTest.java index f490e295a3..f718affd68 100644 --- a/libs/pact-test-helpers/src/test/java/uk/gov/di/ipv/core/library/pacttesthelpers/PactJwtIgnoreSignatureBodyBuilderTest.java +++ b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/pact/PactJwtIgnoreSignatureBodyBuilderTest.java @@ -1,4 +1,4 @@ -package uk.gov.di.ipv.core.library.pacttesthelpers; +package uk.gov.di.ipv.core.library.testhelpers.pact; import au.com.dius.pact.core.model.matchingrules.RegexMatcher; import org.junit.jupiter.api.Test; @@ -13,7 +13,7 @@ @ExtendWith(MockitoExtension.class) class PactJwtIgnoreSignatureBodyBuilderTest { - private final String JWT_HEADER = + private static final String JWT_HEADER = """ { "alg": "ES256", @@ -21,9 +21,9 @@ class PactJwtIgnoreSignatureBodyBuilderTest { } """; - private final String JWT_HEADER_BASE64 = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; + private static final String JWT_HEADER_BASE64 = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; - private final String JWT_BODY = + private static final String JWT_BODY = """ { "sub": "1234567890", @@ -31,7 +31,7 @@ class PactJwtIgnoreSignatureBodyBuilderTest { } """; - private final String JWT_BODY_BASE64 = + private static final String JWT_BODY_BASE64 = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0"; // pragma: allowlist secret @Test diff --git a/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollectorTest.java b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollectorTest.java new file mode 100644 index 0000000000..a643dd5948 --- /dev/null +++ b/libs/test-helpers/src/test/java/uk/gov/di/ipv/core/library/testhelpers/unit/LogCollectorTest.java @@ -0,0 +1,29 @@ +package uk.gov.di.ipv.core.library.testhelpers.unit; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.StringContains.containsString; + +class LogCollectorTest { + private static final Logger LOGGER = LogManager.getLogger(); + + @Test + void shouldCatchMultipleLogsForInspection() { + // Arrange + var underTest = LogCollector.getLogCollectorFor(LogCollectorTest.class); + + // Act + LOGGER.error("test log 1"); + LOGGER.error("test log 2"); + + // Assert + var collectedMessages = underTest.getLogMessages(); + assertThat(collectedMessages.size(), is(2)); + assertThat(collectedMessages.get(0), containsString("test log 1")); + assertThat(collectedMessages.get(1), containsString("test log 2")); + } +} diff --git a/libs/pact-test-helpers/src/test/resources/dvlaVc/body.json b/libs/test-helpers/src/test/resources/dvlaVc/body.json similarity index 100% rename from libs/pact-test-helpers/src/test/resources/dvlaVc/body.json rename to libs/test-helpers/src/test/resources/dvlaVc/body.json diff --git a/libs/pact-test-helpers/src/test/resources/dvlaVc/header.json b/libs/test-helpers/src/test/resources/dvlaVc/header.json similarity index 100% rename from libs/pact-test-helpers/src/test/resources/dvlaVc/header.json rename to libs/test-helpers/src/test/resources/dvlaVc/header.json diff --git a/libs/pact-test-helpers/src/test/resources/dvlaVc/signature b/libs/test-helpers/src/test/resources/dvlaVc/signature similarity index 100% rename from libs/pact-test-helpers/src/test/resources/dvlaVc/signature rename to libs/test-helpers/src/test/resources/dvlaVc/signature diff --git a/libs/verifiable-credentials/build.gradle b/libs/verifiable-credentials/build.gradle index 509d10cd0c..0a212d9a08 100644 --- a/libs/verifiable-credentials/build.gradle +++ b/libs/verifiable-credentials/build.gradle @@ -24,7 +24,8 @@ dependencies { testImplementation libs.junitJupiter, libs.mockitoJunit, - project(":libs:common-services").sourceSets.test.output + project(":libs:common-services").sourceSets.test.output, + project(path: ':libs:test-helpers') testRuntimeOnly libs.junitPlatform } diff --git a/libs/verifiable-credentials/src/test/java/uk/gov/di/ipv/core/library/verifiablecredential/validator/VerifiableCredentialValidatorTest.java b/libs/verifiable-credentials/src/test/java/uk/gov/di/ipv/core/library/verifiablecredential/validator/VerifiableCredentialValidatorTest.java index 7038f0c31e..b13e1d07cd 100644 --- a/libs/verifiable-credentials/src/test/java/uk/gov/di/ipv/core/library/verifiablecredential/validator/VerifiableCredentialValidatorTest.java +++ b/libs/verifiable-credentials/src/test/java/uk/gov/di/ipv/core/library/verifiablecredential/validator/VerifiableCredentialValidatorTest.java @@ -5,8 +5,6 @@ import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.oauth2.sdk.http.HTTPResponse; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.message.Message; @@ -21,6 +19,7 @@ import uk.gov.di.ipv.core.library.exceptions.VerifiableCredentialException; import uk.gov.di.ipv.core.library.helpers.FixedTimeJWTClaimsVerifier; import uk.gov.di.ipv.core.library.service.ConfigService; +import uk.gov.di.ipv.core.library.testhelpers.unit.LogCollector; import java.text.ParseException; import java.time.Clock; @@ -371,11 +370,7 @@ void throwsVerifiableCredentialExceptionWhenSigningKeyCannotBeParsed() { @Test void validateDoesNotLogUserIdWhenFailingValidation() { - var testAppender = new TestAppender(); - var verifiableCredentialValidatorLogger = - (org.apache.logging.log4j.core.Logger) - LogManager.getLogger(VerifiableCredentialValidator.class); - verifiableCredentialValidatorLogger.get().addAppender(testAppender, Level.ERROR, null); + var logCollector = LogCollector.getLogCollectorFor(VerifiableCredentialValidator.class); assertThrows( VerifiableCredentialException.class, @@ -390,7 +385,7 @@ void validateDoesNotLogUserIdWhenFailingValidation() { assertEquals( "description=\"JWT sub claim does not match expected\"", - testAppender.getLogMessages().get(0).getFormattedMessage()); + logCollector.getLogMessages().get(0)); } private static class TestAppender extends AbstractAppender { diff --git a/settings.gradle b/settings.gradle index a899bd353c..4262ec9b77 100644 --- a/settings.gradle +++ b/settings.gradle @@ -44,7 +44,7 @@ include "lambdas", "libs:evcs-migration", "libs:gpg45-evaluator", "libs:journey-uris", - "libs:pact-test-helpers", + "libs:test-helpers", "libs:user-identity-service", "libs:verifiable-credentials", "libs:oauth-key-service",