From d001f47b1a1c12fd96488faea65f4b2e06ec37ec Mon Sep 17 00:00:00 2001 From: Rafael Tonholo Date: Thu, 25 Sep 2025 14:54:45 -0300 Subject: [PATCH] refactor(notifications): simplify notification command outcomes --- .../SecretDebugSettingsScreenPreview.kt | 17 +- feature/notification/api/build.gradle.kts | 1 + .../notification/api/NotificationId.kt | 6 +- .../api/command/NotificationCommand.kt | 46 +----- .../command/outcome/CommandExecutionFailed.kt | 22 +++ .../api/command/outcome/CommandNotCreated.kt | 10 ++ .../outcome/NotificationCommandOutcome.kt | 88 +++++++++++ .../api/command/outcome/UnsupportedCommand.kt | 42 +++++ .../api/dismisser/NotificationDismisser.kt | 9 +- .../compat/NotificationDismisserCompat.kt | 6 +- .../api/sender/NotificationSender.kt | 8 +- .../sender/compat/NotificationSenderCompat.kt | 6 +- .../compat/NotificationSenderCompatTest.kt | 13 +- .../NotificationSenderCompatJavaTest.java | 13 +- .../command/DismissNotificationCommand.kt | 27 ++-- .../DismissSystemNotificationCommand.kt | 6 +- .../DisplayInAppNotificationCommand.kt | 24 ++- .../DisplaySystemNotificationCommand.kt | 42 ++--- .../dismisser/DefaultNotificationDismisser.kt | 66 +++----- .../impl/sender/DefaultNotificationSender.kt | 17 +- .../DismissInAppNotificationCommandTest.kt | 42 +++-- .../DismissSystemNotificationCommandTest.kt | 42 +++-- .../DisplayInAppNotificationCommandTest.kt | 33 ++-- .../DisplaySystemNotificationCommandTest.kt | 43 ++--- .../DefaultNotificationDismisserTest.kt | 147 ++++++++---------- .../sender/DefaultNotificationSenderTest.kt | 34 ++-- .../testing/fake/FakeNotificationManager.kt | 19 +-- .../command/FakeInAppNotificationCommand.kt | 4 +- .../command/FakeSystemNotificationCommand.kt | 4 +- .../fake/sender/FakeNotificationSender.kt | 8 +- 30 files changed, 440 insertions(+), 405 deletions(-) create mode 100644 feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandExecutionFailed.kt create mode 100644 feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandNotCreated.kt create mode 100644 feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/NotificationCommandOutcome.kt create mode 100644 feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/UnsupportedCommand.kt diff --git a/feature/debug-settings/src/debug/kotlin/net/thunderbird/feature/debug/settings/SecretDebugSettingsScreenPreview.kt b/feature/debug-settings/src/debug/kotlin/net/thunderbird/feature/debug/settings/SecretDebugSettingsScreenPreview.kt index 3040ad5bdb9..427c3c3d4c1 100644 --- a/feature/debug-settings/src/debug/kotlin/net/thunderbird/feature/debug/settings/SecretDebugSettingsScreenPreview.kt +++ b/feature/debug-settings/src/debug/kotlin/net/thunderbird/feature/debug/settings/SecretDebugSettingsScreenPreview.kt @@ -10,12 +10,10 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.flowOf import net.thunderbird.core.common.resources.StringsResourceManager -import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.debug.settings.notification.DebugNotificationSectionViewModel import net.thunderbird.feature.mail.account.api.AccountManager import net.thunderbird.feature.mail.account.api.BaseAccount -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.receiver.InAppNotificationEvent import net.thunderbird.feature.notification.api.receiver.InAppNotificationReceiver @@ -25,6 +23,12 @@ import net.thunderbird.feature.notification.api.sender.NotificationSender @Composable private fun SecretDebugSettingsScreenPreview() { koinPreview { + single { + object : InAppNotificationReceiver { + override val events: SharedFlow + get() = error("not implemented") + } + } single { DebugNotificationSectionViewModel( stringsResourceManager = object : StringsResourceManager { @@ -47,13 +51,10 @@ private fun SecretDebugSettingsScreenPreview() { notificationSender = object : NotificationSender { override fun send( notification: Notification, - ): Flow, Failure>> = + ): Flow> = error("not implemented") }, - notificationReceiver = object : InAppNotificationReceiver { - override val events: SharedFlow - get() = error("not implemented") - }, + notificationReceiver = get(), ) } } WithContent { diff --git a/feature/notification/api/build.gradle.kts b/feature/notification/api/build.gradle.kts index 255c88e8a3d..ac29c46ca89 100644 --- a/feature/notification/api/build.gradle.kts +++ b/feature/notification/api/build.gradle.kts @@ -7,6 +7,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.core.common) + implementation(projects.core.featureflag) implementation(projects.core.outcome) } commonTest.dependencies { diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/NotificationId.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/NotificationId.kt index a205c776371..3461ab40af6 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/NotificationId.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/NotificationId.kt @@ -10,4 +10,8 @@ package net.thunderbird.feature.notification.api * @property value The integer value of the notification ID. */ @JvmInline -value class NotificationId(val value: Int) : Comparable by value +value class NotificationId(val value: Int) : Comparable by value { + companion object { + val Undefined = NotificationId(Int.MIN_VALUE) + } +} diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/NotificationCommand.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/NotificationCommand.kt index 6a748644437..db810f4b2c6 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/NotificationCommand.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/NotificationCommand.kt @@ -1,8 +1,6 @@ package net.thunderbird.feature.notification.api.command -import androidx.annotation.Discouraged -import net.thunderbird.core.outcome.Outcome -import net.thunderbird.feature.notification.api.NotificationId +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier @@ -24,45 +22,5 @@ abstract class NotificationCommand( * Executes the command. * @return The result of the execution. */ - abstract suspend fun execute(): Outcome, Failure> - - /** - * Represents the outcome of a command's execution. - */ - sealed interface CommandOutcome - - /** - * Represents a successful command execution. - * - * @param TNotification The type of notification associated with the command. - * @property notificationId The ID of the notification that was successfully acted upon. - * @property command The command that was executed successfully. - */ - data class Success( - val notificationId: NotificationId, - val command: NotificationCommand, - ) : CommandOutcome { - companion object { - @Discouraged( - message = "This is a utility function to enable usage in Java code. " + - "Use Success(NotificationId, NotificationCommand) instead.", - ) - operator fun invoke( - notificationId: Int, - command: NotificationCommand<*>, - ): Success = Success(NotificationId(notificationId), command) - } - } - - /** - * Represents a failed command execution. - * - * @param TNotification The type of notification associated with the command. - * @property command The command that failed. - * @property throwable The exception that caused the failure. - */ - data class Failure( - val command: NotificationCommand?, - val throwable: Throwable, - ) : CommandOutcome + abstract suspend fun execute(): NotificationCommandOutcome } diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandExecutionFailed.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandExecutionFailed.kt new file mode 100644 index 00000000000..fc2ab4b09f0 --- /dev/null +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandExecutionFailed.kt @@ -0,0 +1,22 @@ +package net.thunderbird.feature.notification.api.command.outcome + +import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.content.Notification + +/** + * Represents a failure that occurred while attempting to execute a command. + * + * Use this when a command was recognized and supported, but its execution failed due to an error + * (e.g., I/O failure, unexpected state, or an exception thrown by the underlying system). + * + * @param TNotification The type of notification associated with the command. + * @property command The command whose execution failed. May be null if the command couldn't be + * fully constructed. + * @property message A human-readable description of the failure. Defaults to a generic message. + * @property throwable An optional exception that provides additional context about the failure. + */ +data class CommandExecutionFailed( + override val command: NotificationCommand, + val message: String = "Command failed to execute.", + val throwable: Throwable? = null, +) : Failure diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandNotCreated.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandNotCreated.kt new file mode 100644 index 00000000000..14ba416f136 --- /dev/null +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/CommandNotCreated.kt @@ -0,0 +1,10 @@ +package net.thunderbird.feature.notification.api.command.outcome + +import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.content.Notification + +data class CommandNotCreated( + val message: String, +) : Failure { + override val command: NotificationCommand? = null +} diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/NotificationCommandOutcome.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/NotificationCommandOutcome.kt new file mode 100644 index 00000000000..e75445de29d --- /dev/null +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/NotificationCommandOutcome.kt @@ -0,0 +1,88 @@ +@file:JvmName("NotificationCommandOutcome") + +package net.thunderbird.feature.notification.api.command.outcome + +import androidx.annotation.Discouraged +import net.thunderbird.core.outcome.Outcome +import net.thunderbird.feature.notification.api.NotificationId +import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.content.Notification + +/** + * Outcome type for executing a NotificationCommand. + * + * When executing a command, the result will be either a [Success] or a [Failure]. + */ +typealias NotificationCommandOutcome = Outcome, Failure> + +/** + * Represents a successful command execution. + * + * @param TNotification The type of notification associated with the command. + * @property notificationId The ID of the notification that was successfully acted upon. + * @property command The command that was executed successfully. + */ +sealed interface Success { + val notificationId: NotificationId + val command: NotificationCommand? + + /** + * Indicates that the command was executed as requested. + * + * This is the canonical success case that carries the command instance that was executed. + * + * @param TNotification The type of notification associated with the command. + * @property command The concrete command that was executed. + */ + @ConsistentCopyVisibility + data class Executed internal constructor( + override val notificationId: NotificationId, + override val command: NotificationCommand, + ) : Success + + /** + * Indicates that executing the command resulted in no operation. + * + * This can be used when the system is already in the desired state or when there is + * intentionally nothing to do. The [command] may be null if there isn't a specific + * command instance associated with the no-op result. + * + * @param TNotification The type of notification associated with the command. + * @property command The command related to this no-op outcome, if any. + */ + data class NoOperation( + override val command: NotificationCommand? = null, + ) : Success { + override val notificationId: NotificationId = NotificationId.Undefined + } +} + +/** + * Convenience factory that wraps the given command in a [Success.Executed] instance. + */ +fun Success( + notificationId: NotificationId, + command: NotificationCommand, +): Success = Success.Executed(notificationId, command) + +/** + * Convenience factory that wraps the given command in a [Success.Executed] instance. + */ +@Discouraged( + message = "This is a utility function to enable usage in Java code. " + + "Use Success(NotificationId, NotificationCommand) instead.", +) +fun Success( + notificationId: Int, + command: NotificationCommand, +): Success = Success.Executed(NotificationId(notificationId), command) + +/** + * Represents a failed command execution. + * + * @param TNotification The type of notification associated with the command. + * @property command The command that failed. + */ +sealed interface Failure { + val command: NotificationCommand? +} diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/UnsupportedCommand.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/UnsupportedCommand.kt new file mode 100644 index 00000000000..aec508b83c2 --- /dev/null +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/command/outcome/UnsupportedCommand.kt @@ -0,0 +1,42 @@ +package net.thunderbird.feature.notification.api.command.outcome + +import net.thunderbird.core.featureflag.FeatureFlagKey +import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.content.Notification + +/** + * Represents a command that cannot be executed because it is not supported in the current context. + * + * Typical reasons include disabled feature flags or an unknown/unsupported command on the + * current platform or build variant. + * + * @param TNotification The type of notification associated with the command. + * @property command The command that was deemed unsupported. May be null when the unsupported + * state is determined before a concrete command instance is created. + * @property reason The specific reason why the command is not supported. + */ +data class UnsupportedCommand( + override val command: NotificationCommand?, + val reason: Reason, +) : Failure { + + /** + * Describes why a command is unsupported. + */ + sealed interface Reason { + /** + * The command is behind a feature flag that is currently disabled. + * + * @property key The feature flag key that disabled this command. + */ + data class FeatureFlagDisabled(val key: FeatureFlagKey) : Reason + + /** + * A generic, unknown reason for an unsupported command. + * + * @property message A human-readable description of the issue. + * @property throwable An optional underlying exception that provides more context. + */ + data class Unknown(val message: String, val throwable: Throwable? = null) : Reason + } +} diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/NotificationDismisser.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/NotificationDismisser.kt index b96a2b42e8d..cfd76eef037 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/NotificationDismisser.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/NotificationDismisser.kt @@ -3,8 +3,9 @@ package net.thunderbird.feature.notification.api.dismisser import kotlinx.coroutines.flow.Flow import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.Failure +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success import net.thunderbird.feature.notification.api.content.Notification /** @@ -18,7 +19,7 @@ interface NotificationDismisser { * @return A [Flow] of [Outcome] that emits either a [Success] with the dismissed [Notification] * or a [Failure] with the [Notification] that failed to be dismissed. */ - fun dismiss(id: NotificationId): Flow, Failure>> + fun dismiss(id: NotificationId): Flow> /** * Dismisses a notification. @@ -28,5 +29,5 @@ interface NotificationDismisser { * The [Outcome] will be a [Success] containing the dismissed [Notification] if the operation was successful, * or a [Failure] containing the [Notification] if the operation failed. */ - fun dismiss(notification: Notification): Flow, Failure>> + fun dismiss(notification: Notification): Flow> } diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/compat/NotificationDismisserCompat.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/compat/NotificationDismisserCompat.kt index 7d1ba13cb97..9e5960b25aa 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/compat/NotificationDismisserCompat.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/dismisser/compat/NotificationDismisserCompat.kt @@ -9,9 +9,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import net.thunderbird.core.outcome.Outcome -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.dismisser.NotificationDismisser @@ -45,6 +43,6 @@ class NotificationDismisserCompat @JvmOverloads constructor( } fun interface OnResultListener { - fun onResult(outcome: Outcome, Failure>) + fun onResult(outcome: NotificationCommandOutcome) } } diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/NotificationSender.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/NotificationSender.kt index 4cc4051c8fe..ef1c0b8a147 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/NotificationSender.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/NotificationSender.kt @@ -1,10 +1,8 @@ package net.thunderbird.feature.notification.api.sender import kotlinx.coroutines.flow.Flow -import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification /** @@ -15,7 +13,7 @@ fun interface NotificationSender { * Sends a notification by creating and executing the appropriate commands. * * @param notification The [Notification] to be sent. - * @return A [Flow] that emits the [NotificationCommand.CommandOutcome] for each executed command. + * @return A [Flow] that emits the [NotificationCommand] for each executed command. */ - fun send(notification: Notification): Flow, Failure>> + fun send(notification: Notification): Flow> } diff --git a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompat.kt b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompat.kt index b5a75ba8036..69c0544ba85 100644 --- a/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompat.kt +++ b/feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompat.kt @@ -9,9 +9,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import net.thunderbird.core.outcome.Outcome -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.sender.NotificationSender @@ -45,6 +43,6 @@ class NotificationSenderCompat @JvmOverloads constructor( } fun interface OnResultListener { - fun onResult(outcome: Outcome, Failure>) + fun onResult(outcome: NotificationCommandOutcome) } } diff --git a/feature/notification/api/src/commonTest/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatTest.kt b/feature/notification/api/src/commonTest/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatTest.kt index 40416b328f9..ce3a525b3b6 100644 --- a/feature/notification/api/src/commonTest/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatTest.kt +++ b/feature/notification/api/src/commonTest/kotlin/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatTest.kt @@ -11,9 +11,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.testing.fake.FakeNotification import net.thunderbird.feature.notification.testing.fake.command.FakeInAppNotificationCommand @@ -25,18 +26,18 @@ class NotificationSenderCompatTest { @Test fun `send should call listener callback whenever a result is received`() { // Arrange - val expectedResults = listOf, Failure>>( + val expectedResults = listOf>( Outcome.success(Success(NotificationId(1), FakeInAppNotificationCommand())), - Outcome.success(Success(NotificationId(2), FakeSystemNotificationCommand())), + Outcome.success(Success(NotificationId(1), FakeSystemNotificationCommand())), Outcome.failure( - error = Failure( + error = CommandExecutionFailed( command = FakeSystemNotificationCommand(), throwable = NotificationCommandException("What an issue?"), ), ), ) val sender = FakeNotificationSender(results = expectedResults) - val actualResults = mutableListOf, Failure>>() + val actualResults = mutableListOf>() val listener = spy( NotificationSenderCompat.OnResultListener { outcome -> actualResults += outcome diff --git a/feature/notification/api/src/jvmTest/java/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatJavaTest.java b/feature/notification/api/src/jvmTest/java/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatJavaTest.java index 20677829366..4012675be44 100644 --- a/feature/notification/api/src/jvmTest/java/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatJavaTest.java +++ b/feature/notification/api/src/jvmTest/java/net/thunderbird/feature/notification/api/sender/compat/NotificationSenderCompatJavaTest.java @@ -9,9 +9,11 @@ import kotlinx.coroutines.test.TestDispatcher; import kotlinx.coroutines.test.TestDispatchers; import net.thunderbird.core.outcome.Outcome; -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure; -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success; import net.thunderbird.feature.notification.api.command.NotificationCommandException; +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed; +import net.thunderbird.feature.notification.api.command.outcome.Failure; +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome; +import net.thunderbird.feature.notification.api.command.outcome.Success; import net.thunderbird.feature.notification.api.content.Notification; import net.thunderbird.feature.notification.testing.fake.FakeNotification; import net.thunderbird.feature.notification.testing.fake.command.FakeInAppNotificationCommand; @@ -53,11 +55,12 @@ public void send_shouldCallListenerCallback_wheneverAResultIsReceived() { ? extends @NotNull Failure > > expectedResults = List.of( - Outcome.Companion.success(Success.Companion.invoke(1, new FakeInAppNotificationCommand())), - Outcome.Companion.success(Success.Companion.invoke(2, new FakeSystemNotificationCommand())), + Outcome.Companion.success(NotificationCommandOutcome.Success(1, new FakeInAppNotificationCommand())), + Outcome.Companion.success(NotificationCommandOutcome.Success(2, new FakeSystemNotificationCommand())), Outcome.Companion.failure( - new Failure<>( + new CommandExecutionFailed<>( new FakeSystemNotificationCommand(), + "What an issue?", new NotificationCommandException("What an issue?") ) ) diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissNotificationCommand.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissNotificationCommand.kt index b9f2331f4cc..6267fa876e7 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissNotificationCommand.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissNotificationCommand.kt @@ -7,7 +7,10 @@ import net.thunderbird.core.logging.Logger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier @@ -37,16 +40,14 @@ sealed class DismissNotificationCommand( ) { abstract val featureFlagKey: FeatureFlagKey - override suspend fun execute(): Outcome, Failure> { + override suspend fun execute(): NotificationCommandOutcome { logger.verbose(logTag) { "execute() called with: notification = $notification" } return when { featureFlagProvider.provide(featureFlagKey).isDisabledOrUnavailable() -> Outcome.failure( - error = Failure( + error = UnsupportedCommand( command = this, - throwable = NotificationCommandException( - message = "${featureFlagKey.key} feature flag is not enabled", - ), + reason = UnsupportedCommand.Reason.FeatureFlagDisabled(key = featureFlagKey), ), ) @@ -63,14 +64,12 @@ sealed class DismissNotificationCommand( Outcome.success(Success(notificationId = id, command = this)) } - else -> { - Outcome.failure( - Failure( - command = this, - throwable = NotificationCommandException("Can't execute command."), - ), - ) - } + else -> Outcome.failure( + error = CommandExecutionFailed( + command = this, + message = "Notification is not registered in the NotificationRegistry.", + ), + ) } } } diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommand.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommand.kt index a9d14304c44..0ea997a5a34 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommand.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommand.kt @@ -10,9 +10,9 @@ import net.thunderbird.feature.notification.api.receiver.NotificationNotifier private const val TAG = "DismissSystemNotificationCommand" class DismissSystemNotificationCommand( - private val logger: Logger, - private val featureFlagProvider: FeatureFlagProvider, - private val notificationRegistry: NotificationRegistry, + logger: Logger, + featureFlagProvider: FeatureFlagProvider, + notificationRegistry: NotificationRegistry, notification: SystemNotification, notifier: NotificationNotifier, ) : DismissNotificationCommand( diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommand.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommand.kt index e9480580a06..821df919460 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommand.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommand.kt @@ -2,12 +2,14 @@ package net.thunderbird.feature.notification.impl.command import net.thunderbird.core.featureflag.FeatureFlagKey import net.thunderbird.core.featureflag.FeatureFlagProvider -import net.thunderbird.core.featureflag.FeatureFlagResult import net.thunderbird.core.logging.Logger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier @@ -28,19 +30,15 @@ internal class DisplayInAppNotificationCommand( notification: InAppNotification, notifier: NotificationNotifier, ) : NotificationCommand(notification, notifier) { - private val isFeatureFlagEnabled: Boolean - get() = featureFlagProvider - .provide(FeatureFlagKey.DisplayInAppNotifications) == FeatureFlagResult.Enabled - - override suspend fun execute(): Outcome, Failure> { + override suspend fun execute(): NotificationCommandOutcome { logger.debug(TAG) { "execute() called with: notification = $notification" } return when { - isFeatureFlagEnabled.not() -> + featureFlagProvider.provide(FeatureFlagKey.DisplayInAppNotifications).isDisabledOrUnavailable() -> Outcome.failure( - error = Failure( + error = UnsupportedCommand( command = this, - throwable = NotificationCommandException( - message = "${FeatureFlagKey.DisplayInAppNotifications.key} feature flag is not enabled", + reason = UnsupportedCommand.Reason.FeatureFlagDisabled( + key = FeatureFlagKey.DisplayInAppNotifications, ), ), ) @@ -51,9 +49,7 @@ internal class DisplayInAppNotificationCommand( Outcome.success(Success(notificationId = id, command = this)) } - else -> { - Outcome.failure(Failure(command = this, throwable = Exception("Can't execute command."))) - } + else -> Outcome.failure(CommandExecutionFailed(command = this)) } } diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommand.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommand.kt index 9f9be069842..f0002b0a951 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommand.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommand.kt @@ -2,13 +2,15 @@ package net.thunderbird.feature.notification.impl.command import net.thunderbird.core.featureflag.FeatureFlagKey import net.thunderbird.core.featureflag.FeatureFlagProvider -import net.thunderbird.core.featureflag.FeatureFlagResult import net.thunderbird.core.logging.Logger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.NotificationSeverity import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.content.SystemNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier @@ -32,24 +34,19 @@ internal class DisplaySystemNotificationCommand( false }, ) : NotificationCommand(notification, notifier) { - - private val isFeatureFlagEnabled: Boolean - get() = featureFlagProvider - .provide(FeatureFlagKey.UseNotificationSenderForSystemNotifications) == FeatureFlagResult.Enabled - - override suspend fun execute(): Outcome, Failure> { - logger.debug(TAG) { "execute() called" } + override suspend fun execute(): NotificationCommandOutcome { + logger.debug(TAG) { "execute() called with notification = $notification" } return when { - isFeatureFlagEnabled.not() -> - Outcome.failure( - error = Failure( - command = this, - throwable = NotificationCommandException( - message = "${FeatureFlagKey.UseNotificationSenderForSystemNotifications.key} feature flag" + - "is not enabled", - ), + featureFlagProvider + .provide(FeatureFlagKey.UseNotificationSenderForSystemNotifications) + .isDisabledOrUnavailable() -> Outcome.failure( + error = UnsupportedCommand( + command = this, + reason = UnsupportedCommand.Reason.FeatureFlagDisabled( + key = FeatureFlagKey.UseNotificationSenderForSystemNotifications, ), - ) + ), + ) canExecuteCommand() -> { val id = notificationRegistry.register(notification) @@ -57,14 +54,7 @@ internal class DisplaySystemNotificationCommand( Outcome.success(Success(notificationId = id, command = this)) } - else -> { - Outcome.failure( - error = Failure( - command = this, - throwable = NotificationCommandException("Can't execute command."), - ), - ) - } + else -> Outcome.failure(error = CommandExecutionFailed(command = this)) } } diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisser.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisser.kt index 0192b33e79f..f1dcc2d5d8d 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisser.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisser.kt @@ -9,9 +9,9 @@ import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandNotCreated +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome +import net.thunderbird.feature.notification.api.command.outcome.Success import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.content.SystemNotification @@ -43,58 +43,34 @@ class DefaultNotificationDismisser internal constructor( private val systemNotificationNotifier: NotificationNotifier, private val inAppNotificationNotifier: NotificationNotifier, ) : NotificationDismisser { - override fun dismiss(id: NotificationId): Flow, Failure>> = flow { + override fun dismiss(id: NotificationId): Flow> = flow { logger.verbose(TAG) { "dismiss() called with: id = $id" } val notification = notificationRegistry[id] if (notification == null) { - emit( - value = Outcome.failure( - error = Failure( - command = null, - throwable = NotificationCommandException(message = "Notification with id '$id' not found"), - ), - ), - ) + emit(Outcome.success(Success.NoOperation())) } else { emitAll(dismiss(notification)) } } - override fun dismiss(notification: Notification): Flow, Failure>> = - flow { - logger.verbose(TAG) { "dismiss() called with: notification = $notification" } + override fun dismiss(notification: Notification): Flow> = flow { + logger.verbose(TAG) { "dismiss() called with: notification = $notification" } - if (notification in notificationRegistry) { - val commands = buildCommands(notification) - commands - .ifEmpty { - val message = "The notification is present in the registrar; " + - "however no commands where found to execute for notification $notification" - logger.warn { message } - emit( - Outcome.Failure( - Failure( - command = null, - throwable = NotificationCommandException(message), - ), - ), - ) - emptyList() - } - .forEach { command -> emit(command.execute()) } - } else { - emit( - value = Outcome.failure( - error = Failure( - command = null, - throwable = NotificationCommandException( - message = "Can't dismiss notification that is already dismissed", - ), - ), - ), - ) - } + if (notification in notificationRegistry) { + val commands = buildCommands(notification) + commands + .ifEmpty { + val message = "The notification is present in the registrar; " + + "however no commands where found to execute for notification $notification" + logger.warn { message } + emit(Outcome.failure(CommandNotCreated(message))) + emptyList() + } + .forEach { command -> emit(command.execute()) } + } else { + emit(Outcome.success(Success.NoOperation())) } + } private fun buildCommands(notification: Notification): List> = buildList { if (notification is SystemNotification) { diff --git a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSender.kt b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSender.kt index 4bb1960655d..d44f0c7305e 100644 --- a/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSender.kt +++ b/feature/notification/impl/src/commonMain/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSender.kt @@ -7,9 +7,8 @@ import net.thunderbird.core.logging.Logger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.command.NotificationCommand -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandNotCreated +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.content.SystemNotification @@ -39,21 +38,13 @@ class DefaultNotificationSender internal constructor( private val systemNotificationNotifier: NotificationNotifier, private val inAppNotificationNotifier: NotificationNotifier, ) : NotificationSender { - override fun send(notification: Notification): Flow, Failure>> = flow { + override fun send(notification: Notification): Flow> = flow { val commands = buildCommands(notification) - commands .ifEmpty { val message = "No commands to execute for notification $notification" logger.warn { message } - emit( - Outcome.Failure( - Failure( - command = null, - throwable = NotificationCommandException(message), - ), - ), - ) + emit(Outcome.failure(CommandNotCreated(message))) emptyList() } .forEach { command -> emit(command.execute()) } diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissInAppNotificationCommandTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissInAppNotificationCommandTest.kt index 3bacfb2b50c..9222f6742bd 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissInAppNotificationCommandTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissInAppNotificationCommandTest.kt @@ -2,7 +2,6 @@ package net.thunderbird.feature.notification.impl.command import assertk.all import assertk.assertThat -import assertk.assertions.hasMessage import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.prop @@ -18,9 +17,9 @@ import net.thunderbird.core.featureflag.FeatureFlagResult import net.thunderbird.core.logging.testing.TestLogger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeNotification @@ -47,16 +46,15 @@ class DismissInAppNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.DisplayInAppNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.DisplayInAppNotifications) } } @@ -79,16 +77,15 @@ class DismissInAppNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.DisplayInAppNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.DisplayInAppNotifications) } } @@ -141,14 +138,13 @@ class DismissInAppNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(CommandExecutionFailed::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage("Can't execute command.") + prop(CommandExecutionFailed::message) + .isEqualTo("Notification is not registered in the NotificationRegistry.") } verifySuspend(exactly(0)) { notifier.dismiss(any()) } diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommandTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommandTest.kt index 5909db633f4..71b7cd9df3e 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommandTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DismissSystemNotificationCommandTest.kt @@ -2,7 +2,6 @@ package net.thunderbird.feature.notification.impl.command import assertk.all import assertk.assertThat -import assertk.assertions.hasMessage import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.prop @@ -18,9 +17,9 @@ import net.thunderbird.core.featureflag.FeatureFlagResult import net.thunderbird.core.logging.testing.TestLogger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.SystemNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeNotification @@ -48,16 +47,15 @@ class DismissSystemNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.UseNotificationSenderForSystemNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.UseNotificationSenderForSystemNotifications) } } @@ -80,16 +78,15 @@ class DismissSystemNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.UseNotificationSenderForSystemNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.UseNotificationSenderForSystemNotifications) } } @@ -142,14 +139,13 @@ class DismissSystemNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(CommandExecutionFailed::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage("Can't execute command.") + prop(CommandExecutionFailed::message) + .isEqualTo("Notification is not registered in the NotificationRegistry.") } verifySuspend(exactly(0)) { notifier.dismiss(any()) } diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommandTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommandTest.kt index f10e8deb23a..ea3e9b140a4 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommandTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplayInAppNotificationCommandTest.kt @@ -2,7 +2,6 @@ package net.thunderbird.feature.notification.impl.command import assertk.all import assertk.assertThat -import assertk.assertions.hasMessage import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.prop @@ -19,9 +18,8 @@ import net.thunderbird.core.logging.testing.TestLogger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.NotificationSeverity -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeNotification @@ -47,18 +45,16 @@ class DisplayInAppNotificationCommandTest { val outcome = testSubject.execute() // Assert - assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.DisplayInAppNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.DisplayInAppNotifications) } } @@ -80,16 +76,15 @@ class DisplayInAppNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.DisplayInAppNotifications.key} feature flag is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.DisplayInAppNotifications) } } diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommandTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommandTest.kt index 617bb281f2c..f074e718f01 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommandTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/command/DisplaySystemNotificationCommandTest.kt @@ -2,7 +2,6 @@ package net.thunderbird.feature.notification.impl.command import assertk.all import assertk.assertThat -import assertk.assertions.hasMessage import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.prop @@ -20,9 +19,9 @@ import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.NotificationSeverity -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandExecutionFailed +import net.thunderbird.feature.notification.api.command.outcome.Success +import net.thunderbird.feature.notification.api.command.outcome.UnsupportedCommand import net.thunderbird.feature.notification.api.content.SystemNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeNotification @@ -48,19 +47,16 @@ class DisplaySystemNotificationCommandTest { val outcome = testSubject.execute() // Assert - assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.UseNotificationSenderForSystemNotifications.key} feature flag" + - "is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.UseNotificationSenderForSystemNotifications) } } @@ -82,17 +78,15 @@ class DisplaySystemNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(UnsupportedCommand::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage( - "${FeatureFlagKey.UseNotificationSenderForSystemNotifications.key} feature flag" + - "is not enabled", - ) + prop(UnsupportedCommand::reason) + .isInstanceOf() + .prop(UnsupportedCommand.Reason.FeatureFlagDisabled::key) + .isEqualTo(FeatureFlagKey.UseNotificationSenderForSystemNotifications) } } @@ -114,14 +108,11 @@ class DisplaySystemNotificationCommandTest { // Assert assertThat(outcome) - .isInstanceOf>>() + .isInstanceOf>>() .prop("error") { it.error } .all { - prop(Failure::command) + prop(CommandExecutionFailed::command) .isEqualTo(testSubject) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage("Can't execute command.") } } diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisserTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisserTest.kt index acf2baeeea1..868d4cf5062 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisserTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/dismisser/DefaultNotificationDismisserTest.kt @@ -3,7 +3,6 @@ package net.thunderbird.feature.notification.impl.dismisser import assertk.all import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.hasMessage import assertk.assertions.hasSize import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf @@ -25,9 +24,8 @@ import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.NotificationSeverity -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandNotCreated +import net.thunderbird.feature.notification.api.command.outcome.Success import net.thunderbird.feature.notification.api.content.AppNotification import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.content.Notification @@ -48,7 +46,7 @@ import net.thunderbird.feature.notification.testing.fake.receiver.FakeSystemNoti class DefaultNotificationDismisserTest { @Test - fun `dismiss(id) should emit Failure when notification is not found`() = runTest { + fun `dismiss(id) should emit SuccessNoOperation when notification is not found`() = runTest { // Arrange val registry = FakeNotificationRegistry() val dismisser = createTestSubject(notificationRegistry = registry) @@ -59,73 +57,70 @@ class DefaultNotificationDismisserTest { // Assert assertThat(outcomes.single()) - .isInstanceOf>>() - .prop("error") { it.error } - .all { - prop(Failure::command).isEqualTo(null) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage("Notification with id '$missingId' not found") - } - } - - @Test - fun `dismiss(notification) should emit Success and call system notifier when registered SystemNotification`() = runTest { - // Arrange - val registry = FakeNotificationRegistry() - val systemNotifier = spy(FakeSystemNotificationNotifier()) - val inAppNotifier = spy(FakeInAppNotificationNotifier()) - val dismisser = createTestSubject( - notificationRegistry = registry, - systemNotificationNotifier = systemNotifier, - inAppNotificationNotifier = inAppNotifier, - ) - val notification: SystemNotification = FakeSystemOnlyNotification() - // register notification to be dismissible - registry.register(notification) - - // Act - val outcomes = dismisser.dismiss(notification).toList(mutableListOf()) - - // Assert - assertThat(outcomes.single()) - .isInstanceOf>>() + .isInstanceOf>>() .prop("data") { it.data } - .prop(Success::command) - .isInstanceOf() - verifySuspend(exactly(1)) { systemNotifier.dismiss(id = any()) } - verifySuspend(exactly(0)) { inAppNotifier.dismiss(id = any()) } + .isInstanceOf>() } @Test - fun `dismiss(notification) should emit Success and call in-app notifier when registered InAppNotification`() = runTest { - // Arrange - val registry = FakeNotificationRegistry() - val systemNotifier = spy(FakeSystemNotificationNotifier()) - val inAppNotifier = spy(FakeInAppNotificationNotifier()) - val dismisser = createTestSubject( - notificationRegistry = registry, - systemNotificationNotifier = systemNotifier, - inAppNotificationNotifier = inAppNotifier, - ) - val notification: InAppNotification = FakeInAppOnlyNotification() - registry.register(notification) - - // Act - val outcomes = dismisser.dismiss(notification).toList(mutableListOf()) + fun `dismiss(notification) should emit Success and call system notifier when registered SystemNotification`() = + runTest { + // Arrange + val registry = FakeNotificationRegistry() + val systemNotifier = spy(FakeSystemNotificationNotifier()) + val inAppNotifier = spy(FakeInAppNotificationNotifier()) + val dismisser = createTestSubject( + notificationRegistry = registry, + systemNotificationNotifier = systemNotifier, + inAppNotificationNotifier = inAppNotifier, + ) + val notification: SystemNotification = FakeSystemOnlyNotification() + // register notification to be dismissible + registry.register(notification) + + // Act + val outcomes = dismisser.dismiss(notification).toList(mutableListOf()) + + // Assert + assertThat(outcomes.single()) + .isInstanceOf>>() + .prop("data") { it.data } + .prop(Success.Executed::command) + .isInstanceOf() + verifySuspend(exactly(1)) { systemNotifier.dismiss(id = any()) } + verifySuspend(exactly(0)) { inAppNotifier.dismiss(id = any()) } + } - // Assert - assertThat(outcomes.single()) - .isInstanceOf>>() - .prop("data") { it.data } - .prop(Success::command) - .isInstanceOf() - verifySuspend(exactly(1)) { inAppNotifier.dismiss(id = any()) } - verifySuspend(exactly(0)) { systemNotifier.dismiss(id = any()) } - } + @Test + fun `dismiss(notification) should emit Success and call in-app notifier when registered InAppNotification`() = + runTest { + // Arrange + val registry = FakeNotificationRegistry() + val systemNotifier = spy(FakeSystemNotificationNotifier()) + val inAppNotifier = spy(FakeInAppNotificationNotifier()) + val dismisser = createTestSubject( + notificationRegistry = registry, + systemNotificationNotifier = systemNotifier, + inAppNotificationNotifier = inAppNotifier, + ) + val notification: InAppNotification = FakeInAppOnlyNotification() + registry.register(notification) + + // Act + val outcomes = dismisser.dismiss(notification).toList(mutableListOf()) + + // Assert + assertThat(outcomes.single()) + .isInstanceOf>>() + .prop("data") { it.data } + .prop(Success.Executed::command) + .isInstanceOf() + verifySuspend(exactly(1)) { inAppNotifier.dismiss(id = any()) } + verifySuspend(exactly(0)) { systemNotifier.dismiss(id = any()) } + } @Test - fun `dismiss(notification) should emit Failure when notification is not registered`() = runTest { + fun `dismiss(notification) should emit SuccessNoOperation when notification is not registered`() = runTest { // Arrange val registry = FakeNotificationRegistry() // empty val dismisser = createTestSubject(notificationRegistry = registry) @@ -136,14 +131,9 @@ class DefaultNotificationDismisserTest { // Assert assertThat(outcomes.single()) - .isInstanceOf>>() - .prop("error") { it.error } - .all { - prop(Failure::command).isEqualTo(null) - prop(Failure::throwable) - .isInstanceOf() - .hasMessage("Can't dismiss notification that is already dismissed") - } + .isInstanceOf>>() + .prop("data") { it.data } + .isInstanceOf>() } @Test @@ -180,13 +170,12 @@ class DefaultNotificationDismisserTest { assertThat(outcomes).all { hasSize(size = 1) transform { it.single() } - .isInstanceOf>>() - .prop(Outcome.Failure>::error) + .isInstanceOf>>() + .prop(Outcome.Failure>::error) .all { - prop(Failure::command).isNull() - prop(Failure::throwable) - .isInstanceOf() - .hasMessage(expectedMessage) + prop(CommandNotCreated::command).isNull() + prop(CommandNotCreated::message) + .isEqualTo(expectedMessage) } } assertThat(logger.events) diff --git a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSenderTest.kt b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSenderTest.kt index 68392a9072f..4f1311c8a9d 100644 --- a/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSenderTest.kt +++ b/feature/notification/impl/src/commonTest/kotlin/net/thunderbird/feature/notification/impl/sender/DefaultNotificationSenderTest.kt @@ -3,8 +3,8 @@ package net.thunderbird.feature.notification.impl.sender import assertk.all import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.hasMessage import assertk.assertions.hasSize +import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.isNull import assertk.assertions.prop @@ -22,9 +22,8 @@ import net.thunderbird.core.logging.testing.TestLogger import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationRegistry import net.thunderbird.feature.notification.api.NotificationSeverity -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success -import net.thunderbird.feature.notification.api.command.NotificationCommandException +import net.thunderbird.feature.notification.api.command.outcome.CommandNotCreated +import net.thunderbird.feature.notification.api.command.outcome.Success import net.thunderbird.feature.notification.api.content.AppNotification import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.content.Notification @@ -61,9 +60,9 @@ class DefaultNotificationSenderTest { // Assert assertThat(outcomes.single()) - .isInstanceOf>>() + .isInstanceOf>>() .prop("data") { it.data } - .prop(Success::command) + .prop(Success.Executed::command) .isInstanceOf() verifySuspend(exactly(1)) { systemNotifier.show(id = any(), notification = any()) } @@ -92,14 +91,14 @@ class DefaultNotificationSenderTest { // Assert: we expect two outcomes in order: system then in-app assertThat(outcomes[0]) - .isInstanceOf>>() + .isInstanceOf>>() .prop("data") { it.data } - .prop(Success::command) + .prop(Success.Executed::command) .isInstanceOf() assertThat(outcomes[1]) - .isInstanceOf>>() + .isInstanceOf>>() .prop("data") { it.data } - .prop(Success::command) + .prop(Success.Executed::command) .isInstanceOf() verifySuspend(exactly(1)) { systemNotifier.show(id = any(), notification) } @@ -124,9 +123,9 @@ class DefaultNotificationSenderTest { // Assert assertThat(outcomes.single()) - .isInstanceOf>>() + .isInstanceOf>>() .prop("data") { it.data } - .prop(Success::command) + .prop(Success.Executed::command) .isInstanceOf() verifySuspend(exactly(1)) { inAppNotifier.show(id = any(), notification = any()) } @@ -164,13 +163,12 @@ class DefaultNotificationSenderTest { assertThat(outcomes).all { hasSize(size = 1) transform { it.single() } - .isInstanceOf>>() - .prop(Outcome.Failure>::error) + .isInstanceOf>>() + .prop(Outcome.Failure>::error) .all { - prop(Failure::command).isNull() - prop(Failure::throwable) - .isInstanceOf() - .hasMessage(expectedMessage) + prop(CommandNotCreated::command).isNull() + prop(CommandNotCreated::message) + .isEqualTo(expectedMessage) } } assertThat(logger.events) diff --git a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/FakeNotificationManager.kt b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/FakeNotificationManager.kt index 25c43fdfff7..a4544e2cd06 100644 --- a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/FakeNotificationManager.kt +++ b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/FakeNotificationManager.kt @@ -2,30 +2,25 @@ package net.thunderbird.feature.notification.testing.fake import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.NotificationId import net.thunderbird.feature.notification.api.NotificationManager -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification open class FakeNotificationManager( - private val emitOnSend: (notification: Notification) -> Outcome, Failure>, - private val emitOnDismissNotification: (notification: Notification) -> Outcome< - Success, - Failure, - >, - private val emitOnDismissId: (id: NotificationId) -> Outcome, Failure>, + private val emitOnSend: (notification: Notification) -> NotificationCommandOutcome, + private val emitOnDismissNotification: (notification: Notification) -> NotificationCommandOutcome, + private val emitOnDismissId: (id: NotificationId) -> NotificationCommandOutcome, ) : NotificationManager { - override fun send(notification: Notification): Flow, Failure>> = flow { + override fun send(notification: Notification): Flow> = flow { emit(emitOnSend(notification)) } - override fun dismiss(id: NotificationId): Flow, Failure>> = flow { + override fun dismiss(id: NotificationId): Flow> = flow { emit(emitOnDismissId(id)) } - override fun dismiss(notification: Notification): Flow, Failure>> = + override fun dismiss(notification: Notification): Flow> = flow { emit(emitOnDismissNotification(notification)) } diff --git a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeInAppNotificationCommand.kt b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeInAppNotificationCommand.kt index 61dcba410df..205d65fabb5 100644 --- a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeInAppNotificationCommand.kt +++ b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeInAppNotificationCommand.kt @@ -1,7 +1,7 @@ package net.thunderbird.feature.notification.testing.fake.command -import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.InAppNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeInAppOnlyNotification @@ -11,6 +11,6 @@ class FakeInAppNotificationCommand( notification: InAppNotification = FakeInAppOnlyNotification(), notifier: NotificationNotifier = FakeInAppNotificationNotifier(), ) : NotificationCommand(notification, notifier) { - override suspend fun execute(): Outcome, Failure> = + override suspend fun execute(): NotificationCommandOutcome = error("not implemented") } diff --git a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeSystemNotificationCommand.kt b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeSystemNotificationCommand.kt index 57decdbf2fa..d297f73c3a2 100644 --- a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeSystemNotificationCommand.kt +++ b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/command/FakeSystemNotificationCommand.kt @@ -1,7 +1,7 @@ package net.thunderbird.feature.notification.testing.fake.command -import net.thunderbird.core.outcome.Outcome import net.thunderbird.feature.notification.api.command.NotificationCommand +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.SystemNotification import net.thunderbird.feature.notification.api.receiver.NotificationNotifier import net.thunderbird.feature.notification.testing.fake.FakeSystemOnlyNotification @@ -11,6 +11,6 @@ class FakeSystemNotificationCommand( notification: SystemNotification = FakeSystemOnlyNotification(), notifier: NotificationNotifier = FakeSystemNotificationNotifier(), ) : NotificationCommand(notification, notifier) { - override suspend fun execute(): Outcome, Failure> = + override suspend fun execute(): NotificationCommandOutcome = error("not implemented") } diff --git a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/sender/FakeNotificationSender.kt b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/sender/FakeNotificationSender.kt index 3f2c272dcef..2f6faefe343 100644 --- a/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/sender/FakeNotificationSender.kt +++ b/feature/notification/testing/src/commonMain/kotlin/net/thunderbird/feature/notification/testing/fake/sender/FakeNotificationSender.kt @@ -2,15 +2,13 @@ package net.thunderbird.feature.notification.testing.fake.sender import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow -import net.thunderbird.core.outcome.Outcome -import net.thunderbird.feature.notification.api.command.NotificationCommand.Failure -import net.thunderbird.feature.notification.api.command.NotificationCommand.Success +import net.thunderbird.feature.notification.api.command.outcome.NotificationCommandOutcome import net.thunderbird.feature.notification.api.content.Notification import net.thunderbird.feature.notification.api.sender.NotificationSender class FakeNotificationSender( - private val results: List, Failure>>, + private val results: List>, ) : NotificationSender { - override fun send(notification: Notification): Flow, Failure>> = + override fun send(notification: Notification): Flow> = results.asFlow() }