diff --git a/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/api/repository/ObservationRepository.kt b/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/api/repository/ObservationRepository.kt index 47bfc0737..8808edc01 100644 --- a/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/api/repository/ObservationRepository.kt +++ b/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/api/repository/ObservationRepository.kt @@ -16,9 +16,8 @@ import com.openmrs.android_sdk.library.databases.entities.ObservationEntity import com.openmrs.android_sdk.library.models.Observation import com.openmrs.android_sdk.library.models.Resource import com.openmrs.android_sdk.utilities.NetworkUtils +import com.openmrs.android_sdk.utilities.execute import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers import java.util.concurrent.Callable import javax.inject.Inject import javax.inject.Singleton @@ -132,19 +131,8 @@ class ObservationRepository @Inject constructor( val observationResources: List = this.body()!!.results for (observationResource in observationResources) { - getObservationByUuid(observationResource.uuid!!).subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { observation -> - observationList.add(observation) - }, - { error -> - Log.e("Observation Repository", "Error: ${error.message}") - }, - { - Log.d("Observation Repository", "Observable completed") - } - ) + val observation = getObservationByUuid(observationResource.uuid!!) + observationList.add(observation.execute()) } observationDAO.deleteAllStandaloneObservations(uuid) //delete previous list observationDAO.saveStandaloneObservations(observationList) //save latest list @@ -172,19 +160,8 @@ class ObservationRepository @Inject constructor( val observationResources: List = this.body()!!.results for (observationResource in observationResources) { - getObservationByUuid(observationResource.uuid!!).subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { observation -> - observationList.add(observation) - }, - { error -> - Log.e("Observation Repository", "Error: ${error.message}") - }, - { - Log.d("Observation Repository", "Observable completed") - } - ) + val observation = getObservationByUuid(observationResource.uuid!!) + observationList.add(observation.execute()) } observationDAO.saveStandaloneObservations(observationList) //save latest list return Observable.just(observationList.toList()) @@ -213,19 +190,8 @@ class ObservationRepository @Inject constructor( val observationResources: List = this.body()!!.results for (observationResource in observationResources) { - getObservationByUuid(observationResource.uuid!!).subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { observation -> - observationList.add(observation) - }, - { error -> - Log.e("Observation Repository", "Error: ${error.message}") - }, - { - Log.d("Observation Repository", "Observable completed") - } - ) + val observation = getObservationByUuid(observationResource.uuid!!) + observationList.add(observation.execute()) } observationDAO.saveStandaloneObservations(observationList) //save latest list return Observable.just(observationList.toList()) diff --git a/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/databases/AppDatabaseHelper.kt b/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/databases/AppDatabaseHelper.kt index f7917bbc2..650c9fde5 100644 --- a/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/databases/AppDatabaseHelper.kt +++ b/openmrs-android-sdk/src/main/java/com/openmrs/android_sdk/library/databases/AppDatabaseHelper.kt @@ -65,9 +65,6 @@ import java.io.ByteArrayOutputStream import java.util.concurrent.Callable object AppDatabaseHelper { - val encounterRoomDAO: EncounterRoomDAO = AppDatabase.getDatabase( - OpenmrsAndroid.getInstance()!!.applicationContext - ).encounterRoomDAO() @JvmStatic fun convert(obs: Observation, encounterID: Long): ObservationEntity { @@ -87,6 +84,9 @@ object AppDatabaseHelper { @JvmStatic fun convert(obs: ObservationEntity): Observation { + val encounterRoomDAO: EncounterRoomDAO = AppDatabase.getDatabase( + OpenmrsAndroid.getInstance()!!.applicationContext + ).encounterRoomDAO() val observation = Observation() diff --git a/openmrs-android-sdk/src/test/java/com/openmrs/android_sdk/library/api/repository/ObservationRepositoryTest.kt b/openmrs-android-sdk/src/test/java/com/openmrs/android_sdk/library/api/repository/ObservationRepositoryTest.kt new file mode 100644 index 000000000..ca0e231fa --- /dev/null +++ b/openmrs-android-sdk/src/test/java/com/openmrs/android_sdk/library/api/repository/ObservationRepositoryTest.kt @@ -0,0 +1,191 @@ +package com.openmrs.android_sdk.library.api.repository + +import android.content.Context +import com.google.gson.GsonBuilder +import com.openmrs.android_sdk.library.OpenmrsAndroid +import com.openmrs.android_sdk.library.api.RestApi +import com.openmrs.android_sdk.library.dao.ObservationRoomDAO +import com.openmrs.android_sdk.library.databases.AppDatabase +import com.openmrs.android_sdk.library.databases.AppDatabaseHelper +import com.openmrs.android_sdk.library.databases.entities.ObservationEntity +import com.openmrs.android_sdk.library.models.Observation +import com.openmrs.android_sdk.utilities.NetworkUtils +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okio.buffer +import okio.source +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import javax.inject.Inject + +@HiltAndroidTest +@RunWith(RobolectricTestRunner::class) +@Config(application = HiltTestApplication::class) +class ObservationRepositoryTest { + lateinit var mockWebServer: MockWebServer + lateinit var observationApi: RestApi + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + val context: Context = mockk(relaxed = true) + val appContext: Context = mockk(relaxed = true) + val appDatabase: AppDatabase = mockk() + val observationRoomDAO: ObservationRoomDAO = mockk(relaxed = true) + + @Before + fun init() { + every { context.getApplicationContext() } returns appContext + mockkStatic(OpenmrsAndroid::class) + every { OpenmrsAndroid.getInstance() } returns context + mockkStatic(AppDatabase::class) + every { AppDatabase.getDatabase(appContext) } returns appDatabase + every { appDatabase.observationRoomDAO() } returns observationRoomDAO + + mockkStatic(NetworkUtils::class) + every { NetworkUtils.isOnline() } returns true + + mockWebServer = MockWebServer() + mockWebServer.start() + + val gsonBuilder = GsonBuilder() + val myGson = gsonBuilder + .excludeFieldsWithoutExposeAnnotation() + .create() + + val retrofit = Retrofit.Builder() + .baseUrl(mockWebServer.url("/")) + .addConverterFactory(GsonConverterFactory.create(myGson)) + .build() + + observationApi = retrofit.create(RestApi::class.java) + hiltRule.inject() + } + + @After + fun tearDown() { + clearAllMocks() + mockWebServer.shutdown() + } + + @Inject + lateinit var observationRepository: ObservationRepository + + @Test + fun `createObservationFromLocal success return Observation`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + + observationRepository.restApi = observationApi + + val observationEntity: ObservationEntity = mockk(relaxed = true) + val observationToPost = Observation() + + mockkStatic(AppDatabaseHelper::class) + every { AppDatabaseHelper.convert(observationEntity) } returns observationToPost + + val observation = observationRepository.createObservationFromLocal(observationEntity).toBlocking().first() + + assertEquals(observation.uuid, "12345") + assertEquals(observation.display, "obsDisplay") + assertEquals(observation.value, "120/80") + assertEquals(observation.obsDatetime, "2023-07-27 12:34:56") + assertEquals(observation.accessionNumber, 987) + } + + @Test + fun `getObservationByUuid success return Observation`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + + observationRepository.restApi = observationApi + val testUuid = "12345" + val observation = observationRepository.getObservationByUuid(testUuid).toBlocking().first() + + assertEquals(observation.uuid, "12345") + assertEquals(observation.display, "obsDisplay") + assertEquals(observation.value, "120/80") + assertEquals(observation.obsDatetime, "2023-07-27 12:34:56") + assertEquals(observation.accessionNumber, 987) + } + + @Test + fun `getAllObservationResourcesByPatientUuid success return List of Resources`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") + + observationRepository.restApi = observationApi + val patientUuid = "070f0120-0283-4858-885d-a20d967729cf" + val observation = observationRepository.getAllObservationResourcesByPatientUuid(patientUuid).toBlocking().first().get(1) + + assertEquals(observation.uuid, "99a0c42b-d50e-4ae3-b826-d1959c737e74") + assertEquals(observation.display, "Visit Diagnoses: Primary, Confirmed diagnosis, Disease of bone and joint") + } + + @Test + fun `getAllObservationResourcesByEncounterUuid success return List of Resources`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") + + observationRepository.restApi = observationApi + val encounterUuid = "11c22e25-f9f8-4c79-b384-1da39ee7d5d2" + val observation = observationRepository.getAllObservationResourcesByEncounterUuid(encounterUuid).toBlocking().first().get(2) + + assertEquals(observation.uuid, "33c83406-cd64-4c04-9506-7bdf754570a3") + assertEquals(observation.display, "Diagnosis order: Primary") + } + + @Test + fun `getAllObservationsByPatientUuidAndSaveLocally success return List of Observations`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + + observationRepository.restApi = observationApi + val patientUuid = "070f0120-0283-4858-885d-a20d967729cf" + val observation = observationRepository.getAllObservationsByPatientUuidAndSaveLocally(patientUuid).toBlocking().first().get(0) + + assertEquals(observation.uuid, "12345") + assertEquals(observation.display, "obsDisplay") + } + + @Test + fun `getAllObservationsByEncounterUuidAndSaveLocally success return List of Observations`(){ + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationResources-Get.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + enqueueMockResponse("mocked_responses/ObservationRepository/ObservationPost-success.json") + + observationRepository.restApi = observationApi + val encounterUuid = "11c22e25-f9f8-4c79-b384-1da39ee7d5d2" + val observation = observationRepository.getAllObservationsByPatientUuidAndSaveLocally(encounterUuid).toBlocking().first().get(0) + + assertEquals(observation.uuid, "12345") + assertEquals(observation.display, "obsDisplay") + } + + fun enqueueMockResponse(fileName: String) { + javaClass.classLoader?.let { + val inputStream = it.getResourceAsStream(fileName) + val source = inputStream.source().buffer() + val mockResponse = MockResponse() + mockResponse.setBody(source.readString(Charsets.UTF_8)) + mockResponse.setResponseCode(200) + mockWebServer.enqueue(mockResponse) + } + } +} diff --git a/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationPost-success.json b/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationPost-success.json new file mode 100644 index 000000000..64bebe002 --- /dev/null +++ b/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationPost-success.json @@ -0,0 +1,61 @@ +{ + "uuid": "12345", + "display": "obsDisplay", + "concept": { + "uuid": "abc123", + "display": "Blood Pressure" + }, + "value": "120/80", + "person": { + "uuid": "xyz789", + "display": "John Doe" + }, + "obsDatetime": "2023-07-27 12:34:56", + "accessionNumber": 987, + "obsGroup": { + "id": 56789, + "concept": { + "uuid": "def456", + "display": "Temperature" + }, + "value": "98.6", + "person": { + "uuid": "uvw987", + "display": "Jane Smith" + }, + "obsDatetime": "2023-07-27 13:45:30", + "obsGroup": null, + "valueCodedName": "Normal", + "comment": "No concerns", + "location": { + "uuid": "mno654", + "display": "Clinic A" + }, + "encounter": { + "uuid": "pqr321", + "display": "Initial Checkup" + }, + "voided": false, + "formFieldPath": "/encounter/observations[0]/value", + "formFieldNamespace": "http://example.org/form", + "groupMembers": null, + "order": "Labs", + "status": "Final" + }, + "valueCodedName": "Normal", + "comment": "No issues", + "location": { + "uuid": "mno654", + "display": "Clinic A" + }, + "encounter": { + "uuid": "pqr321", + "display": "Initial Checkup" + }, + "voided": false, + "formFieldPath": "/encounter/observations[0]/value", + "formFieldNamespace": "http://example.org/form", + "groupMembers": null, + "order": "Labs", + "status": "Final" +} diff --git a/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationResources-Get.json b/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationResources-Get.json new file mode 100644 index 000000000..5887dd02e --- /dev/null +++ b/openmrs-android-sdk/src/test/resources/mocked_responses/ObservationRepository/ObservationResources-Get.json @@ -0,0 +1,48 @@ +{ + "results": [ + { + "uuid": "94616eb5-6a87-4b83-b1e2-2083b3f7a36b", + "display": "Text of encounter note: Lorem ipsum dolor sit amet", + "links": [ + { + "rel": "self", + "uri": "http://demo.openmrs.org/openmrs/ws/rest/v1/obs/94616eb5-6a87-4b83-b1e2-2083b3f7a36b", + "resourceAlias": "obs" + } + ] + }, + { + "uuid": "99a0c42b-d50e-4ae3-b826-d1959c737e74", + "display": "Visit Diagnoses: Primary, Confirmed diagnosis, Disease of bone and joint", + "links": [ + { + "rel": "self", + "uri": "http://demo.openmrs.org/openmrs/ws/rest/v1/obs/99a0c42b-d50e-4ae3-b826-d1959c737e74", + "resourceAlias": "obs" + } + ] + }, + { + "uuid": "33c83406-cd64-4c04-9506-7bdf754570a3", + "display": "Diagnosis order: Primary", + "links": [ + { + "rel": "self", + "uri": "http://demo.openmrs.org/openmrs/ws/rest/v1/obs/33c83406-cd64-4c04-9506-7bdf754570a3", + "resourceAlias": "obs" + } + ] + }, + { + "uuid": "fbcc82b6-65af-43ec-8939-5e96ecf9a335", + "display": "Diagnosis certainty: Confirmed diagnosis", + "links": [ + { + "rel": "self", + "uri": "http://demo.openmrs.org/openmrs/ws/rest/v1/obs/fbcc82b6-65af-43ec-8939-5e96ecf9a335", + "resourceAlias": "obs" + } + ] + } + ] +} \ No newline at end of file