import com.correx.core.events.events.OrchestrationPausedEvent import com.correx.core.events.events.OrchestrationResumedEvent import com.correx.core.events.events.RetryAttemptedEvent import com.correx.core.events.events.WorkflowCompletedEvent import com.correx.core.events.events.WorkflowFailedEvent import com.correx.core.events.events.WorkflowStartedEvent import com.correx.core.events.orchestration.OrchestrationState import com.correx.core.events.orchestration.OrchestrationStatus import com.correx.core.events.types.SessionId import com.correx.core.events.types.StageId import com.correx.core.kernel.orchestration.DefaultOrchestrationReducer import com.correx.testing.fixtures.EventFixtures.stored import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test class OrchestrationReducerTest { private val reducer = DefaultOrchestrationReducer() private val sessionId = SessionId("s1") private val stageId = StageId("stage-1") private val workflowId = "workflow-test" private val state = OrchestrationState(workflowId, stageId) @Test fun `WorkflowStartedEvent sets status to RUNNING`() { val state = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) assertEquals(OrchestrationStatus.RUNNING, state.status) assertEquals(stageId, state.currentStageId) } @Test fun `WorkflowFailedEvent sets status to FAILED and failure reason`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val failed = reducer.reduce( started, stored(payload = WorkflowFailedEvent(sessionId, stageId, "Something went wrong", false)), ) assertFalse(failed.failureReason.isNullOrBlank()) assertEquals(OrchestrationStatus.FAILED, failed.status) } @Test fun `WorkflowCompletedEvent sets status to COMPLETED`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val completed = reducer.reduce( started, stored(payload = WorkflowCompletedEvent(sessionId, stageId, 1)), ) assertTrue(completed.failureReason.isNullOrBlank()) assertEquals(OrchestrationStatus.COMPLETED, completed.status) assertEquals(stageId, completed.currentStageId) } @Test fun `OrchestrationPausedEvent sets status to PAUSED with reason and pending approval`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val paused = reducer.reduce( started, stored(payload = OrchestrationPausedEvent(sessionId, stageId, "Requires user approval")), ) assertFalse(paused.pauseReason.isNullOrBlank()) assertEquals(OrchestrationStatus.PAUSED, paused.status) assertTrue(paused.pendingApproval) } @Test fun `OrchestrationResumedEvent sets status to RUNNING with null reason and no pending approval`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val paused = reducer.reduce( started, stored(payload = OrchestrationPausedEvent(sessionId, stageId, "Requires user approval")), ) val resumed = reducer.reduce( paused, stored(payload = OrchestrationResumedEvent(sessionId, stageId)), ) assertTrue(resumed.pauseReason.isNullOrBlank()) assertEquals(OrchestrationStatus.RUNNING, resumed.status) assertFalse(resumed.pendingApproval) } @Test fun `RetryAttemptedEvent sets status to RUNNING with null reason and no pending approval`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val failed = reducer.reduce( started, stored(payload = WorkflowFailedEvent(sessionId, stageId, "Something went wrong", false)), ) val retrying1 = reducer.reduce( failed, stored(payload = RetryAttemptedEvent(sessionId, stageId, 1, 3, "Something went wrong")), ) val retrying2 = reducer.reduce( retrying1, stored(payload = RetryAttemptedEvent(sessionId, stageId, 2, 3, "Something went wrong")), ) val retrying3 = reducer.reduce( retrying2, stored(payload = RetryAttemptedEvent(sessionId, stageId, 3, 3, "Something went wrong")), ) assertTrue(retrying1.retryCount < retrying2.retryCount && retrying2.retryCount < retrying3.retryCount) assertEquals(OrchestrationStatus.RUNNING, retrying1.status) assertNull(retrying1.failureReason) } @Test fun `unrelated event does nothing`() { val started = reducer.reduce( state, stored(payload = WorkflowStartedEvent(sessionId, workflowId, stageId)), ) val unrelated = reducer.reduce(started, stored()) assertEquals(started, unrelated) } }