diff --git a/core/data/src/main/java/com/youthtalk/repository/LoginRepositoryImpl.kt b/core/data/src/main/java/com/youthtalk/repository/LoginRepositoryImpl.kt index c156c2c9..295fc9a2 100644 --- a/core/data/src/main/java/com/youthtalk/repository/LoginRepositoryImpl.kt +++ b/core/data/src/main/java/com/youthtalk/repository/LoginRepositoryImpl.kt @@ -4,58 +4,36 @@ import com.core.dataapi.repository.LoginRepository import com.core.datastore.datasource.DataStoreDataSource import com.core.exception.NoDataException import com.youthtalk.data.LoginService -import com.youthtalk.dto.MemberId import com.youthtalk.dto.login.LoginRequest import com.youthtalk.dto.login.SignRequest import com.youthtalk.model.typeenum.toRegion -import com.youthtalk.utils.ErrorUtils.throwableError +import com.youthtalk.utils.ErrorUtils.createResult import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow import timber.log.Timber class LoginRepositoryImpl @Inject constructor( private val loginService: LoginService, private val dataStoreDataSource: DataStoreDataSource ) : LoginRepository { - override fun postLogin(socialId: String): Flow = flow { - Timber.e("LoginRepositoryImpl postLogin start") - runCatching { loginService.postLogin(LoginRequest(socialType = "kakao", socialId = socialId).toRequestBody()) } - .onSuccess { token -> - Timber.e("LoginRepositoryImpl postLogin Success $token") - token.data?.let { data -> - emit(data.memberId) - } ?: throw NoDataException() - } - .onFailure { error -> - Timber.e("LoginRepositoryImpl postLogin error : $error") - throwableError(error) - } + override suspend fun postLogin(socialId: String): Result = createResult { + loginService.postLogin(LoginRequest(socialType = "kakao", socialId = socialId).toRequestBody()).data?.memberId ?: throw NoDataException() + }.onFailure { error -> + Timber.e("error : $error") } override fun hasToken(): Flow = dataStoreDataSource.hasToken() - override fun postSign(id: String, nickname: String, region: String): Flow = flow { - Timber.e("LoginRepositoryImpl start") - runCatching { - loginService.postSignUp( - SignRequest( - socialType = "kakao", - socialId = id, - nickname = nickname, - region = region.toRegion().region - ).toRequestBody() - ) - } - .onSuccess { response -> - Timber.e("LoginRepositoryImpl Success $response") - response.data?.let { - emit(it) - } ?: throw NoDataException() - } - .onFailure { error -> - Timber.e("LoginRepositoryImpl error : $error") - throwableError(error) - } + override suspend fun postSign(id: String, nickname: String, region: String): Result = createResult { + loginService.postSignUp( + SignRequest( + socialType = "kakao", + socialId = id, + nickname = nickname, + region = region.toRegion().region + ).toRequestBody() + ).data ?: throw NoDataException() + }.onFailure { error -> + Timber.e("error : $error") } } diff --git a/core/data/src/main/java/com/youthtalk/utils/ErrorUtils.kt b/core/data/src/main/java/com/youthtalk/utils/ErrorUtils.kt index 2d0829bd..2995c3ae 100644 --- a/core/data/src/main/java/com/youthtalk/utils/ErrorUtils.kt +++ b/core/data/src/main/java/com/youthtalk/utils/ErrorUtils.kt @@ -8,6 +8,7 @@ import com.core.exception.NotPermissionMethod import com.core.exception.UnAuthorizedException import com.youthtalk.dto.CommonResponse import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import retrofit2.HttpException import timber.log.Timber @@ -48,7 +49,7 @@ object ErrorUtils { inline fun mapToCustomException(it: HttpException): Exception { val error = it.response()?.errorBody()?.string() ?: throw InvalidValueException(it.message) - val response = Json.decodeFromString>(error) + val response = Json.decodeFromString>(error) return when (it.code()) { 401 -> UnAuthorizedException(response.message) 404 -> NotFoundResource(response.message) diff --git a/core/data/src/test/java/com/youthtalk/repository/LoginRepositoryTest.kt b/core/data/src/test/java/com/youthtalk/repository/LoginRepositoryTest.kt new file mode 100644 index 00000000..a968baf9 --- /dev/null +++ b/core/data/src/test/java/com/youthtalk/repository/LoginRepositoryTest.kt @@ -0,0 +1,137 @@ +package com.youthtalk.repository + +import com.core.datastore.datasource.DataStoreDataSource +import com.core.exception.BadRequestException +import com.core.exception.UnAuthorizedException +import com.youthtalk.data.LoginService +import com.youthtalk.dto.CommonResponse +import com.youthtalk.dto.MemberId +import com.youthtalk.dto.login.LoginRequest +import com.youthtalk.dto.login.SignRequest +import com.youthtalk.dto.toResponseBody +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import retrofit2.HttpException +import retrofit2.Response + +@RunWith(MockitoJUnitRunner::class) +class LoginRepositoryTest { + + @InjectMocks + private lateinit var sut: LoginRepositoryImpl + + @Mock + private lateinit var loginService: LoginService + + @Mock + private lateinit var dataStoreDataSource: DataStoreDataSource + + @Test + fun givenSocialId_whenLogin_thenReturnsMemberId() { + runTest { + // given + val socialId = "88888" + val memberId = 23L + val loginRequest = LoginRequest(socialType = "kakao", socialId = socialId) + + whenever(loginService.postLogin(any())).thenReturn(CommonResponse(200, "요청에 성공하였습니다", "S01", MemberId(memberId))) + + // when + val result = sut.postLogin(socialId).getOrThrow() + + // then + assertEquals(memberId, result) + verify(loginService).postLogin(any()) + } + } + + @Test + fun givenWrongSocialId_whenLogin_thenThrowsNull() { + runTest { + // given + val socialId = "xxxxxx" + val memberId = null + val loginRequest = LoginRequest(socialType = "kakao", socialId = socialId) + + whenever(loginService.postLogin(any())).thenThrow( + HttpException( + Response.error( + 401, + toResponseBody(CommonResponse(401, "회원이 아닙니다.", "F01", memberId)) + ) + ) + ) + + // when + Assert.assertThrows(UnAuthorizedException::class.java) { + runBlocking { + sut.postLogin(socialId).getOrThrow() + } + } + + // then + verify(loginService).postLogin(any()) + } + } + + @Test + fun givenSignInfo_whenSignUp_thenReturnsMemberId() { + runTest { + val socialId = "666666" + val socialType = "kakao" + val nickname = "압도적도적" + val region = "서울" + val memberId = 23 + + val signUpRequest = SignRequest(socialType, socialId, nickname, region) + whenever(loginService.postSignUp(any())).thenReturn(CommonResponse(200, "요청에 성공하였습니다", "S01", memberId)) + + // when + val result = sut.postSign(socialId, nickname, region).getOrThrow() + + // then + assertEquals(memberId, result) + verify(loginService).postSignUp(any()) + } + } + + @Test + fun givenWrongRegion_whenSignUp_thenThrows400Exception() { + runTest { + val socialId = "666666" + val socialType = "kakao" + val nickname = "압도적도적" + val region = "없는지역" + val memberId = 23 + + whenever(loginService.postSignUp(any())).thenThrow( + HttpException( + Response.error( + 400, + toResponseBody(CommonResponse>(400, "유효하지 않은 값을 입력하였습니다.", "F01", arrayListOf("지역이 유효하지 않습니다."))) + ) + ) + ) + + // when + Assert.assertThrows(BadRequestException::class.java) { + runBlocking { + sut.postSign(socialId, nickname, region).getOrThrow() + } + } + + // then + verify(loginService).postSignUp(any()) + } + } +} diff --git a/core/dataApi/src/main/java/com/core/dataapi/repository/LoginRepository.kt b/core/dataApi/src/main/java/com/core/dataapi/repository/LoginRepository.kt index b36db5cf..f205dc62 100644 --- a/core/dataApi/src/main/java/com/core/dataapi/repository/LoginRepository.kt +++ b/core/dataApi/src/main/java/com/core/dataapi/repository/LoginRepository.kt @@ -3,9 +3,9 @@ package com.core.dataapi.repository import kotlinx.coroutines.flow.Flow interface LoginRepository { - fun postLogin(socialId: String): Flow + suspend fun postLogin(socialId: String): Result fun hasToken(): Flow - fun postSign(id: String, nickname: String, region: String): Flow + suspend fun postSign(id: String, nickname: String, region: String): Result } diff --git a/core/domain/src/main/java/com/core/domain/usercase/user/PostLoginUseCase.kt b/core/domain/src/main/java/com/core/domain/usercase/user/PostLoginUseCase.kt index 45683cb4..ce3a7db6 100644 --- a/core/domain/src/main/java/com/core/domain/usercase/user/PostLoginUseCase.kt +++ b/core/domain/src/main/java/com/core/domain/usercase/user/PostLoginUseCase.kt @@ -6,5 +6,5 @@ import javax.inject.Inject class PostLoginUseCase @Inject constructor( private val loginRepository: LoginRepository ) { - operator fun invoke(socialId: String) = loginRepository.postLogin(socialId) + suspend operator fun invoke(socialId: String) = loginRepository.postLogin(socialId) } diff --git a/core/domain/src/main/java/com/core/domain/usercase/user/PostSignUseCase.kt b/core/domain/src/main/java/com/core/domain/usercase/user/PostSignUseCase.kt index 7a183345..478ad633 100644 --- a/core/domain/src/main/java/com/core/domain/usercase/user/PostSignUseCase.kt +++ b/core/domain/src/main/java/com/core/domain/usercase/user/PostSignUseCase.kt @@ -2,13 +2,14 @@ package com.core.domain.usercase.user import com.core.dataapi.repository.LoginRepository import javax.inject.Inject -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flatMapMerge class PostSignUseCase @Inject constructor( private val loginRepository: LoginRepository ) { - @OptIn(ExperimentalCoroutinesApi::class) - operator fun invoke(id: String, nickname: String, region: String) = loginRepository.postSign(id, nickname, region) - .flatMapMerge { loginRepository.postLogin(id) } + suspend operator fun invoke(id: String, nickname: String, region: String): Result { + val signResult = loginRepository.postSign(id, nickname, region) + if (signResult.isFailure) return Result.failure(signResult.exceptionOrNull()!!) + + return loginRepository.postLogin(id) + } } diff --git a/core/exception/src/main/java/com/core/exception/BadRequestException.kt b/core/exception/src/main/java/com/core/exception/BadRequestException.kt index 1241c50e..a47b00dd 100644 --- a/core/exception/src/main/java/com/core/exception/BadRequestException.kt +++ b/core/exception/src/main/java/com/core/exception/BadRequestException.kt @@ -1,5 +1,3 @@ package com.core.exception -class BadRequestException( - override val message: String? -): Exception() +class BadRequestException(override val message: String?) : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/InvalidValueException.kt b/core/exception/src/main/java/com/core/exception/InvalidValueException.kt index 79dccac1..aa9310be 100644 --- a/core/exception/src/main/java/com/core/exception/InvalidValueException.kt +++ b/core/exception/src/main/java/com/core/exception/InvalidValueException.kt @@ -1,8 +1,3 @@ package com.core.exception -class InvalidValueException( - private val m: String? = "InvalidValueException" -): Exception() { - override val message: String? - get() = m -} +class InvalidValueException(override val message: String? = "InvalidValueException") : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/NetworkErrorException.kt b/core/exception/src/main/java/com/core/exception/NetworkErrorException.kt index b7f5c1e7..86a8b231 100644 --- a/core/exception/src/main/java/com/core/exception/NetworkErrorException.kt +++ b/core/exception/src/main/java/com/core/exception/NetworkErrorException.kt @@ -1,8 +1,3 @@ package com.core.exception -class NetworkErrorException( - private val m: String? = "NetworkErrorException" -) : Exception() { - override val message: String? - get() = m -} +class NetworkErrorException(override val message: String? = "NetworkErrorException") : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/NoDataException.kt b/core/exception/src/main/java/com/core/exception/NoDataException.kt index d2412aad..f6e9703c 100644 --- a/core/exception/src/main/java/com/core/exception/NoDataException.kt +++ b/core/exception/src/main/java/com/core/exception/NoDataException.kt @@ -1,8 +1,3 @@ package com.core.exception -class NoDataException( - private val m: String? = "NoDataException" -): Exception() { - override val message: String? - get() = m -} +class NoDataException(override val message: String? = "NoDataException") : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/NotFoundResource.kt b/core/exception/src/main/java/com/core/exception/NotFoundResource.kt index 1aabe8e1..0a466196 100644 --- a/core/exception/src/main/java/com/core/exception/NotFoundResource.kt +++ b/core/exception/src/main/java/com/core/exception/NotFoundResource.kt @@ -1,8 +1,3 @@ package com.core.exception -class NotFoundResource( - private val m: String? = "NotFoundResource" -): Exception() { - override val message: String? - get() = m -} +class NotFoundResource(override val message: String? = "NotFoundResource") : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/NotPermissionMethod.kt b/core/exception/src/main/java/com/core/exception/NotPermissionMethod.kt index c037a2b1..4f8cce17 100644 --- a/core/exception/src/main/java/com/core/exception/NotPermissionMethod.kt +++ b/core/exception/src/main/java/com/core/exception/NotPermissionMethod.kt @@ -1,8 +1,3 @@ package com.core.exception -class NotPermissionMethod( - private val m: String? = "NotPermissionMethod" -): Exception() { - override val message: String? - get() = m -} +class NotPermissionMethod(override val message: String? = "NotPermissionMethod") : RuntimeException() diff --git a/core/exception/src/main/java/com/core/exception/UnAuthorizedException.kt b/core/exception/src/main/java/com/core/exception/UnAuthorizedException.kt index c90086ce..fee912c9 100644 --- a/core/exception/src/main/java/com/core/exception/UnAuthorizedException.kt +++ b/core/exception/src/main/java/com/core/exception/UnAuthorizedException.kt @@ -1,9 +1,3 @@ package com.core.exception -//401 에러 Exception -class UnAuthorizedException( - private val errorMessage : String? = "UnAuthorizedException" -) : Exception() { - override val message: String? - get() = errorMessage -} +class UnAuthorizedException(override val message: String? = "UnAuthorizedException") : RuntimeException() diff --git a/feature/community/src/main/java/com/core/community/screen/detail/CommunityDetailScreen.kt b/feature/community/src/main/java/com/core/community/screen/detail/CommunityDetailScreen.kt index d13654d3..33ae737b 100644 --- a/feature/community/src/main/java/com/core/community/screen/detail/CommunityDetailScreen.kt +++ b/feature/community/src/main/java/com/core/community/screen/detail/CommunityDetailScreen.kt @@ -145,7 +145,7 @@ fun CommunityDetailScreen( is CommunityDetailUiEffect.ShowSnackBarReportFail -> { showSnackBar(it.message.toString()) } - + is CommunityDetailUiEffect.InitError -> { showSnackBar(it.message) onBack() @@ -154,7 +154,6 @@ fun CommunityDetailScreen( showSnackBar(context.getString(R.string.block_user_snackbar_message, it.userName)) onBack() } - } } } @@ -259,7 +258,6 @@ fun CommunityDetailScreen( } ) } - } @OptIn(ExperimentalMaterial3Api::class) diff --git a/feature/community/src/main/java/com/core/community/viewmodel/CommunityDetailViewModel.kt b/feature/community/src/main/java/com/core/community/viewmodel/CommunityDetailViewModel.kt index 35b01adb..47740dd9 100644 --- a/feature/community/src/main/java/com/core/community/viewmodel/CommunityDetailViewModel.kt +++ b/feature/community/src/main/java/com/core/community/viewmodel/CommunityDetailViewModel.kt @@ -227,7 +227,7 @@ class CommunityDetailViewModel @Inject constructor( } } } - + private fun initData(postId: Long) { viewModelScope.launch { combine( diff --git a/feature/login/src/main/java/com/core/login/LoginViewModel.kt b/feature/login/src/main/java/com/core/login/LoginViewModel.kt index c9f61420..b6e53ec3 100644 --- a/feature/login/src/main/java/com/core/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/core/login/LoginViewModel.kt @@ -15,8 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import timber.log.Timber @@ -59,33 +57,25 @@ class LoginViewModel @Inject constructor( fun postLogin(userId: Long) { socialId = "$userId" viewModelScope.launch { - postLoginUseCase("950331") - .catch { - Timber.e("viewModel postLogin error $it") - _error.emit(it) - } - .collectLatest { + postLoginUseCase(socialId) + .onSuccess { uiEffect.emit(LoginUiEffect.GoMainActivity) + }.onFailure { + _error.emit(it) } } } fun postSign(nickname: String, region: String) { - Timber.e("postSign Start") viewModelScope.launch { + loading.value = true postSignUseCase(socialId, nickname, region) - .onStart { - loading.value = true - } - .onCompletion { + .onSuccess { loading.value = false - } - .catch { - Timber.e("viewModel sign error $it") - _error.emit(it) - } - .collectLatest { uiEffect.emit(LoginUiEffect.GoMainActivity) + }.onFailure { + loading.value = false + _error.emit(it) } } }