diff --git a/app/build.gradle b/app/build.gradle index 1aefdd3..06f039e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,8 +19,8 @@ android { applicationId "in.sminnovations.hpostesting.quality" minSdk 21 targetSdk 34 - versionCode 95 - versionName "2.1.95" + versionCode 97 + versionName "2.1.97" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt index bfc531b..bffe71e 100644 --- a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt +++ b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt @@ -57,7 +57,7 @@ class DatabaseRepository @Inject constructor( } } - suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result { + override suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result { return safeApiCall { molbioAuthApi.deviceProvision(deviceProvisionRequest) } } diff --git a/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt b/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt index b0f5a0d..cd3e9b5 100644 --- a/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt +++ b/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt @@ -2,6 +2,8 @@ package com.example.hpostesting.data.repository import com.example.hpostesting.data.Result import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse import com.example.hpostesting.data.model.diagnostics.DiagnosticsData import com.example.hpostesting.data.model.log.UploadLogsResponse import com.example.hpostesting.data.model.login.LoginRequest @@ -33,6 +35,8 @@ interface Repository { abstract fun addTestToDatabase(testDetails: UserData): Any // suspend fun addToDatabase(data: PatientDetails) + suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result + suspend fun login(loginRequest: LoginRequest): Result suspend fun uploadResults(molbioV2ResultRequest: MolbioV2ResultRequest): Result diff --git a/app/src/main/java/com/example/hpostesting/presentation/assurance/AssuranceControlsFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/assurance/AssuranceControlsFragment.kt index cba8ef7..4043938 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/assurance/AssuranceControlsFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/assurance/AssuranceControlsFragment.kt @@ -12,7 +12,6 @@ import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Spinner -import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.constant.Constants @@ -22,7 +21,7 @@ import `in`.sminnovations.hpostesting.databinding.FragmentAssuranceControlsBindi import java.time.Instant class AssuranceControlsFragment: Fragment() { - private lateinit var binding: FragmentAssuranceControlsBinding + lateinit var binding: FragmentAssuranceControlsBinding private lateinit var sharedPreferences: SharedPreferences override fun onCreateView( @@ -43,7 +42,7 @@ class AssuranceControlsFragment: Fragment() { // binding.btnSubmit.visibility = View.GONE val solutionSpinner: Spinner = binding.spinnerSolutions - val solutionOptions = arrayOf("Select solution", "KMnO4", "Tartrazine", "AR", "HB", "Blood") + val solutionOptions = arrayOf("Select solution", "Tartrazine", "AR") val solutionAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, solutionOptions) solutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) solutionSpinner.adapter = solutionAdapter @@ -65,7 +64,7 @@ class AssuranceControlsFragment: Fragment() { } val concentrationSpinner: Spinner = binding.spinnerConcentration - val concentrationOptions = arrayOf("Select concentration", "250umol", "350umol", "450umol", "550umol", "650umol") + val concentrationOptions = arrayOf("Select concentration", "65umol", "45umol", "22.5umol", "12.25umol", "6.125umol", "75umol", "50umol", "25umol", "12.5umol", "6.25umol") val concentrationAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, concentrationOptions) concentrationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) concentrationSpinner.adapter = concentrationAdapter diff --git a/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt index 8c4d0ac..b64a2a9 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt @@ -1,29 +1,22 @@ package com.example.hpostesting.presentation.deviceprovision -import android.content.Context -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.hpostesting.data.NetworkStatusLiveData import com.example.hpostesting.data.Result import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse -import com.example.hpostesting.data.repository.DatabaseRepository +import com.example.hpostesting.data.repository.Repository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class DeviceProvisionViewModel @Inject constructor( - private val databaseRepository: DatabaseRepository, - context: Context + private val databaseRepository: Repository ) : ViewModel() { var isServiceConnected = false - private val _networkStatusLiveData = NetworkStatusLiveData(context) - val networkStatusLiveData: LiveData - get() = _networkStatusLiveData val deviceProvisionResponse = MutableLiveData>() diff --git a/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt index bde77a6..91b4c0b 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/diagnostics/DiagnosticsViewModel.kt @@ -1,39 +1,22 @@ package com.example.hpostesting.presentation.diagnostics -import android.content.Context -import android.content.SharedPreferences -import android.util.Log -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.example.hpostesting.data.DataHolder -import com.example.hpostesting.data.NetworkStatusLiveData -import com.example.hpostesting.data.constant.Constants -import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.model.Response import com.example.hpostesting.data.model.diagnostics.DiagnosticsData import com.example.hpostesting.data.model.patient.DeviceData -import com.example.hpostesting.data.model.patient.HemoCubeTestData -import com.example.hpostesting.data.model.patient.toHemoCubeTestData -import com.example.hpostesting.data.repository.DatabaseRepository +import com.example.hpostesting.data.repository.Repository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Locale import javax.inject.Inject @HiltViewModel class DiagnosticsViewModel @Inject constructor( - private val hemoCubeDao: HemoCubeDao, - private val repository: DatabaseRepository, - context: Context + private val repository: Repository ) : ViewModel() { var isServiceConnected = false val progressBar = MutableLiveData(false) val messages = MutableLiveData() - private val sharedPreference: SharedPreferences = - context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) val deviceData = MutableLiveData() val fireBaseUpload = MutableLiveData() diff --git a/app/src/main/res/layout/fragment_assurance_controls.xml b/app/src/main/res/layout/fragment_assurance_controls.xml index f2331db..c343a7e 100644 --- a/app/src/main/res/layout/fragment_assurance_controls.xml +++ b/app/src/main/res/layout/fragment_assurance_controls.xml @@ -62,6 +62,7 @@ +// // Mock the shared preferences +// `when`(fragment.requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)).thenReturn(sharedPreferences) +// +// // Mock the solution spinner selection +// fragment.binding.spinnerSolutions.setSelection(1) +// +// // Verify that DataHolder.hemoCubeTestData!!.solution is set correctly +// assertEquals("KMnO4", DataHolder.hemoCubeTestData!!.solution) +// +// // You can similarly test other parts of initViews() based on your requirements +// } +// } + + @Test + fun testGetPositionOfValue() { + val fragment = AssuranceControlsFragment() + + val testData = listOf("Select solution", "KMnO4", "Tartrazine", "AR", "HB", "Blood") + + // Test a value that exists in the list + val positionKMnO4 = fragment.getPositionOfValue("KMnO4", testData) + assertEquals(1, positionKMnO4) + + // Test a value that does not exist in the list + val positionNonExistent = fragment.getPositionOfValue("NonExistent", testData) + assertEquals(0, positionNonExistent) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/example/hpostesting/DeviceProvisionViewModelTest.kt b/app/src/test/java/com/example/hpostesting/DeviceProvisionViewModelTest.kt new file mode 100644 index 0000000..ae3208e --- /dev/null +++ b/app/src/test/java/com/example/hpostesting/DeviceProvisionViewModelTest.kt @@ -0,0 +1,83 @@ +package com.example.hpostesting + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.Observer +import com.example.hpostesting.data.Result +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse +import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionViewModel +import com.example.hpostesting.util.TestCoroutineRule +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +class DeviceProvisionViewModelTest { + + // Rule to make LiveData updates instant + @get:Rule + val instantExecutorRule = InstantTaskExecutorRule() + + // Rule for testing coroutines + @get:Rule + val coroutineRule = TestCoroutineRule() + + // Mocks + @Mock + private lateinit var databaseRepository: Repository + + @Mock + private lateinit var deviceProvisionObserver: Observer> + + // Class under test + private lateinit var viewModel: DeviceProvisionViewModel + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + viewModel = DeviceProvisionViewModel(databaseRepository) + viewModel.deviceProvisionResponse.observeForever(deviceProvisionObserver) + } + + @After + fun tearDown() { + viewModel.deviceProvisionResponse.removeObserver(deviceProvisionObserver) + } + + @Test + fun `deviceProvision success`() = coroutineRule.runBlockingTest { + // Arrange + val deviceProvisionRequest = DeviceProvisionRequest(/* provide necessary parameters */) + val expectedResult = Result.Success(DeviceProvisionResponse(/* provide necessary response data */)) + Mockito.`when`(databaseRepository.deviceProvision(deviceProvisionRequest)).thenReturn(expectedResult) + + // Act + viewModel.deviceProvision(deviceProvisionRequest) + + // Assert +// Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(Result.Loading()) + Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(expectedResult) + } + + @Test + fun `deviceProvision error`() = coroutineRule.runBlockingTest { + // Arrange + val deviceProvisionRequest = DeviceProvisionRequest(/* provide necessary parameters */) + val expectedError = Result.Error(Exception("Test error")) + Mockito.`when`(databaseRepository.deviceProvision(deviceProvisionRequest)).thenReturn(expectedError) + + // Act + viewModel.deviceProvision(deviceProvisionRequest) + + // Assert +// Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(Result.Loading()) + Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(expectedError) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/example/hpostesting/DiagnosticsViewModelTest.kt b/app/src/test/java/com/example/hpostesting/DiagnosticsViewModelTest.kt new file mode 100644 index 0000000..b357535 --- /dev/null +++ b/app/src/test/java/com/example/hpostesting/DiagnosticsViewModelTest.kt @@ -0,0 +1,86 @@ +package com.example.hpostesting + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.Observer +import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.diagnostics.DiagnosticsData +import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.presentation.diagnostics.DiagnosticsViewModel +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.setMain +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +class DiagnosticsViewModelTest { + + // Use this rule to swap the background executor used by the Architecture Components + @get:Rule + var instantTaskExecutorRule = InstantTaskExecutorRule() + + // Use this dispatcher to control execution of coroutines in tests + private val testDispatcher = TestCoroutineDispatcher() + + // Use this scope to control the execution of coroutines in tests + private val testCoroutineScope = TestCoroutineScope(testDispatcher) + + // Mock Repository + private val mockRepository = mockk() + + // Mock Observer for LiveData + @Mock + private lateinit var mockObserver: Observer + + // Subject under test + private lateinit var diagnosticsViewModel: DiagnosticsViewModel + + @Before + fun setup() { + // Initialize mocks + MockitoAnnotations.openMocks(this) + + // Create the ViewModel with the mock repository and set the coroutine dispatcher + diagnosticsViewModel = DiagnosticsViewModel(repository = mockRepository) +// diagnosticsViewModel.viewModelScope = testCoroutineScope + + // Observe the LiveData +// diagnosticsViewModel.fireBaseUpload.observeForever(mockObserver) + + Dispatchers.setMain(TestCoroutineDispatcher()) + } + + @Test + fun `test addDiagnosticsDataToDb success`() = runBlocking { + // Given + val diagnosticsData = mockk() + coEvery { mockRepository.addDiagnostics(diagnosticsData) } returns Response.Success("Success") + + // When + diagnosticsViewModel.addDiagnosticsDataToDb(diagnosticsData) + + // Then + assert(diagnosticsViewModel.fireBaseUpload.value == "Success") + } + + @Test + fun `addDiagnosticsDataToDb error`() = testCoroutineScope.runBlockingTest { + // Given + coEvery { mockRepository.addDiagnostics(any()) } returns Response.Error(Exception("Test error")) + + // When + diagnosticsViewModel.addDiagnosticsDataToDb(mockk()) + + // Then + assert(diagnosticsViewModel.fireBaseUpload.value == "Error") + } +} \ No newline at end of file