Merge pull request #8141 from vector-im/feature/fre/poll_sync_error_handling

[Poll] Error handling for push rules synchronization
This commit is contained in:
Florian Renaud 2023-02-24 09:11:38 +01:00 committed by GitHub
commit ea635976a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 370 additions and 116 deletions

1
changelog.d/8141.feature Normal file
View File

@ -0,0 +1 @@
[Poll] Error handling for push rules synchronization

View File

@ -862,6 +862,8 @@
<string name="settings_notification_keyword_contains_dot">Keywords cannot start with \'.\'</string> <string name="settings_notification_keyword_contains_dot">Keywords cannot start with \'.\'</string>
<string name="settings_notification_keyword_contains_invalid_character">Keywords cannot contain \'%s\'</string> <string name="settings_notification_keyword_contains_invalid_character">Keywords cannot contain \'%s\'</string>
<string name="settings_notification_error_on_update">An error occurred when updating your notification preferences. Please try again.</string>
<string name="settings_notification_troubleshoot">Troubleshoot Notifications</string> <string name="settings_notification_troubleshoot">Troubleshoot Notifications</string>
<string name="settings_troubleshoot_diagnostic">Troubleshooting diagnostics</string> <string name="settings_troubleshoot_diagnostic">Troubleshooting diagnostics</string>
<string name="settings_troubleshoot_diagnostic_run_button_title">Run Tests</string> <string name="settings_troubleshoot_diagnostic_run_button_title">Run Tests</string>

View File

@ -488,7 +488,7 @@ fun Event.getPollContent(): MessagePollContent? {
} }
fun Event.supportsNotification() = fun Event.supportsNotification() =
this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.STATE_ROOM_BEACON_INFO.values this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values
fun Event.isContentReportable() = fun Event.isContentReportable() =
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values

View File

@ -32,6 +32,8 @@ class VectorCheckboxPreference : CheckBoxPreference {
constructor(context: Context) : super(context) constructor(context: Context) : super(context)
var summaryTextColor: Int? = null
init { init {
// Set to false to remove the space when there is no icon // Set to false to remove the space when there is no icon
isIconSpaceReserved = true isIconSpaceReserved = true
@ -42,4 +44,9 @@ class VectorCheckboxPreference : CheckBoxPreference {
(holder.findViewById(android.R.id.title) as? TextView)?.isSingleLine = false (holder.findViewById(android.R.id.title) as? TextView)?.isSingleLine = false
super.onBindViewHolder(holder) super.onBindViewHolder(holder)
} }
override fun syncSummaryView(holder: PreferenceViewHolder) {
super.syncSummaryView(holder)
summaryTextColor?.let { (holder.findViewById(android.R.id.summary) as? TextView)?.setTextColor(it) }
}
} }

View File

