import com.correx.core.events.events.EventMetadata import com.correx.core.events.events.NewEvent import com.correx.core.events.events.SessionPausedEvent import com.correx.core.events.events.SessionResumedEvent import com.correx.core.events.events.SessionStartedEvent import com.correx.core.events.stores.EventStore import com.correx.core.events.types.EventId import com.correx.core.events.types.SessionId import com.correx.core.sessions.DefaultSessionReducer import com.correx.core.sessions.SessionProjector import com.correx.core.sessions.projections.replay.DefaultEventReplayer import com.correx.infrastructure.persistence.InMemoryEventStore import kotlinx.coroutines.runBlocking import kotlinx.datetime.Clock import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test class SessionReplayDeterminismTest { private fun build(store: EventStore) = DefaultEventReplayer( store, SessionProjector(DefaultSessionReducer()), ) @Test fun `same events produce same state`(): Unit = runBlocking { val sessionId = SessionId("s1") val store1 = InMemoryEventStore() val store2 = InMemoryEventStore() val events = mapOf( EventMetadata(EventId("start"), sessionId, Clock.System.now(), 1, null, null) to SessionStartedEvent( sessionId, ), EventMetadata(EventId("paused"), sessionId, Clock.System.now(), 1, null, null) to SessionPausedEvent( sessionId, ), EventMetadata(EventId("resumed"), sessionId, Clock.System.now(), 1, null, null) to SessionResumedEvent( sessionId, ), ) events.forEach { (meta, payload) -> store1.append(NewEvent(meta, payload)) store2.append(NewEvent(meta, payload)) } val replayer1 = build(store1) val replayer2 = build(store2) val state1 = replayer1.rebuild(sessionId) val state2 = replayer2.rebuild(sessionId) assertEquals(state1, state2) } }