Skip to content

Commit

Permalink
Client side exceptions now disposes all subscriptions managed by Kelm
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanHasegawa committed Dec 17, 2019
1 parent 5529e82 commit be97d24
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
6 changes: 6 additions & 0 deletions kelm-core/src/main/kotlin/kelm/exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ data class SubscriptionException(val subscription: Any, override val cause: Thro

data class CmdException(val cmd: Any, override val cause: Throwable) :
ExternalException("The command [$cmd] threw an error", cause)

data class UnhandledException(override val cause: Throwable) :
ExternalException(
"An unhandled exception was caught. Could be caused by a client side function.",
cause
)
7 changes: 7 additions & 0 deletions kelm-core/src/main/kotlin/kelm/internal/core.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import kelm.Sub
import kelm.SubContext
import kelm.SubFactoryNotImplementedException
import kelm.SubscriptionException
import kelm.UnhandledException
import kelm.UpdateContext
import kelm.UpdateF
import kelm.toNullable
Expand Down Expand Up @@ -264,6 +265,12 @@ internal fun <ModelT, MsgT, CmdT : Cmd, SubT : Sub> build(
subDisposables.values.forEach(Disposable::dispose)
loggerDisposables.forEach(Disposable::dispose)
}
.doOnError {
errorToMsg(UnhandledException(it))
cmdDisposables.values.forEach(Disposable::dispose)
subDisposables.values.forEach(Disposable::dispose)
loggerDisposables.forEach(Disposable::dispose)
}
}

private fun <SubT : Sub> computeSubsDiff(old: List<SubT>, new: List<SubT>): List<SubsDiffOp<SubT>> {
Expand Down
64 changes: 64 additions & 0 deletions kelm-core/src/test/kotlin/clientSideError/ClientSideErrorTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package clientSideError

import clientSideError.AElement.Cmd
import clientSideError.AElement.Model
import clientSideError.AElement.Msg
import clientSideError.AElement.Sub
import io.kotlintest.matchers.types.shouldBeInstanceOf
import io.kotlintest.shouldBe
import io.reactivex.Observable
import kelm.ExternalException
import kelm.Kelm
import kelm.SubContext
import kelm.UnhandledException
import kelm.UpdateContext
import org.spekframework.spek2.Spek

object AElement : Kelm.Element<Model, Msg, Cmd, Sub>() {
var errorToMsgSaved: ExternalException? = null

object Model
object Msg
object Cmd : kelm.Cmd()
object Sub : kelm.Sub("sub")

override fun UpdateContext<Model, Msg, Cmd, Sub>.update(model: Model, msg: Msg): Model? {
+Cmd
return null
}

override fun SubContext<Sub>.subscriptions(model: Model) {
+Sub
}

override fun errorToMsg(error: ExternalException): Msg? {
errorToMsgSaved = error
return null
}
}

object ClientSideErrorTest : Spek({
group("given an element with faulty client side functions") {
val cmdToMaybe = { cmd: Cmd -> error("ops") }
var disposedSub = false
val subToObs = { _: Sub, _: Observable<Msg>, _: Observable<Model> ->
Observable.never<Msg>()
.doOnDispose { disposedSub = true }
}

test("make sure in case of faulty client-side error, Kelm disposes all subs") {
val ts = AElement
.start(
initModel = Model,
msgInput = Observable.just(Msg),
cmdToMaybe = cmdToMaybe,
subToObs = subToObs
)
.test()

ts.assertError { it is IllegalStateException && it.localizedMessage == "ops" }
disposedSub shouldBe true
AElement.errorToMsgSaved.shouldBeInstanceOf<UnhandledException>()
}
}
})

0 comments on commit be97d24

Please sign in to comment.