@ -42,8 +42,7 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, RuleIds.RULE_ID_POLL_START_ONE_TO_ONE,
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE,
RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, RuleIds.RULE_ID_POLL_END_ONE_TO_ONE,
RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE ->
->
when (index) { when (index) {
NotificationIndex.OFF -> StandardActions.DontNotify NotificationIndex.OFF -> StandardActions.DontNotify
NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.SILENT -> StandardActions.Notify

View File

@ -21,64 +21,79 @@ import android.view.View
import androidx.preference.Preference import androidx.preference.Preference
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.preference.VectorCheckboxPreference import im.vector.app.core.preference.VectorCheckboxPreference
import im.vector.app.features.settings.VectorSettingsBaseFragment import im.vector.app.features.settings.VectorSettingsBaseFragment
import im.vector.app.features.themes.ThemeUtils
abstract class VectorSettingsPushRuleNotificationFragment : abstract class VectorSettingsPushRuleNotificationFragment :
VectorSettingsBaseFragment() { VectorSettingsBaseFragment() {
private val viewModel: VectorSettingsPushRuleNotificationViewModel by fragmentViewModel() protected val viewModel: VectorSettingsPushRuleNotificationViewModel by fragmentViewModel()
abstract val prefKeyToPushRuleId: Map<String, String> abstract val prefKeyToPushRuleId: Map<String, String>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
observeViewEvents() observeViewEvents()
viewModel.onEach(VectorSettingsPushRuleNotificationViewState::isLoading) { isLoading -> viewModel.onEach(VectorSettingsPushRuleNotificationViewState::allRules) { refreshPreferences() }
if (isLoading) { viewModel.onEach(VectorSettingsPushRuleNotificationViewState::isLoading) { updateLoadingView(it) }
displayLoadingView() viewModel.onEach(VectorSettingsPushRuleNotificationViewState::rulesOnError) { refreshErrors(it) }
} else {
hideLoadingView()
}
}
} }
private fun observeViewEvents() { private fun observeViewEvents() {
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
is VectorSettingsPushRuleNotificationViewEvent.Failure -> refreshDisplay() is VectorSettingsPushRuleNotificationViewEvent.Failure -> onFailure(it.ruleId)
is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> updatePreference(it.ruleId, it.checked) is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> {
updatePreference(it.ruleId, it.checked)
if (it.failure != null) {
onFailure(it.ruleId)
}
}
} }
} }
} }
override fun bindPref() { override fun bindPref() {
for (preferenceKey in prefKeyToPushRuleId.keys) { prefKeyToPushRuleId.forEach { (preferenceKey, ruleId) ->
val preference = findPreference<VectorCheckboxPreference>(preferenceKey)!! findPreference<VectorCheckboxPreference>(preferenceKey)?.apply {
preference.isIconSpaceReserved = false isIconSpaceReserved = false
val ruleAndKind = prefKeyToPushRuleId[preferenceKey]?.let { viewModel.getPushRuleAndKind(it) } onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
if (ruleAndKind == null) { viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleId, newValue as Boolean))
// The rule is not defined, hide the preference
preference.isVisible = false
} else {
preference.isVisible = true
updatePreference(ruleAndKind.pushRule.ruleId, viewModel.isPushRuleChecked(ruleAndKind.pushRule.ruleId))
preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleAndKind, newValue as Boolean))
false false
} }
} }
} }
} }
override fun invalidate() = withState(viewModel) { state -> private fun updateLoadingView(isLoading: Boolean) {
if (state.isLoading) { if (isLoading) {
displayLoadingView() displayLoadingView()
} else { } else {
hideLoadingView() hideLoadingView()
} }
} }
private fun refreshPreferences() {
prefKeyToPushRuleId.values.forEach { ruleId -> updatePreference(ruleId, viewModel.isPushRuleChecked(ruleId)) }
}
private fun refreshErrors(rulesWithError: Set<String>) {
if (withState(viewModel, VectorSettingsPushRuleNotificationViewState::isLoading)) return
prefKeyToPushRuleId.forEach { (preferenceKey, ruleId) ->
findPreference<VectorCheckboxPreference>(preferenceKey)?.apply {
if (ruleId in rulesWithError) {
summaryTextColor = ThemeUtils.getColor(context, R.attr.colorError)
setSummary(R.string.settings_notification_error_on_update)
} else {
summaryTextColor = null
summary = null
}
}
}
}
protected fun refreshDisplay() { protected fun refreshDisplay() {
listView?.adapter?.notifyDataSetChanged() listView?.adapter?.notifyDataSetChanged()
} }
@ -86,6 +101,12 @@ abstract class VectorSettingsPushRuleNotificationFragment :
private fun updatePreference(ruleId: String, checked: Boolean) { private fun updatePreference(ruleId: String, checked: Boolean) {
val preferenceKey = prefKeyToPushRuleId.entries.find { it.value == ruleId }?.key ?: return val preferenceKey = prefKeyToPushRuleId.entries.find { it.value == ruleId }?.key ?: return
val preference = findPreference<VectorCheckboxPreference>(preferenceKey) ?: return val preference = findPreference<VectorCheckboxPreference>(preferenceKey) ?: return
val ruleIds = withState(viewModel) { state -> state.allRules.map { it.ruleId } }
preference.isVisible = ruleId in ruleIds
preference.isChecked = checked preference.isChecked = checked
} }
protected open fun onFailure(ruleId: String) {
refreshDisplay()
}
} }

