From 6517c522991f2e02ac450c33219770e8ae7fc95d Mon Sep 17 00:00:00 2001 From: Afra Hussaindeen Date: Sun, 6 Oct 2024 19:25:37 +0530 Subject: [PATCH] Add unit tests --- .../pom.xml | 21 + .../api/v1/core/OnfidoIdvServiceTest.java | 478 ++++++++++++++ .../v1/impl/DefaultApiServiceImplTest.java | 182 ++++++ .../pom.xml | 21 + .../connector/OnfidoIdentityVerifierTest.java | 597 ++++++++++++++++++ .../connector/web/OnfidoAPIClientTest.java | 284 +++++++++ pom.xml | 45 +- 7 files changed, 1627 insertions(+), 1 deletion(-) create mode 100644 components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/core/OnfidoIdvServiceTest.java create mode 100644 components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/impl/DefaultApiServiceImplTest.java create mode 100644 components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/OnfidoIdentityVerifierTest.java create mode 100644 components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/web/OnfidoAPIClientTest.java diff --git a/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/pom.xml b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/pom.xml index a585906..c34be13 100644 --- a/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/pom.xml +++ b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/pom.xml @@ -102,6 +102,27 @@ org.wso2.carbon.identity.verification.onfido.api.common provided + + + org.testng + testng + test + + + org.jacoco + org.jacoco.agent + runtime + + + org.mockito + mockito-core + test + + + org.mockito + mockito-testng + test + diff --git a/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/core/OnfidoIdvServiceTest.java b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/core/OnfidoIdvServiceTest.java new file mode 100644 index 0000000..814ecc5 --- /dev/null +++ b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/core/OnfidoIdvServiceTest.java @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.verification.onfido.api.v1.core; + +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.extension.identity.verification.mgt.IdentityVerificationManager; +import org.wso2.carbon.extension.identity.verification.mgt.model.IdVClaim; +import org.wso2.carbon.extension.identity.verification.provider.IdVProviderManager; +import org.wso2.carbon.extension.identity.verification.provider.exception.IdVProviderMgtException; +import org.wso2.carbon.extension.identity.verification.provider.model.IdVConfigProperty; +import org.wso2.carbon.extension.identity.verification.provider.model.IdVProvider; +import org.wso2.carbon.identity.verification.onfido.api.common.OnfidoIdvServiceHolder; +import org.wso2.carbon.identity.verification.onfido.api.common.Util; +import org.wso2.carbon.identity.verification.onfido.api.common.error.APIError; +import org.wso2.carbon.identity.verification.onfido.api.common.error.ErrorDTO; +import org.wso2.carbon.identity.verification.onfido.api.v1.interceptors.RawRequestBodyInterceptor; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequest; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequestPayload; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequestPayloadObject; +import org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; +import static org.wso2.carbon.identity.verification.onfido.api.common.Constants.ErrorMessage.CLIENT_ERROR_RESOLVING_IDVP; +import static org.wso2.carbon.identity.verification.onfido.api.common.Constants.ErrorMessage.CLIENT_ERROR_SIGNATURE_MISMATCH; +import static org.wso2.carbon.identity.verification.onfido.api.common.Constants.ErrorMessage.CLIENT_ERROR_UNSUPPORTED_RESOURCE_TYPE_OR_ACTION; +import static org.wso2.carbon.identity.verification.onfido.api.common.Constants.ErrorMessage.SERVER_ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_INVALID; + +public class OnfidoIdvServiceTest { + + @Mock + private IdVProviderManager idVProviderManager; + + @Mock + private IdentityVerificationManager identityVerificationManager; + + @Mock + private IdVProvider idVProvider; + + @InjectMocks + private OnfidoIdvService onfidoIdvService; + + private static final String WORKFLOW_ID = "workflow_id"; + private static final String BASE_URL = "base_url"; + private static final String WEBHOOK_TOKEN = "webhook_token"; + private static final String TOKEN = "token"; + + private static final String TEST_IDVP_ID = "test-idvp-id"; + private static final int TEST_TENANT_ID = 1; + private static final String TEST_VALID_RESOURCE_TYPE = "workflow_run"; + private static final String TEST_INVALID_RESOURCE_TYPE = "invalid_resource_type"; + private static final String TEST_VALID_RESOURCE_ACTION = "workflow_run.completed"; + private static final String TEST_INVALID_RESOURCE_ACTION = "invalid_resource_action"; + private static final String TEST_VALID_SIGNATURE = + "9524f4134c6335b4495284859531b92d3d5c089eb3e7c679e5b9c1debee91f1e"; + private static final String TEST_INVALID_SIGNATURE = "test_invalid_signature"; + private static final String TEST_WORKFLOW_RUN_ID = "test-workflow-run-id"; + private static final String TEST_COMPLETED_AT = "2023-05-01T12:00:00Z"; + private static final String TEST_WORKFLOW_ID = "test_workflow_id"; + private static final String TEST_BASE_URL = "test_base_url"; + private static final String TEST_WEBHOOK_TOKEN = "test_webhook_token"; + private static final String TEST_API_TOKEN = "test_api_token"; + private static final String TEST_USER_ID = "test-user-id"; + private static final String TEST_APPLICANT_ID = "test_applicant_id"; + private static final String TEST_SUCCESS_VERIFICATION_STATUS = "clear"; + + private static final String FIELD_DATE_OF_BIRTH = "date_of_birth"; + private static final String FIELD_FIRST_NAME = "first_name"; + private static final String FIELD_DOCUMENT_NUMBERS = "document_numbers"; + private static final String FIELD_DOCUMENT_TYPE = "document_type"; + private static final String FIELD_LAST_NAME = "last_name"; + private static final String FIELD_DATE_OF_EXPIRY = "date_of_expiry"; + private static final String FIELD_GENDER = "gender"; + private static final String FIELD_ISSUING_COUNTRY = "issuing_country"; + + private static final String KEY_RESULT = "result"; + private static final String KEY_PROPERTIES = "properties"; + private static final String VALUE_CLEAR = "clear"; + private static final String VALUE_CONSIDER = "consider"; + private static final String KEY_DATA_COMPARISON = "data_comparison"; + private static final String KEY_OUTPUT = "output"; + + private static final String CLAIM_URI_LASTNAME = "http://wso2.org/claims/lastname"; + private static final String CLAIM_URI_FIRSTNAME = "http://wso2.org/claims/firstname"; + private static final String CLAIM_URI_DOB = "http://wso2.org/claims/dob"; + + private static final String ONFIDO_ATTR_LASTNAME = "last_name"; + private static final String ONFIDO_ATTR_FIRSTNAME = "first_name"; + private static final String ONFIDO_ATTR_DOB = "dob"; + + private static final String METADATA_ONFIDO_APPLICANT_ID = "onfido_applicant_id"; + private static final String METADATA_ONFIDO_WORKFLOW_RUN_ID = "onfido_workflow_run_id"; + private static final String METADATA_ONFIDO_WORKFLOW_STATUS = "onfido_workflow_status"; + public static final String METADATA_ONFIDO_VERIFICATION_STATUS = "onfido_verification_status"; + + private static final String RAW_REQUEST_BODY = "{\"payload\":{\"resource_type\":\"workflow_run\"," + + "\"action\":\"workflow_run.completed\",\"object\":{\"id\":\"test-workflow-run-id\"," + + "\"status\":\"approved\",\"completed_at_iso8601\":\"2024-10-05T09:59:34Z\"},\"resource\":" + + "{\"output\":{\"data_comparison\":{\"date_of_birth\":{\"result\":\"clear\",\"properties\":{}}," + + "\"first_name\":{\"result\":\"clear\",\"properties\":{}},\"document_numbers\":{\"result\":\"clear\"," + + "\"properties\":{}},\"document_type\":{\"result\":\"clear\",\"properties\":{}},\"last_name\":" + + "{\"result\":\"clear\",\"properties\":{}},\"date_of_expiry\":{\"result\":\"clear\",\"properties\":{}}," + + "\"gender\":{\"result\":\"clear\",\"properties\":{}},\"issuing_country\":{\"result\":\"clear\"," + + "\"properties\":{}}}}}}}"; + + private static MockedStatic mockedUtil; + private static MockedStatic mockedHolder; + private static MockedStatic mockedInterceptor; + + @BeforeClass + public static void setUpClass() { + + mockedUtil = mockStatic(Util.class); + mockedHolder = mockStatic(OnfidoIdvServiceHolder.class); + mockedInterceptor = mockStatic(RawRequestBodyInterceptor.class); + } + + @AfterClass + public static void tearDownClass() { + + mockedUtil.close(); + mockedHolder.close(); + mockedInterceptor.close(); + } + + @BeforeMethod + public void setUp() throws Exception { + + MockitoAnnotations.openMocks(this); + setupMocks(); + } + + private void setupMocks() throws Exception { + + mockedUtil.when(Util::getTenantId).thenReturn(TEST_TENANT_ID); + mockedHolder.when(OnfidoIdvServiceHolder::getIdVProviderManager).thenReturn(idVProviderManager); + mockedHolder.when(OnfidoIdvServiceHolder::getIdentityVerificationManager) + .thenReturn(identityVerificationManager); + mockedInterceptor.when(RawRequestBodyInterceptor::getRawRequestBody).thenReturn(RAW_REQUEST_BODY); + + when(idVProviderManager.getIdVProvider(eq(TEST_IDVP_ID), eq(TEST_TENANT_ID))).thenReturn(idVProvider); + when(idVProvider.isEnabled()).thenReturn(true); + when(idVProvider.getIdVConfigProperties()).thenReturn(createMockConfigProperties()); + when(idVProvider.getClaimMappings()).thenReturn(createMockClaimMappings()); + } + + private Map createMockClaimMappings() { + + Map claimMappings = new HashMap<>(); + claimMappings.put(CLAIM_URI_LASTNAME, ONFIDO_ATTR_LASTNAME); + claimMappings.put(CLAIM_URI_FIRSTNAME, ONFIDO_ATTR_FIRSTNAME); + claimMappings.put(CLAIM_URI_DOB, ONFIDO_ATTR_DOB); + return claimMappings; + } + + private IdVConfigProperty[] createMockConfigProperties() { + + IdVConfigProperty[] properties = new IdVConfigProperty[4]; + + properties[0] = new IdVConfigProperty(); + properties[0].setName(TOKEN); + properties[0].setValue(TEST_API_TOKEN); + + properties[1] = new IdVConfigProperty(); + properties[1].setName(BASE_URL); + properties[1].setValue(TEST_BASE_URL); + + properties[2] = new IdVConfigProperty(); + properties[2].setName(WEBHOOK_TOKEN); + properties[2].setValue(TEST_WEBHOOK_TOKEN); + + properties[3] = new IdVConfigProperty(); + properties[3].setName(WORKFLOW_ID); + properties[3].setValue(TEST_WEBHOOK_TOKEN); + + return properties; + } + + private VerifyRequest createVerifyRequest(OnfidoConstants.WorkflowRunStatus workflowStatus) { + + VerifyRequest verifyRequest = new VerifyRequest(); + VerifyRequestPayload payload = new VerifyRequestPayload(); + payload.setResourceType(TEST_VALID_RESOURCE_TYPE); + payload.setAction(TEST_VALID_RESOURCE_ACTION); + + VerifyRequestPayloadObject object = new VerifyRequestPayloadObject(); + object.setId(TEST_WORKFLOW_RUN_ID); + object.setCompletedAtIso8601(TEST_COMPLETED_AT); + object.setStatus(workflowStatus.getStatus()); + payload.setObject(object); + + Map resource = new HashMap<>(); + Map output = new HashMap<>(); + Map dataComparison = new HashMap<>(); + + String[] fields = {FIELD_DATE_OF_BIRTH, FIELD_FIRST_NAME, FIELD_DOCUMENT_NUMBERS, FIELD_DOCUMENT_TYPE, + FIELD_LAST_NAME, FIELD_DATE_OF_EXPIRY, FIELD_GENDER, FIELD_ISSUING_COUNTRY}; + + String resultValue = + (workflowStatus == OnfidoConstants.WorkflowRunStatus.APPROVED) ? VALUE_CLEAR : VALUE_CONSIDER; + + for (String field : fields) { + Map fieldData = new HashMap<>(); + fieldData.put(KEY_RESULT, resultValue); + fieldData.put(KEY_PROPERTIES, new HashMap<>()); + dataComparison.put(field, fieldData); + } + + output.put(KEY_DATA_COMPARISON, dataComparison); + resource.put(KEY_OUTPUT, output); + + payload.setResource(resource); + verifyRequest.setPayload(payload); + return verifyRequest; + } + + private IdVClaim[] createMockIdVClaimsBeforeVerificationStatusUpdate() { + + String[] claimUris = {CLAIM_URI_FIRSTNAME, CLAIM_URI_LASTNAME, CLAIM_URI_DOB}; + IdVClaim[] claims = new IdVClaim[claimUris.length]; + + Map metadata = new HashMap<>(); + metadata.put(METADATA_ONFIDO_APPLICANT_ID, TEST_APPLICANT_ID); + metadata.put(METADATA_ONFIDO_WORKFLOW_RUN_ID, TEST_WORKFLOW_RUN_ID); + metadata.put(METADATA_ONFIDO_WORKFLOW_STATUS, OnfidoConstants.WorkflowRunStatus.PROCESSING.getStatus()); + + for (int i = 0; i < claimUris.length; i++) { + claims[i] = new IdVClaim(); + claims[i].setClaimUri(claimUris[i]); + claims[i].setUserId(TEST_USER_ID); + claims[i].setIsVerified(false); + claims[i].setMetadata(new HashMap<>(metadata)); + } + + return claims; + } + + @DataProvider(name = "invalidResourceTypeAndActionDataProvider") + public Object[][] invalidResourceTypeAndActionDataProvider() { + + return new Object[][]{ + {TEST_INVALID_RESOURCE_TYPE, TEST_VALID_RESOURCE_ACTION}, + {TEST_VALID_RESOURCE_TYPE, TEST_INVALID_RESOURCE_ACTION}, + {TEST_INVALID_RESOURCE_TYPE, TEST_INVALID_RESOURCE_ACTION} + }; + } + + @Test(dataProvider = "invalidResourceTypeAndActionDataProvider") + public void testInvalidResourceTypeAndAction(String resourceType, String action) { + + OnfidoIdvService service = new OnfidoIdvService(); + + VerifyRequest verifyRequest = new VerifyRequest(); + VerifyRequestPayload payload = new VerifyRequestPayload(); + payload.setResourceType(resourceType); + payload.setAction(action); + verifyRequest.setPayload(payload); + + try { + service.verify(TEST_VALID_SIGNATURE, TEST_IDVP_ID, verifyRequest); + fail("Should have thrown an APIError for invalid resource type or action"); + } catch (APIError e) { + assertEquals(e.getStatus().getStatusCode(), Response.Status.BAD_REQUEST.getStatusCode()); + ErrorDTO errorDTO = e.getResponseEntity(); + assertEquals(errorDTO.getCode(), CLIENT_ERROR_UNSUPPORTED_RESOURCE_TYPE_OR_ACTION.getCode()); + assertEquals(errorDTO.getMessage(), CLIENT_ERROR_UNSUPPORTED_RESOURCE_TYPE_OR_ACTION.getMessage()); + assertEquals(errorDTO.getDescription(), CLIENT_ERROR_UNSUPPORTED_RESOURCE_TYPE_OR_ACTION.getDescription()); + } + } + + @Test + public void testInvalidGetIdVProvider() throws IdVProviderMgtException { + + IdVProviderManager mockIdVProviderManager = mock(IdVProviderManager.class); + mockedHolder.when(OnfidoIdvServiceHolder::getIdVProviderManager).thenReturn(mockIdVProviderManager); + when(mockIdVProviderManager.getIdVProvider(anyString(), anyInt())).thenReturn(null); + + VerifyRequest verifyRequest = createVerifyRequest(OnfidoConstants.WorkflowRunStatus.APPROVED); + + try { + onfidoIdvService.verify(TEST_VALID_SIGNATURE, TEST_IDVP_ID, verifyRequest); + fail("Expected APIError to be thrown"); + } catch (APIError e) { + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), e.getStatus().getStatusCode()); + ErrorDTO errorDTO = e.getResponseEntity(); + assertEquals(CLIENT_ERROR_RESOLVING_IDVP.getCode(), errorDTO.getCode()); + assertEquals(CLIENT_ERROR_RESOLVING_IDVP.getMessage(), errorDTO.getMessage()); + assertEquals(CLIENT_ERROR_RESOLVING_IDVP.getDescription(), errorDTO.getDescription()); + } + } + + @DataProvider(name = "invalidConfigPropertiesDataProvider") + public Object[][] invalidConfigPropertiesDataProvider() { + + return new Object[][]{ + {null, TEST_BASE_URL, TEST_WEBHOOK_TOKEN, TEST_WORKFLOW_ID}, + {TEST_API_TOKEN, null, TEST_WEBHOOK_TOKEN, TEST_WORKFLOW_ID}, + {TEST_API_TOKEN, TEST_BASE_URL, null, TEST_WORKFLOW_ID}, + {TEST_API_TOKEN, TEST_BASE_URL, TEST_WEBHOOK_TOKEN, null} + }; + } + + @Test(dataProvider = "invalidConfigPropertiesDataProvider") + public void testInvalidIdVProviderConfigProperties(String token, String baseUrl, String webhookToken, + String workflowId) { + + IdVConfigProperty[] idVProviderConfigProperties = new IdVConfigProperty[4]; + + idVProviderConfigProperties[0] = new IdVConfigProperty(); + idVProviderConfigProperties[0].setName(TOKEN); + idVProviderConfigProperties[0].setValue(token); + + idVProviderConfigProperties[1] = new IdVConfigProperty(); + idVProviderConfigProperties[1].setName(BASE_URL); + idVProviderConfigProperties[1].setValue(baseUrl); + + idVProviderConfigProperties[2] = new IdVConfigProperty(); + idVProviderConfigProperties[2].setName(WEBHOOK_TOKEN); + idVProviderConfigProperties[2].setValue(webhookToken); + + idVProviderConfigProperties[3] = new IdVConfigProperty(); + idVProviderConfigProperties[3].setName(WORKFLOW_ID); + idVProviderConfigProperties[3].setValue(workflowId); + + when(idVProvider.getIdVConfigProperties()).thenReturn(idVProviderConfigProperties); + + VerifyRequest verifyRequest = createVerifyRequest(OnfidoConstants.WorkflowRunStatus.APPROVED); + + try { + onfidoIdvService.verify(TEST_VALID_SIGNATURE, TEST_IDVP_ID, verifyRequest); + fail("Expected APIError to be thrown for invalid config properties"); + } catch (APIError e) { + assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e.getStatus().getStatusCode()); + ErrorDTO errorDTO = e.getResponseEntity(); + assertEquals(SERVER_ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_INVALID.getCode(), errorDTO.getCode()); + assertEquals(SERVER_ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_INVALID.getMessage(), errorDTO.getMessage()); + assertEquals(SERVER_ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_INVALID.getDescription(), + errorDTO.getDescription()); + } + } + + @DataProvider(name = "invalidSignatureDataProvider") + public Object[][] invalidSignatureDataProvider() { + + return new Object[][]{ + {TEST_INVALID_SIGNATURE}, + {null} + }; + } + + @Test(dataProvider = "invalidSignatureDataProvider") + public void testInvalidSignature(String signature) { + + VerifyRequest verifyRequest = createVerifyRequest(OnfidoConstants.WorkflowRunStatus.APPROVED); + APIError receivedApiError = null; + try { + onfidoIdvService.verify(signature, TEST_IDVP_ID, verifyRequest); + } catch (APIError e) { + receivedApiError = e; + } + + assertNotNull(receivedApiError, "APIError should be thrown"); + assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), + receivedApiError.getStatus().getStatusCode(), + "Response status should be 401 UNAUTHORIZED"); + ErrorDTO errorDTO = receivedApiError.getResponseEntity(); + assertEquals(CLIENT_ERROR_SIGNATURE_MISMATCH.getCode(), errorDTO.getCode()); + assertEquals(CLIENT_ERROR_SIGNATURE_MISMATCH.getMessage(), errorDTO.getMessage()); + assertEquals(CLIENT_ERROR_SIGNATURE_MISMATCH.getDescription(), errorDTO.getDescription()); + } + + @Test + public void testVerifySuccessWithWorkflowRunStatusApproved() throws Exception { + + testVerifyWithWorkflowStatus(OnfidoConstants.WorkflowRunStatus.APPROVED, true); + } + + @DataProvider(name = "unApprovedWorkflowStatusProvider") + public Object[][] unApprovedWorkflowStatusProvider() { + + return new Object[][]{ + {OnfidoConstants.WorkflowRunStatus.PROCESSING}, + {OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT}, + {OnfidoConstants.WorkflowRunStatus.DECLINED}, + {OnfidoConstants.WorkflowRunStatus.REVIEW}, + {OnfidoConstants.WorkflowRunStatus.ABANDONED}, + {OnfidoConstants.WorkflowRunStatus.ERROR} + }; + } + + @Test(dataProvider = "unApprovedWorkflowStatusProvider") + public void testVerifySuccessWithUnApprovedWorkflowStatus(OnfidoConstants.WorkflowRunStatus workflowStatus) + throws Exception { + + testVerifyWithWorkflowStatus(workflowStatus, false); + } + + private void testVerifyWithWorkflowStatus(OnfidoConstants.WorkflowRunStatus workflowStatus, boolean isApproved) + throws Exception { + + VerifyRequest verifyRequest = createVerifyRequest(workflowStatus); + + when(identityVerificationManager.getIdVClaimsByMetadata( + eq(METADATA_ONFIDO_WORKFLOW_RUN_ID), + eq(TEST_WORKFLOW_RUN_ID), + eq(TEST_IDVP_ID), + eq(TEST_TENANT_ID) + )).thenReturn( + createMockIdVClaimsBeforeVerificationStatusUpdate()); + + ArgumentCaptor idVClaimCaptor = ArgumentCaptor.forClass(IdVClaim.class); + doReturn(null).when(identityVerificationManager) + .updateIdVClaim(anyString(), idVClaimCaptor.capture(), anyInt()); + + onfidoIdvService.verify(TEST_VALID_SIGNATURE, TEST_IDVP_ID, verifyRequest); + + List capturedClaims = idVClaimCaptor.getAllValues(); + assertEquals(capturedClaims.size(), 3, "Expected 3 IdVClaim updates"); + + for (IdVClaim claim : capturedClaims) { + assertNotNull(claim, "Captured IdVClaim should not be null"); + assertEquals(claim.isVerified(), isApproved, "IdVClaim verification status mismatch"); + + Map metadata = claim.getMetadata(); + assertNotNull(metadata, "Metadata should not be null"); + assertEquals(workflowStatus.getStatus(), metadata.get(METADATA_ONFIDO_WORKFLOW_STATUS), + "Incorrect workflow status being set."); + + if (isApproved) { + assertEquals(TEST_SUCCESS_VERIFICATION_STATUS, metadata.get(METADATA_ONFIDO_VERIFICATION_STATUS), + "Verification status in metadata should match 'clear'"); + } else { + assertFalse(metadata.containsKey(METADATA_ONFIDO_VERIFICATION_STATUS), + "METADATA_ONFIDO_VERIFICATION_STATUS should not be present for unapproved workflow status"); + } + } + } +} diff --git a/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/impl/DefaultApiServiceImplTest.java b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/impl/DefaultApiServiceImplTest.java new file mode 100644 index 0000000..004b2bd --- /dev/null +++ b/components/org.wso2.carbon.identity.verification.onfido.api/org.wso2.carbon.identity.verification.onfido.api.v1/src/test/java/org/wso2/carbon/identity/verification/onfido/api/v1/impl/DefaultApiServiceImplTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.verification.onfido.api.v1.impl; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.verification.onfido.api.common.Constants; +import org.wso2.carbon.identity.verification.onfido.api.common.error.APIError; +import org.wso2.carbon.identity.verification.onfido.api.common.error.ErrorDTO; +import org.wso2.carbon.identity.verification.onfido.api.common.error.ErrorResponse; +import org.wso2.carbon.identity.verification.onfido.api.v1.core.OnfidoIdvService; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequest; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequestPayload; +import org.wso2.carbon.identity.verification.onfido.api.v1.model.VerifyRequestPayloadObject; + +import javax.ws.rs.core.Response; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class DefaultApiServiceImplTest { + + @Mock + private OnfidoIdvService onfidoIdvService; + @InjectMocks + private DefaultApiServiceImpl defaultApiService; + private VerifyRequest testVerifyRequest; + + private static final String TEST_X_SHA2_SIGNATURE = "test-signature"; + private static final String TEST_IDVP_ID = "test-idvp-id"; + private static final String TEST_RESOURCE_TYPE = "workflow_run"; + private static final String TEST_ACTION = "workflow_run.completed"; + private static final String TEST_WORKFLOW_RUN_ID = "test_workflow_run_id"; + private static final String TEST_STATUS = "approved"; + private static final String TEST_COMPLETED_AT = "2024-10-05T09:59:34Z"; + private static final String TEST_HREF = "https://api.eu.onfido.com/v3.6/workflow_runs/test_workflow_run_id"; + + @BeforeMethod + public void setUp() { + + MockitoAnnotations.openMocks(this); + testVerifyRequest = createTestVerifyRequest(); + } + + @Test + public void testVerifySuccess() { + + doNothing().when(onfidoIdvService).verify(TEST_X_SHA2_SIGNATURE, TEST_IDVP_ID, testVerifyRequest); + + Response response = defaultApiService.verify(TEST_X_SHA2_SIGNATURE, TEST_IDVP_ID, testVerifyRequest); + + verify(onfidoIdvService, times(1)).verify(TEST_X_SHA2_SIGNATURE, TEST_IDVP_ID, testVerifyRequest); + assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), "Response status should be OK"); + } + + @DataProvider(name = "serverErrorDataProvider") + public Object[][] serverErrorDataProvider() { + return new Object[][] { + {Constants.ErrorMessage.SERVER_ERROR_GENERAL_ERROR}, + {Constants.ErrorMessage.SERVER_ERROR_RESOLVING_IDVP}, + {Constants.ErrorMessage.SERVER_ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_INVALID}, + {Constants.ErrorMessage.SERVER_ERROR_SIGNATURE_VALIDATION_FAILURE}, + {Constants.ErrorMessage.SERVER_ERROR_INVALID_WORKFLOW_RUN_STATUS}, + {Constants.ErrorMessage.SERVER_ERROR_UPDATING_IDV_CLAIM_VERIFICATION_STATUS} + }; + } + + @Test(dataProvider = "serverErrorDataProvider") + public void testVerifyServerError(Constants.ErrorMessage errorMessage) { + ErrorResponse errorResponse = new ErrorResponse.Builder() + .withCode(errorMessage.getCode()) + .withMessage(errorMessage.getMessage()) + .withDescription(errorMessage.getDescription()) + .build(); + + APIError apiError = new APIError(Response.Status.INTERNAL_SERVER_ERROR, errorResponse); + + doThrow(apiError).when(onfidoIdvService).verify(anyString(), anyString(), any(VerifyRequest.class)); + + APIError receivedApiError = null; + try { + defaultApiService.verify(TEST_X_SHA2_SIGNATURE, TEST_IDVP_ID, testVerifyRequest); + } catch (APIError e) { + receivedApiError = e; + } + + assertNotNull(receivedApiError, "APIError should be thrown"); + assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), + receivedApiError.getStatus().getStatusCode(), + "Response status should be 500 INTERNAL_SERVER_ERROR"); + ErrorDTO errorDTO = receivedApiError.getResponseEntity(); + assertEquals(errorMessage.getCode(), errorDTO.getCode()); + assertEquals(errorMessage.getMessage(), errorDTO.getMessage()); + assertEquals(errorMessage.getDescription(), errorDTO.getDescription()); + } + + @DataProvider(name = "clientErrorDataProvider") + public Object[][] clientErrorDataProvider() { + return new Object[][] { + {Constants.ErrorMessage.CLIENT_ERROR_SIGNATURE_MISMATCH, Response.Status.UNAUTHORIZED}, + {Constants.ErrorMessage.CLIENT_ERROR_RESOLVING_IDVP, Response.Status.NOT_FOUND}, + {Constants.ErrorMessage.CLIENT_ERROR_UNSUPPORTED_RESOURCE_TYPE_OR_ACTION, Response.Status.BAD_REQUEST}, + {Constants.ErrorMessage.CLIENT_ERROR_INVALID_WORKFLOW_OUTPUT, Response.Status.BAD_REQUEST}, + {Constants.ErrorMessage.CLIENT_ERROR_DATA_COMPARISON_RESULT_NOT_FOUND, Response.Status.BAD_REQUEST}, + {Constants.ErrorMessage.CLIENT_ERROR_DATA_COMPARISON_RESULT_NULL, Response.Status.BAD_REQUEST}, + {Constants.ErrorMessage.CLIENT_ERROR_INVALID_REQUEST, Response.Status.BAD_REQUEST} + }; + } + + @Test(dataProvider = "clientErrorDataProvider") + public void testVerifyClientError(Constants.ErrorMessage errorMessage, Response.Status expectedStatus) { + ErrorResponse errorResponse = new ErrorResponse.Builder() + .withCode(errorMessage.getCode()) + .withMessage(errorMessage.getMessage()) + .withDescription(errorMessage.getDescription()) + .build(); + + APIError apiError = new APIError(expectedStatus, errorResponse); + + doThrow(apiError).when(onfidoIdvService).verify(anyString(), anyString(), any(VerifyRequest.class)); + + APIError receivedApiError = null; + try { + defaultApiService.verify(TEST_X_SHA2_SIGNATURE, TEST_IDVP_ID, testVerifyRequest); + } catch (APIError e) { + receivedApiError = e; + } + + assertNotNull(receivedApiError, "APIError should be thrown"); + assertEquals(expectedStatus.getStatusCode(), receivedApiError.getStatus().getStatusCode(), + "Response status should match the expected status"); + ErrorDTO errorDTO = receivedApiError.getResponseEntity(); + assertEquals(errorMessage.getCode(), errorDTO.getCode()); + assertEquals(errorMessage.getMessage(), errorDTO.getMessage()); + assertEquals(errorMessage.getDescription(), errorDTO.getDescription()); + } + + private VerifyRequest createTestVerifyRequest() { + + VerifyRequestPayload verifyRequestPayload = new VerifyRequestPayload(); + verifyRequestPayload.setResourceType(TEST_RESOURCE_TYPE); + verifyRequestPayload.setAction(TEST_ACTION); + + VerifyRequestPayloadObject object = new VerifyRequestPayloadObject(); + object.setId(TEST_WORKFLOW_RUN_ID); + object.setStatus(TEST_STATUS); + object.setCompletedAtIso8601(TEST_COMPLETED_AT); + object.setHref(TEST_HREF); + verifyRequestPayload.setObject(object); + + VerifyRequest verifyRequest = new VerifyRequest(); + verifyRequest.setPayload(verifyRequestPayload); + + return verifyRequest; + } +} diff --git a/components/org.wso2.carbon.identity.verification.onfido.connector/pom.xml b/components/org.wso2.carbon.identity.verification.onfido.connector/pom.xml index 63ee704..28a0a27 100644 --- a/components/org.wso2.carbon.identity.verification.onfido.connector/pom.xml +++ b/components/org.wso2.carbon.identity.verification.onfido.connector/pom.xml @@ -62,6 +62,27 @@ org.wso2.carbon.user.core provided + + + org.testng + testng + test + + + org.jacoco + org.jacoco.agent + runtime + + + org.mockito + mockito-core + test + + + org.mockito + mockito-testng + test + diff --git a/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/OnfidoIdentityVerifierTest.java b/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/OnfidoIdentityVerifierTest.java new file mode 100644 index 0000000..f148dc5 --- /dev/null +++ b/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/OnfidoIdentityVerifierTest.java @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.verification.onfido.connector; + +import org.json.JSONObject; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.extension.identity.verification.mgt.IdentityVerificationManager; +import org.wso2.carbon.extension.identity.verification.mgt.exception.IdentityVerificationClientException; +import org.wso2.carbon.extension.identity.verification.mgt.internal.IdentityVerificationDataHolder; +import org.wso2.carbon.extension.identity.verification.mgt.model.IdVClaim; +import org.wso2.carbon.extension.identity.verification.mgt.model.IdVProperty; +import org.wso2.carbon.extension.identity.verification.mgt.model.IdentityVerifierData; +import org.wso2.carbon.extension.identity.verification.provider.IdVProviderManager; +import org.wso2.carbon.extension.identity.verification.provider.exception.IdVProviderMgtException; +import org.wso2.carbon.extension.identity.verification.provider.model.IdVConfigProperty; +import org.wso2.carbon.extension.identity.verification.provider.model.IdVProvider; +import org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants; +import org.wso2.carbon.identity.verification.onfido.connector.internal.OnfidoIDVDataHolder; +import org.wso2.carbon.identity.verification.onfido.connector.web.OnfidoAPIClient; +import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.jdbc.UniqueIDJDBCUserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage.ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_EMPTY; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage.ERROR_IDV_PROVIDER_INVALID_OR_DISABLED; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage.ERROR_INVALID_ONFIDO_VERIFICATION_FLOW_STATUS; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage.ERROR_VERIFICATION_FLOW_STATUS_NOT_FOUND; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage.ERROR_VERIFICATION_REQUIRED_CLAIMS_NOT_FOUND; + +public class OnfidoIdentityVerifierTest { + + @Mock + private IdVProvider mockIdVProvider; + @Mock + private IdentityVerificationDataHolder mockDataHolderInstance; + @Mock + private IdVProviderManager mockIdVProviderManager; + @Mock + private IdentityVerificationManager mockIdentityVerificationManager; + @Mock + private UniqueIDJDBCUserStoreManager mockUniqueIDUserStoreManager; + @Mock + private RealmService mockRealmService; + @Mock + private UserRealm mockUserRealm; + + @Spy + @InjectMocks + private OnfidoIdentityVerifier onfidoIdentityVerifier; + + private static MockedStatic mockedIdVDataHolder; + private static MockedStatic mockedOnfidoAPIClient; + private static MockedStatic mockedOnfidoIDVDataHolder; + + private static final String TEST_USER_ID = "test_user_id"; + private static final int TEST_TENANT_ID = 1; + private static final String TEST_IDV_PROVIDER_ID = "test_idv_provider_id"; + private static final String TEST_WORKFLOW_ID = "test_workflow_id"; + private static final String TEST_BASE_URL = "test_base_url"; + private static final String TEST_WEBHOOK_TOKEN = "test_webhook_token"; + private static final String TEST_API_TOKEN = "test_api_token"; + private static final String TEST_APPLICANT_ID = "test_applicant_id"; + private static final String TEST_WORKFLOW_RUN_ID = "test_workflow_run_id"; + private static final String TEST_SDK_TOKEN = "test_sdk_token"; + private static final String TEST_FIRST_NAME = "Joe"; + private static final String TEST_LAST_NAME = "Doe"; + + private static final String CLAIM_URI_FIRST_NAME = "http://wso2.org/claims/givenname"; + private static final String CLAIM_URI_LAST_NAME = "http://wso2.org/claims/lastname"; + + private static final String ONFIDO_ATTR_LASTNAME = "last_name"; + private static final String ONFIDO_ATTR_FIRSTNAME = "first_name"; + public static final String ONFIDO_APPLICANT_ID = "onfido_applicant_id"; + public static final String ONFIDO_WORKFLOW_RUN_ID = "onfido_workflow_run_id"; + public static final String ONFIDO_WORKFLOW_STATUS = "onfido_workflow_status"; + + private static final String WORKFLOW_ID = "workflow_id"; + private static final String BASE_URL = "base_url"; + private static final String WEBHOOK_TOKEN = "webhook_token"; + private static final String ID = "id"; + private static final String TOKEN = "token"; + private static final String APPLICANT_ID = "applicant_id"; + private static final String STATUS = "status"; + + @BeforeClass + public void setUpClass() { + + onfidoIdentityVerifier = new OnfidoIdentityVerifier(); + mockedIdVDataHolder = mockStatic(IdentityVerificationDataHolder.class); + mockedOnfidoAPIClient = mockStatic(OnfidoAPIClient.class); + mockedOnfidoIDVDataHolder = mockStatic(OnfidoIDVDataHolder.class); + } + + @AfterClass + public void tearDownClass() { + + mockedIdVDataHolder.close(); + mockedOnfidoAPIClient.close(); + mockedOnfidoIDVDataHolder.close(); + } + + @BeforeMethod + public void setUp() throws Exception { + + MockitoAnnotations.openMocks(this); + setupMocks(); + } + + private void setupMocks() throws Exception { + + mockedOnfidoIDVDataHolder.when(() -> OnfidoIDVDataHolder.getIdentityVerificationManager()) + .thenReturn(mockIdentityVerificationManager); + mockedIdVDataHolder.when(IdentityVerificationDataHolder::getInstance).thenReturn(mockDataHolderInstance); + when(mockDataHolderInstance.getIdVProviderManager()).thenReturn(mockIdVProviderManager); + when(mockIdVProviderManager.getIdVProvider(anyString(), anyInt())).thenReturn(mockIdVProvider); + setupMockIdVProvider(); + + mockedOnfidoIDVDataHolder.when(OnfidoIDVDataHolder::getRealmService).thenReturn(mockRealmService); + when(mockRealmService.getTenantUserRealm(anyInt())).thenReturn(mockUserRealm); + when(mockUserRealm.getUserStoreManager()).thenReturn(mockUniqueIDUserStoreManager); + when(mockUniqueIDUserStoreManager.getUserClaimValueWithID(TEST_USER_ID, CLAIM_URI_FIRST_NAME, null)) + .thenReturn(TEST_FIRST_NAME); + when(mockUniqueIDUserStoreManager.getUserClaimValueWithID(TEST_USER_ID, CLAIM_URI_LAST_NAME, null)) + .thenReturn(TEST_LAST_NAME); + } + + private void setupMockIdVProvider() { + + when(mockIdVProvider.getIdVProviderUuid()).thenReturn(TEST_IDV_PROVIDER_ID); + when(mockIdVProvider.isEnabled()).thenReturn(true); + + IdVConfigProperty[] configProperties = new IdVConfigProperty[4]; + configProperties[0] = createIdVConfigProperty(TOKEN, TEST_API_TOKEN, true); + configProperties[1] = createIdVConfigProperty(BASE_URL, TEST_BASE_URL, false); + configProperties[2] = createIdVConfigProperty(WEBHOOK_TOKEN, TEST_WEBHOOK_TOKEN, true); + configProperties[3] = createIdVConfigProperty(WORKFLOW_ID, TEST_WORKFLOW_ID, false); + + when(mockIdVProvider.getIdVConfigProperties()).thenReturn(configProperties); + + Map claimMappings = new HashMap<>(); + claimMappings.put(CLAIM_URI_FIRST_NAME, ONFIDO_ATTR_FIRSTNAME); + claimMappings.put(CLAIM_URI_LAST_NAME, ONFIDO_ATTR_LASTNAME); + + when(mockIdVProvider.getClaimMappings()).thenReturn(claimMappings); + } + + private IdentityVerifierData createMockIdentityVerifierData(String status) { + + IdVClaim firstNameClaim = new IdVClaim(); + firstNameClaim.setClaimUri(CLAIM_URI_FIRST_NAME); + firstNameClaim.setIsVerified(false); + + IdVClaim lastNameClaim = new IdVClaim(); + lastNameClaim.setClaimUri(CLAIM_URI_LAST_NAME); + lastNameClaim.setIsVerified(false); + + List idVClaims = new ArrayList<>(); + idVClaims.add(firstNameClaim); + idVClaims.add(lastNameClaim); + + IdVProperty statusProperty = new IdVProperty(); + statusProperty.setName(STATUS); + statusProperty.setValue(status); + + List idVProperties = new ArrayList<>(); + idVProperties.add(statusProperty); + + // Create IdentityVerifierData and set its properties + IdentityVerifierData identityVerifierData = new IdentityVerifierData(); + identityVerifierData.setIdVProviderId(TEST_IDV_PROVIDER_ID); + identityVerifierData.setIdVClaims(idVClaims); + identityVerifierData.setIdVProperties(idVProperties); + + return identityVerifierData; + } + + private List createMockFinalIdVClaims(OnfidoConstants.WorkflowRunStatus workflowRunStatus) { + + IdVClaim firstNameClaim = new IdVClaim(); + firstNameClaim.setClaimUri(CLAIM_URI_FIRST_NAME); + firstNameClaim.setId("1"); + + IdVClaim lastNameClaim = new IdVClaim(); + lastNameClaim.setClaimUri(CLAIM_URI_LAST_NAME); + firstNameClaim.setId("2"); + + List idVClaims = new ArrayList<>(); + idVClaims.add(firstNameClaim); + idVClaims.add(lastNameClaim); + + for (IdVClaim claim : idVClaims) { + claim.setUserId(TEST_USER_ID); + claim.setIsVerified(false); + claim.setMetadata(createClaimMetadata(workflowRunStatus)); + } + + return idVClaims; + } + + private IdVConfigProperty createIdVConfigProperty(String name, String value, boolean isConfidential) { + + IdVConfigProperty property = new IdVConfigProperty(); + property.setName(name); + property.setValue(value); + property.setConfidential(isConfidential); + return property; + } + + private Map createClaimMetadata(OnfidoConstants.WorkflowRunStatus workflowRunStatus) { + + Map claimMetadata = new HashMap<>(); + claimMetadata.put(ONFIDO_APPLICANT_ID, TEST_APPLICANT_ID); + claimMetadata.put(ONFIDO_WORKFLOW_RUN_ID, TEST_WORKFLOW_RUN_ID); + claimMetadata.put(ONFIDO_WORKFLOW_STATUS, workflowRunStatus.getStatus()); + return claimMetadata; + } + + @DataProvider(name = "invalidIdVProviderDataProvider") + public Object[][] invalidIdVProviderDataProvider() { + + return new Object[][]{ + {null, "Null IdV Provider"}, + {mockIdVProvider, "Disabled IdV Provider"} + }; + } + + @Test(dataProvider = "invalidIdVProviderDataProvider") + public void testInvalidIdVProvider(IdVProvider idVProvider, String scenario) throws IdVProviderMgtException { + + when(mockIdVProviderManager.getIdVProvider(anyString(), anyInt())).thenReturn(idVProvider); + if (idVProvider != null) { + when(idVProvider.isEnabled()).thenReturn(false); + } + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.INITIATED.getStatus()); + + try { + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + fail("Expected IdentityVerificationClientException was not thrown for " + scenario); + } catch (IdentityVerificationClientException e) { + assertEquals(e.getErrorCode(), ERROR_IDV_PROVIDER_INVALID_OR_DISABLED.getCode(), + "Unexpected error code for " + scenario); + assertTrue(e.getMessage().contains(ERROR_IDV_PROVIDER_INVALID_OR_DISABLED.getMessage()), + "Unexpected error message for " + scenario); + } catch (Exception e) { + fail("Unexpected exception thrown for " + scenario + ": " + e.getMessage()); + } + } + + @DataProvider(name = "invalidConfigPropertiesDataProvider") + public Object[][] invalidConfigPropertiesDataProvider() { + + return new Object[][]{ + {null, TEST_BASE_URL, TEST_WEBHOOK_TOKEN, TEST_WORKFLOW_ID, "Null API Token"}, + {TEST_API_TOKEN, null, TEST_WEBHOOK_TOKEN, TEST_WORKFLOW_ID, "Null Base URL"}, + {TEST_API_TOKEN, TEST_BASE_URL, null, TEST_WORKFLOW_ID, "Null Webhook Token"}, + {TEST_API_TOKEN, TEST_BASE_URL, TEST_WEBHOOK_TOKEN, null, "Null Workflow ID"} + }; + } + + @Test(dataProvider = "invalidConfigPropertiesDataProvider") + public void testInvalidIdVConfigProperties(String apiToken, String baseUrl, String webhookToken, + String workflowId, String scenario) { + + IdVConfigProperty[] configProperties = new IdVConfigProperty[4]; + configProperties[0] = createIdVConfigProperty(TOKEN, apiToken, true); + configProperties[1] = createIdVConfigProperty(BASE_URL, baseUrl, false); + configProperties[2] = createIdVConfigProperty(WEBHOOK_TOKEN, webhookToken, true); + configProperties[3] = createIdVConfigProperty(WORKFLOW_ID, workflowId, false); + when(mockIdVProvider.getIdVConfigProperties()).thenReturn(configProperties); + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.INITIATED.getStatus()); + + try { + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + fail("Expected IdentityVerificationClientException was not thrown for " + scenario); + } catch (IdentityVerificationClientException e) { + assertEquals(e.getErrorCode(), ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_EMPTY.getCode(), + "Unexpected error code for " + scenario); + assertTrue(e.getMessage().contains(ERROR_IDV_PROVIDER_CONFIG_PROPERTIES_EMPTY.getMessage()), + "Unexpected error message for " + scenario); + } catch (Exception e) { + fail("Unexpected exception thrown for " + scenario + ": " + e.getMessage()); + } + } + + @DataProvider(name = "invalidVerificationFlowStatusDataProvider") + public Object[][] invalidVerificationFlowStatusDataProvider() { + + return new Object[][]{ + {"INVALID_STATUS", ERROR_INVALID_ONFIDO_VERIFICATION_FLOW_STATUS, "Invalid Status"}, + {"", ERROR_VERIFICATION_FLOW_STATUS_NOT_FOUND, "Empty Status"}, + {null, ERROR_VERIFICATION_FLOW_STATUS_NOT_FOUND, "Null Status"} + }; + } + + @Test(dataProvider = "invalidVerificationFlowStatusDataProvider") + public void testInvalidVerificationFlowStatus(String status, OnfidoConstants.ErrorMessage expectedError, + String scenario) { + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData(status); + + try { + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + fail("Expected IdentityVerificationClientException was not thrown for " + scenario); + } catch (IdentityVerificationClientException e) { + assertEquals(e.getErrorCode(), expectedError.getCode(), "Unexpected error code for " + scenario); + assertEquals(e.getMessage(), expectedError.getMessage(), + "Unexpected error message for " + scenario); + } catch (Exception e) { + fail("Unexpected exception thrown for " + scenario + ": " + e.getMessage()); + } + } + + @Test + public void testVerificationRequiredClaimsNull() { + + IdVProperty statusProperty = new IdVProperty(); + statusProperty.setName(STATUS); + statusProperty.setValue(OnfidoConstants.VerificationFlowStatus.INITIATED.getStatus()); + + List idVProperties = new ArrayList<>(); + idVProperties.add(statusProperty); + + IdentityVerifierData identityVerifierData = new IdentityVerifierData(); + identityVerifierData.setIdVProviderId(TEST_IDV_PROVIDER_ID); + identityVerifierData.setIdVProperties(idVProperties); + + try { + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + fail("Expected IdentityVerificationClientException was not thrown"); + } catch (IdentityVerificationClientException e) { + assertEquals(e.getErrorCode(), ERROR_VERIFICATION_REQUIRED_CLAIMS_NOT_FOUND.getCode(), + "Unexpected error code for no verification required claims found."); + assertEquals(e.getMessage(), ERROR_VERIFICATION_REQUIRED_CLAIMS_NOT_FOUND.getMessage(), + "Unexpected error message for no verification required claims found."); + } catch (Exception e) { + fail("Unexpected exception thrown for, no verification required claims found : " + e.getMessage()); + } + } + + private JSONObject createApplicantResponse() { + + return new JSONObject() + .put(ID, TEST_APPLICANT_ID) + .put(ONFIDO_ATTR_FIRSTNAME, TEST_FIRST_NAME) + .put(ONFIDO_ATTR_LASTNAME, TEST_LAST_NAME); + } + + private JSONObject createWorkflowRunResponse(OnfidoConstants.WorkflowRunStatus workflowRunStatus) { + + return new JSONObject() + .put(ID, TEST_WORKFLOW_RUN_ID) + .put(STATUS, workflowRunStatus.getStatus()) + .put(WORKFLOW_ID, TEST_WORKFLOW_ID); + } + + private JSONObject createSDKTokenResponse() { + + return new JSONObject() + .put(TOKEN, TEST_SDK_TOKEN) + .put(APPLICANT_ID, TEST_APPLICANT_ID); + } + + private void assertCommonClaimProperties(List claims, String expectedWorkflowStatus) { + + for (IdVClaim claim : claims) { + assertFalse(claim.isVerified(), "Claim should not be verified yet"); + Map metadata = claim.getMetadata(); + assertNotNull(metadata, "Claim metadata should not be null"); + assertEquals(metadata.get(ONFIDO_APPLICANT_ID), TEST_APPLICANT_ID, "Applicant ID should match"); + assertEquals(metadata.get(ONFIDO_WORKFLOW_RUN_ID), TEST_WORKFLOW_RUN_ID, "Workflow Run ID should match"); + assertEquals(metadata.get(ONFIDO_WORKFLOW_STATUS), expectedWorkflowStatus, + "Workflow status should match expected status"); + } + } + + @Test + public void testSuccessfulIdentityVerificationInitiationWithNewApplicant() throws Exception { + + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createApplicant(any(), any())) + .thenReturn(createApplicantResponse()); + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createWorkflowRun(any(), any())) + .thenReturn(createWorkflowRunResponse( + OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT)); + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createSDKToken(any(), any())) + .thenReturn(createSDKTokenResponse()); + + // Since this is a new applicant no prior idv claims exist + when(mockIdentityVerificationManager.getIdVClaims(eq(TEST_USER_ID), eq(TEST_IDV_PROVIDER_ID), isNull(), + eq(TEST_TENANT_ID))).thenReturn(new IdVClaim[0]); + + doReturn(null).when(onfidoIdentityVerifier).storeIdVClaims(anyString(), anyList(), anyInt()); + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.INITIATED.getStatus()); + + IdentityVerifierData result = + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + + assertNotNull(result, "Result should not be null"); + assertEquals(result.getIdVProviderId(), TEST_IDV_PROVIDER_ID, "IdV Provider ID should match"); + + List resultClaims = result.getIdVClaims(); + assertNotNull(resultClaims, "Result claims should not be null"); + assertEquals(resultClaims.size(), 2, "Should have two claims"); + assertCommonClaimProperties(resultClaims, OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT.getStatus()); + + // Verify updateIdVClaim is not called at all and storeIdVClaims is called once + verify(onfidoIdentityVerifier, times(0)) + .updateIdVClaim(eq(TEST_USER_ID), any(IdVClaim.class), eq(TEST_TENANT_ID)); + verify(onfidoIdentityVerifier, times(1)) + .storeIdVClaims(eq(TEST_USER_ID), anyList(), eq(TEST_TENANT_ID)); + + } + + @Test + public void testSuccessfulIdentityVerificationInitiationWithExistingApplicant() throws Exception { + + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.updateApplicant(any(), any(), any())) + .thenReturn(createApplicantResponse()); + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createWorkflowRun(any(), any())) + .thenReturn(createWorkflowRunResponse( + OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT)); + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createSDKToken(any(), any())) + .thenReturn(createSDKTokenResponse()); + + IdVClaim firstNameClaim = new IdVClaim(); + firstNameClaim.setClaimUri(CLAIM_URI_FIRST_NAME); + firstNameClaim.setIsVerified(false); + firstNameClaim.setMetadata(createClaimMetadata(OnfidoConstants.WorkflowRunStatus.DECLINED)); + + // Since the applicant already exists and has prior identity verification claims, for simplicity, + // we'll consider only the first name-related identity verification claim as available. + when(mockIdentityVerificationManager.getIdVClaims(eq(TEST_USER_ID), eq(TEST_IDV_PROVIDER_ID), isNull(), + eq(TEST_TENANT_ID))) + .thenReturn(new IdVClaim[]{firstNameClaim}); + when(mockIdentityVerificationManager.getIdVClaim(eq(TEST_USER_ID), eq(CLAIM_URI_FIRST_NAME), + eq(TEST_IDV_PROVIDER_ID), eq(TEST_TENANT_ID))) + .thenReturn(firstNameClaim); + + doReturn(null).when(onfidoIdentityVerifier) + .updateIdVClaim(anyString(), any(IdVClaim.class), anyInt()); + doReturn(null).when(onfidoIdentityVerifier).storeIdVClaims(anyString(), anyList(), anyInt()); + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.INITIATED.getStatus()); + + IdentityVerifierData result = + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + + assertNotNull(result, "Result should not be null"); + assertEquals(result.getIdVProviderId(), TEST_IDV_PROVIDER_ID, "IdV Provider ID should match"); + + List resultClaims = result.getIdVClaims(); + assertNotNull(resultClaims, "Result claims should not be null"); + assertEquals(resultClaims.size(), 2, "Should have two claims"); + assertCommonClaimProperties(resultClaims, OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT.getStatus()); + + // Verify updateIdVClaim and storeIdVClaims are called once each + verify(onfidoIdentityVerifier, times(1)) + .updateIdVClaim(eq(TEST_USER_ID), any(IdVClaim.class), eq(TEST_TENANT_ID)); + verify(onfidoIdentityVerifier, times(1)) + .storeIdVClaims(eq(TEST_USER_ID), anyList(), eq(TEST_TENANT_ID)); + } + + @DataProvider(name = "workflowStatusDataProvider") + public Object[][] workflowStatusDataProvider() { + + return new Object[][]{ + {OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT}, + {OnfidoConstants.WorkflowRunStatus.REVIEW}, + {OnfidoConstants.WorkflowRunStatus.APPROVED}, + {OnfidoConstants.WorkflowRunStatus.DECLINED}, + {OnfidoConstants.WorkflowRunStatus.ABANDONED}, + {OnfidoConstants.WorkflowRunStatus.ERROR} + }; + } + + @Test(dataProvider = "workflowStatusDataProvider") + public void testSuccessfulIdentityVerificationCompletion(OnfidoConstants.WorkflowRunStatus workflowRunStatus) + throws Exception { + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.COMPLETED.getStatus()); + + // Mock existing IdVClaims associated with the user + List existingClaimsList = createMockFinalIdVClaims(OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT); + IdVClaim[] existingClaims = existingClaimsList.toArray(new IdVClaim[0]); + when(mockIdentityVerificationManager.getIdVClaims(anyString(), anyString(), any(), anyInt())).thenReturn( + existingClaims); + when(mockIdentityVerificationManager.getIdVClaimsByMetadata(anyString(), anyString(), anyString(), + anyInt())).thenReturn(existingClaims); + + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.getWorkflowRunStatus(any(), any())) + .thenReturn(createWorkflowRunResponse(workflowRunStatus)); + + doReturn(null).when(onfidoIdentityVerifier) + .updateIdVClaim(anyString(), any(IdVClaim.class), anyInt()); + + IdentityVerifierData result = + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + + assertNotNull(result, "Result should not be null"); + assertEquals(result.getIdVProviderId(), TEST_IDV_PROVIDER_ID, "IdV Provider ID should match"); + + List resultClaims = result.getIdVClaims(); + assertNotNull(resultClaims, "Result claims should not be null"); + assertEquals(resultClaims.size(), 2, "Should have two claims"); + + String expectedWorkflowStatus = workflowRunStatus.isEndingStatus() ? + OnfidoConstants.WorkflowRunStatus.PROCESSING.getStatus() : + workflowRunStatus.getStatus(); + assertCommonClaimProperties(resultClaims, expectedWorkflowStatus); + verify(onfidoIdentityVerifier, times(2)) + .updateIdVClaim(eq(TEST_USER_ID), any(IdVClaim.class), eq(TEST_TENANT_ID)); + } + + @Test + public void testSuccessfulIdentityVerificationReinitiation() throws Exception { + + IdentityVerifierData identityVerifierData = createMockIdentityVerifierData( + OnfidoConstants.VerificationFlowStatus.REINITIATED.getStatus()); + + // Mock existing IdVClaims associated with the user + List existingClaimsList = createMockFinalIdVClaims(OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT); + IdVClaim[] existingClaims = existingClaimsList.toArray(new IdVClaim[0]); + when(mockIdentityVerificationManager.getIdVClaims(eq(TEST_USER_ID), eq(TEST_IDV_PROVIDER_ID), isNull(), + eq(TEST_TENANT_ID))) + .thenReturn(existingClaims); + when(mockIdentityVerificationManager.getIdVClaimsByMetadata(eq(ONFIDO_WORKFLOW_RUN_ID), + eq(TEST_WORKFLOW_RUN_ID), eq(TEST_IDV_PROVIDER_ID), eq(TEST_TENANT_ID))) + .thenReturn(existingClaims); + + JSONObject sdkTokenResponse = createSDKTokenResponse(); + mockedOnfidoAPIClient.when(() -> OnfidoAPIClient.createSDKToken(any(), any())).thenReturn(sdkTokenResponse); + + IdentityVerifierData result = + onfidoIdentityVerifier.verifyIdentity(TEST_USER_ID, identityVerifierData, TEST_TENANT_ID); + + assertNotNull(result, "Result should not be null"); + assertEquals(result.getIdVProviderId(), TEST_IDV_PROVIDER_ID, "IdV Provider ID should match"); + + List resultClaims = result.getIdVClaims(); + assertNotNull(resultClaims, "Result claims should not be null"); + assertEquals(resultClaims.size(), 2, "Should have two claims"); + assertCommonClaimProperties(resultClaims, OnfidoConstants.WorkflowRunStatus.AWAITING_INPUT.getStatus()); + } +} diff --git a/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/web/OnfidoAPIClientTest.java b/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/web/OnfidoAPIClientTest.java new file mode 100644 index 0000000..27160a8 --- /dev/null +++ b/components/org.wso2.carbon.identity.verification.onfido.connector/src/test/java/org/wso2/carbon/identity/verification/onfido/connector/web/OnfidoAPIClientTest.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.verification.onfido.connector.web; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicStatusLine; +import org.json.JSONObject; +import org.mockito.MockedStatic; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.verification.onfido.connector.exception.OnfidoServerException; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mockStatic; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertThrows; +import static org.wso2.carbon.identity.verification.onfido.connector.constants.OnfidoConstants.ErrorMessage; + +public class OnfidoAPIClientTest { + + private static final String TEST_TOKEN = "test-token"; + public static final String BASE_URL = "base_url"; + public static final String TOKEN = "token"; + private static final String TEST_BASE_URL = "https://api.onfido.com/v3.6"; + private static final String FIRST_NAME = "first_name"; + private static final String LAST_NAME = "last_name"; + private static final String ID = "id"; + private static final String TEST_FIRST_NAME = "John"; + private static final String TEST_LAST_NAME = "Doe"; + private static final String WORKFLOW_ID = "workflow_id"; + private static final String TEST_WORKFLOW_ID = "test_workflow_id"; + private static final String TEST_WORKFLOW_RUN_ID = "test_workflow_run_id"; + private static final String APPLICANT_ID = "applicant_id"; + private static final String TEST_APPLICANT_ID = "test_applicant_id"; + private static final String TEST_SDK_TOKEN = "test_sdk_token"; + private static final String STATUS = "status"; + private static final String TEST_STATUS_APPROVED = "approved"; + + private Map idVConfigPropertyMap; + + private static final int[] ERROR_STATUS_CODES = + {HttpStatus.SC_BAD_REQUEST, HttpStatus.SC_UNAUTHORIZED, HttpStatus.SC_FORBIDDEN, HttpStatus.SC_NOT_FOUND, + HttpStatus.SC_INTERNAL_SERVER_ERROR}; + + @BeforeMethod + public void setUp() { + + idVConfigPropertyMap = new HashMap<>(); + idVConfigPropertyMap.put(TOKEN, TEST_TOKEN); + idVConfigPropertyMap.put(BASE_URL, TEST_BASE_URL); + } + + private JSONObject createTestIdvClaimsWithValues() { + + JSONObject idvClaimsWithValues = new JSONObject(); + idvClaimsWithValues.put(FIRST_NAME, TEST_FIRST_NAME); + idvClaimsWithValues.put(LAST_NAME, TEST_LAST_NAME); + return idvClaimsWithValues; + } + + private JSONObject createTestWorkflowRunRequestBody() { + + JSONObject workflowRunRequestBody = new JSONObject(); + workflowRunRequestBody.put(WORKFLOW_ID, TEST_WORKFLOW_ID); + workflowRunRequestBody.put(APPLICANT_ID, TEST_APPLICANT_ID); + return workflowRunRequestBody; + } + + private JSONObject createTestSdkTokenRequestBody() { + + JSONObject sdkTokenRequestBody = new JSONObject(); + sdkTokenRequestBody.put(APPLICANT_ID, TEST_APPLICANT_ID); + return sdkTokenRequestBody; + } + + @Test + public void testCreateApplicant() throws Exception { + + JSONObject idvClaimsWithValues = createTestIdvClaimsWithValues(); + + JSONObject responseJson = new JSONObject(); + responseJson.put(ID, APPLICANT_ID); + responseJson.put(FIRST_NAME, TEST_FIRST_NAME); + responseJson.put(LAST_NAME, TEST_LAST_NAME); + + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(responseJson, HttpStatus.SC_CREATED); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPost(any(), any(), any())).thenReturn(response); + + JSONObject result = OnfidoAPIClient.createApplicant(idVConfigPropertyMap, idvClaimsWithValues); + validateApplicantResponse(result, APPLICANT_ID, TEST_FIRST_NAME, TEST_LAST_NAME); + } + } + + @Test + public void testCreateWorkflowRun() throws Exception { + + JSONObject workflowRunRequestBody = createTestWorkflowRunRequestBody(); + + JSONObject responseJson = new JSONObject(); + responseJson.put(ID, TEST_WORKFLOW_RUN_ID); + responseJson.put(WORKFLOW_ID, TEST_WORKFLOW_ID); + responseJson.put(APPLICANT_ID, TEST_APPLICANT_ID); + + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(responseJson, HttpStatus.SC_CREATED); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPost(any(), any(), any())).thenReturn(response); + + JSONObject result = OnfidoAPIClient.createWorkflowRun(idVConfigPropertyMap, workflowRunRequestBody); + + assertNotNull(result, "Result JSON object should not be null"); + assertEquals(result.getString(ID), TEST_WORKFLOW_RUN_ID, "ID mismatch"); + assertEquals(result.getString(WORKFLOW_ID), TEST_WORKFLOW_ID, "Workflow ID mismatch"); + assertEquals(result.getString(APPLICANT_ID), TEST_APPLICANT_ID, "Applicant ID mismatch"); + } + } + + @Test + public void testCreateSDKToken() throws Exception { + + JSONObject sdkTokenRequestBody = createTestSdkTokenRequestBody(); + + JSONObject responseJson = new JSONObject(); + responseJson.put(TOKEN, TEST_SDK_TOKEN); + + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(responseJson, HttpStatus.SC_OK); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPost(any(), any(), any())).thenReturn(response); + + JSONObject result = OnfidoAPIClient.createSDKToken(idVConfigPropertyMap, sdkTokenRequestBody); + + assertNotNull(result, "Result JSON object should not be null"); + assertEquals(result.getString(TOKEN), TEST_SDK_TOKEN, "SDK token mismatch"); + } + } + + @Test + public void testUpdateApplicant() throws Exception { + + JSONObject idvClaimsWithValues = createTestIdvClaimsWithValues(); + + JSONObject responseJson = new JSONObject(); + responseJson.put(ID, TEST_APPLICANT_ID); + responseJson.put(FIRST_NAME, TEST_FIRST_NAME); + responseJson.put(LAST_NAME, TEST_LAST_NAME); + + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(responseJson, HttpStatus.SC_OK); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPut(any(), any(), any())).thenReturn(response); + + JSONObject result = + OnfidoAPIClient.updateApplicant(idVConfigPropertyMap, idvClaimsWithValues, TEST_APPLICANT_ID); + + validateApplicantResponse(result, TEST_APPLICANT_ID, TEST_FIRST_NAME, TEST_LAST_NAME); + } + } + + @Test + public void testGetWorkflowRunStatus() throws Exception { + + JSONObject responseJson = new JSONObject(); + responseJson.put(ID, TEST_WORKFLOW_RUN_ID); + responseJson.put(APPLICANT_ID, TEST_APPLICANT_ID); + responseJson.put(WORKFLOW_ID, TEST_WORKFLOW_ID); + responseJson.put(STATUS, TEST_STATUS_APPROVED); + + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(responseJson, HttpStatus.SC_OK); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpGet(any(), any())).thenReturn(response); + + JSONObject result = OnfidoAPIClient.getWorkflowRunStatus(idVConfigPropertyMap, TEST_WORKFLOW_RUN_ID); + + assertNotNull(result, "Result JSON object should not be null"); + assertEquals(result.getString(ID), TEST_WORKFLOW_RUN_ID, "Workflow run ID mismatch"); + assertEquals(result.getString(APPLICANT_ID), TEST_APPLICANT_ID, "Applicant ID mismatch"); + assertEquals(result.getString(WORKFLOW_ID), TEST_WORKFLOW_ID, "Workflow ID mismatch"); + assertEquals(result.getString(STATUS), TEST_STATUS_APPROVED, "Status mismatch"); + } + } + + @Test + public void testCreateApplicantWithErrorResponse() throws Exception { + + JSONObject idvClaimsWithValues = createTestIdvClaimsWithValues(); + + testErrorResponse(() -> OnfidoAPIClient.createApplicant(idVConfigPropertyMap, idvClaimsWithValues), + ErrorMessage.ERROR_CREATING_ONFIDO_APPLICANT); + } + + @Test + public void testCreateWorkflowRunWithErrorResponse() throws Exception { + + JSONObject workflowRunRequestBody = createTestWorkflowRunRequestBody(); + + testErrorResponse(() -> OnfidoAPIClient.createWorkflowRun(idVConfigPropertyMap, workflowRunRequestBody), + ErrorMessage.ERROR_CREATING_WORKFLOW_RUN); + } + + @Test + public void testCreateSDKTokenWithErrorResponse() throws Exception { + + JSONObject sdkTokenRequestBody = createTestSdkTokenRequestBody(); + + testErrorResponse(() -> OnfidoAPIClient.createSDKToken(idVConfigPropertyMap, sdkTokenRequestBody), + ErrorMessage.ERROR_GETTING_ONFIDO_SDK_TOKEN); + } + + @Test + public void testUpdateApplicantWithErrorResponse() throws Exception { + + JSONObject idvClaimsWithValues = createTestIdvClaimsWithValues(); + + testErrorResponse( + () -> OnfidoAPIClient.updateApplicant(idVConfigPropertyMap, idvClaimsWithValues, TEST_APPLICANT_ID), + ErrorMessage.ERROR_UPDATING_ONFIDO_APPLICANT); + } + + @Test + public void testGetWorkflowRunStatusWithErrorResponse() throws Exception { + + testErrorResponse(() -> OnfidoAPIClient.getWorkflowRunStatus(idVConfigPropertyMap, TEST_WORKFLOW_RUN_ID), + ErrorMessage.ERROR_GETTING_ONFIDO_WORKFLOW_STATUS); + } + + private void testErrorResponse(Callable apiCall, ErrorMessage expectedError) throws Exception { + + for (int statusCode : ERROR_STATUS_CODES) { + try (MockedStatic mockedOnfidoWebUtils = mockStatic(OnfidoWebUtils.class)) { + HttpResponse response = createMockResponse(new JSONObject(), statusCode); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPost(any(), any(), any())).thenReturn(response); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpPut(any(), any(), any())).thenReturn(response); + mockedOnfidoWebUtils.when(() -> OnfidoWebUtils.httpGet(any(), any())).thenReturn(response); + + assertThrows(OnfidoServerException.class, apiCall::call); + try { + apiCall.call(); + } catch (OnfidoServerException e) { + assertEquals(e.getErrorCode(), expectedError.getCode()); + assertEquals(e.getMessage(), String.format(expectedError.getMessage(), statusCode)); + } + } + } + } + + private HttpResponse createMockResponse(JSONObject responseJson, int statusCode) throws Exception { + + HttpResponse response = new BasicHttpResponse(new BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, null)); + response.setEntity(new StringEntity(responseJson.toString())); + return response; + } + + private void validateApplicantResponse(JSONObject result, String id, String firstName, String lastName) { + + assertNotNull(result, "Result JSON object should not be null"); + assertEquals(result.getString(ID), id, "ID mismatch"); + assertEquals(result.getString(FIRST_NAME), firstName, "First name mismatch"); + assertEquals(result.getString(LAST_NAME), lastName, "Last name mismatch"); + } +} diff --git a/pom.xml b/pom.xml index d903116..9e86d79 100644 --- a/pom.xml +++ b/pom.xml @@ -206,11 +206,23 @@ org.wso2.carbon.extension.identity.verification org.wso2.carbon.extension.identity.verification.provider ${identity.verification.version} + + + org.powermock + powermock-api-mockito2 + + org.wso2.carbon.extension.identity.verification org.wso2.carbon.extension.identity.verification.mgt ${identity.verification.version} + + + org.powermock + powermock-api-mockito2 + + org.wso2.orbit.org.apache.httpcomponents @@ -242,6 +254,31 @@ org.wso2.carbon.identity.verification.onfido.api.v1 ${project.version} + + + org.testng + testng + ${testng.version} + test + + + org.jacoco + org.jacoco.agent + runtime + ${jacoco.version} + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mockito + mockito-testng + ${mockito-testng.version} + test + @@ -408,7 +445,7 @@ 4.5.13 4.10.16 3.10 - 1.0.7 + 1.0.12 [1.7.0, 2.0.0) [1.2.0, 2.0.0) @@ -429,6 +466,12 @@ 1.6.2 2.1.1 + + 7.10.1 + 0.8.4 + 5.3.1 + 0.5.2 + 3.2.0 3.10.1 2.2