Skip to content
Snippets Groups Projects
Unverified Commit 048d5e4a authored by Lukas Lechner's avatar Lukas Lechner Committed by GitHub
Browse files

Adapt auto-checkout default values (EXPOSUREAPP-6243) (#2791)


* Prepare calculation of defaultAutoCheckoutLength

* Not allow defaultCheckInLength < 15min and > 23h45min

* Add more test cases

* Make some tests green

* Make ALL tests green

* Disable Confirm-CheckIn Button when automatic checkout is set to 00:00

* Suppress detekt issue

* Add test for enabling and disabling the confirm button in ConfirmCheckInViewModel.kt

* Add and adapt some comments

* Extract default auto checkout duration logic to extension function

* Add test and comments for roundToNearest15Minutes

* Rename AutoCheckoutHelper.kt -> DefaultAutoCheckoutLength.kt

Co-authored-by: default avatarharambasicluka <64483219+harambasicluka@users.noreply.github.com>
parent 41d3c1bc
No related branches found
No related tags found
No related merge requests found
package de.rki.coronawarnapp.eventregistration.checkins.qrcode
import androidx.annotation.VisibleForTesting
import org.joda.time.Duration
import org.joda.time.Instant
import java.util.concurrent.TimeUnit
import kotlin.math.roundToLong
/**
* Evaluates the default auto-checkout length depending on the current time
*/
@Suppress("ReturnCount")
fun TraceLocation.getDefaultAutoCheckoutLengthInMinutes(now: Instant): Int {
// min valid value is 00:15h
val minDefaultAutoCheckOutLengthInMinutes = 15
// max valid value is 23:45h
val maxDefaultAutoCheckOutLengthInMinutes = (TimeUnit.HOURS.toMinutes(23) + 45).toInt()
// for permanent traceLocations, a defaultCheckInLength is always available
if (defaultCheckInLengthInMinutes != null) {
if (defaultCheckInLengthInMinutes < 15) {
return minDefaultAutoCheckOutLengthInMinutes
}
if (defaultCheckInLengthInMinutes > maxDefaultAutoCheckOutLengthInMinutes) {
return maxDefaultAutoCheckOutLengthInMinutes
}
return roundToNearest15Minutes(defaultCheckInLengthInMinutes)
}
// for temporary traceLocations, the defaultCheckInLength could be empty
else {
// For QR-codes generated by CWA, endDate can never be null here. However, a QR code that was created or
// modified by a third party could have an endDate that is null.
if (endDate == null) {
return minDefaultAutoCheckOutLengthInMinutes
}
if (now.isAfter(endDate)) {
return minDefaultAutoCheckOutLengthInMinutes
}
val minutesUntilEndDate = Duration(now, endDate).standardMinutes.toInt()
if (minutesUntilEndDate < minDefaultAutoCheckOutLengthInMinutes) {
return minDefaultAutoCheckOutLengthInMinutes
}
if (minutesUntilEndDate > maxDefaultAutoCheckOutLengthInMinutes) {
return maxDefaultAutoCheckOutLengthInMinutes
}
return roundToNearest15Minutes(minutesUntilEndDate)
}
}
/**
* Rounds to the nearest 15 minute interval.
* for more details see AutoCheckoutHelperTest
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun roundToNearest15Minutes(minutes: Int): Int {
val roundingStepInMinutes = 15
return Duration
.standardMinutes(
(minutes.toFloat() / roundingStepInMinutes)
.roundToLong() * roundingStepInMinutes
)
.standardMinutes.toInt()
}
...@@ -91,6 +91,7 @@ class ConfirmCheckInFragment : Fragment(R.layout.fragment_confirm_check_in), Aut ...@@ -91,6 +91,7 @@ class ConfirmCheckInFragment : Fragment(R.layout.fragment_confirm_check_in), Aut
uiState.eventInFutureDateText, uiState.eventInFutureDateText,
uiState.eventInFutureTimeText uiState.eventInFutureTimeText
) )
confirmCheckinConfirmButton.isEnabled = uiState.confirmButtonEnabled
} }
} }
......
...@@ -8,6 +8,7 @@ import de.rki.coronawarnapp.eventregistration.checkins.CheckIn ...@@ -8,6 +8,7 @@ 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.eventregistration.checkins.qrcode.TraceLocation import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation import de.rki.coronawarnapp.eventregistration.checkins.qrcode.VerifiedTraceLocation
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.getDefaultAutoCheckoutLengthInMinutes
import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat
import de.rki.coronawarnapp.ui.durationpicker.toReadableDuration import de.rki.coronawarnapp.ui.durationpicker.toReadableDuration
import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.mapTraceLocationToTitleRes import de.rki.coronawarnapp.ui.eventregistration.organizer.category.adapter.category.mapTraceLocationToTitleRes
...@@ -20,7 +21,6 @@ import kotlinx.coroutines.flow.combine ...@@ -20,7 +21,6 @@ import kotlinx.coroutines.flow.combine
import org.joda.time.Duration import org.joda.time.Duration
import org.joda.time.Instant import org.joda.time.Instant
import org.joda.time.format.DateTimeFormat import org.joda.time.format.DateTimeFormat
import kotlin.math.roundToLong
class ConfirmCheckInViewModel @AssistedInject constructor( class ConfirmCheckInViewModel @AssistedInject constructor(
@Assisted private val verifiedTraceLocation: VerifiedTraceLocation, @Assisted private val verifiedTraceLocation: VerifiedTraceLocation,
...@@ -30,13 +30,11 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -30,13 +30,11 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
private val traceLocation = MutableStateFlow(verifiedTraceLocation.traceLocation) private val traceLocation = MutableStateFlow(verifiedTraceLocation.traceLocation)
private val createJournalEntry = MutableStateFlow(true) private val createJournalEntry = MutableStateFlow(true)
private val truncatedDefaultCheckInLength = roundToNearestValidDuration( private val autoCheckOutLength = MutableStateFlow(
verifiedTraceLocation.traceLocation.defaultCheckInLengthInMinutes ?: 0 Duration.standardMinutes(
verifiedTraceLocation.traceLocation.getDefaultAutoCheckoutLengthInMinutes(timeStamper.nowUTC).toLong()
)
) )
private val checkInLength = MutableStateFlow(truncatedDefaultCheckInLength)
private fun roundToNearestValidDuration(minutes: Int): Duration =
Duration.standardMinutes((minutes.toFloat() / 15).roundToLong() * 15)
val openDatePickerEvent = SingleLiveEvent<String>() val openDatePickerEvent = SingleLiveEvent<String>()
val events = SingleLiveEvent<ConfirmCheckInNavigation>() val events = SingleLiveEvent<ConfirmCheckInNavigation>()
...@@ -44,14 +42,15 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -44,14 +42,15 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
val uiState = combine( val uiState = combine(
traceLocation, traceLocation,
createJournalEntry, createJournalEntry,
checkInLength autoCheckOutLength
) { traceLocation, createEntry, checkInLength -> ) { traceLocation, createEntry, checkInLength ->
UiState( UiState(
traceLocation = traceLocation, traceLocation = traceLocation,
createJournalEntry = createEntry, createJournalEntry = createEntry,
checkInEndOffset = checkInLength, checkInEndOffset = checkInLength,
eventInPastVisible = traceLocation.isAfterEndTime(timeStamper.nowUTC), eventInPastVisible = traceLocation.isAfterEndTime(timeStamper.nowUTC),
eventInFutureVisible = traceLocation.isBeforeStartTime(timeStamper.nowUTC) eventInFutureVisible = traceLocation.isBeforeStartTime(timeStamper.nowUTC),
confirmButtonEnabled = checkInLength.standardMinutes > 0
) )
}.asLiveData() }.asLiveData()
...@@ -66,7 +65,7 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -66,7 +65,7 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
verifiedTraceLocation.toCheckIn( verifiedTraceLocation.toCheckIn(
checkInStart = now, checkInStart = now,
createJournalEntry = createJournalEntry.value, createJournalEntry = createJournalEntry.value,
checkInEnd = now + checkInLength.value checkInEnd = now + autoCheckOutLength.value
) )
) )
events.postValue(ConfirmCheckInNavigation.ConfirmNavigation) events.postValue(ConfirmCheckInNavigation.ConfirmNavigation)
...@@ -78,18 +77,16 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -78,18 +77,16 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
} }
fun dateSelectorClicked() { fun dateSelectorClicked() {
openDatePickerEvent.value = checkInLength.value.toContactDiaryFormat() openDatePickerEvent.value = autoCheckOutLength.value.toContactDiaryFormat()
} }
fun durationUpdated(duration: Duration) { fun durationUpdated(duration: Duration) {
checkInLength.value = duration autoCheckOutLength.value = duration
} }
private fun VerifiedTraceLocation.toCheckIn( private fun VerifiedTraceLocation.toCheckIn(
checkInStart: Instant, checkInStart: Instant,
checkInEnd: Instant = checkInStart.plus( checkInEnd: Instant,
Duration.standardMinutes(traceLocation.defaultCheckInLengthInMinutes?.toLong() ?: 3L)
),
completed: Boolean = false, completed: Boolean = false,
createJournalEntry: Boolean = true createJournalEntry: Boolean = true
): CheckIn { ): CheckIn {
...@@ -102,7 +99,7 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -102,7 +99,7 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
address = traceLocation.address, address = traceLocation.address,
traceLocationStart = traceLocation.startDate, traceLocationStart = traceLocation.startDate,
traceLocationEnd = traceLocation.endDate, traceLocationEnd = traceLocation.endDate,
defaultCheckInLengthInMinutes = truncatedDefaultCheckInLength.standardMinutes.toInt(), defaultCheckInLengthInMinutes = traceLocation.defaultCheckInLengthInMinutes,
cryptographicSeed = traceLocation.cryptographicSeed, cryptographicSeed = traceLocation.cryptographicSeed,
cnPublicKey = traceLocation.cnPublicKey, cnPublicKey = traceLocation.cnPublicKey,
checkInStart = checkInStart, checkInStart = checkInStart,
...@@ -124,7 +121,8 @@ class ConfirmCheckInViewModel @AssistedInject constructor( ...@@ -124,7 +121,8 @@ class ConfirmCheckInViewModel @AssistedInject constructor(
private val checkInEndOffset: Duration, private val checkInEndOffset: Duration,
val createJournalEntry: Boolean, val createJournalEntry: Boolean,
val eventInPastVisible: Boolean, val eventInPastVisible: Boolean,
val eventInFutureVisible: Boolean val eventInFutureVisible: Boolean,
val confirmButtonEnabled: Boolean
) { ) {
val description get() = traceLocation.description val description get() = traceLocation.description
val typeRes get() = mapTraceLocationToTitleRes(traceLocation.type) val typeRes get() = mapTraceLocationToTitleRes(traceLocation.type)
......
...@@ -14,6 +14,7 @@ import io.mockk.coEvery ...@@ -14,6 +14,7 @@ import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.decodeBase64
import org.joda.time.Duration
import org.joda.time.Instant import org.joda.time.Instant
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
...@@ -29,8 +30,6 @@ class ConfirmCheckInViewModelTest : BaseTest() { ...@@ -29,8 +30,6 @@ class ConfirmCheckInViewModelTest : BaseTest() {
@MockK lateinit var checkInRepository: CheckInRepository @MockK lateinit var checkInRepository: CheckInRepository
@MockK lateinit var timeStamper: TimeStamper @MockK lateinit var timeStamper: TimeStamper
private lateinit var viewModel: ConfirmCheckInViewModel
private val traceLocation = TraceLocation( private val traceLocation = TraceLocation(
id = 1, id = 1,
type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER, type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER,
...@@ -51,23 +50,35 @@ class ConfirmCheckInViewModelTest : BaseTest() { ...@@ -51,23 +50,35 @@ class ConfirmCheckInViewModelTest : BaseTest() {
coEvery { checkInRepository.addCheckIn(any()) } returns 1L coEvery { checkInRepository.addCheckIn(any()) } returns 1L
every { verifiedTraceLocation.traceLocation } returns traceLocation every { verifiedTraceLocation.traceLocation } returns traceLocation
every { timeStamper.nowUTC } returns Instant.parse("2021-03-04T10:30:00Z") every { timeStamper.nowUTC } returns Instant.parse("2021-03-04T10:30:00Z")
}
private fun createInstance() = ConfirmCheckInViewModel(
verifiedTraceLocation = verifiedTraceLocation,
checkInRepository = checkInRepository,
timeStamper = timeStamper
)
viewModel = ConfirmCheckInViewModel( @Test
verifiedTraceLocation = verifiedTraceLocation, fun onClose() = with(createInstance()) {
checkInRepository = checkInRepository, onClose()
timeStamper = timeStamper events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.BackNavigation
) }
@Test
fun onConfirmEvent() = with(createInstance()) {
onConfirmTraceLocation()
events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.ConfirmNavigation
} }
@Test @Test
fun onClose() { fun `confirm button should be disabled when autoCheckOutLength is 0`() = with(createInstance()) {
viewModel.onClose() durationUpdated(Duration.standardMinutes(0))
viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.BackNavigation uiState.getOrAwaitValue().confirmButtonEnabled shouldBe false
} }
@Test @Test
fun onConfirmEvent() { fun `confirm button should be enabled when autoCheckOutLength is greater than 0`() = with(createInstance()) {
viewModel.onConfirmTraceLocation() durationUpdated(Duration.standardMinutes(15))
viewModel.events.getOrAwaitValue() shouldBe ConfirmCheckInNavigation.ConfirmNavigation uiState.getOrAwaitValue().confirmButtonEnabled shouldBe true
} }
} }
package de.rki.coronawarnapp.eventregistration.checkins.qrcode
import io.kotest.matchers.shouldBe
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
internal class AutoCheckoutHelperTest {
@ParameterizedTest
@MethodSource("provideArguments")
fun `roundToNearest15Minutes() should round correctly`(testCase: TestCase) = with(testCase) {
roundToNearest15Minutes(minutesToRound) shouldBe expectedRoundingResult
}
companion object {
@Suppress("unused")
@JvmStatic
fun provideArguments() = listOf(
TestCase(
minutesToRound = 0,
expectedRoundingResult = 0
),
TestCase(
minutesToRound = 7,
expectedRoundingResult = 0
),
TestCase(
minutesToRound = 8,
expectedRoundingResult = 15
),
TestCase(
minutesToRound = 22,
expectedRoundingResult = 15
),
TestCase(
minutesToRound = 23,
expectedRoundingResult = 30
)
)
}
data class TestCase(
val minutesToRound: Int,
val expectedRoundingResult: Int
)
}
package de.rki.coronawarnapp.eventregistration.events
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.TraceLocation
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.getDefaultAutoCheckoutLengthInMinutes
import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass
import io.kotest.matchers.shouldBe
import okio.ByteString.Companion.encode
import org.joda.time.Instant
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import testhelpers.BaseTest
import java.util.concurrent.TimeUnit
class DefaultAutoCheckoutLengthTest : BaseTest() {
@ParameterizedTest
@MethodSource("provideArguments")
fun `getDefaultAuthCheckoutLengthInMinutes(now) should return correct value`(
testCase: DefaultAutoCheckoutLengthTestCase
) = with(testCase) {
createTraceLocation(this)
.getDefaultAutoCheckoutLengthInMinutes(now) shouldBe expectedDefaultAutoCheckoutLength
}
private fun createTraceLocation(testCase: DefaultAutoCheckoutLengthTestCase) = TraceLocation(
id = 1,
type = TraceLocationOuterClass.TraceLocationType.UNRECOGNIZED,
description = "",
address = "",
startDate = testCase.startDate,
endDate = testCase.endDate,
defaultCheckInLengthInMinutes = testCase.defaultCheckInLengthInMinutes,
cryptographicSeed = "seed byte array".encode(),
cnPublicKey = "cnPublicKey"
)
companion object {
// min valid length = 00:15h
private const val MIN_VALID_LENGTH = 15
// max valid length = 23:45h
private val MAX_VALID_LENGTH = (TimeUnit.HOURS.toMinutes(23) + 45).toInt()
@Suppress("unused")
@JvmStatic
fun provideArguments() = listOf(
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
defaultCheckInLengthInMinutes = 30,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = 30,
),
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter here, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
// min valid length = 00:15h
defaultCheckInLengthInMinutes = 0,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = MIN_VALID_LENGTH
),
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter here, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
// TraceLocations with CWA can actually only have 15 minute interval lengths. However, a trace location
// created by a third party could create arbitrary lengths.
// 22 min should be rounded to 15
defaultCheckInLengthInMinutes = 22,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = 15
),
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter here, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
// TraceLocations with CWA can actually only have 15 minute interval lengths. However, a trace location
// created by a third party could create arbitrary lengths.
// 23 min should be rounded to 30
defaultCheckInLengthInMinutes = 23,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = 30
),
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter here, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
// max valid length = 23:45h
defaultCheckInLengthInMinutes = MAX_VALID_LENGTH,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = MAX_VALID_LENGTH
),
DefaultAutoCheckoutLengthTestCase(
// now doesn't matter here, as defaultCheckInLengthInMinutes is not null
now = Instant.parse("1970-01-01T00:00:00.000Z"),
// max valid length = 23:45h
// TraceLocations with CWA can actually only have a max length of 23:45h. However, a trace location
// created by a third party could have a bigger length.
defaultCheckInLengthInMinutes = MAX_VALID_LENGTH + 1,
startDate = null,
endDate = null,
expectedDefaultAutoCheckoutLength = MAX_VALID_LENGTH
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-23T17:00:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// use 23:45h if user checks in earlier than 23:45 before the event ends
expectedDefaultAutoCheckoutLength = MAX_VALID_LENGTH
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-23T17:30:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// 23:30h in minutes
expectedDefaultAutoCheckoutLength = (TimeUnit.HOURS.toMinutes(23) + 30).toInt()
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T16:00:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
expectedDefaultAutoCheckoutLength = 60
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T17:01:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
expectedDefaultAutoCheckoutLength = 15
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T17:30:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// We take the min value when the user checks in after the event has already ended
expectedDefaultAutoCheckoutLength = 15
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T16:31:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// Event ends in 29min -> we round to the nearest 15 minutes
expectedDefaultAutoCheckoutLength = 30
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T16:38:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// Event ends in 22min -> we round to the nearest 15 minutes
expectedDefaultAutoCheckoutLength = 15
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T16:59:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// Event ends in 1min -> we are not rounding to 0 but to the min value (15min)
expectedDefaultAutoCheckoutLength = 15
),
DefaultAutoCheckoutLengthTestCase(
now = Instant.parse("2021-12-24T17:00:00.000Z"),
defaultCheckInLengthInMinutes = null,
startDate = Instant.parse("2021-12-24T15:00:00.000Z"),
endDate = Instant.parse("2021-12-24T17:00:00.000Z"),
// We check in at event end, return min value (15min)
expectedDefaultAutoCheckoutLength = 15
)
)
}
}
data class DefaultAutoCheckoutLengthTestCase(
val now: Instant,
val defaultCheckInLengthInMinutes: Int?,
val startDate: Instant?,
val endDate: Instant?,
val expectedDefaultAutoCheckoutLength: Int,
)
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