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: Proximity feature interactors tests #199

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ class TestPresentationLoadingInteractor {

// Case 3:
// 1. deviceAuthenticationInteractor.getBiometricsAvailability returns:
// BiometricsAvailability.Failed
// BiometricsAvailability.Failure

// Case 3 Expected Result:
// resultHandler.onAuthenticationFailure called once.
Expand Down Expand Up @@ -291,8 +291,12 @@ class TestPresentationLoadingInteractor {

@Test
fun `when interactor stopPresentation is called then it delegates to walletCoreInteractor stopPresentation`() {
// When
interactor.stopPresentation()
verify(walletCorePresentationController, times(1)).stopPresentation()

// Then
verify(walletCorePresentationController, times(1))
.stopPresentation()
}

//endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import eu.europa.ec.testlogic.extension.runTest
import eu.europa.ec.testlogic.extension.toFlow
import eu.europa.ec.testlogic.rule.CoroutineTestRule
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
Expand Down Expand Up @@ -420,13 +420,13 @@ class TestPresentationRequestInteractor {
private fun mockEmissionOfIntentionallyNotHandledEvents() {
whenever(walletCorePresentationController.events)
.thenReturn(
flow {
emit(TransferEventPartialState.Connected)
emit(TransferEventPartialState.Connecting)
emit(TransferEventPartialState.QrEngagementReady(""))
emit(TransferEventPartialState.Redirect(uri = URI("")))
emit(TransferEventPartialState.ResponseSent)
}
flowOf(
TransferEventPartialState.Connected,
TransferEventPartialState.Connecting,
TransferEventPartialState.QrEngagementReady(""),
TransferEventPartialState.Redirect(uri = URI("")),
TransferEventPartialState.ResponseSent,
)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
/*
* Copyright (c) 2023 European Commission
*
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
* Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
* except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the Licence for the specific language
* governing permissions and limitations under the Licence.
*/

package eu.europa.ec.proximityfeature.interactor

import android.content.Context
import eu.europa.ec.authenticationlogic.controller.authentication.BiometricsAvailability
import eu.europa.ec.authenticationlogic.controller.authentication.DeviceAuthenticationResult
import eu.europa.ec.authenticationlogic.model.BiometricCrypto
import eu.europa.ec.commonfeature.interactor.DeviceAuthenticationInteractor
import eu.europa.ec.commonfeature.util.TestsData.mockedUriPath1
import eu.europa.ec.corelogic.controller.WalletCorePartialState
import eu.europa.ec.corelogic.controller.WalletCorePresentationController
import eu.europa.ec.testfeature.mockedPlainFailureMessage
import eu.europa.ec.testlogic.extension.expectNoEvents
import eu.europa.ec.testlogic.extension.runFlowTest
import eu.europa.ec.testlogic.extension.runTest
import eu.europa.ec.testlogic.extension.toFlow
import eu.europa.ec.testlogic.rule.CoroutineTestRule
import junit.framework.TestCase.assertEquals
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.net.URI

class TestProximityLoadingInteractor {

@get:Rule
val coroutineRule = CoroutineTestRule()

@Mock
private lateinit var walletCorePresentationController: WalletCorePresentationController

@Mock
private lateinit var deviceAuthenticationInteractor: DeviceAuthenticationInteractor

@Mock
private lateinit var resultHandler: DeviceAuthenticationResult

@Mock
private lateinit var context: Context

private lateinit var interactor: ProximityLoadingInteractor

private lateinit var crypto: BiometricCrypto

private lateinit var closeable: AutoCloseable

@Before
fun before() {
closeable = MockitoAnnotations.openMocks(this)

interactor = ProximityLoadingInteractorImpl(
walletCorePresentationController = walletCorePresentationController,
deviceAuthenticationInteractor = deviceAuthenticationInteractor
)

crypto = BiometricCrypto(cryptoObject = null)
}

@After
fun after() {
closeable.close()
}

//region observeResponse
// Case 1
// 1. walletCorePresentationController.events emits:
// TransferEventPartialState.Failure, with an error message.

// Case 1 Expected Result:
// ProximityLoadingObserveResponsePartialState.Failure state, with the same error message.
@Test
fun `Given Case 1, When observeResponse is called, Then Case 1 Expected Result is returned`() {
coroutineRule.runTest {
// Given
mockWalletCorePresentationControllerEventEmission(
event = WalletCorePartialState.Failure(
error = mockedPlainFailureMessage
)
)

// When
interactor.observeResponse()
.runFlowTest {
// Then
assertEquals(
ProximityLoadingObserveResponsePartialState.Failure(
error = mockedPlainFailureMessage
),
awaitItem()
)
}
}
}

// Case 2
// 1. walletCorePresentationController.events emits:
// WalletCorePartialState.Success

// Case 2 Expected Result:
// PresentationLoadingObserveResponsePartialState.Success.
@Test
fun `Given Case 2, When observeResponse is called, Then Case 2 Expected Result is returned`() {
coroutineRule.runTest {
// Given
mockWalletCorePresentationControllerEventEmission(
event = WalletCorePartialState.Success
)

// When
interactor.observeResponse()
.runFlowTest {
val expectedResult = ProximityLoadingObserveResponsePartialState.Success
// Then
assertEquals(
expectedResult,
awaitItem()
)
}
}
}

// Case 3:
// 1. walletCorePresentationController.events emits an event that we do not want to react to, i.e:
// TransferEventPartialState.Redirect

// Case 3 Expected Result is that no events are emitted
@Test
fun `Given Case 3, When observeResponse is called, Then Case 3 Expected Result is returned`() {
coroutineRule.runTest {
// Given
mockEmissionOfIntentionallyNotHandledEvent()

// When
interactor.observeResponse()
.expectNoEvents()
}
}

// Case 4:
// 1. walletCorePresentationController.events emits:
// WalletCorePartialState.UserAuthenticationRequired.

// Case 4 Expected Result:
// PresentationLoadingObserveResponsePartialState.UserAuthenticationRequired.
@Test
fun `Given Case 4, When observeResponse is called, Then Case 4 Expected Result is returned`() {
coroutineRule.runTest {
mockWalletCorePresentationControllerEventEmission(
event = WalletCorePartialState.UserAuthenticationRequired(
crypto = crypto,
resultHandler = resultHandler
)
)

// When
interactor.observeResponse()
.runFlowTest {
val expectedResult =
ProximityLoadingObserveResponsePartialState.UserAuthenticationRequired(
crypto = crypto,
resultHandler = resultHandler
)
assertEquals(expectedResult, awaitItem())
}
}
}
//endregion

//region handleUserAuthentication
//
// Case 1:
// 1. deviceAuthenticationInteractor.getBiometricsAvailability returns:
// BiometricsAvailability.CanAuthenticate

// Case 1 Expected Result:
// deviceAuthenticationInteractor.authenticateWithBiometrics called once.
@Test
fun `Given case 1, When handleUserAuthentication is called, Then Case 1 Expected Result is returned`() {
// Given
mockBiometricsAvailabilityResponse(
response = BiometricsAvailability.CanAuthenticate
)

// When
interactor.handleUserAuthentication(
context = context,
crypto = crypto,
resultHandler = resultHandler
)

// Then
verify(deviceAuthenticationInteractor, times(1))
.authenticateWithBiometrics(context, crypto, resultHandler)
}

// Case 2:
// 1. deviceAuthenticationInteractor.getBiometricsAvailability returns:
// BiometricsAvailability.NonEnrolled

// Case 2 Expected Result:
// deviceAuthenticationInteractor.authenticateWithBiometrics called once.
@Test
fun `Given case 2, When handleUserAuthentication is called, Then Case 2 expected result is returned`() {
// Given
mockBiometricsAvailabilityResponse(
response = BiometricsAvailability.NonEnrolled
)

// When
interactor.handleUserAuthentication(
context = context,
crypto = crypto,
resultHandler = resultHandler
)

// Then
verify(deviceAuthenticationInteractor, times(1))
.authenticateWithBiometrics(context, crypto, resultHandler)
}

// Case 3:
// 1. deviceAuthenticationInteractor.getBiometricsAvailability returns:
// BiometricsAvailability.Failure with message

// Case 3 Expected Result:
// resultHandler.onAuthenticationFailure called once.
@Test
fun `Given case 3, When handleUserAuthentication is called, Then Case 3 expected result is returned`() {
// Given
mockBiometricsAvailabilityResponse(
response = BiometricsAvailability.Failure(
errorMessage = mockedPlainFailureMessage
)
)
val mockedOnAuthenticationFailure: () -> Unit = {}
whenever(resultHandler.onAuthenticationFailure)
.thenReturn(mockedOnAuthenticationFailure)

// When
interactor.handleUserAuthentication(
context = context,
crypto = crypto,
resultHandler = resultHandler
)

// Then
verify(resultHandler, times(1))
.onAuthenticationFailure
}

//endregion

//region stopPresentation

@Test
fun `when interactor stopPresentation is called then it delegates to walletCoreInteractor stopPresentation`() {
// When
interactor.stopPresentation()

// Then
verify(walletCorePresentationController, times(1))
.stopPresentation()
}

//endregion

//region verifierName

@Test
fun `when interactor verifierName is called, then verifierName should be invoked on the controller`() {
// When
interactor.verifierName

// Then
verify(walletCorePresentationController, times(1))
.verifierName
}

//endregion

//region helper functions
private fun mockWalletCorePresentationControllerEventEmission(event: WalletCorePartialState) {
whenever(walletCorePresentationController.observeSentDocumentsRequest())
.thenReturn(event.toFlow())
}

private fun mockBiometricsAvailabilityResponse(response: BiometricsAvailability) {
whenever(deviceAuthenticationInteractor.getBiometricsAvailability(listener = any()))
.thenAnswer {
val bioAvailability = it.getArgument<(BiometricsAvailability) -> Unit>(0)
bioAvailability(response)
}
}

private fun mockEmissionOfIntentionallyNotHandledEvent() {
whenever(walletCorePresentationController.observeSentDocumentsRequest()).thenReturn(
WalletCorePartialState.Redirect(uri = URI(mockedUriPath1)).toFlow()
)
}
//endregion
}
Loading
Loading