/*
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.parse;
import com.parse.http.ParseHttpRequest;
import com.parse.http.ParseHttpResponse;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import bolts.Task;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ParseConfigControllerTest {
@Before
public void setUp() throws MalformedURLException {
ParseRESTCommand.server = new URL("https://api.parse.com/1");
}
@After
public void tearDown() {
ParseRESTCommand.server = null;
}
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
//region testConstructor
@Test
public void testConstructor() throws Exception {
ParseHttpClient restClient = mock(ParseHttpClient.class);
ParseCurrentConfigController currentConfigController = mock(ParseCurrentConfigController.class);
ParseConfigController controller = new ParseConfigController(restClient,
currentConfigController);
assertSame(currentConfigController, controller.getCurrentConfigController());
}
//endregion
//region testGetAsync
@Test
public void testGetAsyncSuccess() throws Exception {
// Construct sample response from server
final Date date = new Date();
final ParseFile file = new ParseFile(
new ParseFile.State.Builder().name("image.png").url("http://yarr.com/image.png").build());
final ParseGeoPoint geoPoint = new ParseGeoPoint(44.484, 26.029);
final List<Object> list = new ArrayList<Object>() {{
add("foo");
add("bar");
add("baz");
}};
final Map<String, Object> map = new HashMap<String, Object>() {{
put("first", "foo");
put("second", "bar");
put("third", "baz");
}};
final Map<String, Object> sampleConfigParameters = new HashMap<String, Object>() {{
put("string", "value");
put("int", 42);
put("double", 0.2778);
put("trueBool", true);
put("falseBool", false);
put("date", date);
put("file", file);
put("geoPoint", geoPoint);
put("array", list);
put("object", map);
}};
JSONObject responseJson = new JSONObject() {{
put("params", NoObjectsEncoder.get().encode(sampleConfigParameters));
}};
// Make ParseConfigController and call getAsync
ParseHttpClient restClient = mockParseHttpClientWithResponse(responseJson, 200, "OK");
ParseCurrentConfigController currentConfigController = mockParseCurrentConfigController();
ParseConfigController configController =
new ParseConfigController(restClient, currentConfigController);
Task<ParseConfig> configTask = configController.getAsync(null);
ParseConfig config = ParseTaskUtils.wait(configTask);
// Verify httpClient is called
verify(restClient, times(1)).execute(any(ParseHttpRequest.class));
// Verify currentConfigController is called
verify(currentConfigController, times(1)).setCurrentConfigAsync(eq(config));
// Verify ParseConfig we get, do not use ParseConfig getter to keep test separately
Map<String, Object> paramsAgain = config.getParams();
assertEquals(10, paramsAgain.size());
assertEquals("value", paramsAgain.get("string"));
assertEquals(42, paramsAgain.get("int"));
assertEquals(0.2778, paramsAgain.get("double"));
assertTrue((Boolean) paramsAgain.get("trueBool"));
assertFalse((Boolean) paramsAgain.get("falseBool"));
assertEquals(date, paramsAgain.get("date"));
ParseFile fileAgain = (ParseFile) paramsAgain.get("file");
assertEquals(file.getUrl(), fileAgain.getUrl());
assertEquals(file.getName(), fileAgain.getName());
ParseGeoPoint geoPointAgain = (ParseGeoPoint) paramsAgain.get("geoPoint");
assertEquals(geoPoint.getLatitude(), geoPointAgain.getLatitude(), 0.0000001);
assertEquals(geoPoint.getLongitude(), geoPointAgain.getLongitude(), 0.0000001);
List<Object> listAgain = (List<Object>) paramsAgain.get("array");
assertArrayEquals(list.toArray(), listAgain.toArray());
Map<String, Object> mapAgain = (Map<String, Object>) paramsAgain.get("object");
assertEquals(map.size(), mapAgain.size());
for (Map.Entry<String, Object> entry : map.entrySet()) {
assertEquals(entry.getValue(), mapAgain.get(entry.getKey()));
}
}
@Test
public void testGetAsyncFailureWithConnectionFailure() throws Exception {
// TODO(mengyan): Remove once we no longer rely on retry logic.
ParseRequest.setDefaultInitialRetryDelay(1L);
// Make ParseConfigController and call getAsync
ParseHttpClient restClient = mock(ParseHttpClient.class);
when(restClient.execute(any(ParseHttpRequest.class))).thenThrow(new IOException());
ParseCurrentConfigController currentConfigController = mockParseCurrentConfigController();
ParseConfigController configController =
new ParseConfigController(restClient, currentConfigController);
Task<ParseConfig> configTask = configController.getAsync(null);
// Do not use ParseTaskUtils.wait() since we do not want to throw the exception
configTask.waitForCompletion();
// Verify httpClient is tried enough times
// TODO(mengyan): Abstract out command runner so we don't have to account for retries.
verify(restClient, times(5)).execute(any(ParseHttpRequest.class));
assertTrue(configTask.isFaulted());
Exception error = configTask.getError();
assertThat(error, instanceOf(ParseException.class));
assertEquals(ParseException.CONNECTION_FAILED, ((ParseException) error).getCode());
// Verify currentConfigController is not called
verify(currentConfigController, times(0)).setCurrentConfigAsync(any(ParseConfig.class));
}
//endregion
private ParseCurrentConfigController mockParseCurrentConfigController() {
ParseCurrentConfigController currentConfigController = mock(ParseCurrentConfigController.class);
when(currentConfigController.setCurrentConfigAsync(any(ParseConfig.class)))
.thenReturn(Task.<Void>forResult(null));
return currentConfigController;
}
//TODO(mengyan) Create unit test helper and move all similar methods to the class
private ParseHttpClient mockParseHttpClientWithResponse(JSONObject content, int statusCode,
String reasonPhrase) throws IOException {
byte[] contentBytes = content.toString().getBytes();
ParseHttpResponse response = new ParseHttpResponse.Builder()
.setContent(new ByteArrayInputStream(contentBytes))
.setStatusCode(statusCode)
.setTotalSize(contentBytes.length)
.setContentType("application/json")
.build();
ParseHttpClient client = mock(ParseHttpClient.class);
when(client.execute(any(ParseHttpRequest.class))).thenReturn(response);
return client;
}
}