package com.rapidftr.task; import android.app.Notification; import android.app.NotificationManager; import android.content.SharedPreferences; import android.view.Menu; import android.view.MenuItem; import com.rapidftr.CustomTestRunner; import com.rapidftr.R; import com.rapidftr.RapidFtrApplication; import com.rapidftr.activity.RapidFtrActivity; import com.rapidftr.model.Child; import com.rapidftr.model.User; import com.rapidftr.repository.ChildRepository; import com.rapidftr.roboelectric.shadows.ShadowTaskStackBuilder; import com.rapidftr.service.*; import org.apache.http.HttpException; import org.json.JSONException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Matchers; import org.mockito.Mock; import org.robolectric.Robolectric; import org.robolectric.annotation.Config; import java.io.IOException; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import static com.google.common.collect.Lists.newArrayList; import static org.junit.Assert.assertEquals; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @RunWith(CustomTestRunner.class) @Config(shadows = {ShadowTaskStackBuilder.class}) public class SyncAllDataAsyncTaskTest { @Mock private ChildSyncService childSyncService; @Mock private ChildRepository childRepository; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private RapidFtrActivity rapidFtrActivity; @Mock private NotificationManager notificationManager; @Mock private Menu menu; @Mock private MenuItem syncAll; @Mock private MenuItem cancelSyncAll; @Mock private User currentUser; @Mock private FormService formService; @Mock private DeviceService deviceService; private RapidFtrApplication application; private SyncAllDataAsyncTask syncAllDataAsyncTask; @Before public void setUp() throws Exception { initMocks(this); doReturn(syncAll).when(menu).getItem(0); doReturn(cancelSyncAll).when(menu).getItem(1); doReturn(menu).when(rapidFtrActivity).getMenu(); given(rapidFtrActivity.getSystemService(Matchers.<String>any())).willReturn(notificationManager); application = mock(RapidFtrApplication.class); doReturn(application).when(rapidFtrActivity).getApplication(); syncAllDataAsyncTask = new SyncAllDataAsyncTask(formService, childSyncService, deviceService, childRepository, currentUser); doReturn("Notify").when(rapidFtrActivity).getString(any(Integer.class)); doReturn("Notify").when(application).getString(any(Integer.class)); doReturn(new User("foo", "bar")).when(application).getCurrentUser(); doReturn("Child Synchronization").when(childSyncService).getNotificationTitle(); } @Test public void shouldSyncFormsAndChildren() throws Exception { Child child1 = mock(Child.class); Child child2 = mock(Child.class); given(childRepository.toBeSynced()).willReturn(newArrayList(child1, child2)); syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.execute(); verify(formService).downloadPublishedFormSections(); verify(childSyncService).sync(child1, currentUser); verify(childSyncService).sync(child2, currentUser); } @Test public void shouldNotSyncFormsIfTaskIsCancelled() throws Exception { syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask = spy(syncAllDataAsyncTask); doReturn(true).when(syncAllDataAsyncTask).isCancelled(); syncAllDataAsyncTask.doInBackground(); verify(formService, never()).downloadPublishedFormSections(); } @Test public void shouldNotSyncChildrenIfCancelled() throws Exception { Child child1 = mock(Child.class); Child child2 = mock(Child.class); given(childRepository.toBeSynced()).willReturn(newArrayList(child1, child2)); syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask = spy(syncAllDataAsyncTask); doReturn(true).when(syncAllDataAsyncTask).isCancelled(); syncAllDataAsyncTask.onPreExecute(); syncAllDataAsyncTask.doInBackground(); verify(childSyncService, never()).sync(child1, currentUser); verify(childSyncService, never()).sync(child2, currentUser); } @Test public void shouldNotGetIncomingChildrenFromServerIfCancelled() throws Exception { syncAllDataAsyncTask.setContext(rapidFtrActivity); HashMap<String, String> repositoryIDRevs = createRepositoryIdRevMap(); given(childSyncService.getIdsToDownload()).willReturn(Arrays.asList("asd97")); given(childRepository.getAllIdsAndRevs()).willReturn(repositoryIDRevs); syncAllDataAsyncTask = spy(syncAllDataAsyncTask); doReturn(true).when(syncAllDataAsyncTask).isCancelled(); syncAllDataAsyncTask.onPreExecute(); syncAllDataAsyncTask.doInBackground(); verify(childSyncService).getRecord(any(String.class)); verify(childRepository, never()).createOrUpdate((Child) any()); verify(childSyncService, never()).setMedia((Child) any()); } @Test public void shouldNotGetIncomingChildrenFromServerIfBlacklisted() throws Exception { syncAllDataAsyncTask.setContext(rapidFtrActivity); Child child1 = mock(Child.class); ArrayList<Child> childList = new ArrayList<Child>(); childList.add(child1); given(childRepository.toBeSynced()).willReturn(childList); given(deviceService.isBlacklisted()).willReturn(true); syncAllDataAsyncTask.execute(); verify(childSyncService).sync(child1, currentUser); verify(childSyncService, never()).getRecord(any(String.class)); verify(childSyncService, never()).getIdsToDownload(); } @Test public void shouldCreateOrUpdateExistingChild() throws Exception { Child child1 = mock(Child.class); Child child2 = mock(Child.class); HashMap<String, String> repositoryIDRevs = createRepositoryIdRevMap(); given(childSyncService.getIdsToDownload()).willReturn(Arrays.asList("qwerty0987", "abcd1234")); given(childRepository.getAllIdsAndRevs()).willReturn(repositoryIDRevs); given(child1.getUniqueId()).willReturn("1234"); given(child2.getUniqueId()).willReturn("5678"); given(childSyncService.getRecord("qwerty0987")).willReturn(child1); given(childSyncService.getRecord("abcd1234")).willReturn(child2); given(childRepository.exists("1234")).willReturn(true); given(childRepository.exists("5678")).willReturn(false); syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.execute(); verify(childSyncService).getRecord("qwerty0987"); verify(childRepository).createOrUpdateWithoutHistory(child1); verify(childRepository).createOrUpdateWithoutHistory(child2); } @Test public void shouldToggleMenuOnPreExecute() { syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.onPreExecute(); verify(syncAll).setVisible(false); verify(cancelSyncAll).setVisible(true); } @Test public void shouldToggleMenuOnCancelAndOnPostExecute() { syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.onPreExecute(); syncAllDataAsyncTask.onCancelled(); verify(syncAll).setVisible(true); verify(cancelSyncAll).setVisible(false); syncAllDataAsyncTask.onPreExecute(); verify(syncAll).setVisible(true); verify(cancelSyncAll).setVisible(false); } @Test public void shouldNotCallSetProgressAndNotifyIfCancelled() { syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask = spy(syncAllDataAsyncTask); doReturn(true).when(syncAllDataAsyncTask).isCancelled(); syncAllDataAsyncTask.onPreExecute(); verify(notificationManager, never()).notify(anyInt(), (Notification) anyObject()); } @Test public void shouldShowSessionTimeoutMessage() throws JSONException, IOException { Robolectric.getFakeHttpLayer().setDefaultHttpResponse(401, "Unauthorized"); given(rapidFtrActivity.getString(R.string.session_timeout)).willReturn("Your session is timed out"); EntityHttpDao<Child> dao = EntityHttpDaoFactory.createChildHttpDao(application, "","",""); syncAllDataAsyncTask.recordSyncService = new ChildSyncService(RapidFtrApplication.getApplicationInstance(), dao, childRepository); syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.execute(); verify(notificationManager, never()).notify(anyInt(), (Notification) anyObject()); } @Test public void shouldCompareAndRetrieveIdsToBeDownloadedFromServer() throws JSONException, IOException, HttpException { SharedPreferences sharedPreferences = RapidFtrApplication.getApplicationInstance().getSharedPreferences(); sharedPreferences.edit().putLong(RapidFtrApplication.LAST_CHILD_SYNC, 0).commit(); Child child1 = mock(Child.class); Child child2 = mock(Child.class); given(childRepository.toBeSynced()).willReturn(newArrayList(child1, child2)); given(childSyncService.getIdsToDownload()).willReturn(Arrays.asList("qwerty0987", "abcd1234")); given(childSyncService.getRecord("qwerty0987")).willReturn(mock(Child.class)); given(childSyncService.getRecord("abcd1234")).willReturn(mock(Child.class)); syncAllDataAsyncTask.setContext(rapidFtrActivity); syncAllDataAsyncTask.execute(); verify(formService).downloadPublishedFormSections(); verify(childSyncService).sync(child1, currentUser); verify(childSyncService).sync(child2, currentUser); verify(childSyncService).getIdsToDownload(); verify(childSyncService).getRecord("qwerty0987"); verify(childSyncService).getRecord("abcd1234"); } @Test public void shouldWipeDeviceIfItIsBlacklisted() throws IOException, JSONException { syncAllDataAsyncTask.setContext(rapidFtrActivity); ArrayList<Child> childList = new ArrayList<Child>(); given(childRepository.toBeSynced()).willReturn(childList); given(deviceService.isBlacklisted()).willReturn(true); doNothing().when(deviceService).wipeData(); syncAllDataAsyncTask.execute(); verify(deviceService).wipeData(); } @Test public void shouldNotWipeDeviceIfChildRecordsArePending() throws JSONException, IOException { syncAllDataAsyncTask.setContext(rapidFtrActivity); ArrayList<Child> childList = new ArrayList<Child>(); Child child = mock(Child.class); childList.add(child); given(childRepository.toBeSynced()).willReturn(childList); given(deviceService.isBlacklisted()).willReturn(true); syncAllDataAsyncTask.execute(); verify(childRepository, times(2)).toBeSynced(); verify(deviceService, never()).wipeData(); } private HashMap<String, String> createRepositoryIdRevMap() { HashMap<String, String> repositoryIDRevs = new HashMap<String, String>(); repositoryIDRevs.put("abcd1234", "1-zxy321"); repositoryIDRevs.put("abcd5678", "2-zxy765"); repositoryIDRevs.put("abcd7689", "3-cdsf76"); return repositoryIDRevs; } }