test: dedupe DefaultSessionReducerTest into one file
Two copies had drifted — the core:sessions one and the testing:projections one — and one had rotted into asserting the ACTIVE-forever bug as correct. Port the unique boundProfile + freestyle-planning cases into the comprehensive projections copy and delete the core:sessions duplicate.
This commit is contained in:
@@ -1,121 +0,0 @@
|
|||||||
package com.correx.core.sessions
|
|
||||||
|
|
||||||
import com.correx.core.events.events.EventMetadata
|
|
||||||
import com.correx.core.events.events.OperatorProfileBoundEvent
|
|
||||||
import com.correx.core.events.events.SessionWorkspaceBoundEvent
|
|
||||||
import com.correx.core.events.events.StoredEvent
|
|
||||||
import com.correx.core.events.types.EventId
|
|
||||||
import com.correx.core.events.types.SessionId
|
|
||||||
import kotlinx.datetime.Instant
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Assertions.assertNull
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
class DefaultSessionReducerTest {
|
|
||||||
|
|
||||||
private val reducer = DefaultSessionReducer()
|
|
||||||
private val sessionId = SessionId("s-test")
|
|
||||||
private val initialState = SessionState(status = SessionStatus.ACTIVE)
|
|
||||||
|
|
||||||
private fun stored(payload: com.correx.core.events.events.EventPayload) = StoredEvent(
|
|
||||||
metadata = EventMetadata(
|
|
||||||
eventId = EventId("e-1"),
|
|
||||||
sessionId = sessionId,
|
|
||||||
timestamp = Instant.parse("2026-01-01T00:00:00Z"),
|
|
||||||
schemaVersion = 1,
|
|
||||||
causationId = null,
|
|
||||||
correlationId = null,
|
|
||||||
),
|
|
||||||
sequence = 1L,
|
|
||||||
sessionSequence = 1L,
|
|
||||||
payload = payload,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `OperatorProfileBoundEvent populates boundProfile`() {
|
|
||||||
val event = stored(
|
|
||||||
OperatorProfileBoundEvent(
|
|
||||||
sessionId = sessionId,
|
|
||||||
about = "Expert Kotlin engineer",
|
|
||||||
approvalMode = "auto",
|
|
||||||
preferredModels = listOf("model-x"),
|
|
||||||
conventions = listOf("no var"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = reducer.reduce(initialState, event)
|
|
||||||
|
|
||||||
val profile = result.boundProfile
|
|
||||||
assertEquals("Expert Kotlin engineer", profile?.about)
|
|
||||||
assertEquals("auto", profile?.approvalMode)
|
|
||||||
assertEquals(listOf("model-x"), profile?.preferredModels)
|
|
||||||
assertEquals(listOf("no var"), profile?.conventions)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `boundProfile is null by default`() {
|
|
||||||
assertNull(initialState.boundProfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WorkflowFailedEvent flips session status to FAILED`() {
|
|
||||||
val event = stored(
|
|
||||||
com.correx.core.events.events.WorkflowFailedEvent(
|
|
||||||
sessionId = sessionId,
|
|
||||||
stageId = com.correx.core.events.types.StageId("define_styling_tokens"),
|
|
||||||
reason = "did not produce declared artifacts",
|
|
||||||
retryExhausted = true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
assertEquals(SessionStatus.FAILED, reducer.reduce(initialState, event).status)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WorkflowCompletedEvent flips session status to COMPLETED`() {
|
|
||||||
val event = stored(
|
|
||||||
com.correx.core.events.events.WorkflowCompletedEvent(
|
|
||||||
sessionId = sessionId,
|
|
||||||
terminalStageId = com.correx.core.events.types.StageId("report"),
|
|
||||||
totalStages = 3,
|
|
||||||
workflowId = "review_loop",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
assertEquals(SessionStatus.COMPLETED, reducer.reduce(initialState, event).status)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `WorkflowCompletedEvent for freestyle planning stays ACTIVE (hands off to execution)`() {
|
|
||||||
val event = stored(
|
|
||||||
com.correx.core.events.events.WorkflowCompletedEvent(
|
|
||||||
sessionId = sessionId,
|
|
||||||
terminalStageId = com.correx.core.events.types.StageId("architect"),
|
|
||||||
totalStages = 2,
|
|
||||||
workflowId = "freestyle_planning",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
assertEquals(SessionStatus.ACTIVE, reducer.reduce(initialState, event).status)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `unrelated event leaves boundProfile unchanged`() {
|
|
||||||
val state = initialState.copy(
|
|
||||||
boundProfile = BoundProfile(
|
|
||||||
about = "original",
|
|
||||||
approvalMode = "",
|
|
||||||
preferredModels = emptyList(),
|
|
||||||
conventions = emptyList(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val event = stored(
|
|
||||||
SessionWorkspaceBoundEvent(
|
|
||||||
sessionId = sessionId,
|
|
||||||
workspaceRoot = "/ws",
|
|
||||||
allowedPaths = listOf("/ws"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = reducer.reduce(state, event)
|
|
||||||
|
|
||||||
assertEquals("original", result.boundProfile?.about)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,10 @@ import com.correx.core.events.types.SessionId
|
|||||||
import com.correx.core.events.types.StageId
|
import com.correx.core.events.types.StageId
|
||||||
import com.correx.core.events.types.TransitionId
|
import com.correx.core.events.types.TransitionId
|
||||||
import com.correx.core.events.events.AgentInstructionsBoundEvent
|
import com.correx.core.events.events.AgentInstructionsBoundEvent
|
||||||
|
import com.correx.core.events.events.OperatorProfileBoundEvent
|
||||||
import com.correx.core.events.events.ProjectProfileBoundEvent
|
import com.correx.core.events.events.ProjectProfileBoundEvent
|
||||||
import com.correx.core.sessions.BoundAgentInstructions
|
import com.correx.core.sessions.BoundAgentInstructions
|
||||||
|
import com.correx.core.sessions.BoundProfile
|
||||||
import com.correx.core.sessions.BoundProjectProfile
|
import com.correx.core.sessions.BoundProjectProfile
|
||||||
import com.correx.core.sessions.BoundWorkspace
|
import com.correx.core.sessions.BoundWorkspace
|
||||||
import com.correx.core.sessions.DefaultSessionReducer
|
import com.correx.core.sessions.DefaultSessionReducer
|
||||||
@@ -378,6 +380,72 @@ class DefaultSessionReducerTest {
|
|||||||
status = SessionStatus.CREATED
|
status = SessionStatus.CREATED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `OperatorProfileBoundEvent populates boundProfile`() {
|
||||||
|
val result = reducer.reduce(
|
||||||
|
state = activeState(),
|
||||||
|
event = stored(
|
||||||
|
sessionId = sessionId,
|
||||||
|
payload = OperatorProfileBoundEvent(
|
||||||
|
sessionId = sessionId,
|
||||||
|
about = "Expert Kotlin engineer",
|
||||||
|
approvalMode = "auto",
|
||||||
|
preferredModels = listOf("model-x"),
|
||||||
|
conventions = listOf("no var"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val profile = result.boundProfile
|
||||||
|
assertEquals("Expert Kotlin engineer", profile?.about)
|
||||||
|
assertEquals("auto", profile?.approvalMode)
|
||||||
|
assertEquals(listOf("model-x"), profile?.preferredModels)
|
||||||
|
assertEquals(listOf("no var"), profile?.conventions)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unrelated event leaves boundProfile unchanged`() {
|
||||||
|
val state = activeState().copy(
|
||||||
|
boundProfile = BoundProfile(
|
||||||
|
about = "original",
|
||||||
|
approvalMode = "",
|
||||||
|
preferredModels = emptyList(),
|
||||||
|
conventions = emptyList(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val result = reducer.reduce(
|
||||||
|
state = state,
|
||||||
|
event = stored(
|
||||||
|
sessionId = sessionId,
|
||||||
|
payload = SessionWorkspaceBoundEvent(
|
||||||
|
sessionId = sessionId,
|
||||||
|
workspaceRoot = "/ws",
|
||||||
|
allowedPaths = listOf("/ws"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals("original", result.boundProfile?.about)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WorkflowCompletedEvent for freestyle planning stays ACTIVE (hands off to execution)`() {
|
||||||
|
val result = reducer.reduce(
|
||||||
|
state = activeState(),
|
||||||
|
event = stored(
|
||||||
|
sessionId = sessionId,
|
||||||
|
payload = WorkflowCompletedEvent(
|
||||||
|
sessionId,
|
||||||
|
terminalStageId = StageId("architect"),
|
||||||
|
totalStages = 2,
|
||||||
|
workflowId = "freestyle_planning",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(SessionStatus.ACTIVE, result.status)
|
||||||
|
}
|
||||||
|
|
||||||
private fun activeState() =
|
private fun activeState() =
|
||||||
SessionState(
|
SessionState(
|
||||||
status = SessionStatus.ACTIVE
|
status = SessionStatus.ACTIVE
|
||||||
|
|||||||
Reference in New Issue
Block a user