test: add scriptable infrared fakes
This commit is contained in:
+28
-2
@@ -82,6 +82,8 @@ class FakeInfraredGateway : InfraredGateway {
|
|||||||
val learnedKeys: MutableList<InfraredCall> = Collections.synchronizedList(mutableListOf())
|
val learnedKeys: MutableList<InfraredCall> = Collections.synchronizedList(mutableListOf())
|
||||||
val downloadCalls: MutableList<InfraredDownloadCall> =
|
val downloadCalls: MutableList<InfraredDownloadCall> =
|
||||||
Collections.synchronizedList(mutableListOf())
|
Collections.synchronizedList(mutableListOf())
|
||||||
|
private val sendResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>()
|
||||||
|
private val learnResults = ConcurrentHashMap<InfraredOperationKey, DomainResult<Unit>>()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
var sendResult: DomainResult<Unit> = DomainResult.Success(Unit)
|
var sendResult: DomainResult<Unit> = DomainResult.Success(Unit)
|
||||||
@@ -98,7 +100,7 @@ class FakeInfraredGateway : InfraredGateway {
|
|||||||
key: String,
|
key: String,
|
||||||
): DomainResult<Unit> {
|
): DomainResult<Unit> {
|
||||||
sentKeys += InfraredCall(profileId, deviceName, key)
|
sentKeys += InfraredCall(profileId, deviceName, key)
|
||||||
return sendResult
|
return sendResults[InfraredOperationKey(profileId, deviceName, key)] ?: sendResult
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun learnKey(
|
override suspend fun learnKey(
|
||||||
@@ -107,7 +109,25 @@ class FakeInfraredGateway : InfraredGateway {
|
|||||||
key: String,
|
key: String,
|
||||||
): DomainResult<Unit> {
|
): DomainResult<Unit> {
|
||||||
learnedKeys += InfraredCall(profileId, deviceName, key)
|
learnedKeys += InfraredCall(profileId, deviceName, key)
|
||||||
return learnResult
|
return learnResults[InfraredOperationKey(profileId, deviceName, key)] ?: learnResult
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSendResult(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
deviceName: DeviceName,
|
||||||
|
key: String,
|
||||||
|
result: DomainResult<Unit>,
|
||||||
|
) {
|
||||||
|
sendResults[InfraredOperationKey(profileId, deviceName, key)] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLearnResult(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
deviceName: DeviceName,
|
||||||
|
key: String,
|
||||||
|
result: DomainResult<Unit>,
|
||||||
|
) {
|
||||||
|
learnResults[InfraredOperationKey(profileId, deviceName, key)] = result
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun downloadCodes(
|
override suspend fun downloadCodes(
|
||||||
@@ -131,6 +151,12 @@ data class InfraredDownloadCall(
|
|||||||
val codes: List<InfraredCode>,
|
val codes: List<InfraredCode>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class InfraredOperationKey(
|
||||||
|
val profileId: ProviderProfileId,
|
||||||
|
val deviceName: DeviceName,
|
||||||
|
val key: String,
|
||||||
|
)
|
||||||
|
|
||||||
private data class DeviceKey(
|
private data class DeviceKey(
|
||||||
val profileId: ProviderProfileId,
|
val profileId: ProviderProfileId,
|
||||||
val deviceName: DeviceName,
|
val deviceName: DeviceName,
|
||||||
|
|||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
package com.flagship.abox.manager.testing.fakes
|
||||||
|
|
||||||
|
import com.flagship.abox.manager.domain.DeviceName
|
||||||
|
import com.flagship.abox.manager.domain.DomainError
|
||||||
|
import com.flagship.abox.manager.domain.DomainResult
|
||||||
|
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||||
|
import com.flagship.abox.manager.domain.infrared.INTERRUPTED_LEARNING_ERROR_DETAIL
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredDeviceConfigurationRepository
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRepository
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyStatus
|
||||||
|
import com.flagship.abox.manager.domain.infrared.SavedInfraredKey
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class FakeInfraredKeyRepository : InfraredKeyRepository {
|
||||||
|
private val lock = Any()
|
||||||
|
private val keys = linkedMapOf<InfraredKeyRef, SavedInfraredKey>()
|
||||||
|
private val state = MutableStateFlow<List<SavedInfraredKey>>(emptyList())
|
||||||
|
|
||||||
|
override fun observeKeys(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
deviceName: DeviceName,
|
||||||
|
): Flow<DomainResult<List<SavedInfraredKey>>> = state.map { values ->
|
||||||
|
DomainResult.Success(
|
||||||
|
values.filter { it.profileId == profileId && it.deviceName == deviceName },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getKey(ref: InfraredKeyRef): DomainResult<SavedInfraredKey?> =
|
||||||
|
synchronized(lock) {
|
||||||
|
DomainResult.Success(keys[ref])
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insert(key: SavedInfraredKey): DomainResult<Unit> = synchronized(lock) {
|
||||||
|
val ref = key.ref()
|
||||||
|
if (keys.keys.any { it.keyId == key.id }) {
|
||||||
|
return@synchronized invalidRequest("Infrared key id already exists")
|
||||||
|
}
|
||||||
|
if (keys.values.any {
|
||||||
|
it.profileId == key.profileId &&
|
||||||
|
it.deviceName == key.deviceName &&
|
||||||
|
it.sdkKey == key.sdkKey
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return@synchronized invalidRequest("Infrared key binding already exists")
|
||||||
|
}
|
||||||
|
keys[ref] = key
|
||||||
|
publishLocked()
|
||||||
|
DomainResult.Success(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun transitionStatus(
|
||||||
|
ref: InfraredKeyRef,
|
||||||
|
expectedStatuses: Set<InfraredKeyStatus>,
|
||||||
|
targetStatus: InfraredKeyStatus,
|
||||||
|
lastErrorCode: String?,
|
||||||
|
lastErrorDetail: String?,
|
||||||
|
updatedAtEpochMillis: Long,
|
||||||
|
): DomainResult<Unit> = synchronized(lock) {
|
||||||
|
val existing = keys[ref] ?: return@synchronized keyNotFound()
|
||||||
|
if (existing.status !in expectedStatuses) {
|
||||||
|
return@synchronized invalidRequest("Infrared key status changed")
|
||||||
|
}
|
||||||
|
keys[ref] = existing.copy(
|
||||||
|
status = targetStatus,
|
||||||
|
lastErrorCode = lastErrorCode,
|
||||||
|
lastErrorDetail = lastErrorDetail,
|
||||||
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
|
)
|
||||||
|
publishLocked()
|
||||||
|
DomainResult.Success(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun rename(
|
||||||
|
ref: InfraredKeyRef,
|
||||||
|
displayName: String,
|
||||||
|
updatedAtEpochMillis: Long,
|
||||||
|
): DomainResult<Unit> = synchronized(lock) {
|
||||||
|
val existing = keys[ref] ?: return@synchronized keyNotFound()
|
||||||
|
keys[ref] = existing.copy(
|
||||||
|
displayName = displayName,
|
||||||
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
|
)
|
||||||
|
publishLocked()
|
||||||
|
DomainResult.Success(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteIfNotLearning(ref: InfraredKeyRef): DomainResult<Unit> =
|
||||||
|
synchronized(lock) {
|
||||||
|
val existing = keys[ref] ?: return@synchronized keyNotFound()
|
||||||
|
if (existing.status == InfraredKeyStatus.LEARNING) {
|
||||||
|
return@synchronized invalidRequest("Cannot delete a learning infrared key")
|
||||||
|
}
|
||||||
|
keys.remove(ref)
|
||||||
|
publishLocked()
|
||||||
|
DomainResult.Success(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun failInterruptedLearning(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
updatedAtEpochMillis: Long,
|
||||||
|
): DomainResult<Int> = synchronized(lock) {
|
||||||
|
var changed = 0
|
||||||
|
keys.replaceAll { _, key ->
|
||||||
|
if (key.profileId == profileId && key.status == InfraredKeyStatus.LEARNING) {
|
||||||
|
changed += 1
|
||||||
|
key.copy(
|
||||||
|
status = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = INTERRUPTED_LEARNING_ERROR_DETAIL,
|
||||||
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed > 0) publishLocked()
|
||||||
|
DomainResult.Success(changed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun publishLocked() {
|
||||||
|
state.value = keys.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun keyNotFound(): DomainResult.Failure =
|
||||||
|
invalidRequest("Infrared key not found")
|
||||||
|
|
||||||
|
private fun invalidRequest(detail: String): DomainResult.Failure =
|
||||||
|
DomainResult.Failure(DomainError.InvalidRequest(detail = detail))
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeInfraredDeviceConfigurationRepository : InfraredDeviceConfigurationRepository {
|
||||||
|
private val lock = Any()
|
||||||
|
private val deviceNames = MutableStateFlow<Map<ProviderProfileId, DeviceName>>(emptyMap())
|
||||||
|
|
||||||
|
override fun observeDeviceName(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
): Flow<DomainResult<DeviceName?>> = deviceNames.map { values ->
|
||||||
|
DomainResult.Success(values[profileId])
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setDeviceName(
|
||||||
|
profileId: ProviderProfileId,
|
||||||
|
deviceName: DeviceName,
|
||||||
|
): DomainResult<Unit> = synchronized(lock) {
|
||||||
|
val value = deviceName.value
|
||||||
|
if (value.isBlank() || value != value.trim()) {
|
||||||
|
return@synchronized DomainResult.Failure(
|
||||||
|
DomainError.InvalidRequest(detail = "Infrared device name must be trimmed and non-blank"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
deviceNames.value = deviceNames.value + (profileId to deviceName)
|
||||||
|
DomainResult.Success(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SavedInfraredKey.ref() = InfraredKeyRef(
|
||||||
|
profileId = profileId,
|
||||||
|
keyId = id,
|
||||||
|
)
|
||||||
+52
@@ -54,4 +54,56 @@ class FakeDeviceGatewaysTest {
|
|||||||
assertEquals(InfraredDownloadCall(profileOne, listOf(code)), gateway.downloadCalls.single())
|
assertEquals(InfraredDownloadCall(profileOne, listOf(code)), gateway.downloadCalls.single())
|
||||||
assertTrue(download is DomainResult.Success)
|
assertTrue(download is DomainResult.Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun infraredLearnResultsCanDifferByProfileDeviceAndKey() = runTest {
|
||||||
|
val gateway = FakeInfraredGateway()
|
||||||
|
val infraredDevice = DeviceName("ir-1")
|
||||||
|
gateway.setLearnResult(
|
||||||
|
profileOne,
|
||||||
|
infraredDevice,
|
||||||
|
"power",
|
||||||
|
DomainResult.Failure(DomainError.DeviceOffline("20206")),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(gateway.learnKey(profileOne, infraredDevice, "power") is DomainResult.Failure)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.learnKey(profileTwo, infraredDevice, "power"),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.learnKey(profileOne, infraredDevice, "volume-up"),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.learnKey(profileOne, DeviceName("ir-2"), "power"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun infraredSendResultsCanDifferByProfileDeviceAndKey() = runTest {
|
||||||
|
val gateway = FakeInfraredGateway()
|
||||||
|
val infraredDevice = DeviceName("ir-1")
|
||||||
|
gateway.setSendResult(
|
||||||
|
profileOne,
|
||||||
|
infraredDevice,
|
||||||
|
"power",
|
||||||
|
DomainResult.Failure(DomainError.DeviceOffline("20206")),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(gateway.sendKey(profileOne, infraredDevice, "power") is DomainResult.Failure)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.sendKey(profileTwo, infraredDevice, "power"),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.sendKey(profileOne, infraredDevice, "volume-up"),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
gateway.sendKey(profileOne, DeviceName("ir-2"), "power"),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+264
@@ -0,0 +1,264 @@
|
|||||||
|
package com.flagship.abox.manager.testing.fakes
|
||||||
|
|
||||||
|
import com.flagship.abox.manager.domain.DeviceName
|
||||||
|
import com.flagship.abox.manager.domain.DomainError
|
||||||
|
import com.flagship.abox.manager.domain.DomainResult
|
||||||
|
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||||
|
import com.flagship.abox.manager.domain.infrared.INTERRUPTED_LEARNING_ERROR_DETAIL
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyId
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyStatus
|
||||||
|
import com.flagship.abox.manager.domain.infrared.SavedInfraredKey
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.take
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FakeInfraredKeyRepositoryTest {
|
||||||
|
private val profileOne = ProviderProfileId("one")
|
||||||
|
private val profileTwo = ProviderProfileId("two")
|
||||||
|
private val deviceOne = DeviceName("ir-1")
|
||||||
|
private val deviceTwo = DeviceName("ir-2")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun observeKeysEmitsChangesAndFiltersByProfileAndDevice() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
val emissions = backgroundScope.async(start = CoroutineStart.UNDISPATCHED) {
|
||||||
|
repository.observeKeys(profileOne, deviceOne).take(2).toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.insert(savedKey(id = "matching"))
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf(
|
||||||
|
DomainResult.Success(emptyList<SavedInfraredKey>()),
|
||||||
|
DomainResult.Success(listOf(savedKey(id = "matching"))),
|
||||||
|
),
|
||||||
|
emissions.await(),
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.insert(savedKey(id = "other-profile", profileId = profileTwo))
|
||||||
|
repository.insert(savedKey(id = "other-device", deviceName = deviceTwo))
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(listOf(savedKey(id = "matching"))),
|
||||||
|
repository.observeKeys(profileOne, deviceOne).first(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getKeyReturnsOnlyTheMatchingProfileAndId() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
repository.insert(savedKey(id = "power"))
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(savedKey(id = "power")),
|
||||||
|
repository.getKey(InfraredKeyRef(profileOne, InfraredKeyId("power"))),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(null),
|
||||||
|
repository.getKey(InfraredKeyRef(profileTwo, InfraredKeyId("power"))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun insertRejectsDuplicateIdAndDuplicateProviderDeviceSdkBinding() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
assertEquals(DomainResult.Success(Unit), repository.insert(savedKey(id = "power")))
|
||||||
|
|
||||||
|
val duplicateId = repository.insert(
|
||||||
|
savedKey(
|
||||||
|
id = "power",
|
||||||
|
profileId = profileTwo,
|
||||||
|
deviceName = deviceTwo,
|
||||||
|
sdkKey = "other-sdk-key",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val duplicateBinding = repository.insert(
|
||||||
|
savedKey(id = "volume", displayName = "Volume"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertInvalidRequest(duplicateId)
|
||||||
|
assertInvalidRequest(duplicateBinding)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(listOf(savedKey(id = "power"))),
|
||||||
|
repository.observeKeys(profileOne, deviceOne).first(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transitionStatusRequiresExpectedStatusAndUpdatesFailureContextAtomically() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
val ref = InfraredKeyRef(profileOne, InfraredKeyId("power"))
|
||||||
|
repository.insert(savedKey(id = "power", status = InfraredKeyStatus.LEARNING))
|
||||||
|
|
||||||
|
val rejected = repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(InfraredKeyStatus.READY),
|
||||||
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = "20206",
|
||||||
|
lastErrorDetail = "offline",
|
||||||
|
updatedAtEpochMillis = 20L,
|
||||||
|
)
|
||||||
|
assertInvalidRequest(rejected)
|
||||||
|
assertEquals(InfraredKeyStatus.LEARNING, repository.successfulKey(ref)?.status)
|
||||||
|
assertNull(repository.successfulKey(ref)?.lastErrorCode)
|
||||||
|
assertNull(repository.successfulKey(ref)?.lastErrorDetail)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = "20206",
|
||||||
|
lastErrorDetail = "offline",
|
||||||
|
updatedAtEpochMillis = 20L,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
savedKey(
|
||||||
|
id = "power",
|
||||||
|
status = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = "20206",
|
||||||
|
lastErrorDetail = "offline",
|
||||||
|
updatedAtEpochMillis = 20L,
|
||||||
|
),
|
||||||
|
repository.successfulKey(ref),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameUpdatesDisplayNameAndTimestamp() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
val ref = InfraredKeyRef(profileOne, InfraredKeyId("power"))
|
||||||
|
repository.insert(savedKey(id = "power", status = InfraredKeyStatus.READY))
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
repository.rename(ref, displayName = "Main Power", updatedAtEpochMillis = 30L),
|
||||||
|
)
|
||||||
|
assertEquals("Main Power", repository.successfulKey(ref)?.displayName)
|
||||||
|
assertEquals(30L, repository.successfulKey(ref)?.updatedAtEpochMillis)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteRejectsLearningKeysAndRemovesOtherStatuses() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
val learningRef = InfraredKeyRef(profileOne, InfraredKeyId("learning"))
|
||||||
|
val readyRef = InfraredKeyRef(profileOne, InfraredKeyId("ready"))
|
||||||
|
repository.insert(savedKey(id = "learning", sdkKey = "learning", status = InfraredKeyStatus.LEARNING))
|
||||||
|
repository.insert(savedKey(id = "ready", sdkKey = "ready", status = InfraredKeyStatus.READY))
|
||||||
|
|
||||||
|
assertInvalidRequest(repository.deleteIfNotLearning(learningRef))
|
||||||
|
assertEquals(DomainResult.Success(Unit), repository.deleteIfNotLearning(readyRef))
|
||||||
|
assertTrue(repository.successfulKey(learningRef) != null)
|
||||||
|
assertNull(repository.successfulKey(readyRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun failInterruptedLearningRecoversOnlyLearningKeysForOneProfileWithFixedDetail() = runTest {
|
||||||
|
val repository = FakeInfraredKeyRepository()
|
||||||
|
repository.insert(savedKey(id = "one-learning", sdkKey = "one-learning"))
|
||||||
|
repository.insert(
|
||||||
|
savedKey(
|
||||||
|
id = "one-ready",
|
||||||
|
sdkKey = "one-ready",
|
||||||
|
status = InfraredKeyStatus.READY,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
repository.insert(
|
||||||
|
savedKey(
|
||||||
|
id = "two-learning",
|
||||||
|
profileId = profileTwo,
|
||||||
|
sdkKey = "two-learning",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(1),
|
||||||
|
repository.failInterruptedLearning(profileOne, updatedAtEpochMillis = 40L),
|
||||||
|
)
|
||||||
|
|
||||||
|
val recovered = repository.successfulKey(
|
||||||
|
InfraredKeyRef(profileOne, InfraredKeyId("one-learning")),
|
||||||
|
)
|
||||||
|
assertEquals(InfraredKeyStatus.FAILED, recovered?.status)
|
||||||
|
assertNull(recovered?.lastErrorCode)
|
||||||
|
assertEquals(INTERRUPTED_LEARNING_ERROR_DETAIL, recovered?.lastErrorDetail)
|
||||||
|
assertEquals(40L, recovered?.updatedAtEpochMillis)
|
||||||
|
assertEquals(
|
||||||
|
InfraredKeyStatus.READY,
|
||||||
|
repository.successfulKey(InfraredKeyRef(profileOne, InfraredKeyId("one-ready")))?.status,
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
InfraredKeyStatus.LEARNING,
|
||||||
|
repository.successfulKey(InfraredKeyRef(profileTwo, InfraredKeyId("two-learning")))?.status,
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(0),
|
||||||
|
repository.failInterruptedLearning(profileOne, updatedAtEpochMillis = 50L),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deviceConfigurationIsIsolatedByProfile() = runTest {
|
||||||
|
val repository = FakeInfraredDeviceConfigurationRepository()
|
||||||
|
|
||||||
|
assertEquals(DomainResult.Success(null), repository.observeDeviceName(profileOne).first())
|
||||||
|
assertEquals(
|
||||||
|
DomainResult.Success(Unit),
|
||||||
|
repository.setDeviceName(profileOne, deviceOne),
|
||||||
|
)
|
||||||
|
assertEquals(DomainResult.Success(deviceOne), repository.observeDeviceName(profileOne).first())
|
||||||
|
assertEquals(DomainResult.Success(null), repository.observeDeviceName(profileTwo).first())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deviceConfigurationRejectsBlankOrSurroundingWhitespace() = runTest {
|
||||||
|
val repository = FakeInfraredDeviceConfigurationRepository()
|
||||||
|
|
||||||
|
assertInvalidRequest(repository.setDeviceName(profileOne, DeviceName(" ")))
|
||||||
|
assertInvalidRequest(repository.setDeviceName(profileOne, DeviceName(" ir-1")))
|
||||||
|
assertInvalidRequest(repository.setDeviceName(profileOne, DeviceName("ir-1 ")))
|
||||||
|
assertEquals(DomainResult.Success(null), repository.observeDeviceName(profileOne).first())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun savedKey(
|
||||||
|
id: String,
|
||||||
|
profileId: ProviderProfileId = profileOne,
|
||||||
|
deviceName: DeviceName = deviceOne,
|
||||||
|
displayName: String = "Power",
|
||||||
|
sdkKey: String = "power",
|
||||||
|
status: InfraredKeyStatus = InfraredKeyStatus.LEARNING,
|
||||||
|
lastErrorCode: String? = null,
|
||||||
|
lastErrorDetail: String? = null,
|
||||||
|
updatedAtEpochMillis: Long = 10L,
|
||||||
|
) = SavedInfraredKey(
|
||||||
|
id = InfraredKeyId(id),
|
||||||
|
profileId = profileId,
|
||||||
|
deviceName = deviceName,
|
||||||
|
displayName = displayName,
|
||||||
|
sdkKey = sdkKey,
|
||||||
|
status = status,
|
||||||
|
lastErrorCode = lastErrorCode,
|
||||||
|
lastErrorDetail = lastErrorDetail,
|
||||||
|
createdAtEpochMillis = 10L,
|
||||||
|
updatedAtEpochMillis = updatedAtEpochMillis,
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun FakeInfraredKeyRepository.successfulKey(
|
||||||
|
ref: InfraredKeyRef,
|
||||||
|
): SavedInfraredKey? = (getKey(ref) as DomainResult.Success<SavedInfraredKey?>).value
|
||||||
|
|
||||||
|
private fun assertInvalidRequest(result: DomainResult<*>) {
|
||||||
|
assertTrue(result is DomainResult.Failure)
|
||||||
|
assertTrue((result as DomainResult.Failure).error is DomainError.InvalidRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user