f827685ed0
fixed detekt issues where possible. fixed disttar failing build because tools is added twice in the server module. added workflowId where required. fixed some tests not being recognized because of runBlocking without explicit return type. formatting + imports.
211 lines
7.0 KiB
Kotlin
211 lines
7.0 KiB
Kotlin
import com.correx.core.events.events.SessionCompletedEvent
|
|
import com.correx.core.events.events.SessionStartedEvent
|
|
import com.correx.core.events.events.StageCompletedEvent
|
|
import com.correx.core.events.events.StageFailedEvent
|
|
import com.correx.core.events.events.StageStartedEvent
|
|
import com.correx.core.events.events.TransitionExecutedEvent
|
|
import com.correx.core.events.types.EventId
|
|
import com.correx.core.events.types.SessionId
|
|
import com.correx.core.events.types.StageId
|
|
import com.correx.core.events.types.TransitionId
|
|
import com.correx.core.sessions.DefaultSessionReducer
|
|
import com.correx.core.sessions.SessionProjector
|
|
import com.correx.core.sessions.SessionStatus
|
|
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
|
|
import com.correx.infrastructure.persistence.InMemoryEventStore
|
|
import com.correx.testing.fixtures.EventFixtures.newEvent
|
|
import kotlinx.coroutines.runBlocking
|
|
import org.junit.jupiter.api.Assertions.assertEquals
|
|
import org.junit.jupiter.api.Test
|
|
|
|
class TransitionReplayIntegrationTest {
|
|
|
|
private val sessionId = SessionId("session-1")
|
|
|
|
@Test
|
|
fun `workflow replay reconstructs COMPLETED session`(): Unit = runBlocking {
|
|
|
|
val store = InMemoryEventStore()
|
|
|
|
store.appendAll(
|
|
listOf(
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-1"),
|
|
payload = SessionStartedEvent(sessionId)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-2"),
|
|
payload = TransitionExecutedEvent(
|
|
sessionId = sessionId,
|
|
from = StageId("draft"),
|
|
to = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-3"),
|
|
payload = StageStartedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-4"),
|
|
payload = StageCompletedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-5"),
|
|
payload = SessionCompletedEvent(sessionId)
|
|
)
|
|
)
|
|
)
|
|
|
|
val replayer = DefaultEventReplayer(
|
|
store = store,
|
|
projection = SessionProjector(
|
|
reducer = DefaultSessionReducer()
|
|
)
|
|
)
|
|
|
|
val state = replayer.rebuild(sessionId)
|
|
|
|
assertEquals(
|
|
SessionStatus.COMPLETED,
|
|
state.status
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `workflow replay reconstructs FAILED session`(): Unit = runBlocking {
|
|
|
|
val store = InMemoryEventStore()
|
|
|
|
store.appendAll(
|
|
listOf(
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-1"),
|
|
payload = SessionStartedEvent(sessionId)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-2"),
|
|
payload = TransitionExecutedEvent(
|
|
sessionId = sessionId,
|
|
from = StageId("draft"),
|
|
to = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-3"),
|
|
payload = StageStartedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-4"),
|
|
payload = StageFailedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1"),
|
|
reason = "validation failed"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
val replayer = DefaultEventReplayer(
|
|
store = store,
|
|
projection = SessionProjector(
|
|
reducer = DefaultSessionReducer()
|
|
)
|
|
)
|
|
|
|
val state = replayer.rebuild(sessionId)
|
|
|
|
assertEquals(
|
|
SessionStatus.FAILED,
|
|
state.status
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `replay is deterministic for identical event stream`(): Unit = runBlocking {
|
|
val events = listOf(
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-1"),
|
|
payload = SessionStartedEvent(sessionId)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-2"),
|
|
payload = TransitionExecutedEvent(
|
|
sessionId = sessionId,
|
|
from = StageId("draft"),
|
|
to = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-3"),
|
|
payload = StageStartedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
),
|
|
newEvent(
|
|
sessionId = sessionId,
|
|
eventId = EventId("event-4"),
|
|
payload = StageCompletedEvent(
|
|
sessionId = sessionId,
|
|
stageId = StageId("review"),
|
|
transitionId = TransitionId("transition-1")
|
|
)
|
|
)
|
|
)
|
|
|
|
val store1 = InMemoryEventStore()
|
|
val store2 = InMemoryEventStore()
|
|
|
|
store1.appendAll(events)
|
|
store2.appendAll(events)
|
|
|
|
val projection = SessionProjector(
|
|
reducer = DefaultSessionReducer()
|
|
)
|
|
|
|
val replayer1 = DefaultEventReplayer(
|
|
store = store1,
|
|
projection = projection
|
|
)
|
|
|
|
val replayer2 = DefaultEventReplayer(
|
|
store = store2,
|
|
projection = projection
|
|
)
|
|
|
|
val state1 = replayer1.rebuild(sessionId)
|
|
val state2 = replayer2.rebuild(sessionId)
|
|
|
|
assertEquals(state1, state2)
|
|
}
|
|
}
|