Skip to content

Commit

Permalink
MemoryView implementation -- tests and remarks
Browse files Browse the repository at this point in the history
  • Loading branch information
DomenicDev committed Dec 14, 2024
1 parent e9f081e commit 145a95c
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import arrow.core.raise.either
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.fold


suspend fun <S, E, Q, MV> MV.handleWithEffect(query: Q): Either<Error, S>
where MV : ViewStateComputation<S, E>, MV : EphemeralViewRepository<E, Q> {
/**
* Extension function - Handles the query of type [Q]
*
* @param query Query of type [Q] to be handled
* @return [Either] (either [Error] or State of type [S])
*
* @author Domenic Cassisi
*/
suspend fun <S, E, Q, EV> EV.handleWithEffect(query: Q): Either<Error, S>
where EV : ViewStateComputation<S, E>, EV : EphemeralViewRepository<E, Q> {

fun Q.fetchEventsWithEffect(): Either<Error, Flow<E>> =
either {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.fraktalio.fmodel.application

import arrow.core.Either
import com.fraktalio.fmodel.application.examples.numbers.even.query.EvenNumberEphemeralViewRepository
import com.fraktalio.fmodel.application.examples.numbers.even.query.evenNumberEphemeralViewRepository
import com.fraktalio.fmodel.domain.IView
import com.fraktalio.fmodel.domain.examples.numbers.api.Description
import com.fraktalio.fmodel.domain.examples.numbers.api.EvenNumberState
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberValue
import com.fraktalio.fmodel.domain.examples.numbers.even.query.evenNumberView
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf

/**
* DSL - Given
*/
private suspend fun <S, E, Q> IView<S, E>.given(repository: EphemeralViewRepository<E, Q>, query: () -> Q): Either<Error, S> =
EphemeralView(
view = this,
ephemeralViewRepository = repository
).handleWithEffect(query())

/**
* DSL - When
*/
private fun <Q> whenQuery(query: Q): Q = query

/**
* DSL - Then
*/
private infix fun <S> Either<Error, S>.thenState(expected: S) {
val state = when (this) {
is Either.Right -> value
is Either.Left -> throw AssertionError("Expected Either.Right, but found Either.Left with value $value")
}
state shouldBe expected
}

private fun <S> Either<Error, S>.thenError() {
val error = when (this) {
is Either.Right -> throw AssertionError("Expected Either.Left, but found Either.Right with value $value")
is Either.Left -> value
}
error.shouldBeInstanceOf<Error>()
}

/**
* Ephemeral View Test
*/
class EphemeralViewTest : FunSpec({
val evenView = evenNumberView()
val ephemeralViewRepository = evenNumberEphemeralViewRepository() as EvenNumberEphemeralViewRepository

test("Ephemeral View - load number flow 1") {
with(evenView) {
given(ephemeralViewRepository) {
whenQuery(1)
} thenState EvenNumberState(Description("Initial state, Number 2, Number 4"), NumberValue(6))
}
}

test("Ephemeral View - load number flow 2") {
with(evenView) {
given(ephemeralViewRepository) {
whenQuery(2)
} thenState EvenNumberState(Description("Initial state, Number 4, Number 2"), NumberValue(2))
}
}

test("Ephemeral View - load number flow 3 - with error") {
with(evenView) {
given(ephemeralViewRepository) {
whenQuery(3)
}.thenError()
}
}

test("Ephemeral View - load non-existing number flow") {
with(evenView) {
given(ephemeralViewRepository) {
whenQuery(4)
} thenState EvenNumberState(Description("Initial state"), NumberValue(0))
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fraktalio.fmodel.application.examples.numbers.even.query

import com.fraktalio.fmodel.application.EphemeralViewRepository
import com.fraktalio.fmodel.domain.examples.numbers.api.Description
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberEvent
import com.fraktalio.fmodel.domain.examples.numbers.api.NumberValue
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf

/**
* Simple flows of events to represent previously stored events in the event store
*/
private var numberFlow1 = flowOf(
NumberEvent.EvenNumberEvent.EvenNumberAdded(Description("Number 2"), NumberValue(2)),
NumberEvent.EvenNumberEvent.EvenNumberAdded(Description("Number 4"), NumberValue(4))
)

private var numberFlow2 = flowOf(
NumberEvent.EvenNumberEvent.EvenNumberAdded(Description("Number 4"), NumberValue(4)),
NumberEvent.EvenNumberEvent.EvenNumberSubtracted(Description("Number 2"), NumberValue(2))
)

/**
* Even number ephemeral view implementation
*/
class EvenNumberEphemeralViewRepository : EphemeralViewRepository<NumberEvent.EvenNumberEvent?, Int> {

override fun Int.fetchEvents(): Flow<NumberEvent.EvenNumberEvent?> {
return when (this) {
1 -> numberFlow1
2 -> numberFlow2
3 -> throw RuntimeException("Some fake error while fetching events.")
else -> emptyFlow()
}
}

}

/**
* Helper function to create an [EvenNumberEphemeralViewRepository]
*/
fun evenNumberEphemeralViewRepository(): EphemeralViewRepository<NumberEvent.EvenNumberEvent?, Int> =
EvenNumberEphemeralViewRepository()
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ import kotlinx.coroutines.flow.fold
*
* @author Domenic Cassisi
*/
suspend fun <S, E, Q, MV> MV.handle(query: Q): S where MV : ViewStateComputation<S, E>, MV : EphemeralViewRepository<E, Q> =
suspend fun <S, E, Q, EV> EV.handle(query: Q): S where EV : ViewStateComputation<S, E>, EV : EphemeralViewRepository<E, Q> =
query.fetchEvents().fold(initialState) { s, e -> evolve(s, e) }
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import io.kotest.matchers.shouldBe
/**
* DSL - Given
*/
private suspend fun <S, E, I> IView<S, E>.given(repository: EphemeralViewRepository<E, I>, id: () -> I): S =
private suspend fun <S, E, Q> IView<S, E>.given(repository: EphemeralViewRepository<E, Q>, query: () -> Q): S =
EphemeralView(
view = this,
ephemeralViewRepository = repository
).handle(id())
).handle(query())

/**
* DSL - When
*/
private fun <I> whenId(id: I): I = id
private fun <Q> whenQuery(query: Q): Q = query

/**
* DSL - Then
Expand All @@ -36,23 +36,23 @@ class EphemeralViewTest : FunSpec({
test("Ephemeral View - load number flow 1") {
with(evenView) {
given(ephemeralViewRepository) {
whenId(1)
whenQuery(1)
} thenState EvenNumberState(Description("Initial state, Number 2, Number 4"), NumberValue(6))
}
}

test("Ephemeral View - load number flow 2") {
with(evenView) {
given(ephemeralViewRepository) {
whenId(2)
whenQuery(2)
} thenState EvenNumberState(Description("Initial state, Number 4, Number 2"), NumberValue(2))
}
}

test("Ephemeral View - load non-existing number flow") {
with(evenView) {
given(ephemeralViewRepository) {
whenId(3)
whenQuery(3)
} thenState EvenNumberState(Description("Initial state"), NumberValue(0))
}
}
Expand Down

0 comments on commit 145a95c

Please sign in to comment.