/* * 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 org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import bolts.Task; import static org.hamcrest.CoreMatchers.instanceOf; 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.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; // For org.json @RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = TestHelper.ROBOLECTRIC_SDK_VERSION) public class ParseFileControllerTest { @Before public void setUp() throws MalformedURLException { ParseRESTCommand.server = new URL("https://api.parse.com/1"); } @After public void tearDown() { // TODO(grantland): Remove once we no longer rely on retry logic. ParseRequest.setDefaultInitialRetryDelay(ParseRequest.DEFAULT_INITIAL_RETRY_DELAY); ParseRESTCommand.server = null; } @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void testGetCacheFile() throws Exception { File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(null, root); ParseFile.State state = new ParseFile.State.Builder().name("test_file").build(); File cacheFile = controller.getCacheFile(state); assertEquals(new File(root, "test_file"), cacheFile); } @Test public void testIsDataAvailable() throws IOException { File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(null, root); temporaryFolder.newFile("test_file"); ParseFile.State state = new ParseFile.State.Builder().name("test_file").build(); assertTrue(controller.isDataAvailable(state)); } @Test public void testClearCache() throws IOException { File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(null, root); File file1 = temporaryFolder.newFile("test_file_1"); File file2 = temporaryFolder.newFile("test_file_2"); controller.clearCache(); assertFalse(file1.exists()); assertFalse(file2.exists()); } //region testSaveAsync @Test public void testSaveAsyncRequest() throws Exception { // TODO(grantland): Verify proper command is constructed } @Test public void testSaveAsyncNotDirty() throws Exception { ParseHttpClient restClient = mock(ParseHttpClient.class); ParseFileController controller = new ParseFileController(restClient, null); ParseFile.State state = new ParseFile.State.Builder() .url("http://example.com") .build(); Task<ParseFile.State> task = controller.saveAsync(state, (byte[])null, null, null, null); task.waitForCompletion(); verify(restClient, times(0)).execute(any(ParseHttpRequest.class)); assertFalse(task.isFaulted()); assertFalse(task.isCancelled()); assertSame(state, task.getResult()); } @Test public void testSaveAsyncAlreadyCancelled() throws Exception { ParseHttpClient restClient = mock(ParseHttpClient.class); ParseFileController controller = new ParseFileController(restClient, null); ParseFile.State state = new ParseFile.State.Builder().build(); Task<Void> cancellationToken = Task.cancelled(); Task<ParseFile.State> task = controller.saveAsync(state, (byte[])null, null, null, cancellationToken); task.waitForCompletion(); verify(restClient, times(0)).execute(any(ParseHttpRequest.class)); assertTrue(task.isCancelled()); } @Test public void testSaveAsyncSuccessWithByteArray() throws Exception { JSONObject json = new JSONObject(); json.put("name", "new_file_name"); json.put("url", "http://example.com"); String content = json.toString(); ParseHttpResponse mockResponse = new ParseHttpResponse.Builder() .setStatusCode(200) .setTotalSize((long) content.length()) .setContent(new ByteArrayInputStream(content.getBytes())) .build(); ParseHttpClient restClient = mock(ParseHttpClient.class); when(restClient.execute(any(ParseHttpRequest.class))).thenReturn(mockResponse); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(restClient, root); byte[] data = "hello".getBytes(); ParseFile.State state = new ParseFile.State.Builder() .name("file_name") .mimeType("mime_type") .build(); Task<ParseFile.State> task = controller.saveAsync(state, data, null, null, null); ParseFile.State result = ParseTaskUtils.wait(task); verify(restClient, times(1)).execute(any(ParseHttpRequest.class)); assertEquals("new_file_name", result.name()); assertEquals("http://example.com", result.url()); File file = new File(root, "new_file_name"); assertTrue(file.exists()); assertEquals("hello", ParseFileUtils.readFileToString(file, "UTF-8")); } @Test public void testSaveAsyncSuccessWithFile() throws Exception { JSONObject json = new JSONObject(); json.put("name", "new_file_name"); json.put("url", "http://example.com"); String content = json.toString(); ParseHttpResponse mockResponse = new ParseHttpResponse.Builder() .setStatusCode(200) .setTotalSize((long) content.length()) .setContent(new ByteArrayInputStream(content.getBytes())) .build(); ParseHttpClient restClient = mock(ParseHttpClient.class); when(restClient.execute(any(ParseHttpRequest.class))).thenReturn(mockResponse); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(restClient, root); File file = new File(root, "test"); ParseFileUtils.writeStringToFile(file, "content", "UTF-8"); ParseFile.State state = new ParseFile.State.Builder() .name("file_name") .mimeType("mime_type") .build(); Task<ParseFile.State> task = controller.saveAsync(state, file, null, null, null); ParseFile.State result = ParseTaskUtils.wait(task); verify(restClient, times(1)).execute(any(ParseHttpRequest.class)); assertEquals("new_file_name", result.name()); assertEquals("http://example.com", result.url()); File cachedFile = new File(root, "new_file_name"); assertTrue(cachedFile.exists()); assertTrue(file.exists()); assertEquals("content", ParseFileUtils.readFileToString(cachedFile, "UTF-8")); } @Test public void testSaveAsyncFailureWithByteArray() throws Exception { // TODO(grantland): Remove once we no longer rely on retry logic. ParseRequest.setDefaultInitialRetryDelay(1L); ParseHttpClient restClient = mock(ParseHttpClient.class); when(restClient.execute(any(ParseHttpRequest.class))).thenThrow(new IOException()); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(restClient, root); byte[] data = "hello".getBytes(); ParseFile.State state = new ParseFile.State.Builder() .build(); Task<ParseFile.State> task = controller.saveAsync(state, data, null, null, null); task.waitForCompletion(); // TODO(grantland): Abstract out command runner so we don't have to account for retries. verify(restClient, times(5)).execute(any(ParseHttpRequest.class)); assertTrue(task.isFaulted()); Exception error = task.getError(); assertThat(error, instanceOf(ParseException.class)); assertEquals(ParseException.CONNECTION_FAILED, ((ParseException) error).getCode()); assertEquals(0, root.listFiles().length); } @Test public void testSaveAsyncFailureWithFile() throws Exception { // TODO(grantland): Remove once we no longer rely on retry logic. ParseRequest.setDefaultInitialRetryDelay(1L); ParseHttpClient restClient = mock(ParseHttpClient.class); when(restClient.execute(any(ParseHttpRequest.class))).thenThrow(new IOException()); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(restClient, root); File file = temporaryFolder.newFile("test"); ParseFile.State state = new ParseFile.State.Builder() .build(); Task<ParseFile.State> task = controller.saveAsync(state, file, null, null, null); task.waitForCompletion(); // TODO(grantland): Abstract out command runner so we don't have to account for retries. verify(restClient, times(5)).execute(any(ParseHttpRequest.class)); assertTrue(task.isFaulted()); Exception error = task.getError(); assertThat(error, instanceOf(ParseException.class)); assertEquals(ParseException.CONNECTION_FAILED, ((ParseException) error).getCode()); // Make sure the original file is not deleted and there is no cache file in the folder assertEquals(1, root.listFiles().length); assertTrue(file.exists()); } //endregion //region testFetchAsync @Test public void testFetchAsyncRequest() { // TODO(grantland): Verify proper command is constructed } @Test public void testFetchAsyncAlreadyCancelled() throws Exception{ ParseHttpClient fileClient = mock(ParseHttpClient.class); ParseFileController controller = new ParseFileController(null, null).fileClient(fileClient); ParseFile.State state = new ParseFile.State.Builder().build(); Task<Void> cancellationToken = Task.cancelled(); Task<File> task = controller.fetchAsync(state, null, null, cancellationToken); task.waitForCompletion(); verify(fileClient, times(0)).execute(any(ParseHttpRequest.class)); assertTrue(task.isCancelled()); } @Test public void testFetchAsyncCached() throws Exception { ParseHttpClient fileClient = mock(ParseHttpClient.class); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(null, root).fileClient(fileClient); File file = new File(root, "cached_file_name"); ParseFileUtils.writeStringToFile(file, "hello", "UTF-8"); ParseFile.State state = new ParseFile.State.Builder() .name("cached_file_name") .build(); Task<File> task = controller.fetchAsync(state, null, null, null); File result = ParseTaskUtils.wait(task); verify(fileClient, times(0)).execute(any(ParseHttpRequest.class)); assertEquals(file, result); assertEquals("hello", ParseFileUtils.readFileToString(result, "UTF-8")); } @Test public void testFetchAsyncSuccess() throws Exception { byte[] data = "hello".getBytes(); ParseHttpResponse mockResponse = new ParseHttpResponse.Builder() .setStatusCode(200) .setTotalSize((long) data.length) .setContent(new ByteArrayInputStream(data)) .build(); ParseHttpClient fileClient = mock(ParseHttpClient.class); when(fileClient.execute(any(ParseHttpRequest.class))).thenReturn(mockResponse); // Make sure cache dir does not exist File root = new File(temporaryFolder.getRoot(), "cache"); assertFalse(root.exists()); ParseFileController controller = new ParseFileController(null, root).fileClient(fileClient); ParseFile.State state = new ParseFile.State.Builder() .name("file_name") .url("url") .build(); Task<File> task = controller.fetchAsync(state, null, null, null); File result = ParseTaskUtils.wait(task); verify(fileClient, times(1)).execute(any(ParseHttpRequest.class)); assertTrue(result.exists()); assertEquals("hello", ParseFileUtils.readFileToString(result, "UTF-8")); assertFalse(controller.getTempFile(state).exists()); } @Test public void testFetchAsyncFailure() throws Exception { // TODO(grantland): Remove once we no longer rely on retry logic. ParseRequest.setDefaultInitialRetryDelay(1L); ParseHttpClient fileClient = mock(ParseHttpClient.class); when(fileClient.execute(any(ParseHttpRequest.class))).thenThrow(new IOException()); File root = temporaryFolder.getRoot(); ParseFileController controller = new ParseFileController(null, root).fileClient(fileClient); // We need to set url to make getTempFile() work and check it ParseFile.State state = new ParseFile.State.Builder() .url("test") .build(); Task<File> task = controller.fetchAsync(state, null, null, null); task.waitForCompletion(); // TODO(grantland): Abstract out command runner so we don't have to account for retries. verify(fileClient, times(5)).execute(any(ParseHttpRequest.class)); assertTrue(task.isFaulted()); Exception error = task.getError(); assertThat(error, instanceOf(ParseException.class)); assertEquals(ParseException.CONNECTION_FAILED, ((ParseException) error).getCode()); assertEquals(0, root.listFiles().length); assertFalse(controller.getTempFile(state).exists()); } //endregion }