Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test/unit/login-user-usecase #349

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/domain/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,15 @@ dependencies {
implementation(Dependencies.coroutinesDependency)
// Paging
implementation(Dependencies.pagingCommon)
// Required for Coroutines
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")

testImplementation ("org.junit.jupiter:junit-jupiter-api:5.8.2")
testImplementation ("org.junit.jupiter:junit-jupiter-engine:5.8.2")
testImplementation ("org.mockito:mockito-junit-jupiter:3.11.2")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
}
tasks.withType<Test> {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import org.the_chance.honeymart.domain.model.AdminLogin
import org.the_chance.honeymart.domain.model.Owner
import org.the_chance.honeymart.domain.model.OwnerProfile
import org.the_chance.honeymart.domain.usecase.Tokens
import org.the_chance.honeymart.domain.util.InvalidPasswordInputException
import org.the_chance.honeymart.domain.util.InvalidUserNameOrPasswordException
import org.the_chance.honeymart.domain.util.UnKnownUserException

interface AuthRepository {
@Throws(InvalidUserNameOrPasswordException::class)
suspend fun loginUser(
email: String,
password: String,
deviceToken: String
): Tokens

suspend fun refreshToken(refreshToken: String): Tokens

@Throws(InvalidPasswordInputException::class)
suspend fun saveTokens(accessToken: String, refreshToken: String)

fun getAccessToken(): String?
Expand All @@ -22,7 +26,7 @@ interface AuthRepository {
suspend fun clearToken()

suspend fun registerUser(fullName: String, password: String, email: String): Boolean

@Throws(UnKnownUserException::class)
suspend fun getDeviceToken(): String

suspend fun createOwnerAccount(fullName: String, email: String, password: String): Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.the_chance.honeymart.domain.usecase

import org.the_chance.honeymart.domain.util.ValidationState

interface IValidationUseCase {
fun validateEmail(email: String): ValidationState
fun validateConfirmPassword(password: String, repeatedPassword: String): Boolean
fun validationFullName(fullName: String): ValidationState
fun validationPassword(password: String): ValidationState
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
package org.the_chance.honeymart.domain.usecase

import org.the_chance.honeymart.domain.repository.AuthRepository
import org.the_chance.honeymart.domain.util.InvalidEmailException
import org.the_chance.honeymart.domain.util.InvalidPasswordInputException
import org.the_chance.honeymart.domain.util.ValidationState
import javax.inject.Inject

class LoginUserUseCase @Inject constructor(
private val authRepository: AuthRepository,
private val validationUseCase: IValidationUseCase
) {
suspend operator fun invoke(email: String, password: String) {
when (validationUseCase.validateEmail(email)) {
ValidationState.BLANK_EMAIL, ValidationState.INVALID_EMAIL -> throw InvalidEmailException()
else -> {}
}

when (validationUseCase.validationPassword(password)) {
ValidationState.BLANK_PASSWORD, ValidationState.INVALID_PASSWORD,
ValidationState.INVALID_PASSWORD_LENGTH_SHORT -> throw InvalidPasswordInputException()
else -> {}
}

val deviceToken = authRepository.getDeviceToken()
val tokens = authRepository.loginUser(email, password, deviceToken)
authRepository.saveTokens(tokens.accessToken, tokens.refreshToken)
try {
val tokens = authRepository.loginUser(email, password, deviceToken)
authRepository.saveTokens(tokens.accessToken, tokens.refreshToken)
} catch (e: Exception) {
throw InvalidPasswordInputException()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import org.the_chance.honeymart.domain.util.ValidationState
import java.util.regex.Pattern
import javax.inject.Inject

class ValidationUseCase @Inject constructor() {
class ValidationUseCase @Inject constructor() : IValidationUseCase {

fun validateEmail(email: String): ValidationState {
override fun validateEmail(email: String): ValidationState {
if (email.isBlank()) {
return ValidationState.BLANK_EMAIL
}
Expand All @@ -16,10 +16,10 @@ class ValidationUseCase @Inject constructor() {
return ValidationState.VALID_EMAIL
}

fun validateConfirmPassword(password: String, repeatedPassword: String) =
override fun validateConfirmPassword(password: String, repeatedPassword: String) =
password == repeatedPassword

fun validationFullName(fullName: String): ValidationState {
override fun validationFullName(fullName: String): ValidationState {
if (fullName.isBlank()) {
return ValidationState.BLANK_FULL_NAME
}
Expand All @@ -31,7 +31,7 @@ class ValidationUseCase @Inject constructor() {
}


fun validationPassword(password: String): ValidationState {
override fun validationPassword(password: String): ValidationState {
return when {
password.isBlank() -> {
ValidationState.BLANK_PASSWORD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum class ValidationState {
PASSWORD_REGEX_ERROR_DIGIT,
PASSWORD_REGEX_ERROR_SPECIAL_CHARACTER,
VALID_PASSWORD,
SHORT_PASSWORD,

CONFIRM_PASSWORD_DOES_NOT_MATCH,
CONFIRM_PASSWORD_MATCH,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.the_chance.honeymart

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

//import org.junit.Assert.assertEquals
//import org.junit.Test

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package org.the_chance.honeymart.domain.usecase

import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.*
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import org.the_chance.honeymart.domain.repository.AuthRepository
import org.the_chance.honeymart.domain.util.InvalidEmailException
import org.the_chance.honeymart.domain.util.InvalidPasswordInputException
import org.the_chance.honeymart.domain.util.InvalidUserNameOrPasswordException
import org.the_chance.honeymart.domain.util.UnKnownUserException
import org.the_chance.honeymart.domain.util.ValidationState

@ExtendWith(MockitoExtension::class)
class LoginUserUseCaseTest {

@Mock
private lateinit var mockAuthRepository: AuthRepository

@InjectMocks
private lateinit var loginUserUseCase: LoginUserUseCase

@Mock
private lateinit var mockValidationUseCase: IValidationUseCase

@BeforeEach
fun setup() {
reset(mockAuthRepository)
lenient().`when`(mockValidationUseCase.validateEmail(TEST_EMAIL))
.thenReturn(ValidationState.VALID_EMAIL)
lenient().`when`(mockValidationUseCase.validationPassword(TEST_PASSWORD))
.thenReturn(ValidationState.VALID_PASSWORD)
}

@Test
fun `given valid email and password, when login is invoked, then tokens are saved`() =
runBlocking {
`when`(mockAuthRepository.getDeviceToken()).thenReturn(TEST_DEVICE_TOKEN)
`when`(
mockAuthRepository.loginUser(
TEST_EMAIL,
TEST_PASSWORD,
TEST_DEVICE_TOKEN
)
).thenReturn(TEST_TOKENS)

loginUserUseCase(TEST_EMAIL, TEST_PASSWORD)

verify(mockAuthRepository).getDeviceToken()
verify(mockAuthRepository)
.loginUser(TEST_EMAIL, TEST_PASSWORD, TEST_DEVICE_TOKEN)
verify(mockAuthRepository)
.saveTokens(TEST_TOKENS.accessToken, TEST_TOKENS.refreshToken)
}

@Test
fun `given getDeviceToken fails, when login is invoked, then ensure no further calls are made`() =
runBlocking {
`when`(mockAuthRepository.getDeviceToken())
.thenThrow(UnKnownUserException::class.java)

assertThrows<UnKnownUserException> { loginUserUseCase(TEST_EMAIL, TEST_PASSWORD) }

verify(mockAuthRepository).getDeviceToken()
verifyNoMoreInteractions(mockAuthRepository)
}

@Test
fun `given loginUser fails, when login is invoked, then tokens are not saved`() = runBlocking {
// Arrange
`when`(mockAuthRepository.getDeviceToken()).thenReturn(TEST_DEVICE_TOKEN)
`when`(mockAuthRepository.loginUser(TEST_EMAIL, TEST_PASSWORD, TEST_DEVICE_TOKEN))
.thenThrow(InvalidUserNameOrPasswordException::class.java)

assertThrows<InvalidPasswordInputException> {
// Act
loginUserUseCase(TEST_EMAIL, TEST_PASSWORD)
}

// Assert
verify(mockAuthRepository).getDeviceToken()
verify(mockAuthRepository).loginUser(TEST_EMAIL, TEST_PASSWORD, TEST_DEVICE_TOKEN)
verify(mockAuthRepository, never()).saveTokens(
TEST_TOKENS.accessToken,
TEST_TOKENS.refreshToken
)
}

@ParameterizedTest
@EnumSource(ValidationState::class, names = ["BLANK_PASSWORD", "SHORT_PASSWORD", "INVALID_PASSWORD","INVALID_PASSWORD_LENGTH_SHORT","INVALID_PASSWORD_LENGTH_LONG",
"INVALID_CONFIRM_PASSWORD", "PASSWORD_REGEX_ERROR_LETTER","PASSWORD_REGEX_ERROR_DIGIT", "PASSWORD_REGEX_ERROR_SPECIAL_CHARACTER"])
fun `given invalid passwords, ensure repository calls are not made`(invalidState: ValidationState) = runBlocking {
println("Testing with ValidationState: $invalidState")

`when`(mockValidationUseCase.validationPassword(anyString())).thenReturn(invalidState)

try {
loginUserUseCase(TEST_EMAIL, "invalidPassword")
Assertions.fail("Expected an InvalidPasswordInputException to be thrown.")
} catch (e: Exception) {
if (e is NullPointerException) {
println("NullPointerException encountered. Printing stack trace...")
e.printStackTrace()
} else {
Assertions.assertTrue(
e is InvalidPasswordInputException,
"Expected InvalidPasswordInputException but received ${e::class.java}"
)
}
}
}


@Test
fun `given token saving fails, when login is , then appropriate error is thrown`() =
runBlocking<Unit> {
`when`(mockAuthRepository.getDeviceToken()).thenReturn(TEST_DEVICE_TOKEN)
`when`(
mockAuthRepository.loginUser(
TEST_EMAIL,
TEST_PASSWORD,
TEST_DEVICE_TOKEN
)
).thenReturn(TEST_TOKENS)
doThrow(InvalidPasswordInputException()).`when`(mockAuthRepository).saveTokens(
TEST_TOKENS.accessToken,
TEST_TOKENS.refreshToken
)

assertThrows<InvalidPasswordInputException> { loginUserUseCase(TEST_EMAIL, TEST_PASSWORD) }
}

@ParameterizedTest
@ValueSource(strings = ["", "test@", "test.com"])
fun `given invalid email formats, ensure repository calls are not made`(email: String) =
runBlocking {
`when`(mockValidationUseCase.validateEmail(email)).thenReturn(ValidationState.INVALID_EMAIL)

assertThrows<InvalidEmailException> { loginUserUseCase(email, TEST_PASSWORD) }
verifyNoInteractions(mockAuthRepository)
}


companion object {
private const val TEST_EMAIL = "[email protected]"
private const val TEST_PASSWORD = "password"
private const val TEST_DEVICE_TOKEN = "deviceToken"
private val TEST_TOKENS = Tokens("accessToken", "refreshToken")
}
}
1 change: 1 addition & 0 deletions user/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ dependencies {

//Permission
implementation("com.google.accompanist:accompanist-permissions:0.28.0")
androidTestImplementation ("com.google.truth:truth:1.1.4")
}
Loading