mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-02 21:21:24 +03:00
Improve view events and add unit tests
This commit is contained in:
parent
71455706cb
commit
25d49806cc
5 changed files with 328 additions and 19 deletions
|
@ -19,6 +19,19 @@ package im.vector.app.features.settings.notifications
|
|||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed interface VectorSettingsPushRuleNotificationViewEvent : VectorViewEvents {
|
||||
data class PushRuleUpdated(val ruleId: String, val checked: Boolean) : VectorSettingsPushRuleNotificationViewEvent
|
||||
data class Failure(val throwable: Throwable) : VectorSettingsPushRuleNotificationViewEvent
|
||||
/**
|
||||
* A global push rule checked state has changed.
|
||||
*
|
||||
* @property ruleId the global rule id which has been updated.
|
||||
* @property checked whether the global rule is checked.
|
||||
* @property failure whether there has been a failure when updating the global rule (ie. a sub rule has not been updated).
|
||||
*/
|
||||
data class PushRuleUpdated(val ruleId: String, val checked: Boolean, val failure: Throwable? = null) : VectorSettingsPushRuleNotificationViewEvent
|
||||
|
||||
/**
|
||||
* A failure has occurred.
|
||||
*
|
||||
* @property throwable the related exception, if any.
|
||||
*/
|
||||
data class Failure(val throwable: Throwable?) : VectorSettingsPushRuleNotificationViewEvent
|
||||
}
|
||||
|
|
|
@ -83,9 +83,19 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
setState { copy(isLoading = false) }
|
||||
val failure = results.firstNotNullOfOrNull { it.exceptionOrNull() }
|
||||
if (failure == null) {
|
||||
_viewEvents.post(PushRuleUpdated(ruleId, checked))
|
||||
val failure = results.firstNotNullOfOrNull { result ->
|
||||
// If the failure is a rule not found error, do not consider it
|
||||
result.exceptionOrNull()?.takeUnless { it is ServerError && it.error.code == MatrixError.M_NOT_FOUND }
|
||||
}
|
||||
val newChecked = if (checked) {
|
||||
// If any rule is checked, the global rule is checked
|
||||
results.any { it.isSuccess }
|
||||
} else {
|
||||
// If any rule has not been unchecked, the global rule remains checked
|
||||
failure != null
|
||||
}
|
||||
if (results.any { it.isSuccess }) {
|
||||
_viewEvents.post(PushRuleUpdated(ruleId, newChecked, failure))
|
||||
} else {
|
||||
_viewEvents.post(Failure(failure))
|
||||
}
|
||||
|
@ -93,18 +103,11 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List<Action>?) {
|
||||
try {
|
||||
activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions(
|
||||
kind = kind,
|
||||
ruleId = ruleId,
|
||||
enable = enable,
|
||||
actions = newActions
|
||||
)
|
||||
} catch (failure: ServerError) {
|
||||
// Ignore the error if the rule id is not known from the server
|
||||
if (failure.error.code != MatrixError.M_NOT_FOUND) {
|
||||
throw failure
|
||||
}
|
||||
}
|
||||
activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions(
|
||||
kind = kind,
|
||||
ruleId = ruleId,
|
||||
enable = enable,
|
||||
actions = newActions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.settings.notifications
|
||||
|
||||
import com.airbnb.mvrx.test.MavericksTestRule
|
||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||
import im.vector.app.test.test
|
||||
import im.vector.app.test.testDispatcher
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.pushrules.RuleIds
|
||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
||||
|
||||
internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
||||
|
||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||
private val fakePushRuleService = fakeActiveSessionHolder.fakeSession.fakePushRuleService
|
||||
|
||||
private val initialState = VectorSettingsPushRuleNotificationViewState()
|
||||
private fun createViewModel() = VectorSettingsPushRuleNotificationViewModel(
|
||||
initialState = initialState,
|
||||
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("im.vector.app.features.settings.notifications.NotificationIndexKt")
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a ruleId, when the rule is checked or unchecked, then the related rules are also updated and a view event is posted`() = runTest {
|
||||
// Given
|
||||
val viewModel = createViewModel()
|
||||
|
||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||
|
||||
// When
|
||||
val viewModelTest = viewModel.test()
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true))
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false))
|
||||
|
||||
// Then
|
||||
coVerifyOrder {
|
||||
// first rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), firstRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
|
||||
// second rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), secondRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, any(), any())
|
||||
}
|
||||
|
||||
viewModelTest
|
||||
.assertStatesChanges(
|
||||
initialState,
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
)
|
||||
.assertEvents(
|
||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true),
|
||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a ruleId, when the rule is checked and an error is thrown, then all the related rules are updated and an event is posted with the failure`() = runTest {
|
||||
// Given
|
||||
val viewModel = createViewModel()
|
||||
val failure = mockk<Throwable>()
|
||||
|
||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
||||
|
||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_END, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_END_UNSTABLE, failure)
|
||||
|
||||
// When
|
||||
val viewModelTest = viewModel.test()
|
||||
// One rule failed to update
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true))
|
||||
// All the rules failed to update
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), true))
|
||||
|
||||
// Then
|
||||
coVerifyOrder {
|
||||
// first rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), firstRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
|
||||
// second rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), secondRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, any(), any())
|
||||
}
|
||||
|
||||
viewModelTest
|
||||
.assertStatesChanges(
|
||||
initialState,
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
)
|
||||
.assertEvents(
|
||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure),
|
||||
VectorSettingsPushRuleNotificationViewEvent.Failure(failure),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a ruleId, when the rule is unchecked and an error is thrown, then all the related rules are updated and an event is posted with the failure`() = runTest {
|
||||
// Given
|
||||
val viewModel = createViewModel()
|
||||
val failure = mockk<Throwable>()
|
||||
|
||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
||||
|
||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_END, failure)
|
||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_END_UNSTABLE, failure)
|
||||
|
||||
// When
|
||||
val viewModelTest = viewModel.test()
|
||||
// One rule failed to update
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), false))
|
||||
// All the rules failed to update
|
||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false))
|
||||
|
||||
// Then
|
||||
coVerifyOrder {
|
||||
// first rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), firstRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, any(), any())
|
||||
|
||||
// second rule id
|
||||
fakePushRuleService.updatePushRuleActions(any(), secondRuleId, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_UNSTABLE, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, any(), any())
|
||||
fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, any(), any())
|
||||
}
|
||||
|
||||
viewModelTest
|
||||
.assertStatesChanges(
|
||||
initialState,
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
{ copy(isLoading = true) },
|
||||
{ copy(isLoading = false) },
|
||||
)
|
||||
.assertEvents(
|
||||
// The global rule remains checked if all the rules are not unchecked
|
||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure),
|
||||
VectorSettingsPushRuleNotificationViewEvent.Failure(failure),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a rule id, when requesting the check state, returns the expected value according to the related rules`() {
|
||||
// Given
|
||||
val viewModel = createViewModel()
|
||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||
givenARuleId(firstRuleId, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, NotificationIndex.SILENT)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, NotificationIndex.NOISY)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, NotificationIndex.OFF)
|
||||
|
||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||
givenARuleId(secondRuleId, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_START, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_START_UNSTABLE, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_END, NotificationIndex.OFF)
|
||||
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, NotificationIndex.OFF)
|
||||
|
||||
// When
|
||||
val firstResult = viewModel.isPushRuleChecked(firstRuleId)
|
||||
val secondResult = viewModel.isPushRuleChecked(secondRuleId)
|
||||
|
||||
// Then
|
||||
firstResult shouldBe true
|
||||
secondResult shouldBe false
|
||||
}
|
||||
|
||||
private fun givenARuleId(ruleId: String, notificationIndex: NotificationIndex = NotificationIndex.NOISY): PushRuleAndKind {
|
||||
val ruleAndKind = mockk<PushRuleAndKind> {
|
||||
every { pushRule.ruleId } returns ruleId
|
||||
every { pushRule.notificationIndex } returns notificationIndex
|
||||
every { kind } returns mockk()
|
||||
}
|
||||
|
||||
every { fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
||||
|
||||
return ruleAndKind
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fakes
|
||||
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.mockk
|
||||
import org.matrix.android.sdk.api.session.pushrules.PushRuleService
|
||||
|
||||
class FakePushRuleService : PushRuleService by mockk(relaxed = true) {
|
||||
|
||||
fun givenUpdatePushRuleActionsSucceed(ruleId: String? = null) {
|
||||
coJustRun { updatePushRuleActions(any(), ruleId ?: any(), any(), any()) }
|
||||
}
|
||||
|
||||
fun givenUpdatePushRuleActionsFail(ruleId: String? = null, failure: Throwable = mockk()) {
|
||||
coEvery { updatePushRuleActions(any(), ruleId ?: any(), any(), any()) }.throws(failure)
|
||||
}
|
||||
}
|
|
@ -41,10 +41,11 @@ class FakeSession(
|
|||
val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
|
||||
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
|
||||
val fakeRoomService: FakeRoomService = FakeRoomService(),
|
||||
val fakePushRuleService: FakePushRuleService = FakePushRuleService(),
|
||||
val fakePushersService: FakePushersService = FakePushersService(),
|
||||
val fakeUserService: FakeUserService = FakeUserService(),
|
||||
private val fakeEventService: FakeEventService = FakeEventService(),
|
||||
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService()
|
||||
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService(),
|
||||
) : Session by mockk(relaxed = true) {
|
||||
|
||||
init {
|
||||
|
@ -61,6 +62,7 @@ class FakeSession(
|
|||
override fun sharedSecretStorageService() = fakeSharedSecretStorageService
|
||||
override fun roomService() = fakeRoomService
|
||||
override fun eventService() = fakeEventService
|
||||
override fun pushRuleService() = fakePushRuleService
|
||||
override fun pushersService() = fakePushersService
|
||||
override fun accountDataService() = fakeSessionAccountDataService
|
||||
override fun userService() = fakeUserService
|
||||
|
|
Loading…
Add table
Reference in a new issue