/* * Copyright (C) 2014 SCVNGR, Inc. d/b/a LevelUp * * 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.scvngr.levelup.core.net; import android.support.annotation.NonNull; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; import static com.scvngr.levelup.core.util.NullUtils.nonNullContract; import com.scvngr.levelup.core.model.ErrorFixture; import com.scvngr.levelup.core.model.Error; import com.scvngr.levelup.core.model.factory.json.ErrorJsonFactory; import com.scvngr.levelup.core.net.AbstractRequest.BadRequestException; import com.scvngr.levelup.core.net.BufferedResponse.ResponseTooLargeException; import com.scvngr.levelup.core.net.error.ErrorCode; import com.scvngr.levelup.core.net.error.ErrorObject; import com.scvngr.levelup.core.test.ParcelTestUtils; import com.scvngr.levelup.core.test.SupportAndroidTestCase; import org.json.JSONArray; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedList; import java.util.List; /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse}. */ public final class LevelUpResponseTest extends SupportAndroidTestCase { @NonNull private static final String SERVER_LEVELUP_PLATFORM = "LevelUp"; @NonNull private static final String SERVER_NOT_LEVELUP_PLATFORM = "LevelUp/Failover"; @NonNull private static final String EMPTY = ""; @NonNull private static final Error mError1 = new Error("debit_card_only", "message1", "credit_card", "property1"); @NonNull private static final Error mError2 = new Error("delinquent_bundle", "message2", "payment_token", "property2"); /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse} parceling. */ @SmallTest public void testParcelable() { final JSONArray errorArray = new JSONArray(); errorArray.put(ErrorFixture.getJsonObjectFromModel(mError1)); final LevelUpResponse response = new LevelUpResponse(errorArray.toString(), LevelUpStatus.ERROR_SERVER); final LevelUpResponse parceledResponse = ParcelTestUtils.feedThroughParceling(response); assertEquals("resulting Parcelable differs. ", response, parceledResponse); final LevelUpResponse exceptionResponse = new LevelUpResponse("test", LevelUpStatus.ERROR_SERVER); final LevelUpResponse parceledExceptionResponse = ParcelTestUtils.feedThroughParceling(exceptionResponse); assertEquals("resulting Parcelable differs. ", exceptionResponse, parceledExceptionResponse); assertEquals(exceptionResponse.getError().getMessage(), parceledExceptionResponse.getError().getMessage()); final LevelUpResponse okResponse = new LevelUpResponse("test", LevelUpStatus.OK); final LevelUpResponse parceledOkResponse = ParcelTestUtils.feedThroughParceling(okResponse); assertEquals("resulting Parcelable differs. ", okResponse, parceledOkResponse); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_okCode() { assertEquals(LevelUpStatus.OK, LevelUpResponse.mapStatus(HttpURLConnection.HTTP_OK, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link LevelUpResponse#mapStatus(int, String)} with a sub-200 response code. */ @SmallTest public void testMapStatusHttp_okCode_notFromPlatform() { assertEquals(LevelUpStatus.OK, LevelUpResponse.mapStatus(HttpURLConnection.HTTP_OK, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)} with a * sub-200 response code. */ @SmallTest public void testMapStatusHttp_errorCodeLow() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus(100, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeLow_notFromPlatform() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus(100, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)} with an * unknown code above the 2xx range. * */ @SmallTest public void testMapStatusHttp_errorCodeUnknown() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus(300, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)} with an * unknown code above the 2xx range. */ @SmallTest public void testMapStatusHttp_errorCodeUnknown_notFromPlatform() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus(300, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeNotFound() { assertEquals(LevelUpStatus.ERROR_NOT_FOUND, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_NOT_FOUND, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. 404s should * be treated as generic server failures if the response is coming from a server other than * LevelUp Platform. */ @SmallTest public void testMapStatusHttp_errorCodeNotFound_notFromPlatform() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_NOT_FOUND, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeRedirection() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_MULT_CHOICE, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeClientError() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_BAD_REQUEST, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeMaintenenace() { assertEquals(LevelUpStatus.ERROR_MAINTENANCE, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_UNAVAILABLE, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. Maintenance * mode applies even for responses not coming directly from LevelUp Platform. */ @SmallTest public void testMapStatusHttp_errorCodeMaintenenace_notFromPlatform() { assertEquals(LevelUpStatus.ERROR_MAINTENANCE, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_UNAVAILABLE, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(int, String)}. */ @SmallTest public void testMapStatusHttp_errorCodeUpgrade() { assertEquals(LevelUpStatus.UPGRADE, LevelUpResponse .mapStatus(HttpURLConnection.HTTP_NOT_IMPLEMENTED, SERVER_LEVELUP_PLATFORM)); } /** * Tests {@link LevelUpResponse#mapStatus(int, String)}. Responses not coming from LevelUp * Platform should be treated as generic errors rather than upgrade-required messages. */ @SmallTest public void testMapStatusHttp_errorCodeUpgrade_notFromLevelUp() { assertEquals(LevelUpStatus.ERROR_SERVER, LevelUpResponse.mapStatus( HttpURLConnection.HTTP_NOT_IMPLEMENTED, SERVER_NOT_LEVELUP_PLATFORM)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(Exception)}. */ @SmallTest public void testMapStatusException_tooLarge() { assertEquals(LevelUpStatus.ERROR_RESPONSE_TOO_LARGE, LevelUpResponse.mapStatus(new ResponseTooLargeException())); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(Exception)}. */ @SmallTest public void testMapStatusException_badRequest() { assertEquals(LevelUpStatus.ERROR_BAD_REQUEST, LevelUpResponse.mapStatus(new BadRequestException(""))); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(Exception)}. */ @SmallTest public void testMapStatusException_ioException() { assertEquals(LevelUpStatus.ERROR_NETWORK, LevelUpResponse.mapStatus(new IOException())); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(com.scvngr.levelup.core.net.StreamingResponse)}. * * @throws Exception if the URL in the mock object is malformed (i.e. it won't throw this) */ @SmallTest public void testMapStatusResponse_responseWithoutError() throws Exception { final StreamingResponse response = new StreamingResponse(new MockHttpUrlConnection()); assertEquals(LevelUpStatus.OK, LevelUpResponse.mapStatus(response)); } /** * Tests {@link LevelUpResponse#mapStatus(com.scvngr.levelup.core.net.StreamingResponse)}. * Tests that a response with an error doesn't use the status code to determine the status, but * uses the exception in the response object. */ @SmallTest public void testMapStatusResponse_responseWithError() { final StreamingResponse response = new StreamingResponse(new ResponseTooLargeException()); assertEquals(LevelUpStatus.ERROR_RESPONSE_TOO_LARGE, LevelUpResponse.mapStatus(response)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(com.scvngr.levelup.core.net.StreamingResponse, Exception)}. * * @throws Exception if the URL in the mock object is malformed (i.e. it won't throw this) */ @SmallTest public void testMapStatusResponseException_noError() throws Exception { final StreamingResponse response = new StreamingResponse(new MockHttpUrlConnection()); assertEquals(LevelUpStatus.OK, LevelUpResponse.mapStatus(response, null)); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(com.scvngr.levelup.core.net.StreamingResponse, Exception)}. Tests that passing an * error with the response doesn't use the status code to determine the status, but uses the * exception passed. * * @throws Exception if the URL in the mock object is malformed (i.e. it won't throw this) */ @SmallTest public void testMapStatusResponseException_withError() throws Exception { final StreamingResponse response = new StreamingResponse(new MockHttpUrlConnection()); assertEquals(LevelUpStatus.ERROR_RESPONSE_TOO_LARGE, LevelUpResponse.mapStatus(response, new ResponseTooLargeException())); } /** * Tests {@link com.scvngr.levelup.core.net.LevelUpResponse#mapStatus(com.scvngr.levelup.core.net.StreamingResponse, Exception)}. Tests that passing an * error with the response doesn't use the status code or response error to determine the * status, but uses the exception passed. */ @SmallTest public void testMapStatusResponseException_withErrorAndResponseError() { final StreamingResponse response = new StreamingResponse(new ResponseTooLargeException()); assertEquals(LevelUpStatus.ERROR_NETWORK, LevelUpResponse.mapStatus(response, new IOException())); } /** * Tests the {@link com.scvngr.levelup.core.net.LevelUpResponse#equals(Object)} and * {@link com.scvngr.levelup.core.net.LevelUpResponse#hashCode()} methods. * * @throws Exception if {@link com.scvngr.levelup.core.net.LevelUpResponseTest.MockHttpUrlConnection} * throws */ @SmallTest public void testEqualsAndHashCode() throws Exception { final StreamingResponse baseResponse = new StreamingResponse(); LevelUpResponse response1 = new LevelUpResponse(baseResponse); LevelUpResponse response2 = new LevelUpResponse(baseResponse); // Same base response should be the same MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, true); // New object should be the same. response2 = new LevelUpResponse(new StreamingResponse()); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, true); // Null shouldn't be the same MoreAsserts.checkEqualsAndHashCodeMethods(response1, null, false); // Differing data response2 = new LevelUpResponse(new StreamingResponse(new MockHttpUrlConnection())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, false); // Now have the same response data. response1 = new LevelUpResponse(new StreamingResponse(new MockHttpUrlConnection())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, true); // Reset to initial response1 = new LevelUpResponse(baseResponse); // Build response 2 from error response2 = new LevelUpResponse( new StreamingResponse(new ResponseTooLargeException())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, false); // Match it with response 1 response1 = new LevelUpResponse( new StreamingResponse(new ResponseTooLargeException())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, true); // Build response 2 from error response2 = new LevelUpResponse(new StreamingResponse(new IOException())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, false); // Match it with response 1 response1 = new LevelUpResponse(new StreamingResponse(new IOException())); MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, true); response2 = null; MoreAsserts.checkEqualsAndHashCodeMethods(response1, response2, false); } @SmallTest public void testGetError() { final JSONArray errorList = new JSONArray(); errorList.put(ErrorFixture.getJsonObjectFromModel(mError1)); errorList.put(ErrorFixture.getJsonObjectFromModel(mError2)); final LevelUpResponse response = new LevelUpResponse(nonNullContract(errorList.toString()), LevelUpStatus.ERROR_NETWORK); assertEquals(mError1, response.getServerError( nonNullContract(ErrorObject.fromString(mError1.getObject())), nonNullContract(ErrorCode.fromString(mError1.getCode())))); assertEquals(mError2, response.getServerError( nonNullContract(ErrorObject.fromString(mError2.getObject())), nonNullContract(ErrorCode.fromString(mError2.getCode())))); assertNull(response.getServerError( nonNullContract(ErrorObject.fromString(mError1.getObject())), nonNullContract(ErrorCode.fromString(mError2.getCode())))); assertNull(response.getServerError( nonNullContract(ErrorObject.fromString(mError2.getObject())), nonNullContract(ErrorCode.fromString(mError1.getCode())))); } @SmallTest public void testGetErrorList() { final List<Error> expectedErrorList = new LinkedList<Error>(); expectedErrorList.add(mError1); expectedErrorList.add(mError2); final JSONArray errorList = new JSONArray(); errorList.put(ErrorFixture.getJsonObjectFromModel(mError1)); errorList.put(ErrorFixture.getJsonObjectFromModel(mError2)); final LevelUpResponse response = new LevelUpResponse(nonNullContract(errorList.toString()), LevelUpStatus.ERROR_NETWORK); assertEquals(expectedErrorList, response.getServerErrors()); // Invalid errors should not throw exceptions. final LevelUpResponse responseNoErrors = new LevelUpResponse(EMPTY, LevelUpStatus.ERROR_SERVER); final List<Error> emptyList = responseNoErrors.getServerErrors(); assertTrue(emptyList.isEmpty()); } @SmallTest public void testParsingInvalidError_containsErrorException() { final LevelUpResponse response = new LevelUpResponse(nonNullContract("<html />"), LevelUpStatus.ERROR_NETWORK); assertTrue(response.getError() instanceof IOException); } /** * Wrapped {@link java.net.HttpURLConnection} to return * {@link java.net.HttpURLConnection#HTTP_OK} for * {@link java.net.HttpURLConnection#getResponseCode()}. */ private static class MockHttpUrlConnection extends HttpURLConnection { /** * Constructor. * * @throws java.net.MalformedURLException if the URL is malformed */ public MockHttpUrlConnection() throws MalformedURLException { super(new URL("http://www.example.com")); } @Override public int getResponseCode() throws IOException { return HttpURLConnection.HTTP_OK; } @Override public InputStream getInputStream() throws IOException { return null; } @Override public void disconnect() { // do nothing } @Override public boolean usingProxy() { return false; } @Override public void connect() throws IOException { // do nothing } } }