Skip to content

Commit 6a1f719

Browse files
Saket Narayanvinaysshenoy
authored andcommitted
Fix: summary screen crashes if multiple histories are present for the patient
Steps to reproduce: 1. Send patients to the server without any medical history. This can be done by using an HTTP client or running Android tests for syncing patients 2. Open the patient's summary screen on two phones 3. Answer different medical history questions 4. Exit summary screen 5. Trigger a sync on both phones 6. Open summary screen again for the same patient 7. The app will crash.
1 parent 3f07963 commit 6a1f719

File tree

6 files changed

+48
-14
lines changed

6 files changed

+48
-14
lines changed

app/src/androidTest/java/org/simple/clinic/medicalhistory/MedicalHistoryRepositoryAndroidTest.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,26 @@ class MedicalHistoryRepositoryAndroidTest {
104104
assertThat(emptyHistory.hasDiabetes).isFalse()
105105
assertThat(emptyHistory.syncStatus).isEqualTo(SyncStatus.DONE)
106106
}
107+
108+
@Test
109+
fun when_multiple_medical_histories_are_present_for_a_patient_then_only_the_last_edited_one_should_be_returned() {
110+
val patientUuid = UUID.randomUUID()
111+
112+
val olderHistory = testData.medicalHistory(
113+
patientUuid = patientUuid,
114+
createdAt = Instant.now(clock).minusMillis(100),
115+
updatedAt = Instant.now(clock).minusMillis(100))
116+
117+
val newerHistory = testData.medicalHistory(
118+
patientUuid = patientUuid,
119+
createdAt = Instant.now(clock).minusMillis(100),
120+
updatedAt = Instant.now(clock))
121+
122+
repository.save(olderHistory, updateTime = {olderHistory.updatedAt})
123+
.andThen(repository.save(newerHistory, updateTime = {newerHistory.updatedAt}))
124+
.blockingAwait()
125+
126+
val foundHistory = repository.historyForPatientOrDefault(patientUuid).blockingFirst()
127+
assertThat(foundHistory.uuid).isEqualTo(newerHistory.uuid)
128+
}
107129
}

app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,18 @@ data class MedicalHistory(
5555
@Query("DELETE FROM MedicalHistory")
5656
fun clear()
5757

58-
@Query("SELECT * FROM MedicalHistory WHERE patientUuid = :patientUuid")
58+
/**
59+
* The last updated medical history is returned because it's possible
60+
* to have multiple medical histories present for the same patient.
61+
* See [MedicalHistoryRepository.historyForPatientOrDefault] to
62+
* understand when this will happen.
63+
*/
64+
@Query("""
65+
SELECT * FROM MedicalHistory
66+
WHERE patientUuid = :patientUuid
67+
ORDER BY updatedAt DESC
68+
LIMIT 1
69+
""")
5970
fun historyForPatient(patientUuid: PatientUuid): Flowable<List<MedicalHistory>>
6071
}
6172
}

