package org.edx.mobile.profiles; import android.net.Uri; import android.support.annotation.NonNull; import org.edx.mobile.event.AccountDataLoadedEvent; import org.edx.mobile.event.ProfilePhotoUpdatedEvent; import org.edx.mobile.model.api.ProfileModel; import org.edx.mobile.module.prefs.UserPrefs; import org.edx.mobile.test.BaseTest; import org.edx.mobile.user.Account; import org.edx.mobile.user.LanguageProficiency; import org.edx.mobile.user.ProfileImage; import org.edx.mobile.user.UserService; import org.edx.mobile.util.observer.Observer; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.io.IOException; import java.util.Collections; import de.greenrobot.event.EventBus; import retrofit2.mock.Calls; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.refEq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; public class UserProfileInteractorTest extends BaseTest { @Mock private UserService userService; @Mock private UserPrefs userPrefs; private EventBus eventBus; private UserProfileInteractor interactor; @Mock private Observer<UserProfileViewModel> profileObserver; @Mock private Observer<UserProfileImageViewModel> imageObserver; @Before public void before() { eventBus = new EventBus(); } @After public void after() { interactor.destroy(); } private void createAndObserveInteractor() { interactor = new UserProfileInteractor(ProfileValues.USERNAME, userService, eventBus, userPrefs); interactor.observeProfile().subscribe(profileObserver); interactor.observeProfileImage().subscribe(imageObserver); } @Test public void getUsername_returnsUsernamePassedInConstructor() { when(userService.getAccount(ProfileValues.USERNAME)).thenReturn(Calls.<Account>failure(new IOException())); createAndObserveInteractor(); assertThat(interactor.getUsername(), is(ProfileValues.USERNAME)); } @Test public void isViewingOwnProfile_asAnonymousUser_returnsFalse() { configureBareMockAccount(); createAndObserveInteractor(); assertFalse(interactor.isViewingOwnProfile()); } @Test public void isViewingOwnProfile_asUserBeingViewed_returnsTrue() { setAuthenticatedUsername(ProfileValues.USERNAME); configureBareMockAccount(); createAndObserveInteractor(); assertTrue(interactor.isViewingOwnProfile()); } @Test public void whenProfileObserved_withGetAccountError_emitsError() { final IOException exception = new IOException(); when(userService.getAccount(ProfileValues.USERNAME)).thenReturn(Calls.<Account>failure(exception)); createAndObserveInteractor(); verify(profileObserver).onError(exception); } @Test public void whenProfileObserved_withNoAboutMe_emitsNoAboutMe() throws Exception { configureBareMockAccount(); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null) ))); } @Test public void whenProfileObserved_withNoAboutMe_withPrivateAccount_asAnonymousUser_emitsLimitedProfileMessageAndEmptyContent() throws Exception { final Account account = configureBareMockAccount(); when(account.getAccountPrivacy()).thenReturn(Account.Privacy.PRIVATE); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.OTHER_USERS_PROFILE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.EMPTY, null) ))); } @Test public void whenProfileObserved_withNoAboutMe_withPrivateAccount_asUserBeingViewed_emitsIncompleteProfileMessageAndContent() throws Exception { setAuthenticatedUsername(ProfileValues.USERNAME); final Account account = configureBareMockAccount(); when(account.getAccountPrivacy()).thenReturn(Account.Privacy.PRIVATE); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.OWN_PROFILE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.INCOMPLETE, null ) ))); } @Test public void whenProfileObserved_withParentalConsentRequired_asUserBeingViewed_emitsParentalConsentRequired() { setAuthenticatedUsername(ProfileValues.USERNAME); final Account account = configureBareMockAccount(); when(account.requiresParentalConsent()).thenReturn(true); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.OWN_PROFILE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.PARENTAL_CONSENT_REQUIRED, null )))); } @Test public void whenProfileObserved_withParentalConsentRequired_asAnonymousUser_showsNoAboutMe() { final Account account = configureBareMockAccount(); when(account.requiresParentalConsent()).thenReturn(true); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.OTHER_USERS_PROFILE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null) ))); } @Test public void whenProfileImageObserved_withProfileImage_emitsFullImageUrl() { final Account account = configureBareMockAccount(); final ProfileImage profileImage = account.getProfileImage(); when(profileImage.hasImage()).thenReturn(true); when(profileImage.getImageUrlFull()).thenReturn(ProfileValues.ABSOLUTE_URL); createAndObserveInteractor(); verify(imageObserver).onData(refEq(new UserProfileImageViewModel(Uri.parse(ProfileValues.ABSOLUTE_URL), true))); } @Test public void whenProfileImageObserved_withNoProfileImage_emitsNullPhotoUri() { configureBareMockAccount(); createAndObserveInteractor(); verify(imageObserver).onData(refEq(new UserProfileImageViewModel(null, true))); } @Test public void whenProfileObserved_withFullProfile_emitsLanguageAndLocationAndAboutMe() { final Account account = configureBareMockAccount(); when(account.getBio()).thenReturn(ProfileValues.ABOUT_ME); when(account.getCountry()).thenReturn(ProfileValues.COUNTRY_CODE); when(account.getLanguageProficiencies()).thenReturn(Collections.singletonList(new LanguageProficiency(ProfileValues.LANGUAGE_CODE))); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, ProfileValues.LANGUAGE_NAME, ProfileValues.COUNTRY_NAME, new UserProfileBioModel( UserProfileBioModel.ContentType.ABOUT_ME, ProfileValues.ABOUT_ME) ))); } @Test public void whenProfileObserved_withInvalidCountryCode_emitsProfileWithNoCountry() { final Account account = configureBareMockAccount(); when(account.getCountry()).thenReturn(ProfileValues.INVALID_COUNTRY_CODE); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null) ))); } @Test public void whenProfileObserved_withInvalidLanguageCode_emitsProfileWithNoLanguage() { final Account account = configureBareMockAccount(); when(account.getLanguageProficiencies()).thenReturn(Collections.singletonList(new LanguageProficiency(ProfileValues.INVALID_LANGUAGE_CODE))); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null) ))); } @Test public void onProfilePhotoUpdatedEvent_withMatchingUsername_emitsNewPhotoUri() { configureBareMockAccount(); createAndObserveInteractor(); verify(imageObserver).onData(refEq(new UserProfileImageViewModel(null, true))); final Uri newPhotoUri = Uri.parse(ProfileValues.ABSOLUTE_URL); eventBus.post(new ProfilePhotoUpdatedEvent(ProfileValues.USERNAME, newPhotoUri)); verify(imageObserver).onData(refEq(new UserProfileImageViewModel(newPhotoUri, false))); } @Test public void onProfilePhotoUpdatedEvent_withUnrelatedUsername_emitsNewPhotoUri() { configureBareMockAccount(); createAndObserveInteractor(); verify(imageObserver).onData(refEq(new UserProfileImageViewModel(null, true))); eventBus.post(new ProfilePhotoUpdatedEvent(ProfileValues.ALTERNATE_USERNAME, Uri.parse(ProfileValues.ABSOLUTE_URL))); verifyNoMoreInteractions(imageObserver); } @Test public void onAccountUpdatedEvent_withMatchingUsername_emitsUpdatedProfileContent() throws Exception { final Account account = configureBareMockAccount(); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null) ))); when(account.getBio()).thenReturn(ProfileValues.ABOUT_ME); eventBus.post(new AccountDataLoadedEvent(account)); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.ABOUT_ME, ProfileValues.ABOUT_ME) ))); } @Test public void onAccountUpdatedEvent_withUnrelatedUsername_emitsUpdatedProfileContent() throws Exception { final Account account = configureBareMockAccount(); createAndObserveInteractor(); verify(profileObserver).onData(refEq(new UserProfileViewModel( UserProfileViewModel.LimitedProfileMessage.NONE, null, null, new UserProfileBioModel( UserProfileBioModel.ContentType.NO_ABOUT_ME, null))) ); when(account.getUsername()).thenReturn(ProfileValues.ALTERNATE_USERNAME); when(account.getBio()).thenReturn(ProfileValues.ABOUT_ME); eventBus.post(new AccountDataLoadedEvent(account)); verifyNoMoreInteractions(profileObserver); } private void setAuthenticatedUsername(@NonNull String username) { final ProfileModel profileModel = new ProfileModel(); profileModel.username = username; when(userPrefs.getProfile()).thenReturn(profileModel); } @NonNull private Account configureBareMockAccount() { final Account account = mock(Account.class); when(account.getUsername()).thenReturn(ProfileValues.USERNAME); when(account.getProfileImage()).thenReturn(mock(ProfileImage.class)); when(userService.getAccount(ProfileValues.USERNAME)).thenReturn(Calls.response(account)); return account; } }