fix(infrared): add relearn use case for ready keys and clean up interrupted learning in onCleared and init
This commit is contained in:
+16
@@ -5,15 +5,21 @@ import com.flagship.abox.manager.domain.DomainResult
|
|||||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyId
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyId
|
||||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
||||||
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRepository
|
||||||
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
||||||
|
import com.flagship.abox.manager.domain.infrared.RelearnInfraredKey
|
||||||
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class InfraredLearningCoordinator(
|
class InfraredLearningCoordinator(
|
||||||
private val learnInfraredKey: LearnInfraredKey,
|
private val learnInfraredKey: LearnInfraredKey,
|
||||||
private val retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
private val retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
||||||
|
private val relearnInfraredKey: RelearnInfraredKey,
|
||||||
|
private val keyRepository: InfraredKeyRepository,
|
||||||
private val externalScope: CoroutineScope,
|
private val externalScope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
fun learn(
|
fun learn(
|
||||||
@@ -28,4 +34,14 @@ class InfraredLearningCoordinator(
|
|||||||
fun retry(ref: InfraredKeyRef): Deferred<DomainResult<Unit>> = externalScope.async {
|
fun retry(ref: InfraredKeyRef): Deferred<DomainResult<Unit>> = externalScope.async {
|
||||||
retryInfraredKeyLearning(ref)
|
retryInfraredKeyLearning(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun relearn(ref: InfraredKeyRef): Deferred<DomainResult<Unit>> = externalScope.async {
|
||||||
|
relearnInfraredKey(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun failInterruptedLearning(profileId: ProviderProfileId, updatedAtEpochMillis: Long = System.currentTimeMillis()) {
|
||||||
|
externalScope.launch(NonCancellable) {
|
||||||
|
keyRepository.failInterruptedLearning(profileId, updatedAtEpochMillis)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -170,15 +170,28 @@ object InfraredFeatureModule {
|
|||||||
clock: EpochMillisClock,
|
clock: EpochMillisClock,
|
||||||
): RecoverInterruptedInfraredLearning = RecoverInterruptedInfraredLearning(repository, clock)
|
): RecoverInterruptedInfraredLearning = RecoverInterruptedInfraredLearning(repository, clock)
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideRelearnInfraredKey(
|
||||||
|
repository: InfraredKeyRepository,
|
||||||
|
gateway: InfraredGateway,
|
||||||
|
clock: EpochMillisClock,
|
||||||
|
): com.flagship.abox.manager.domain.infrared.RelearnInfraredKey =
|
||||||
|
com.flagship.abox.manager.domain.infrared.RelearnInfraredKey(repository, gateway, clock)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideInfraredLearningCoordinator(
|
fun provideInfraredLearningCoordinator(
|
||||||
learnInfraredKey: LearnInfraredKey,
|
learnInfraredKey: LearnInfraredKey,
|
||||||
retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
retryInfraredKeyLearning: RetryInfraredKeyLearning,
|
||||||
|
relearnInfraredKey: com.flagship.abox.manager.domain.infrared.RelearnInfraredKey,
|
||||||
|
repository: InfraredKeyRepository,
|
||||||
@InfraredApplicationScope scope: CoroutineScope,
|
@InfraredApplicationScope scope: CoroutineScope,
|
||||||
): InfraredLearningCoordinator = InfraredLearningCoordinator(
|
): InfraredLearningCoordinator = InfraredLearningCoordinator(
|
||||||
learnInfraredKey = learnInfraredKey,
|
learnInfraredKey = learnInfraredKey,
|
||||||
retryInfraredKeyLearning = retryInfraredKeyLearning,
|
retryInfraredKeyLearning = retryInfraredKeyLearning,
|
||||||
|
relearnInfraredKey = relearnInfraredKey,
|
||||||
|
keyRepository = repository,
|
||||||
externalScope = scope,
|
externalScope = scope,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-7
@@ -74,6 +74,12 @@ class InfraredRemoteViewModel @Inject constructor(
|
|||||||
private var activeLearningJob: Job? = null
|
private var activeLearningJob: Job? = null
|
||||||
private var underlyingLearningDeferred: kotlinx.coroutines.Deferred<*>? = null
|
private var underlyingLearningDeferred: kotlinx.coroutines.Deferred<*>? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
authenticatedProfileId?.let { profile ->
|
||||||
|
learningCoordinator.failInterruptedLearning(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private data class InfraredRemoteConfigSnapshot(
|
private data class InfraredRemoteConfigSnapshot(
|
||||||
val effectiveProfileId: ProviderProfileId? = null,
|
val effectiveProfileId: ProviderProfileId? = null,
|
||||||
val runtimeMode: InfraredRuntimeMode = InfraredRuntimeMode.FAKE,
|
val runtimeMode: InfraredRuntimeMode = InfraredRuntimeMode.FAKE,
|
||||||
@@ -151,9 +157,7 @@ class InfraredRemoteViewModel @Inject constructor(
|
|||||||
activeLearningJob = null
|
activeLearningJob = null
|
||||||
activeLearningSlotFlow.value = null
|
activeLearningSlotFlow.value = null
|
||||||
state.value.effectiveProfileId?.let { profile ->
|
state.value.effectiveProfileId?.let { profile ->
|
||||||
viewModelScope.launch(kotlinx.coroutines.NonCancellable) {
|
learningCoordinator.failInterruptedLearning(profile)
|
||||||
keyRepository.failInterruptedLearning(profile, System.currentTimeMillis())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageFlow.value = RemoteUiMessage.Info("🔴 学习录制模式已开启:点击任意按键即可开始录制")
|
messageFlow.value = RemoteUiMessage.Info("🔴 学习录制模式已开启:点击任意按键即可开始录制")
|
||||||
@@ -178,10 +182,10 @@ class InfraredRemoteViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
messageFlow.value = RemoteUiMessage.Info("正在录制【${spec.displayName}】(Slot ${spec.defaultSlot}),请按遥控器按键...")
|
messageFlow.value = RemoteUiMessage.Info("正在录制【${spec.displayName}】(Slot ${spec.defaultSlot}),请按遥控器按键...")
|
||||||
activeLearningSlotFlow.value = spec.defaultSlot
|
activeLearningSlotFlow.value = spec.defaultSlot
|
||||||
val deferred = if (existingKey != null) {
|
val deferred = when (existingKey?.status) {
|
||||||
learningCoordinator.retry(InfraredKeyRef(currentProfile, existingKey.id))
|
InfraredKeyStatus.FAILED -> learningCoordinator.retry(InfraredKeyRef(currentProfile, existingKey.id))
|
||||||
} else {
|
InfraredKeyStatus.READY -> learningCoordinator.relearn(InfraredKeyRef(currentProfile, existingKey.id))
|
||||||
learningCoordinator.learn(
|
else -> learningCoordinator.learn(
|
||||||
profileId = currentProfile,
|
profileId = currentProfile,
|
||||||
deviceName = savedDevice,
|
deviceName = savedDevice,
|
||||||
displayName = spec.displayName,
|
displayName = spec.displayName,
|
||||||
@@ -254,6 +258,9 @@ class InfraredRemoteViewModel @Inject constructor(
|
|||||||
underlyingLearningDeferred = null
|
underlyingLearningDeferred = null
|
||||||
activeLearningJob?.cancel()
|
activeLearningJob?.cancel()
|
||||||
activeLearningJob = null
|
activeLearningJob = null
|
||||||
|
state.value.effectiveProfileId?.let { profile ->
|
||||||
|
learningCoordinator.failInterruptedLearning(profile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearMessage() {
|
fun clearMessage() {
|
||||||
|
|||||||
+54
-2
@@ -9,6 +9,7 @@ import com.flagship.abox.manager.domain.infrared.InfraredKeyId
|
|||||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyRef
|
||||||
import com.flagship.abox.manager.domain.infrared.InfraredKeyStatus
|
import com.flagship.abox.manager.domain.infrared.InfraredKeyStatus
|
||||||
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
import com.flagship.abox.manager.domain.infrared.LearnInfraredKey
|
||||||
|
import com.flagship.abox.manager.domain.infrared.RelearnInfraredKey
|
||||||
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
import com.flagship.abox.manager.domain.infrared.RetryInfraredKeyLearning
|
||||||
import com.flagship.abox.manager.domain.infrared.SavedInfraredKey
|
import com.flagship.abox.manager.domain.infrared.SavedInfraredKey
|
||||||
import com.flagship.abox.manager.testing.fakes.FakeInfraredGateway
|
import com.flagship.abox.manager.testing.fakes.FakeInfraredGateway
|
||||||
@@ -34,10 +35,11 @@ class InfraredLearningCoordinatorTest {
|
|||||||
|
|
||||||
private val learn = LearnInfraredKey(repository, gateway, factory, clock)
|
private val learn = LearnInfraredKey(repository, gateway, factory, clock)
|
||||||
private val retry = RetryInfraredKeyLearning(repository, gateway, clock)
|
private val retry = RetryInfraredKeyLearning(repository, gateway, clock)
|
||||||
|
private val relearn = RelearnInfraredKey(repository, gateway, clock)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun learnRunsAsynchronouslyInExternalScope() = runTest {
|
fun learnRunsAsynchronouslyInExternalScope() = runTest {
|
||||||
val coordinator = InfraredLearningCoordinator(learn, retry, this)
|
val coordinator = InfraredLearningCoordinator(learn, retry, relearn, repository, this)
|
||||||
|
|
||||||
coordinator.learn(profile, device, "Power")
|
coordinator.learn(profile, device, "Power")
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
@@ -65,7 +67,7 @@ class InfraredLearningCoordinatorTest {
|
|||||||
)
|
)
|
||||||
repository.insert(failedKey)
|
repository.insert(failedKey)
|
||||||
|
|
||||||
val coordinator = InfraredLearningCoordinator(learn, retry, this)
|
val coordinator = InfraredLearningCoordinator(learn, retry, relearn, repository, this)
|
||||||
|
|
||||||
coordinator.retry(InfraredKeyRef(profile, InfraredKeyId("key1")))
|
coordinator.retry(InfraredKeyRef(profile, InfraredKeyId("key1")))
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
@@ -73,4 +75,54 @@ class InfraredLearningCoordinatorTest {
|
|||||||
val updated = (repository.getKey(InfraredKeyRef(profile, InfraredKeyId("key1"))) as DomainResult.Success).value!!
|
val updated = (repository.getKey(InfraredKeyRef(profile, InfraredKeyId("key1"))) as DomainResult.Success).value!!
|
||||||
assertEquals(InfraredKeyStatus.READY, updated.status)
|
assertEquals(InfraredKeyStatus.READY, updated.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun relearnRunsAsynchronouslyInExternalScope() = runTest {
|
||||||
|
val readyKey = SavedInfraredKey(
|
||||||
|
id = InfraredKeyId("key1"),
|
||||||
|
profileId = profile,
|
||||||
|
deviceName = device,
|
||||||
|
displayName = "Power",
|
||||||
|
sdkKey = "app_ir_key1",
|
||||||
|
status = InfraredKeyStatus.READY,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
createdAtEpochMillis = 500L,
|
||||||
|
updatedAtEpochMillis = 500L,
|
||||||
|
)
|
||||||
|
repository.insert(readyKey)
|
||||||
|
|
||||||
|
val coordinator = InfraredLearningCoordinator(learn, retry, relearn, repository, this)
|
||||||
|
|
||||||
|
coordinator.relearn(InfraredKeyRef(profile, InfraredKeyId("key1")))
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
val updated = (repository.getKey(InfraredKeyRef(profile, InfraredKeyId("key1"))) as DomainResult.Success).value!!
|
||||||
|
assertEquals(InfraredKeyStatus.READY, updated.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun failInterruptedLearningFailsLearningKeys() = runTest {
|
||||||
|
val learningKey = SavedInfraredKey(
|
||||||
|
id = InfraredKeyId("key1"),
|
||||||
|
profileId = profile,
|
||||||
|
deviceName = device,
|
||||||
|
displayName = "Power",
|
||||||
|
sdkKey = "app_ir_key1",
|
||||||
|
status = InfraredKeyStatus.LEARNING,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
createdAtEpochMillis = 500L,
|
||||||
|
updatedAtEpochMillis = 500L,
|
||||||
|
)
|
||||||
|
repository.insert(learningKey)
|
||||||
|
|
||||||
|
val coordinator = InfraredLearningCoordinator(learn, retry, relearn, repository, this)
|
||||||
|
|
||||||
|
coordinator.failInterruptedLearning(profile, 2_000L)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
val updated = (repository.getKey(InfraredKeyRef(profile, InfraredKeyId("key1"))) as DomainResult.Success).value!!
|
||||||
|
assertEquals(InfraredKeyStatus.FAILED, updated.status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-1
@@ -64,13 +64,14 @@ class InfraredRemoteViewModelTest {
|
|||||||
|
|
||||||
private val learn = LearnInfraredKey(keyRepo, gateway, factory, clock)
|
private val learn = LearnInfraredKey(keyRepo, gateway, factory, clock)
|
||||||
private val retry = RetryInfraredKeyLearning(keyRepo, gateway, clock)
|
private val retry = RetryInfraredKeyLearning(keyRepo, gateway, clock)
|
||||||
|
private val relearn = com.flagship.abox.manager.domain.infrared.RelearnInfraredKey(keyRepo, gateway, clock)
|
||||||
private val send = SendInfraredKey(keyRepo, gateway)
|
private val send = SendInfraredKey(keyRepo, gateway)
|
||||||
private lateinit var coordinator: InfraredLearningCoordinator
|
private lateinit var coordinator: InfraredLearningCoordinator
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
Dispatchers.setMain(testDispatcher)
|
Dispatchers.setMain(testDispatcher)
|
||||||
coordinator = InfraredLearningCoordinator(learn, retry, testScope)
|
coordinator = InfraredLearningCoordinator(learn, retry, relearn, keyRepo, testScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -270,4 +271,38 @@ class InfraredRemoteViewModelTest {
|
|||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun relearningExistingReadyKeyOverwritesKey() = testScope.runTest {
|
||||||
|
deviceRepo.setDeviceName(fakeProfile, device)
|
||||||
|
val readyKey = SavedInfraredKey(
|
||||||
|
id = InfraredKeyId("k1"),
|
||||||
|
profileId = fakeProfile,
|
||||||
|
deviceName = device,
|
||||||
|
displayName = "空调-电源",
|
||||||
|
sdkKey = "1",
|
||||||
|
status = InfraredKeyStatus.READY,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
createdAtEpochMillis = 100L,
|
||||||
|
updatedAtEpochMillis = 100L,
|
||||||
|
)
|
||||||
|
keyRepo.insert(readyKey)
|
||||||
|
|
||||||
|
val vm = createViewModel()
|
||||||
|
val job = launch { vm.state.collect {} }
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
vm.toggleLearningMode(true)
|
||||||
|
vm.onButtonClick(RemoteSceneConfig.AirConditioner.POWER)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertEquals(1, gateway.learnedKeys.size)
|
||||||
|
assertEquals("1", gateway.learnedKeys.first().key)
|
||||||
|
val updated = (keyRepo.getKey(com.flagship.abox.manager.domain.infrared.InfraredKeyRef(fakeProfile, InfraredKeyId("k1"))) as DomainResult.Success).value!!
|
||||||
|
assertEquals(InfraredKeyStatus.READY, updated.status)
|
||||||
|
assertEquals(RemoteUiMessage.Info("【空调-电源】录制成功"), vm.state.value.message)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -71,6 +71,7 @@ class InfraredControlViewModelTest {
|
|||||||
|
|
||||||
private val learnUseCase = LearnInfraredKey(keyRepository, gateway, factory, clock)
|
private val learnUseCase = LearnInfraredKey(keyRepository, gateway, factory, clock)
|
||||||
private val retryUseCase = RetryInfraredKeyLearning(keyRepository, gateway, clock)
|
private val retryUseCase = RetryInfraredKeyLearning(keyRepository, gateway, clock)
|
||||||
|
private val relearnUseCase = com.flagship.abox.manager.domain.infrared.RelearnInfraredKey(keyRepository, gateway, clock)
|
||||||
private val sendUseCase = SendInfraredKey(keyRepository, gateway)
|
private val sendUseCase = SendInfraredKey(keyRepository, gateway)
|
||||||
private val renameUseCase = RenameInfraredKey(keyRepository, clock)
|
private val renameUseCase = RenameInfraredKey(keyRepository, clock)
|
||||||
private val deleteUseCase = DeleteInfraredKey(keyRepository)
|
private val deleteUseCase = DeleteInfraredKey(keyRepository)
|
||||||
@@ -81,7 +82,7 @@ class InfraredControlViewModelTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
Dispatchers.setMain(testDispatcher)
|
Dispatchers.setMain(testDispatcher)
|
||||||
coordinator = InfraredLearningCoordinator(learnUseCase, retryUseCase, testScope)
|
coordinator = InfraredLearningCoordinator(learnUseCase, retryUseCase, relearnUseCase, keyRepository, testScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -106,7 +106,88 @@ class RetryInfraredKeyLearning(
|
|||||||
ref = ref,
|
ref = ref,
|
||||||
expectedStatuses = setOf(
|
expectedStatuses = setOf(
|
||||||
InfraredKeyStatus.FAILED,
|
InfraredKeyStatus.FAILED,
|
||||||
|
),
|
||||||
|
targetStatus = InfraredKeyStatus.LEARNING,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
updatedAtEpochMillis = clock.now(),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
is DomainResult.Success -> Unit
|
||||||
|
is DomainResult.Failure -> return result
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = try {
|
||||||
|
gateway.learnKey(
|
||||||
|
profileId = key.profileId,
|
||||||
|
deviceName = key.deviceName,
|
||||||
|
key = key.sdkKey,
|
||||||
|
)
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
withContext(NonCancellable) {
|
||||||
|
repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = "Cancelled",
|
||||||
|
updatedAtEpochMillis = clock.now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (result) {
|
||||||
|
is DomainResult.Success -> repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
|
targetStatus = InfraredKeyStatus.READY,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
updatedAtEpochMillis = clock.now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
is DomainResult.Failure -> {
|
||||||
|
repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(InfraredKeyStatus.LEARNING),
|
||||||
|
targetStatus = InfraredKeyStatus.FAILED,
|
||||||
|
lastErrorCode = result.error.externalCode,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
updatedAtEpochMillis = clock.now(),
|
||||||
|
)
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RelearnInfraredKey(
|
||||||
|
private val repository: InfraredKeyRepository,
|
||||||
|
private val gateway: InfraredGateway,
|
||||||
|
private val clock: EpochMillisClock,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(ref: InfraredKeyRef): DomainResult<Unit> {
|
||||||
|
val key = when (val result = repository.getKey(ref)) {
|
||||||
|
is DomainResult.Success -> result.value
|
||||||
|
?: return DomainResult.Failure(
|
||||||
|
DomainError.InvalidRequest(detail = "Infrared key not found"),
|
||||||
|
)
|
||||||
|
|
||||||
|
is DomainResult.Failure -> return result
|
||||||
|
}
|
||||||
|
if (key.status == InfraredKeyStatus.LEARNING) {
|
||||||
|
return DomainResult.Failure(
|
||||||
|
DomainError.InvalidRequest(detail = "Infrared key is already learning"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (
|
||||||
|
val result = repository.transitionStatus(
|
||||||
|
ref = ref,
|
||||||
|
expectedStatuses = setOf(
|
||||||
InfraredKeyStatus.READY,
|
InfraredKeyStatus.READY,
|
||||||
|
InfraredKeyStatus.FAILED,
|
||||||
),
|
),
|
||||||
targetStatus = InfraredKeyStatus.LEARNING,
|
targetStatus = InfraredKeyStatus.LEARNING,
|
||||||
lastErrorCode = null,
|
lastErrorCode = null,
|
||||||
|
|||||||
+29
-1
@@ -22,6 +22,7 @@ class LearnInfraredKeyTest {
|
|||||||
private val gateway = RecordingInfraredGateway()
|
private val gateway = RecordingInfraredGateway()
|
||||||
private val learn = LearnInfraredKey(repository, gateway, factory, clock)
|
private val learn = LearnInfraredKey(repository, gateway, factory, clock)
|
||||||
private val retry = RetryInfraredKeyLearning(repository, gateway, clock)
|
private val retry = RetryInfraredKeyLearning(repository, gateway, clock)
|
||||||
|
private val relearn = RelearnInfraredKey(repository, gateway, clock)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun persistsLearningBeforeCallingHardwareThenMarksReady() = runTest {
|
fun persistsLearningBeforeCallingHardwareThenMarksReady() = runTest {
|
||||||
@@ -131,7 +132,34 @@ class LearnInfraredKeyTest {
|
|||||||
assertEquals(0, repository.insertedKeys.size)
|
assertEquals(0, repository.insertedKeys.size)
|
||||||
assertEquals(2, repository.transitionCalls.size)
|
assertEquals(2, repository.transitionCalls.size)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(InfraredKeyStatus.FAILED, InfraredKeyStatus.READY),
|
setOf(InfraredKeyStatus.FAILED),
|
||||||
|
repository.transitionCalls.first().expectedStatuses,
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
setOf(InfraredKeyStatus.LEARNING),
|
||||||
|
repository.transitionCalls.last().expectedStatuses,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun relearnReusesStableIdAndSdkKeyForReadyKey() = runTest {
|
||||||
|
val original = failedKey(id = InfraredKeyId("key1"), sdkKey = "app_ir_key1").copy(
|
||||||
|
status = InfraredKeyStatus.READY,
|
||||||
|
lastErrorCode = null,
|
||||||
|
lastErrorDetail = null,
|
||||||
|
)
|
||||||
|
repository.seed(original)
|
||||||
|
|
||||||
|
val result = relearn(original.ref())
|
||||||
|
|
||||||
|
assertEquals(DomainResult.Success(Unit), result)
|
||||||
|
assertEquals("app_ir_key1", gateway.learnedKeys.single().key)
|
||||||
|
assertEquals(InfraredKeyId("key1"), repository.onlyKey().id)
|
||||||
|
assertEquals(InfraredKeyStatus.READY, repository.onlyKey().status)
|
||||||
|
assertEquals(0, repository.insertedKeys.size)
|
||||||
|
assertEquals(2, repository.transitionCalls.size)
|
||||||
|
assertEquals(
|
||||||
|
setOf(InfraredKeyStatus.READY, InfraredKeyStatus.FAILED),
|
||||||
repository.transitionCalls.first().expectedStatuses,
|
repository.transitionCalls.first().expectedStatuses,
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|||||||
Reference in New Issue
Block a user