app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistoryRepository.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.simple.clinic.medicalhistory
33
import io.reactivex.Completable
44
import io.reactivex.Observable
55
import io.reactivex.Single
6-
import org.simple.clinic.BuildConfig
76
import org.simple.clinic.medicalhistory.sync.MedicalHistoryPayload
87
import org.simple.clinic.patient.PatientUuid
98
import org.simple.clinic.patient.SyncStatus
@@ -37,11 +36,7 @@ class MedicalHistoryRepository @Inject constructor(
3736
.toObservable()
3837
.map { histories ->
3938
if (histories.size > 1) {
40-
if (BuildConfig.DEBUG) {
41-
throw AssertionError("Multiple histories are present for $patientUuid: $histories")
42-
} else {
43-
throw AssertionError("Multiple histories are present")
44-
}
39+
throw AssertionError("DAO shouldn't have returned multiple histories for the same patient")
4540
}
4641
if (histories.isEmpty()) {
4742
// This patient's MedicalHistory hasn't synced yet. We're okay with overriding
@@ -71,11 +66,11 @@ class MedicalHistoryRepository @Inject constructor(
7166
return save(listOf(medicalHistory))
7267
}
7368

74-
fun save(history: MedicalHistory): Completable {
69+
fun save(history: MedicalHistory, updateTime: () -> Instant = { Instant.now(clock) }): Completable {
7570
return Completable.fromAction {
7671
val dirtyHistory = history.copy(
7772
syncStatus = SyncStatus.PENDING,
78-
updatedAt = Instant.now(clock))
73+
updatedAt = updateTime())
7974
dao.save(dirtyHistory)
8075
}
8176
}

app/src/main/java/org/simple/clinic/summary/PatientSummaryScreenController.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.simple.clinic.summary.PatientSummaryCaller.SEARCH
2828
import org.simple.clinic.util.Just
2929
import org.simple.clinic.util.exhaustive
3030
import org.simple.clinic.widgets.UiEvent
31+
import org.threeten.bp.Clock
3132
import javax.inject.Inject
3233

3334
typealias Ui = PatientSummaryScreen
@@ -38,7 +39,8 @@ class PatientSummaryScreenController @Inject constructor(
3839
private val bpRepository: BloodPressureRepository,
3940
private val prescriptionRepository: PrescriptionRepository,
4041
private val medicalHistoryRepository: MedicalHistoryRepository,
41-
private val timestampGenerator: RelativeTimestampGenerator
42+
private val timestampGenerator: RelativeTimestampGenerator,
43+
private val clock: Clock
4244
) : ObservableTransformer<UiEvent, UiChange> {
4345

4446
private lateinit var disposable: Disposable

app/src/test/java/org/simple/clinic/medicalhistory/newentry/NewMedicalHistoryScreenControllerTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class NewMedicalHistoryScreenControllerTest {
4343
fun setUp() {
4444
controller = NewMedicalHistoryScreenController(medicalHistoryRepository, patientRepository)
4545

46-
whenever(medicalHistoryRepository.save(any(), any())).thenReturn(Completable.complete())
46+
whenever(medicalHistoryRepository.save(any<UUID>(), any())).thenReturn(Completable.complete())
4747
whenever(patientRepository.ongoingEntry()).thenReturn(Single.never())
4848

4949
uiEvents.compose(controller).subscribe { uiChange -> uiChange(screen) }

app/src/test/java/org/simple/clinic/summary/PatientSummaryScreenControllerTest.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import org.simple.clinic.patient.PatientRepository
3636
import org.simple.clinic.util.Just
3737
import org.simple.clinic.util.None
3838
import org.simple.clinic.widgets.UiEvent
39+
import org.threeten.bp.Clock
3940
import org.threeten.bp.Instant
41+
import org.threeten.bp.ZoneOffset.UTC
4042
import java.util.UUID
4143

4244
@RunWith(JUnitParamsRunner::class)
@@ -48,6 +50,7 @@ class PatientSummaryScreenControllerTest {
4850
private val prescriptionRepository = mock<PrescriptionRepository>()
4951
private val medicalHistoryRepository = mock<MedicalHistoryRepository>()
5052
private val patientUuid = UUID.randomUUID()
53+
private val clock = Clock.fixed(Instant.now(), UTC)
5154

5255
private val uiEvents = PublishSubject.create<UiEvent>()
5356
private val reporter = MockReporter()
@@ -62,7 +65,8 @@ class PatientSummaryScreenControllerTest {
6265
bpRepository,
6366
prescriptionRepository,
6467
medicalHistoryRepository,
65-
timestampGenerator)
68+
timestampGenerator,
69+
clock)
6670

6771
uiEvents
6872
.compose(controller)
@@ -265,7 +269,7 @@ class PatientSummaryScreenControllerTest {
265269
hasDiabetes = false,
266270
updatedAt = Instant.now())
267271
whenever(medicalHistoryRepository.historyForPatientOrDefault(patientUuid)).thenReturn(Observable.just(medicalHistory))
268-
whenever(medicalHistoryRepository.save(any<MedicalHistory>())).thenReturn(Completable.complete())
272+
whenever(medicalHistoryRepository.save(any<MedicalHistory>(), any())).thenReturn(Completable.complete())
269273

270274
uiEvents.onNext(PatientSummaryScreenCreated(patientUuid, caller = PatientSummaryCaller.SEARCH))
271275
uiEvents.onNext(SummaryMedicalHistoryAnswerToggled(question, selected = true))
@@ -277,7 +281,7 @@ class PatientSummaryScreenControllerTest {
277281
hasHadStroke = question == HAS_HAD_A_STROKE,
278282
hasHadKidneyDisease = question == HAS_HAD_A_KIDNEY_DISEASE,
279283
hasDiabetes = question == HAS_DIABETES)
280-
verify(medicalHistoryRepository).save(updatedMedicalHistory)
284+
verify(medicalHistoryRepository).save(eq(updatedMedicalHistory), any())
281285
}
282286

283287
@Suppress("unused")

0 commit comments

Comments
 (0)