diff --git a/payments-api/pom.xml b/payments-api/pom.xml index 459b6800..97f49d21 100644 --- a/payments-api/pom.xml +++ b/payments-api/pom.xml @@ -1,12 +1,12 @@ org.apache.maven.plugins @@ -135,6 +141,16 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M3 + + + -Djdk.attach.allowAttachSelf + + + diff --git a/payments-api/src/test/java/com/intuit/payment/services/base/ServiceBaseTest.java b/payments-api/src/test/java/com/intuit/payment/services/base/ServiceBaseTest.java new file mode 100644 index 00000000..278b8525 --- /dev/null +++ b/payments-api/src/test/java/com/intuit/payment/services/base/ServiceBaseTest.java @@ -0,0 +1,412 @@ +/******************************************************************************* + * Copyright (c) 2017 Intuit + * + * Licensed 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 com.intuit.payment.services.base; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.intuit.payment.config.RequestContext; +import com.intuit.payment.data.ECheck; +import com.intuit.payment.data.Entity; +import com.intuit.payment.data.Error; +import com.intuit.payment.data.Errors; +import com.intuit.payment.exception.AuthorizationException; +import com.intuit.payment.exception.BadRequestException; +import com.intuit.payment.exception.BaseException; +import com.intuit.payment.exception.ServiceException; +import com.intuit.payment.http.HttpRequestClient; +import com.intuit.payment.http.MethodType; +import com.intuit.payment.http.Request; +import com.intuit.payment.http.Response; +import mockit.Mock; +import mockit.MockUp; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.math.BigDecimal; + +import static org.testng.Assert.fail; + +public class ServiceBaseTest { + private ServiceBase serviceBase; + private MockHttpRequestClient mockHttpRequestClient; + + @BeforeClass + public void setup() { + serviceBase = new ServiceBase(); + } + + @BeforeMethod + public void setupMocks() { + // Each test in this class will use the MockHttpRequestClient to mock the response for "makeRequest()" method + mockHttpRequestClient = new MockHttpRequestClient(); + } + + @Test + public void testSendRequest_successPostRequest() throws BaseException { + String eCheckId = "5"; + String eCheckNumber = "1234"; + String eCheckAmount = "600.60"; + + int httpStatusCode = 200; + String tid = "test-tid-123"; + + String expectedPostJsonContent = "{\n" + + " \"amount\" : " + eCheckAmount + ",\n" + + " \"checkNumber\" : \"" + eCheckNumber + "\"\n" + + "}"; + + String mockResponseContent = "{\"id\":\"" + eCheckId + "\",\"amount\":\"" + eCheckAmount + "\",\"checkNumber\":\"" + eCheckNumber + "\"}"; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + ECheck eCheck = new ECheck.Builder() + .checkNumber(eCheckNumber) + .amount(new BigDecimal(eCheckAmount)) + .build(); + + Request serviceRequest = new Request.RequestBuilder(MethodType.POST, "https://ap.intuit.com") + .context(new RequestContext()) + .requestObject(eCheck) + .typeReference(new TypeReference() {}) + .build(); + + + Response actualResponse = serviceBase.sendRequest(serviceRequest); + ECheck actualResponseObject = (ECheck) actualResponse.getResponseObject(); + Request serviceRequestReceivedByClient = mockHttpRequestClient.getServiceRequestReceived(); + + // Verify that the Echeck object is serialized and sent correctly to "HttpRequestClient" + Assert.assertEquals(serviceRequestReceivedByClient.getPostJson(), expectedPostJsonContent); + + Assert.assertEquals(actualResponseObject.getAmount().toString(), eCheckAmount); + Assert.assertEquals(actualResponseObject.getCheckNumber(), eCheckNumber); + Assert.assertEquals(actualResponseObject.getId(), eCheckId); + + Assert.assertEquals(actualResponse.getContent(), mockResponseContent); + Assert.assertEquals(actualResponse.getIntuit_tid(), tid); + } + + /** + * Tests that the "sendRequest" function is able to serialize the Response content to the expected object (ECheck in this test) + * during a GET request + */ + @Test + public void testSendRequest_successGetRequest() throws BaseException { + String eCheckId = "5"; + String eCheckBalance = "505.50"; + + int httpStatusCode = 200; + String tid = "test-tid-123"; + + String mockResponseContent = "{\"id\":\"" + eCheckId + "\",\"amount\":\"" + eCheckBalance + "\"}"; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + Response actualResponse = serviceBase.sendRequest(serviceRequest); + ECheck actualResponseObject = (ECheck) actualResponse.getResponseObject(); + + Assert.assertEquals(actualResponseObject.getId(), eCheckId); + Assert.assertEquals(actualResponseObject.getAmount().toString(), eCheckBalance); + + Assert.assertEquals(actualResponse.getContent(), mockResponseContent); + Assert.assertEquals(actualResponse.getIntuit_tid(), tid); + } + + /** + * Tests the case where response payload is empty + */ + @Test + public void testSendRequest_successEmptyResponsePayload() throws BaseException { + int httpStatusCode = 200; + String tid = "test-tid-123"; + + String mockResponseContent = ""; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .build(); + + Response actualResponse = serviceBase.sendRequest(serviceRequest); + + Assert.assertNull(actualResponse.getResponseObject()); + Assert.assertEquals(actualResponse.getContent(), mockResponseContent); + Assert.assertEquals(actualResponse.getIntuit_tid(), tid); + } + + /** + * Tests that a BaseException is thrown when the response is null for the service request + */ + @Test(expectedExceptions = BaseException.class, + expectedExceptionsMessageRegExp = "Unexpected Error , service response object was null ") + public void testSendRequest_nullResponse() throws BaseException { + mockHttpRequestClient.setMockResponse(null); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "https://api.intuit.com/quickbooks/v4/payments/") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + serviceBase.sendRequest(serviceRequest); + } + + /** + * Tests the case where the HTTP status code is 404 and the call to httpRequestClient.makeRequest() + * sets the errors in the response content and deserialization of the error goes through fine + */ + @Test + public void testSendRequest_errorPageNotFound() throws BaseException { + int httpStatusCode = 404; + String tid = "test-tid-123"; + String errorDetail = "Could not find requested page"; + + String mockErrorResponseContent = "{\"errors\":[{\"code\":\"" + httpStatusCode + "\",\"detail\":\"" + errorDetail + "\"}]}"; + Response mockResponse = new Response(httpStatusCode, mockErrorResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since BadRequestException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected BadRequestException to be thrown"); + } catch (BadRequestException e) { + Errors errors = e.getErrors(); + Assert.assertNotNull(errors); + Assert.assertEquals(errors.getErrorList().size(), 1); + + Error error = errors.getErrorList().get(0); + Assert.assertEquals(error.getCode(), String.valueOf(httpStatusCode)); + Assert.assertEquals(error.getDetail(), errorDetail); + } + } + + /** + * Tests the case where the HTTP status code is 404 and the call to httpRequestClient.makeRequest() + * sets the errors in the response content but the deserialization of the response error content fails + */ + @Test + public void testSendRequest_errorDeserializingErrorResponse() throws BaseException { + int httpStatusCode = 404; + String tid = "test-tid-123"; + String errorDetail = "Could not find requested page"; + + // This mock error response content has an invalid format and fields, to test the scenario when deserialization fails + String mockErrorResponseContent = "\"errorsInvalid\":[{\"codeInvalid\":\"" + httpStatusCode + "\",\"detailInvalid\":\"" + errorDetail + "\"}]"; + Response mockResponse = new Response(httpStatusCode, mockErrorResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since BadRequestException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected BadRequestException to be thrown"); + } catch (BadRequestException e) { + Errors errors = e.getErrors(); + assertErrorsInException(errors, httpStatusCode, mockErrorResponseContent); + } + } + + /** + * Tests the case where the HTTP status code is 401 but there is no Error set in the response content from the + * call to httpRequestClient.makeRequest() + */ + @Test + public void testSendRequest_errorUnauthorized() throws BaseException { + int httpStatusCode = 401; + String tid = "test-tid-123"; + + String mockResponseContent = ""; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since AuthorizationException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected AuthorizationException to be thrown"); + } catch (AuthorizationException authEx) { + Errors errors = authEx.getErrors(); + assertErrorsInException(errors, httpStatusCode, mockResponseContent);} + } + + /** + * Tests the case where the HTTP status code is 400 but there is no Error set in the response content from the + * call to httpRequestClient.makeRequest() + */ + @Test + public void testSendRequest_errorBadRequest() throws BaseException { + int httpStatusCode = 400; + String tid = "test-tid-123"; + + String mockResponseContent = ""; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since BadRequestException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected BadRequestException to be thrown"); + } catch (BadRequestException badRequestEx) { + Errors errors = badRequestEx.getErrors(); + assertErrorsInException(errors, httpStatusCode, mockResponseContent); + } + } + + /** + * Tests the case where the HTTP status code is 500 but there is no Error set in the response content from the + * call to httpRequestClient.makeRequest() + */ + @Test + public void testSendRequest_errorInternalServerError() throws BaseException { + int httpStatusCode = 500; + String tid = "test-tid-123"; + + String mockResponseContent = ""; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since ServiceException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected ServiceException to be thrown"); + } catch (ServiceException serviceEx) { + Errors errors = serviceEx.getErrors(); + assertErrorsInException(errors, httpStatusCode, mockResponseContent); + } + } + + /** + * Tests the case where the HTTP status code is 301 but there is no Error set in the response content from the + * call to httpRequestClient.makeRequest() + */ + @Test + public void testSendRequest_errorMovedPermanently() { + int httpStatusCode = 301; + String tid = "test-tid-123"; + + String mockResponseContent = ""; + Response mockResponse = new Response(httpStatusCode, mockResponseContent, tid); + mockHttpRequestClient.setMockResponse(mockResponse); + + Request serviceRequest = new Request.RequestBuilder(MethodType.GET, "url") + .context(new RequestContext()) + .typeReference(new TypeReference() {}) + .build(); + + // Since BaseException is a custom exception and we need to assert the Errors + // inside the exception object, we're not using "expectedExceptions" + try { + serviceBase.sendRequest(serviceRequest); + fail("Expected BaseException to be thrown"); + } catch (BaseException baseEx) { + Errors errors = baseEx.getErrors(); + assertErrorsInException(errors, httpStatusCode, mockResponseContent); + } + } + + /** + * Tests that intuit_tid and requestId is being set on the entity + */ + @Test + public void testPrepareResponse_success() { + String expectedRequestId = "146-request-id"; + String expectedTid = "112-tid"; + + RequestContext requestContext = new RequestContext(); + requestContext.setRequestId(expectedRequestId); + + Request request = new Request.RequestBuilder(MethodType.GET, "url") + .context(requestContext) + .build(); + + Response response = new Response(200, "{}", expectedTid); + + Entity entity = new Entity() {}; + + serviceBase.prepareResponse(request, response, entity); + + Assert.assertEquals(entity.getIntuit_tid(), expectedTid); + Assert.assertEquals(entity.getRequestId(), expectedRequestId); + } + + private void assertErrorsInException(Errors actualErrors, int expectedHttpStatusCode, String expectedErrorContent) { + Assert.assertNotNull(actualErrors); + Assert.assertEquals(actualErrors.getErrorList().size(), 1); + + Error error = actualErrors.getErrorList().get(0); + Assert.assertEquals(error.getCode(), "HttpStatusCode-" + expectedHttpStatusCode); + Assert.assertEquals(error.getDetail(), "ResponsePayload: " + expectedErrorContent); + } + + /** + * This is a mock implementation of the HttpRequestClient to mock the response from this class + * Only the "makeRequest()" method is mocked. Other methods can be mocked if necessary. + */ + private final class MockHttpRequestClient extends MockUp { + + private Response mockResponse; + private Request serviceRequestReceived; // Used for asserting the request that was received + + void setMockResponse(Response mockResponse) { + this.mockResponse = mockResponse; + } + + Request getServiceRequestReceived() { + return serviceRequestReceived; + } + + @Mock + public Response makeRequest(Request serviceRequest) throws BadRequestException { + this.serviceRequestReceived = serviceRequest; // Used for asserting the values in the request + + return this.mockResponse; + } + } +}