import com.correx.core.events.events.EventMetadata import com.correx.core.events.events.NewEvent import com.correx.core.events.events.SteeringNoteAddedEvent 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.journal.DecisionJournalProjector import com.correx.core.journal.DecisionJournalRenderer import com.correx.core.journal.DefaultDecisionJournalReducer import com.correx.core.journal.DefaultDecisionJournalRepository 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.Assertions.assertTrue import org.junit.jupiter.api.Test class DecisionJournalReplayTest { private fun meta(sessionId: SessionId, id: String) = EventMetadata(EventId(id), sessionId, Clock.System.now(), 1, null, null) @Test fun `journal replay is deterministic`(): Unit = runBlocking { val sessionId = SessionId("s") val store = InMemoryEventStore() store.append(NewEvent(meta(sessionId, "steer"), SteeringNoteAddedEvent(sessionId, "use jwt"))) store.append( NewEvent( meta(sessionId, "trans"), TransitionExecutedEvent(sessionId, StageId("a"), StageId("b"), TransitionId("t")), ), ) val repo = DefaultDecisionJournalRepository( DefaultEventReplayer(store, DecisionJournalProjector(DefaultDecisionJournalReducer())), ) val renderer = DecisionJournalRenderer() val a = renderer.render(repo.getJournal(sessionId)) val b = renderer.render(repo.getJournal(sessionId)) assertEquals(a, b) assertTrue(a.contains("use jwt")) assertTrue(a.contains("a → b")) } }