View File

@ -17,8 +17,7 @@
package im.vector.app.features.settings.notifications package im.vector.app.features.settings.notifications
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
sealed interface VectorSettingsPushRuleNotificationViewAction : VectorViewModelAction { sealed interface VectorSettingsPushRuleNotificationViewAction : VectorViewModelAction {
data class UpdatePushRule(val pushRuleAndKind: PushRuleAndKind, val checked: Boolean) : VectorSettingsPushRuleNotificationViewAction data class UpdatePushRule(val ruleId: String, val checked: Boolean) : VectorSettingsPushRuleNotificationViewAction
} }

View File

@ -31,7 +31,8 @@ sealed interface VectorSettingsPushRuleNotificationViewEvent : VectorViewEvents
/** /**
* A failure has occurred. * A failure has occurred.
* *
* @property ruleId the global rule id related to the failure.
* @property throwable the related exception, if any. * @property throwable the related exception, if any.
*/ */
data class Failure(val throwable: Throwable?) : VectorSettingsPushRuleNotificationViewEvent data class Failure(val ruleId: String, val throwable: Throwable?) : VectorSettingsPushRuleNotificationViewEvent
} }

View File

@ -20,26 +20,31 @@ import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.Failure import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.Failure
import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated
import im.vector.app.features.settings.notifications.usecase.GetPushRulesOnInvalidStateUseCase
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.failure.Failure.ServerError import org.matrix.android.sdk.api.failure.Failure.ServerError
import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.pushrules.Action import org.matrix.android.sdk.api.session.pushrules.Action
import org.matrix.android.sdk.api.session.pushrules.RuleIds import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.pushrules.RuleKind import org.matrix.android.sdk.api.session.pushrules.RuleKind
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.unwrap
private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel
private typealias ViewState = VectorSettingsPushRuleNotificationViewState private typealias ViewState = VectorSettingsPushRuleNotificationViewState
class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor( class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
@Assisted initialState: ViewState, @Assisted initialState: ViewState,
private val activeSessionHolder: ActiveSessionHolder, private val session: Session,
private val getPushRulesOnInvalidStateUseCase: GetPushRulesOnInvalidStateUseCase,
) : VectorViewModel<VectorSettingsPushRuleNotificationViewState, ) : VectorViewModel<VectorSettingsPushRuleNotificationViewState,
VectorSettingsPushRuleNotificationViewAction, VectorSettingsPushRuleNotificationViewAction,
VectorSettingsPushRuleNotificationViewEvent>(initialState) { VectorSettingsPushRuleNotificationViewEvent>(initialState) {
@ -51,14 +56,28 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
companion object : MavericksViewModelFactory<ViewModel, ViewState> by hiltMavericksViewModelFactory() companion object : MavericksViewModelFactory<ViewModel, ViewState> by hiltMavericksViewModelFactory()
init {
session.flow()
.liveUserAccountData(UserAccountDataTypes.TYPE_PUSH_RULES)
.unwrap()
.setOnEach {
val allRules = session.pushRuleService().getPushRules().getAllRules()
val rulesOnError = getPushRulesOnInvalidStateUseCase.execute(session).map { it.ruleId }.toSet()
copy(
allRules = allRules,
rulesOnError = rulesOnError
)
}
}
override fun handle(action: VectorSettingsPushRuleNotificationViewAction) { override fun handle(action: VectorSettingsPushRuleNotificationViewAction) {
when (action) { when (action) {
is VectorSettingsPushRuleNotificationViewAction.UpdatePushRule -> handleUpdatePushRule(action.pushRuleAndKind, action.checked) is VectorSettingsPushRuleNotificationViewAction.UpdatePushRule -> handleUpdatePushRule(action.ruleId, action.checked)
} }
} }
fun getPushRuleAndKind(ruleId: String): PushRuleAndKind? { fun getPushRuleAndKind(ruleId: String): PushRuleAndKind? {
return activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.getPushRules()?.findDefaultRule(ruleId) return session.pushRuleService().getPushRules().findDefaultRule(ruleId)
} }
fun isPushRuleChecked(ruleId: String): Boolean { fun isPushRuleChecked(ruleId: String): Boolean {
@ -66,13 +85,13 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
return rulesGroup.mapNotNull { getPushRuleAndKind(it) }.any { it.pushRule.notificationIndex != NotificationIndex.OFF } return rulesGroup.mapNotNull { getPushRuleAndKind(it) }.any { it.pushRule.notificationIndex != NotificationIndex.OFF }
} }
private fun handleUpdatePushRule(pushRuleAndKind: PushRuleAndKind, checked: Boolean) { private fun handleUpdatePushRule(ruleId: String, checked: Boolean) {
val ruleId = pushRuleAndKind.pushRule.ruleId val kind = getPushRuleAndKind(ruleId)?.kind ?: return
val kind = pushRuleAndKind.kind
val newIndex = if (checked) NotificationIndex.NOISY else NotificationIndex.OFF val newIndex = if (checked) NotificationIndex.NOISY else NotificationIndex.OFF
val standardAction = getStandardAction(ruleId, newIndex) ?: return val standardAction = getStandardAction(ruleId, newIndex) ?: return
val enabled = standardAction != StandardActions.Disabled val enabled = standardAction != StandardActions.Disabled
val newActions = standardAction.actions val newActions = standardAction.actions
setState { copy(isLoading = true) } setState { copy(isLoading = true) }
viewModelScope.launch { viewModelScope.launch {
@ -82,28 +101,37 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
updatePushRule(kind, ruleId, enabled, newActions) updatePushRule(kind, ruleId, enabled, newActions)
} }
} }
setState { copy(isLoading = false) }
val failure = results.firstNotNullOfOrNull { result -> val failures = results.mapNotNull { result ->
// If the failure is a rule not found error, do not consider it // 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 } result.exceptionOrNull()?.takeUnless { it is ServerError && it.error.code == MatrixError.M_NOT_FOUND }
} }
val newChecked = if (checked) { val hasSuccess = results.any { it.isSuccess }
// If any rule is checked, the global rule is checked val hasFailures = failures.isNotEmpty()
results.any { it.isSuccess }
// Any rule has been checked or some rules have not been unchecked
val newChecked = (checked && hasSuccess) || (!checked && hasFailures)
if (hasSuccess) {
_viewEvents.post(PushRuleUpdated(ruleId, newChecked, failures.firstOrNull()))
} else { } else {
// If any rule has not been unchecked, the global rule remains checked _viewEvents.post(Failure(ruleId, failures.firstOrNull()))
failure != null
} }
if (results.any { it.isSuccess }) {
_viewEvents.post(PushRuleUpdated(ruleId, newChecked, failure)) setState {
} else { copy(
_viewEvents.post(Failure(failure)) isLoading = false,
rulesOnError = when {
hasSuccess && hasFailures -> rulesOnError.plus(ruleId) // some failed
hasSuccess -> rulesOnError.minus(ruleId) // all succeed
else -> rulesOnError // all failed
}
)
} }
} }
} }
private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List<Action>?) { private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List<Action>?) {
activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions( session.pushRuleService().updatePushRuleActions(
kind = kind, kind = kind,
ruleId = ruleId, ruleId = ruleId,
enable = enable, enable = enable,

View File

@ -17,7 +17,10 @@
package im.vector.app.features.settings.notifications package im.vector.app.features.settings.notifications
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
data class VectorSettingsPushRuleNotificationViewState( data class VectorSettingsPushRuleNotificationViewState(
val isLoading: Boolean = false, val isLoading: Boolean = false,
val allRules: List<PushRule> = emptyList(),
val rulesOnError: Set<String> = emptySet(),
) : MavericksState ) : MavericksState

View File

@ -0,0 +1,44 @@
/*
* 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.usecase
import im.vector.app.features.settings.notifications.getParentRule
import im.vector.app.features.settings.notifications.getSyncedRules
import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.pushrules.getActions
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import javax.inject.Inject
class GetPushRulesOnInvalidStateUseCase @Inject constructor() {
fun execute(session: Session): List<PushRule> {
val allRules = session.pushRuleService().getPushRules().getAllRules()
return allRules.filter { it.isOnInvalidState(allRules) }
}
private fun PushRule.isOnInvalidState(allRules: List<PushRule>): Boolean {
val parent = RuleIds.getParentRule(ruleId)?.let { parentId -> allRules.find { it.ruleId == parentId } }
val children = RuleIds.getSyncedRules(ruleId).mapNotNull { childId -> allRules.find { it.ruleId == childId } }
val isAlignedWithParent = parent?.let { isAlignedWithParentRule(it) }.orTrue()
return !isAlignedWithParent || !isAlignedWithChildrenRules(children)
}
private fun PushRule.isAlignedWithParentRule(parent: PushRule) = this.getActions() == parent.getActions() && this.enabled == parent.enabled
private fun PushRule.isAlignedWithChildrenRules(children: List<PushRule>) = children.all { it.isAlignedWithParentRule(this) }
}

View File

@ -24,18 +24,18 @@ import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
import javax.inject.Inject import javax.inject.Inject
class UpdatePushRulesIfNeededUseCase @Inject constructor() { class UpdatePushRulesIfNeededUseCase @Inject constructor(
private val getPushRulesOnInvalidStateUseCase: GetPushRulesOnInvalidStateUseCase,
) {
suspend fun execute(session: Session) { suspend fun execute(session: Session) {
val rulesOnError = getPushRulesOnInvalidStateUseCase.execute(session).takeUnless { it.isEmpty() } ?: return
val ruleSet = session.pushRuleService().getPushRules() val ruleSet = session.pushRuleService().getPushRules()
val pushRules = ruleSet.getAllRules() val rulesToUpdate = rulesOnError.mapNotNull { rule ->
val rulesToUpdate = pushRules.mapNotNull { rule -> RuleIds.getParentRule(rule.ruleId)
val parent = RuleIds.getParentRule(rule.ruleId)?.let { ruleId -> ruleSet.findDefaultRule(ruleId) } ?.let { ruleId -> ruleSet.findDefaultRule(ruleId) }
if (parent != null && (rule.enabled != parent.pushRule.enabled || rule.actions != parent.pushRule.actions)) { ?.let { PushRuleWithParent(rule, it) }
PushRuleWithParent(rule, parent)
} else {
null
}
} }
rulesToUpdate.forEach { rulesToUpdate.forEach {

View File

@ -25,4 +25,4 @@
android:title="@string/settings_encrypted_group_messages" /> android:title="@string/settings_encrypted_group_messages" />
</im.vector.app.core.preference.VectorPreferenceCategory> </im.vector.app.core.preference.VectorPreferenceCategory>
</androidx.preference.PreferenceScreen> </androidx.preference.PreferenceScreen>

View File

@ -17,7 +17,9 @@
package im.vector.app.features.settings.notifications package im.vector.app.features.settings.notifications
import com.airbnb.mvrx.test.MavericksTestRule import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.features.settings.notifications.usecase.GetPushRulesOnInvalidStateUseCase
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fixtures.PushRuleFixture
import im.vector.app.test.test import im.vector.app.test.test
import im.vector.app.test.testDispatcher import im.vector.app.test.testDispatcher
import io.mockk.coVerifyOrder import io.mockk.coVerifyOrder
@ -32,25 +34,27 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.session.pushrules.RuleIds import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
internal class VectorSettingsPushRuleNotificationViewModelTest { internal class VectorSettingsPushRuleNotificationViewModelTest {
@get:Rule @get:Rule
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher) val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
private val fakeActiveSessionHolder = FakeActiveSessionHolder() private val fakeSession = FakeSession()
private val fakePushRuleService = fakeActiveSessionHolder.fakeSession.fakePushRuleService private val fakePushRuleService = fakeSession.fakePushRuleService
private val fakeGetPushRulesOnInvalidStateUseCase = mockk<GetPushRulesOnInvalidStateUseCase>()
private val initialState = VectorSettingsPushRuleNotificationViewState() private val initialState = VectorSettingsPushRuleNotificationViewState()
private fun createViewModel() = VectorSettingsPushRuleNotificationViewModel( private fun createViewModel() = VectorSettingsPushRuleNotificationViewModel(
initialState = initialState, initialState = initialState,
activeSessionHolder = fakeActiveSessionHolder.instance, session = fakeSession,
fakeGetPushRulesOnInvalidStateUseCase,
) )
@Before @Before
fun setup() { fun setup() {
mockkStatic("im.vector.app.features.settings.notifications.NotificationIndexKt") mockkStatic("im.vector.app.features.settings.notifications.NotificationIndexKt")
every { fakeGetPushRulesOnInvalidStateUseCase.execute(any()) } returns emptyList()
} }
@After @After
@ -65,12 +69,14 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
givenARuleId(firstRuleId)
givenARuleId(secondRuleId)
fakePushRuleService.givenUpdatePushRuleActionsSucceed() fakePushRuleService.givenUpdatePushRuleActionsSucceed()
// When // When
val viewModelTest = viewModel.test() val viewModelTest = viewModel.test()
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, true))
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, false))
// Then // Then
coVerifyOrder { coVerifyOrder {
@ -98,8 +104,8 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
{ copy(isLoading = false) }, { copy(isLoading = false) },
) )
.assertEvents( .assertEvents(
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true), VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true),
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false), VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(secondRuleId, false),
) )
.finish() .finish()
} }
@ -111,10 +117,12 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
val failure = mockk<Throwable>() val failure = mockk<Throwable>()
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
givenARuleId(firstRuleId)
fakePushRuleService.givenUpdatePushRuleActionsSucceed() fakePushRuleService.givenUpdatePushRuleActionsSucceed()
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
givenARuleId(secondRuleId)
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
@ -124,9 +132,9 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
// When // When
val viewModelTest = viewModel.test() val viewModelTest = viewModel.test()
// One rule failed to update // One rule failed to update
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, true))
// All the rules failed to update // All the rules failed to update
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), true)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, true))
// Then // Then
coVerifyOrder { coVerifyOrder {
@ -149,13 +157,13 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
.assertStatesChanges( .assertStatesChanges(
initialState, initialState,
{ copy(isLoading = true) }, { copy(isLoading = true) },
{ copy(isLoading = false) }, { copy(isLoading = false, rulesOnError = setOf(firstRuleId)) },
{ copy(isLoading = true) }, { copy(isLoading = true) },
{ copy(isLoading = false) }, { copy(isLoading = false) },
) )
.assertEvents( .assertEvents(
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure), VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true, failure),
VectorSettingsPushRuleNotificationViewEvent.Failure(failure), VectorSettingsPushRuleNotificationViewEvent.Failure(secondRuleId, failure),
) )
.finish() .finish()
} }
@ -167,10 +175,12 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
val failure = mockk<Throwable>() val failure = mockk<Throwable>()
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
givenARuleId(firstRuleId)
fakePushRuleService.givenUpdatePushRuleActionsSucceed() fakePushRuleService.givenUpdatePushRuleActionsSucceed()
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
givenARuleId(secondRuleId)
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure) fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
@ -180,9 +190,9 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
// When // When
val viewModelTest = viewModel.test() val viewModelTest = viewModel.test()
// One rule failed to update // One rule failed to update
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), false)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, false))
// All the rules failed to update // All the rules failed to update
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, false))
// Then // Then
coVerifyOrder { coVerifyOrder {
@ -205,14 +215,14 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
.assertStatesChanges( .assertStatesChanges(
initialState, initialState,
{ copy(isLoading = true) }, { copy(isLoading = true) },
{ copy(isLoading = false) }, { copy(isLoading = false, rulesOnError = setOf(firstRuleId)) },
{ copy(isLoading = true) }, { copy(isLoading = true) },
{ copy(isLoading = false) }, { copy(isLoading = false) },
) )
.assertEvents( .assertEvents(
// The global rule remains checked if all the rules are not unchecked // The global rule remains checked if all the rules are not unchecked
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure), VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true, failure),
VectorSettingsPushRuleNotificationViewEvent.Failure(failure), VectorSettingsPushRuleNotificationViewEvent.Failure(secondRuleId, failure),
) )
.finish() .finish()
} }
@ -244,15 +254,11 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
secondResult shouldBe false secondResult shouldBe false
} }
private fun givenARuleId(ruleId: String, notificationIndex: NotificationIndex = NotificationIndex.NOISY): PushRuleAndKind { private fun givenARuleId(ruleId: String, notificationIndex: NotificationIndex = NotificationIndex.NOISY) {
val ruleAndKind = mockk<PushRuleAndKind> { val pushRule = PushRuleFixture.aPushRule(ruleId)
every { pushRule.ruleId } returns ruleId every { pushRule.notificationIndex } returns notificationIndex
every { pushRule.notificationIndex } returns notificationIndex val ruleAndKind = PushRuleFixture.aPushRuleAndKind(pushRule)
every { kind } returns mockk()
}
every { fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind every { fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
return ruleAndKind
} }
} }

View File

@ -0,0 +1,100 @@
/*
* 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.usecase
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fixtures.PushRuleFixture
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import org.amshove.kluent.shouldBeEqualTo
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.matrix.android.sdk.api.session.pushrules.Action
import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
internal class GetPushRulesOnInvalidStateUseCaseTest {
private val fakeSession = FakeSession()
private val getPushRulesOnInvalidStateUseCase = GetPushRulesOnInvalidStateUseCase()
@Before
fun setup() {
mockkStatic("org.matrix.android.sdk.api.session.pushrules.ActionKt")
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `given a list of push rules with children not matching their parent when execute then returns the list of not matching rules`() {
// Given
val firstActions = listOf(Action.Notify)
val secondActions = listOf(Action.DoNotNotify)
givenARuleList(
listOf(
// first set of related rules
givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, firstActions),
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, true, listOf(Action.Notify)),
// second set of related rules
givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false, secondActions),
givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_START_UNSTABLE, false, listOf(Action.DoNotNotify)),
givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff
// Another rule
givenARuleId(RuleIds.RULE_ID_CONTAIN_USER_NAME, true, listOf(Action.Notify)),
)
)
// When
val result = getPushRulesOnInvalidStateUseCase.execute(fakeSession).map { it.ruleId }
// Then
result shouldBeEqualTo listOf(
RuleIds.RULE_ID_ONE_TO_ONE_ROOM, // parent rule
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE,
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE,
RuleIds.RULE_ID_POLL_END_ONE_TO_ONE,
RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, // parent rule
RuleIds.RULE_ID_POLL_START,
RuleIds.RULE_ID_POLL_END,
RuleIds.RULE_ID_POLL_END_UNSTABLE,
)
}
private fun givenARuleList(rules: List<PushRule>) {
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rules
}
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
val ruleAndKind = PushRuleFixture.aPushRuleAndKind(
PushRuleFixture.aPushRule(ruleId, enabled, actions),
)
every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
return ruleAndKind.pushRule
}
}

View File

@ -17,6 +17,7 @@
package im.vector.app.features.settings.notifications.usecase package im.vector.app.features.settings.notifications.usecase
import im.vector.app.test.fakes.FakeSession import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fixtures.PushRuleFixture
import io.mockk.coVerifySequence import io.mockk.coVerifySequence
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
@ -28,14 +29,13 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.session.pushrules.Action import org.matrix.android.sdk.api.session.pushrules.Action
import org.matrix.android.sdk.api.session.pushrules.RuleIds import org.matrix.android.sdk.api.session.pushrules.RuleIds
import org.matrix.android.sdk.api.session.pushrules.getActions
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
internal class UpdatePushRulesIfNeededUseCaseTest { internal class UpdatePushRulesIfNeededUseCaseTest {
private val fakeSession = FakeSession() private val fakeSession = FakeSession()
private val updatePushRulesIfNeededUseCase = UpdatePushRulesIfNeededUseCase() private val fakeGetPushRulesOnInvalidStateUseCase = mockk<GetPushRulesOnInvalidStateUseCase>()
private val updatePushRulesIfNeededUseCase = UpdatePushRulesIfNeededUseCase(fakeGetPushRulesOnInvalidStateUseCase)
@Before @Before
fun setup() { fun setup() {
@ -50,25 +50,26 @@ internal class UpdatePushRulesIfNeededUseCaseTest {
@Test @Test
fun test() = runTest { fun test() = runTest {
// Given // Given
val firstActions = listOf(Action.Notify) val firstParentEnabled = true
val secondActions = listOf(Action.DoNotNotify) val firstParentActions = listOf(Action.Notify)
val rules = listOf( val firstParent = givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, firstParentEnabled, firstParentActions)
val secondParentEnabled = false
val secondParentActions = listOf(Action.DoNotNotify)
val secondParent = givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, secondParentEnabled, secondParentActions)
val rulesOnError = listOf(
// first set of related rules // first set of related rules
givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, firstActions), firstParent,
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, true, listOf(Action.Notify)),
// second set of related rules // second set of related rules
givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false, secondActions), secondParent,
givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_START_UNSTABLE, false, listOf(Action.DoNotNotify)),
givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff
// Another rule
givenARuleId(RuleIds.RULE_ID_CONTAIN_USER_NAME, true, listOf(Action.Notify)),
) )
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rules every { fakeGetPushRulesOnInvalidStateUseCase.execute(fakeSession) } returns rulesOnError
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rulesOnError
// When // When
updatePushRulesIfNeededUseCase.execute(fakeSession) updatePushRulesIfNeededUseCase.execute(fakeSession)
@ -77,30 +78,23 @@ internal class UpdatePushRulesIfNeededUseCaseTest {
coVerifySequence { coVerifySequence {
fakeSession.fakePushRuleService.getPushRules() fakeSession.fakePushRuleService.getPushRules()
// first set // first set
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, firstActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, firstParentEnabled, firstParentActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, firstActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, firstParentEnabled, firstParentActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, true, firstActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, firstParentEnabled, firstParentActions)
// second set // second set
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, false, secondActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, secondParentEnabled, secondParentActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, false, secondActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, secondParentEnabled, secondParentActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, false, secondActions) fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, secondParentEnabled, secondParentActions)
} }
} }
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule { private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
val pushRule = mockk<PushRule> { val ruleAndKind = PushRuleFixture.aPushRuleAndKind(
every { this@mockk.ruleId } returns ruleId pushRule = PushRuleFixture.aPushRule(ruleId, enabled, actions),
every { this@mockk.enabled } returns enabled )
every { this@mockk.actions } returns actions
every { getActions() } returns actions
}
val ruleAndKind = mockk<PushRuleAndKind> {
every { this@mockk.pushRule } returns pushRule
every { kind } returns mockk()
}
every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
return pushRule return ruleAndKind.pushRule
} }
} }

View File

@ -0,0 +1,49 @@
/*
* 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.test.fixtures
import io.mockk.every
import io.mockk.mockk
import org.matrix.android.sdk.api.session.pushrules.Action
import org.matrix.android.sdk.api.session.pushrules.RuleSetKey
import org.matrix.android.sdk.api.session.pushrules.getActions
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
object PushRuleFixture {
private const val A_RULE_ID = "a-rule-id"
// Needs: mockkStatic("org.matrix.android.sdk.api.session.pushrules.ActionKt")
fun aPushRule(
ruleId: String = A_RULE_ID,
enabled: Boolean = true,
actions: List<Action> = listOf(Action.Notify),
): PushRule = mockk {
every { this@mockk.ruleId } returns ruleId
every { this@mockk.enabled } returns enabled
every { getActions() } returns actions
}
fun aPushRuleAndKind(
pushRule: PushRule = aPushRule(),
kind: RuleSetKey = RuleSetKey.UNDERRIDE,
): PushRuleAndKind = mockk {
every { this@mockk.pushRule } returns pushRule
every { this@mockk.kind } returns kind
}
}