import com.correx.core.events.events.SessionCompletedEvent import com.correx.core.events.events.SessionFailedEvent 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.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.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.SessionState import com.correx.core.sessions.SessionStatus import com.correx.testing.fixtures.EventFixtures.stored import kotlinx.datetime.Instant import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test class DefaultSessionReducerTest { private val reducer = DefaultSessionReducer() private val sessionId = SessionId("session-1") @Test fun `SessionStartedEvent sets ACTIVE status`() { val state = initialState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = SessionStartedEvent(sessionId) ) ) assertEquals(SessionStatus.ACTIVE, result.status) } @Test fun `SessionPausedEvent sets PAUSED status`() { val state = activeState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = SessionPausedEvent(sessionId) ) ) assertEquals(SessionStatus.PAUSED, result.status) } @Test fun `SessionResumedEvent sets ACTIVE status`() { val state = pausedState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = SessionResumedEvent(sessionId) ) ) assertEquals(SessionStatus.ACTIVE, result.status) } @Test fun `SessionCompletedEvent sets COMPLETED status`() { val state = activeState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = SessionCompletedEvent(sessionId) ) ) assertEquals(SessionStatus.COMPLETED, result.status) } @Test fun `SessionFailedEvent sets FAILED status`() { val state = activeState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = SessionFailedEvent(sessionId) ) ) assertEquals(SessionStatus.FAILED, result.status) } @Test fun `StageStartedEvent sets ACTIVE status`() { val state = pausedState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = StageStartedEvent( sessionId, stageId = StageId("stage-a"), transitionId = TransitionId("transition-a") ) ) ) assertEquals(SessionStatus.ACTIVE, result.status) } @Test fun `StageCompletedEvent sets ACTIVE status`() { val state = pausedState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = StageCompletedEvent( sessionId, stageId = StageId("stage-a"), transitionId = TransitionId("transition-a") ) ) ) assertEquals(SessionStatus.ACTIVE, result.status) } @Test fun `StageFailedEvent sets FAILED status`() { val state = activeState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = StageFailedEvent( sessionId, stageId = StageId("stage-a"), transitionId = TransitionId("transition-a"), reason = "boom" ) ) ) assertEquals(SessionStatus.FAILED, result.status) } @Test fun `TransitionExecutedEvent sets ACTIVE status`() { val state = pausedState() val result = reducer.reduce( state = state, event = stored( sessionId = sessionId, payload = TransitionExecutedEvent( sessionId, from = StageId("stage-a"), to = StageId("stage-b"), transitionId = TransitionId("transition-a"), ) ) ) assertEquals(SessionStatus.ACTIVE, result.status) } @Test fun `createdAt is initialized once`() { val timestamp = Instant.parse("2026-01-01T00:00:00Z") val result = reducer.reduce( state = initialState(), event = stored( payload = SessionStartedEvent(sessionId), timestamp = timestamp ) ) assertEquals(timestamp, result.createdAt) } @Test fun `createdAt is preserved after initialization`() { val createdAt = Instant.parse("2026-01-01T00:00:00Z") val updatedAt = Instant.parse("2026-01-02T00:00:00Z") val state = activeState().copy( createdAt = createdAt ) val result = reducer.reduce( state = state, event = stored( payload = SessionPausedEvent(sessionId), timestamp = updatedAt ) ) assertEquals(createdAt, result.createdAt) } @Test fun `updatedAt always reflects latest event timestamp`() { val timestamp = Instant.parse("2026-01-02T00:00:00Z") val result = reducer.reduce( state = activeState(), event = stored( payload = SessionPausedEvent(sessionId), timestamp = timestamp ) ) assertEquals(timestamp, result.updatedAt) } private fun initialState() = SessionState( status = SessionStatus.CREATED ) private fun activeState() = SessionState( status = SessionStatus.ACTIVE ) private fun pausedState() = SessionState( status = SessionStatus.PAUSED ) }