/*
* This program is part of the OpenLMIS logistics management information
* system platform software.
*
* Copyright © 2015 ThoughtWorks, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. This program is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details. You should
* have received a copy of the GNU Affero General Public License along with
* this program. If not, see http://www.gnu.org/licenses. For additional
* information contact info@OpenLMIS.org
*/
package org.openlmis.core.presenter;
import com.google.inject.AbstractModule;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import org.openlmis.core.LMISTestApp;
import org.openlmis.core.LMISTestRunner;
import org.openlmis.core.R;
import org.openlmis.core.exceptions.LMISException;
import org.openlmis.core.manager.SharedPreferenceMgr;
import org.openlmis.core.manager.UserInfoMgr;
import org.openlmis.core.model.Program;
import org.openlmis.core.model.User;
import org.openlmis.core.model.builder.ProgramBuilder;
import org.openlmis.core.model.builder.UserBuilder;
import org.openlmis.core.model.repository.ProgramRepository;
import org.openlmis.core.model.repository.RnrFormRepository;
import org.openlmis.core.model.repository.UserRepository;
import org.openlmis.core.network.LMISRestApi;
import org.openlmis.core.network.LMISRestManagerMock;
import org.openlmis.core.network.model.SyncDownProductsResponse;
import org.openlmis.core.network.model.UserResponse;
import org.openlmis.core.service.SyncDownManager;
import org.openlmis.core.service.SyncDownManager.SyncProgress;
import org.openlmis.core.service.SyncService;
import org.openlmis.core.view.activity.LoginActivity;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowToast;
import java.util.Arrays;
import java.util.List;
import retrofit.Callback;
import retrofit.client.Response;
import roboguice.RoboGuice;
import rx.Subscriber;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.openlmis.core.service.SyncDownManager.SyncProgress.RequisitionSynced;
import static org.roboguice.shaded.goole.common.collect.Lists.newArrayList;
@RunWith(LMISTestRunner.class)
public class LoginPresenterTest {
UserRepository userRepository;
RnrFormRepository rnrFormRepository;
LoginActivity mockActivity;
LoginPresenter presenter;
SyncDownProductsResponse mockSyncDownProductsResponse;
SyncService syncService;
@Captor
private ArgumentCaptor<Callback<UserResponse>> loginCB;
private LMISTestApp appInject;
private Subscriber<SyncProgress> syncSubscriber;
private SyncDownManager syncDownManager;
private ProgramRepository programRepository;
private LMISRestApi mockedApi;
private Response retrofitResponse;
private UserResponse userResponse;
@Before
public void setup() {
appInject = (LMISTestApp) RuntimeEnvironment.application;
userRepository = mock(UserRepository.class);
programRepository = mock(ProgramRepository.class);
rnrFormRepository = mock(RnrFormRepository.class);
mockActivity = mock(LoginActivity.class);
mockSyncDownProductsResponse = mock(SyncDownProductsResponse.class);
syncService = mock(SyncService.class);
syncDownManager = mock(SyncDownManager.class);
mockedApi = mock(LMISRestApi.class);
appInject.setRestApi(mockedApi);
retrofitResponse = LMISRestManagerMock.createDummyJsonResponse("http://unknown.com", 200, "", "");
userResponse = new UserResponse();
userResponse.setUserInformation(new User("username", "password"));
RoboGuice.overrideApplicationInjector(RuntimeEnvironment.application, new MyTestModule());
MockitoAnnotations.initMocks(this);
presenter = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(LoginPresenter.class);
presenter.attachView(mockActivity);
syncSubscriber = presenter.getSyncSubscriber();
presenter = spy(presenter);
}
@Test
public void shouldSaveUserToLocalDBWhenSuccess() throws InterruptedException {
appInject.setNetworkConnection(true);
presenter.startLogin("user", "password");
verify(mockActivity).loading();
verify(mockedApi).authorizeUser(any(User.class), loginCB.capture());
loginCB.getValue().success(userResponse, retrofitResponse);
verify(userRepository).createOrUpdate(any(User.class));
}
@Test
public void shouldSaveUserSupportedProgramsToLocalDBWhenSuccess() throws InterruptedException, LMISException {
appInject.setNetworkConnection(true);
presenter.startLogin("user", "password");
verify(mockActivity).loading();
verify(mockedApi).authorizeUser(any(User.class), loginCB.capture());
List<Program> supportedPrograms = newArrayList(new ProgramBuilder().build(), new ProgramBuilder().build());
userResponse.setFacilitySupportedPrograms(supportedPrograms);
loginCB.getValue().success(userResponse, retrofitResponse);
verify(userRepository).createOrUpdate(any(User.class));
verify(programRepository, times(2)).createOrUpdate(any(Program.class));
}
@Test
public void shouldCreateSyncAccountWhenLoginSuccess() {
appInject.setNetworkConnection(true);
presenter.startLogin("user", "password");
verify(mockedApi).authorizeUser(any(User.class), loginCB.capture());
loginCB.getValue().success(userResponse, retrofitResponse);
verify(syncService).createSyncAccount(userResponse.getUserInformation());
}
@Test
public void shouldSaveUserInfoWhenLoginSuccess() {
appInject.setNetworkConnection(true);
presenter.startLogin("user", "password");
verify(mockedApi).authorizeUser(any(User.class), loginCB.capture());
loginCB.getValue().success(userResponse, retrofitResponse);
verify(userRepository).createOrUpdate(userResponse.getUserInformation());
assertThat(UserInfoMgr.getInstance().getUser()).isEqualTo(userResponse.getUserInformation());
verify(mockActivity).clearErrorAlerts();
}
@Test
public void shouldSyncServerDataWhenLoginSuccessFromNet() {
appInject.setNetworkConnection(true);
presenter.startLogin("user", "password");
verify(mockedApi).authorizeUser(any(User.class), loginCB.capture());
loginCB.getValue().success(userResponse, retrofitResponse);
verify(syncDownManager).syncDownServerData(any(Subscriber.class));
}
@Test
public void shouldGoToInventoryPageAndKickOffPeriodicSyncIfSyncServerDataSuccess() throws InterruptedException {
//given
when(mockActivity.needInitInventory()).thenReturn(true);
//when
syncSubscriber.onCompleted();
//then
verify(mockActivity).loaded();
verify(mockActivity).goToInitInventory();
verify(syncService).kickOff();
}
@Test
public void shouldDoOfflineLoginWhenNoConnectionAndHasSyncedData() {
appInject.setNetworkConnection(false);
when(userRepository.mapUserFromLocal(any(User.class))).thenReturn(new User("user", "password"));
when(mockActivity.needInitInventory()).thenReturn(false);
SharedPreferenceMgr.getInstance().setLastMonthStockCardDataSynced(true);
SharedPreferenceMgr.getInstance().setRequisitionDataSynced(true);
SharedPreferenceMgr.getInstance().setLastSyncProductTime("time");
presenter.startLogin("user", "password");
verify(userRepository).mapUserFromLocal(any(User.class));
assertThat(UserInfoMgr.getInstance().getUser().getUsername()).isEqualTo("user");
verify(mockActivity).loaded();
verify(mockActivity).goToHomePage();
}
@Test
public void shouldShowMessageWhenNoConnectionAndHasNotGetProducts() {
appInject.setNetworkConnection(false);
when(userRepository.mapUserFromLocal(any(User.class))).thenReturn(new User("user", "password"));
when(mockActivity.needInitInventory()).thenReturn(false);
presenter.startLogin("user", "password");
verify(userRepository).mapUserFromLocal(any(User.class));
assertThat(UserInfoMgr.getInstance().getUser().getUsername()).isEqualTo("user");
verify(mockActivity).loaded();
assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(RuntimeEnvironment.application.getString(R.string.msg_sync_products_list_failed));
}
@Test
public void shouldShowLoginFailErrorMsgWhenNoConnectionAndNoLocalCache() {
appInject.setNetworkConnection(false);
when(userRepository.mapUserFromLocal(any(User.class))).thenReturn(null);
presenter.startLogin("user", "password");
verify(userRepository).mapUserFromLocal(any(User.class));
verify(mockActivity).loaded();
verify(mockActivity).showInvalidAlert();
verify(mockActivity).clearPassword();
}
@Test
public void shouldShowUserNameEmptyErrorMessage() {
presenter.startLogin("", "password1");
verify(mockActivity).showUserNameEmpty();
}
@Test
public void shouldShowPasswordEmptyErrorMessage() {
presenter.startLogin("user", "");
verify(mockActivity).showPasswordEmpty();
}
@Test
public void shouldShowSyncingStockCardsMessage() {
syncSubscriber.onNext(SyncProgress.SyncingStockCardsLastMonth);
verify(mockActivity).loading(RuntimeEnvironment.application.getString(R.string.msg_sync_stock_movements_data));
}
@Test
public void shouldGoToInitInventoryWhenRequisitionDataSynced() {
when(mockActivity.needInitInventory()).thenReturn(true);
syncSubscriber.onNext(RequisitionSynced);
verify(mockActivity).loaded();
verify(mockActivity).goToInitInventory();
}
@Test
public void shouldLoginFailedWhenSyncDataFailed() {
when(mockActivity.needInitInventory()).thenReturn(true);
syncSubscriber.onError(new Exception("error"));
verify(mockActivity, times(1)).loaded();
verify(mockActivity, times(0)).goToInitInventory();
verify(mockActivity, times(0)).goToHomePage();
}
@Test
public void shouldSaveUserDataAndSupportedFacilityCodeToDBWhenMultipleProgramToggleON() throws Exception {
User user = UserBuilder.defaultUser();
UserResponse userResponse = new UserResponse();
userResponse.setUserInformation(user);
userResponse.setFacilitySupportedPrograms(Arrays.asList(
new ProgramBuilder().setProgramCode("via_code").setProgramName("VIA name").build(),
new ProgramBuilder().setProgramCode("mmia_code").setProgramName("MMIA name").build(),
new ProgramBuilder().setProgramCode("nutrition_code").setProgramName("Nutrition name").setParentCode("via_code").build()
));
presenter.saveUserDataToLocalDatabase(userResponse);
verify(userRepository).createOrUpdate(user);
verify(programRepository, times(3)).createOrUpdate(any(Program.class));
}
public class MyTestModule extends AbstractModule {
@Override
protected void configure() {
bind(UserRepository.class).toInstance(userRepository);
bind(SyncService.class).toInstance(syncService);
bind(SyncDownManager.class).toInstance(syncDownManager);
bind(RnrFormRepository.class).toInstance(rnrFormRepository);
bind(ProgramRepository.class).toInstance(programRepository);
}
}
}