Skip to content
Snippets Groups Projects
Unverified Commit 13ac744e authored by BMItter's avatar BMItter Committed by GitHub
Browse files

Automatic contact journal entry creation (EXPOSUREAPP-5943) (#2770)


* Created contact journal entry creator

* reduced complexity & clean

* updated CheckOutHandlerTest

* First test, others will follow

* ktlint

* added further tests for location creation

* Changed trace location id to ByteString

* 42

* ContactDiary ContactJournal contactdiaryoverviewViewModelTest Adjustment

* Improved location name, Added test if name gets created as expected

* test adjustment

* Create location visits if missing

* satisfy ci - further tests are coming

* clean

Co-authored-by: default avatarharambasicluka <64483219+harambasicluka@users.noreply.github.com>
parent 8acd75a0
No related branches found
No related tags found
No related merge requests found
Showing
with 272 additions and 22 deletions
...@@ -20,6 +20,7 @@ import io.kotest.assertions.throwables.shouldThrow ...@@ -20,6 +20,7 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okio.ByteString.Companion.decodeBase64
import org.joda.time.Duration import org.joda.time.Duration
import org.joda.time.LocalDate import org.joda.time.LocalDate
import org.junit.Rule import org.junit.Rule
...@@ -176,7 +177,7 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() { ...@@ -176,7 +177,7 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() {
checkInID = null checkInID = null
) )
val locationAfter = location.copy(traceLocationID = "jshrgu-aifhioaio-aofsjof-samofp-kjsadngsgf") val locationAfter = location.copy(traceLocationID = "jshrgu-aifhioaio-aofsjof-samofp-kjsadngsgf".decodeBase64())
val locationVisitAfter = locationVisit.copy(checkInID = 101) val locationVisitAfter = locationVisit.copy(checkInID = 101)
val locationValues = ContentValues().apply { val locationValues = ContentValues().apply {
......
...@@ -14,6 +14,7 @@ import io.kotest.matchers.shouldBe ...@@ -14,6 +14,7 @@ import io.kotest.matchers.shouldBe
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okio.ByteString.Companion.decodeBase64
import org.joda.time.Duration import org.joda.time.Duration
import org.joda.time.LocalDate import org.joda.time.LocalDate
import org.junit.After import org.junit.After
...@@ -37,7 +38,7 @@ class ContactDiaryDatabaseTest : BaseTestInstrumentation() { ...@@ -37,7 +38,7 @@ class ContactDiaryDatabaseTest : BaseTestInstrumentation() {
locationName = "Rewe Wiesloch", locationName = "Rewe Wiesloch",
emailAddress = "location-emailAddress", emailAddress = "location-emailAddress",
phoneNumber = "location-phoneNumber", phoneNumber = "location-phoneNumber",
traceLocationID = "a-b-c-d" traceLocationID = "a-b-c-d".decodeBase64()
) )
private val personEncounter = ContactDiaryPersonEncounterEntity( private val personEncounter = ContactDiaryPersonEncounterEntity(
id = 3, id = 3,
......
package de.rki.coronawarnapp.contactdiary.model package de.rki.coronawarnapp.contactdiary.model
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationId
import de.rki.coronawarnapp.util.lists.HasStableId import de.rki.coronawarnapp.util.lists.HasStableId
import java.util.Locale import java.util.Locale
...@@ -8,7 +9,7 @@ interface ContactDiaryLocation : HasStableId { ...@@ -8,7 +9,7 @@ interface ContactDiaryLocation : HasStableId {
var locationName: String var locationName: String
val phoneNumber: String? val phoneNumber: String?
val emailAddress: String? val emailAddress: String?
val traceLocationID: String? val traceLocationID: TraceLocationId?
} }
fun List<ContactDiaryLocation>.sortByNameAndIdASC(): List<ContactDiaryLocation> = fun List<ContactDiaryLocation>.sortByNameAndIdASC(): List<ContactDiaryLocation> =
......
package de.rki.coronawarnapp.contactdiary.model package de.rki.coronawarnapp.contactdiary.model
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationId
data class DefaultContactDiaryLocation( data class DefaultContactDiaryLocation(
override val locationId: Long = 0L, override val locationId: Long = 0L,
override var locationName: String, override var locationName: String,
override val phoneNumber: String? = null, override val phoneNumber: String? = null,
override val emailAddress: String? = null, override val emailAddress: String? = null,
override val traceLocationID: String? = null override val traceLocationID: TraceLocationId? = null
) : ContactDiaryLocation { ) : ContactDiaryLocation {
override val stableId: Long override val stableId: Long
get() = locationId get() = locationId
......
...@@ -5,6 +5,7 @@ import androidx.room.ColumnInfo ...@@ -5,6 +5,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationId
import de.rki.coronawarnapp.util.trimToLength import de.rki.coronawarnapp.util.trimToLength
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
...@@ -15,7 +16,7 @@ data class ContactDiaryLocationEntity( ...@@ -15,7 +16,7 @@ data class ContactDiaryLocationEntity(
@ColumnInfo(name = "locationName") override var locationName: String, @ColumnInfo(name = "locationName") override var locationName: String,
override val phoneNumber: String?, override val phoneNumber: String?,
override val emailAddress: String?, override val emailAddress: String?,
@ColumnInfo(name = "traceLocationID") override val traceLocationID: String? @ColumnInfo(name = "traceLocationID") override val traceLocationID: TraceLocationId?
) : ContactDiaryLocation, Parcelable { ) : ContactDiaryLocation, Parcelable {
override val stableId: Long override val stableId: Long
get() = locationId get() = locationId
......
package de.rki.coronawarnapp.presencetracing.checkins.checkout package de.rki.coronawarnapp.presencetracing.checkins.checkout
import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import dagger.Reusable
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository
import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.TimeStamper
import org.joda.time.Instant import org.joda.time.Instant
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
@Singleton @Reusable
class CheckOutHandler @Inject constructor( class CheckOutHandler @Inject constructor(
private val repository: CheckInRepository, private val repository: CheckInRepository,
private val timeStamper: TimeStamper, private val timeStamper: TimeStamper,
private val diaryRepository: ContactDiaryRepository, private val contactJournalCheckInEntryCreator: ContactJournalCheckInEntryCreator
) { ) {
/** /**
* Throw **[IllegalArgumentException]** if the check-in does not exist. * Throw **[IllegalArgumentException]** if the check-in does not exist.
...@@ -21,18 +21,16 @@ class CheckOutHandler @Inject constructor( ...@@ -21,18 +21,16 @@ class CheckOutHandler @Inject constructor(
suspend fun checkOut(checkInId: Long, checkOutAt: Instant = timeStamper.nowUTC) { suspend fun checkOut(checkInId: Long, checkOutAt: Instant = timeStamper.nowUTC) {
Timber.d("checkOut(checkInId=$checkInId, checkOutAt=%s)", checkOutAt) Timber.d("checkOut(checkInId=$checkInId, checkOutAt=%s)", checkOutAt)
var createJournalEntry = false var checkIn: CheckIn? = null
repository.updateCheckIn(checkInId) { repository.updateCheckIn(checkInId) {
createJournalEntry = it.createJournalEntry
it.copy( it.copy(
checkInEnd = checkOutAt, checkInEnd = checkOutAt,
completed = true completed = true
) ).also { c -> checkIn = c }
} }
if (createJournalEntry) { if (checkIn?.createJournalEntry == true) {
Timber.d("Creating journal entry for $checkInId") contactJournalCheckInEntryCreator.createEntry(checkIn!!)
// TODO Create journal entry
} }
// Remove auto-checkout timer? // Remove auto-checkout timer?
......
package de.rki.coronawarnapp.presencetracing.checkins.checkout
import androidx.annotation.VisibleForTesting
import dagger.Reusable
import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation
import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit
import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import de.rki.coronawarnapp.eventregistration.checkins.split.splitByMidnightUTC
import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import org.joda.time.Duration
import org.joda.time.Seconds
import org.joda.time.format.DateTimeFormat
import timber.log.Timber
import javax.inject.Inject
import kotlin.math.roundToLong
@Reusable
class ContactJournalCheckInEntryCreator @Inject constructor(
private val diaryRepository: ContactDiaryRepository
) {
suspend fun createEntry(checkIn: CheckIn) {
Timber.d("Creating journal entry for %s", checkIn)
// 1. Create location if missing
val location: ContactDiaryLocation = diaryRepository.locations.first()
.find { it.traceLocationID == checkIn.traceLocationId } ?: checkIn.toLocation()
// 2. Split CheckIn by Midnight UTC
val splitCheckIns = checkIn.splitByMidnightUTC()
Timber.d("Split %s into %s ", this, splitCheckIns)
// 3. Create LocationVisit if missing
splitCheckIns
.createMissingLocationVisits(location)
.forEach { diaryRepository.addLocationVisit(it) }
}
private suspend fun CheckIn.toLocation(): ContactDiaryLocation {
val location = DefaultContactDiaryLocation(
locationName = locationName(),
traceLocationID = traceLocationId
)
Timber.d("Created new location %s and adding it to contact journal db", location)
return diaryRepository.addLocation(location) // Get location from db cause we need the id autogenerated by db
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun CheckIn.locationName(): String {
val nameParts = mutableListOf(description, address)
if (traceLocationStart != null && traceLocationEnd != null) {
if (traceLocationStart.millis > 0 && traceLocationEnd.millis > 0) {
val formattedStartDate = traceLocationStart.toUserTimeZone().toString(DateTimeFormat.shortDateTime())
val formattedEndDate = traceLocationEnd.toUserTimeZone().toString(DateTimeFormat.shortDateTime())
nameParts.add("$formattedStartDate - $formattedEndDate")
}
}
return nameParts.joinToString(separator = ", ")
}
private fun CheckIn.toLocationVisit(location: ContactDiaryLocation): ContactDiaryLocationVisit {
// Use Seconds for more precision
val durationInMinutes = Seconds.secondsBetween(checkInStart, checkInEnd).seconds / 60.0
val duration = (durationInMinutes / 15).roundToLong() * 15
return DefaultContactDiaryLocationVisit(
date = checkInStart.toLocalDateUtc(),
contactDiaryLocation = location,
duration = Duration.standardMinutes(duration),
checkInID = id
)
}
private suspend fun List<CheckIn>.createMissingLocationVisits(location: ContactDiaryLocation):
List<ContactDiaryLocationVisit> {
Timber.d(
"createMissingLocationVisits(location=%s) for %s",
location,
this.joinToString(prefix = System.lineSeparator(), separator = System.lineSeparator())
)
val existingLocationVisits = diaryRepository.locationVisits.firstOrNull() ?: emptyList()
// Existing location visits shall not be updated, so just drop them
return filter {
existingLocationVisits.none { visit ->
visit.date == it.checkInStart.toLocalDateUtc() &&
visit.contactDiaryLocation.locationId == location.locationId
}
}
.map { it.toLocationVisit(location) }
.also {
Timber.d(
"Created locations visits: %s",
it.joinToString(prefix = System.lineSeparator(), separator = System.lineSeparator())
)
}
}
}
...@@ -4,7 +4,9 @@ import androidx.room.TypeConverter ...@@ -4,7 +4,9 @@ import androidx.room.TypeConverter
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationId
import de.rki.coronawarnapp.util.serialization.fromJson import de.rki.coronawarnapp.util.serialization.fromJson
import okio.ByteString.Companion.decodeBase64
import org.joda.time.Instant import org.joda.time.Instant
import org.joda.time.LocalDate import org.joda.time.LocalDate
import org.joda.time.LocalTime import org.joda.time.LocalTime
...@@ -68,4 +70,10 @@ class CommonConverters { ...@@ -68,4 +70,10 @@ class CommonConverters {
@TypeConverter @TypeConverter
fun fromLocationCode(code: LocationCode?): String? = code?.identifier fun fromLocationCode(code: LocationCode?): String? = code?.identifier
@TypeConverter
fun toTraceLocationId(value: String?): TraceLocationId? = value?.decodeBase64()
@TypeConverter
fun fromTraceLocationId(traceLocationId: TraceLocationId?): String? = traceLocationId?.base64()
} }
...@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.contactdiary.ui.edit ...@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.contactdiary.ui.edit
import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryLocationEntity import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryLocationEntity
import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocationId
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.Runs import io.mockk.Runs
...@@ -30,7 +31,7 @@ class ContactDiaryEditLocationsViewModelTest { ...@@ -30,7 +31,7 @@ class ContactDiaryEditLocationsViewModelTest {
override val phoneNumber: String? = null override val phoneNumber: String? = null
override val emailAddress: String? = null override val emailAddress: String? = null
override val stableId = 1L override val stableId = 1L
override val traceLocationID: String? = null override val traceLocationID: TraceLocationId? = null
} }
private val locationList = listOf(location) private val locationList = listOf(location)
......
...@@ -35,6 +35,7 @@ import io.mockk.mockk ...@@ -35,6 +35,7 @@ import io.mockk.mockk
import io.mockk.runs import io.mockk.runs
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import okio.ByteString.Companion.decodeBase64
import org.joda.time.DateTimeZone import org.joda.time.DateTimeZone
import org.joda.time.Instant import org.joda.time.Instant
import org.joda.time.LocalDate import org.joda.time.LocalDate
...@@ -83,13 +84,13 @@ open class ContactDiaryOverviewViewModelTest { ...@@ -83,13 +84,13 @@ open class ContactDiaryOverviewViewModelTest {
private val locationEventLowRisk = DefaultContactDiaryLocation( private val locationEventLowRisk = DefaultContactDiaryLocation(
locationId = 456, locationId = 456,
locationName = "Jahrestreffen der deutschen SAP Anwendergruppe", locationName = "Jahrestreffen der deutschen SAP Anwendergruppe",
traceLocationID = "12ab-34cd-56ef-78gh-456" traceLocationID = "12ab-34cd-56ef-78gh-456".decodeBase64()
) )
private val locationEventHighRisk = DefaultContactDiaryLocation( private val locationEventHighRisk = DefaultContactDiaryLocation(
locationId = 457, locationId = 457,
locationName = "Kiosk", locationName = "Kiosk",
traceLocationID = "12ab-34cd-56ef-78gh-457" traceLocationID = "12ab-34cd-56ef-78gh-457".decodeBase64()
) )
private val locationEventLowRiskVisit = DefaultContactDiaryLocationVisit( private val locationEventLowRiskVisit = DefaultContactDiaryLocationVisit(
......
package de.rki.coronawarnapp.presencetracing.checkins.checkout package de.rki.coronawarnapp.presencetracing.checkins.checkout
import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository
import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.TimeStamper
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.runs
import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.runBlockingTest
import okio.ByteString.Companion.encode import okio.ByteString.Companion.encode
import org.joda.time.Instant import org.joda.time.Instant
...@@ -20,7 +22,7 @@ class CheckOutHandlerTest : BaseTest() { ...@@ -20,7 +22,7 @@ class CheckOutHandlerTest : BaseTest() {
@MockK lateinit var repository: CheckInRepository @MockK lateinit var repository: CheckInRepository
@MockK lateinit var timeStamper: TimeStamper @MockK lateinit var timeStamper: TimeStamper
@MockK lateinit var diaryRepository: ContactDiaryRepository @MockK lateinit var contactJournalCheckInEntryCreator: ContactJournalCheckInEntryCreator
private val testCheckIn = CheckIn( private val testCheckIn = CheckIn(
id = 42L, id = 42L,
...@@ -39,6 +41,12 @@ class CheckOutHandlerTest : BaseTest() { ...@@ -39,6 +41,12 @@ class CheckOutHandlerTest : BaseTest() {
completed = false, completed = false,
createJournalEntry = true createJournalEntry = true
) )
private val testCheckInDontCreate = testCheckIn.copy(
id = 43L,
createJournalEntry = false
)
private var updatedCheckIn: CheckIn? = null private var updatedCheckIn: CheckIn? = null
private val nowUTC = Instant.ofEpochMilli(50) private val nowUTC = Instant.ofEpochMilli(50)
...@@ -52,12 +60,19 @@ class CheckOutHandlerTest : BaseTest() { ...@@ -52,12 +60,19 @@ class CheckOutHandlerTest : BaseTest() {
val callback: (CheckIn) -> CheckIn = arg(1) val callback: (CheckIn) -> CheckIn = arg(1)
updatedCheckIn = callback(testCheckIn) updatedCheckIn = callback(testCheckIn)
} }
coEvery { repository.updateCheckIn(43, any()) } coAnswers {
val callback: (CheckIn) -> CheckIn = arg(1)
updatedCheckIn = callback(testCheckInDontCreate)
}
coEvery { contactJournalCheckInEntryCreator.createEntry(any()) } just runs
} }
private fun createInstance() = CheckOutHandler( private fun createInstance() = CheckOutHandler(
repository = repository, repository = repository,
timeStamper = timeStamper, timeStamper = timeStamper,
diaryRepository = diaryRepository, contactJournalCheckInEntryCreator = contactJournalCheckInEntryCreator
) )
@Test @Test
...@@ -68,7 +83,37 @@ class CheckOutHandlerTest : BaseTest() { ...@@ -68,7 +83,37 @@ class CheckOutHandlerTest : BaseTest() {
checkInEnd = nowUTC, checkInEnd = nowUTC,
completed = true completed = true
) )
// TODO journal creation
coVerify(exactly = 1) {
contactJournalCheckInEntryCreator.createEntry(any())
}
// TODO cancel auto checkouts // TODO cancel auto checkouts
} }
@Test
fun `Creates entry if create journal entry is true`() = runBlockingTest {
createInstance().apply {
checkOut(42)
}
updatedCheckIn?.createJournalEntry shouldBe true
coVerify(exactly = 1) {
contactJournalCheckInEntryCreator.createEntry(any())
}
}
@Test
fun `Does not create entry if create journal entry is false`() = runBlockingTest {
createInstance().apply {
checkOut(43)
}
updatedCheckIn?.createJournalEntry shouldBe false
coVerify(exactly = 0) {
contactJournalCheckInEntryCreator.createEntry(any())
}
}
} }
package de.rki.coronawarnapp.presencetracing.checkins.checkout
import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation
import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.runs
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runBlockingTest
import okio.ByteString.Companion.encode
import org.joda.time.Instant
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
class ContactJournalCheckInEntryCreatorTest : BaseTest() {
@MockK lateinit var contactDiaryRepo: ContactDiaryRepository
private val testCheckIn = CheckIn(
id = 42L,
traceLocationId = "traceLocationId1".encode(),
version = 1,
type = 1,
description = "Restaurant",
address = "Around the corner",
traceLocationStart = Instant.parse("2021-03-04T22:00+01:00"),
traceLocationEnd = Instant.parse("2021-03-04T23:00+01:00"),
defaultCheckInLengthInMinutes = null,
cryptographicSeed = "cryptographicSeed".encode(),
cnPublicKey = "cnPublicKey",
checkInStart = Instant.parse("2021-03-04T22:00+01:00"),
checkInEnd = Instant.parse("2021-03-04T23:00+01:00"),
completed = false,
createJournalEntry = true
)
private val testLocation = DefaultContactDiaryLocation(
locationId = 123L,
locationName = "${testCheckIn.description}, ${testCheckIn.address}, ${testCheckIn.traceLocationStart} - ${testCheckIn.traceLocationEnd}",
traceLocationID = testCheckIn.traceLocationId
)
@BeforeEach
fun setup() {
MockKAnnotations.init(this)
every { contactDiaryRepo.locations } returns flowOf(emptyList())
every { contactDiaryRepo.locationVisits } returns flowOf(emptyList())
coEvery { contactDiaryRepo.addLocationVisit(any()) } just runs
coEvery { contactDiaryRepo.addLocation(any()) } returns testLocation
}
private fun createInstance() = ContactJournalCheckInEntryCreator(
diaryRepository = contactDiaryRepo
)
@Test
fun `Creates location if missing`() = runBlockingTest {
every { contactDiaryRepo.locations } returns flowOf(emptyList()) andThen flowOf(listOf(testLocation))
// Repo returns an empty list for the first call, so location is missing and a new location should be created and added
val instance = createInstance()
instance.createEntry(testCheckIn)
coVerify(exactly = 1) {
contactDiaryRepo.addLocation(any())
}
// Location with trace location id already exists, so that location will be used
instance.createEntry(testCheckIn)
instance.createEntry(testCheckIn)
instance.createEntry(testCheckIn)
coVerify(exactly = 1) {
contactDiaryRepo.addLocation(any())
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment