220 lines
6.1 KiB
Kotlin
220 lines
6.1 KiB
Kotlin
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.transitions.evaluation.EvaluationContext
|
|
import com.correx.core.transitions.evaluation.TransitionConditionEvaluator
|
|
import com.correx.core.transitions.graph.StageConfig
|
|
import com.correx.core.transitions.graph.TransitionCondition
|
|
import com.correx.core.transitions.graph.TransitionEdge
|
|
import com.correx.core.transitions.graph.WorkflowGraph
|
|
import com.correx.core.transitions.resolution.DefaultTransitionResolver
|
|
import com.correx.core.transitions.resolution.TransitionDecision
|
|
import com.correx.core.transitions.resolution.TransitionResolver
|
|
import org.junit.jupiter.api.Assertions.assertEquals
|
|
import org.junit.jupiter.api.Test
|
|
import org.junit.jupiter.api.assertInstanceOf
|
|
|
|
class DefaultTransitionResolverTest {
|
|
|
|
private val evaluator = TransitionConditionEvaluator { condition, context -> condition.evaluate(context) }
|
|
|
|
private val resolver: TransitionResolver = DefaultTransitionResolver(evaluator)
|
|
|
|
@Test
|
|
fun `returns NoMatch when no outgoing transitions exist for current stage`() {
|
|
val graph = graph(
|
|
start = "stage-a",
|
|
transitions = setOf(
|
|
edge(
|
|
id = "t1",
|
|
from = "stage-a",
|
|
to = "stage-b",
|
|
condition = alwaysTrue(),
|
|
),
|
|
),
|
|
stages = setOf("stage-a", "stage-b"),
|
|
)
|
|
|
|
val result = resolver.resolve(
|
|
graph = graph,
|
|
context = context(currentStage = "stage-b"),
|
|
)
|
|
|
|
assertEquals(
|
|
TransitionDecision.NoMatch,
|
|
result,
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `returns Stay when all conditions evaluate to false`() {
|
|
val graph = graph(
|
|
start = "stage-a",
|
|
transitions = setOf(
|
|
edge(
|
|
id = "t1",
|
|
from = "stage-a",
|
|
to = "stage-b",
|
|
condition = alwaysFalse(),
|
|
),
|
|
),
|
|
)
|
|
|
|
val result = resolver.resolve(
|
|
graph = graph,
|
|
context = context(currentStage = "stage-a"),
|
|
)
|
|
|
|
assertEquals(
|
|
TransitionDecision.Stay,
|
|
result,
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `returns matching transition when condition evaluates to true`() {
|
|
val graph = graph(
|
|
start = "stage-a",
|
|
transitions = setOf(
|
|
edge(
|
|
id = "t1",
|
|
from = "stage-a",
|
|
to = "stage-b",
|
|
condition = alwaysTrue(),
|
|
),
|
|
),
|
|
)
|
|
|
|
val result = resolver.resolve(
|
|
graph = graph,
|
|
context = context(currentStage = "stage-a"),
|
|
)
|
|
|
|
val move = assertInstanceOf<TransitionDecision.Move>(result)
|
|
|
|
assertEquals(
|
|
TransitionId("t1"),
|
|
move.transitionId,
|
|
)
|
|
|
|
assertEquals(
|
|
StageId("stage-b"),
|
|
move.to,
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `first matching transition wins deterministically`() {
|
|
val graph = graph(
|
|
start = "stage-a",
|
|
transitions = setOf(
|
|
edge(
|
|
id = "t2",
|
|
from = "stage-a",
|
|
to = "stage-c",
|
|
condition = alwaysTrue(),
|
|
),
|
|
edge(
|
|
id = "t1",
|
|
from = "stage-a",
|
|
to = "stage-b",
|
|
condition = alwaysTrue(),
|
|
),
|
|
),
|
|
)
|
|
|
|
val result = resolver.resolve(
|
|
graph = graph,
|
|
context = context(currentStage = "stage-a"),
|
|
)
|
|
|
|
val move = assertInstanceOf<TransitionDecision.Move>(result)
|
|
|
|
assertEquals(
|
|
TransitionId("t1"),
|
|
move.transitionId,
|
|
)
|
|
|
|
assertEquals(
|
|
StageId("stage-b"),
|
|
move.to,
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `only transitions from current stage are evaluated`() {
|
|
val graph = graph(
|
|
start = "stage-a",
|
|
transitions = setOf(
|
|
edge(
|
|
id = "t1",
|
|
from = "stage-x",
|
|
to = "stage-y",
|
|
condition = alwaysTrue(),
|
|
),
|
|
edge(
|
|
id = "t2",
|
|
from = "stage-a",
|
|
to = "stage-b",
|
|
condition = alwaysTrue(),
|
|
),
|
|
),
|
|
)
|
|
|
|
val result = resolver.resolve(
|
|
graph = graph,
|
|
context = context(currentStage = "stage-a"),
|
|
)
|
|
|
|
val move = assertInstanceOf<TransitionDecision.Move>(result)
|
|
|
|
assertEquals(
|
|
TransitionId("t2"),
|
|
move.transitionId,
|
|
)
|
|
}
|
|
|
|
private fun edge(
|
|
id: String,
|
|
from: String,
|
|
to: String,
|
|
condition: TransitionCondition,
|
|
): TransitionEdge {
|
|
return TransitionEdge(
|
|
id = TransitionId(id),
|
|
from = StageId(from),
|
|
to = StageId(to),
|
|
condition = condition,
|
|
)
|
|
}
|
|
|
|
private fun graph(
|
|
start: String,
|
|
transitions: Set<TransitionEdge>,
|
|
stages: Set<String> = transitions.flatMap { listOf(it.from.value, it.to.value) }.toSet(),
|
|
) = WorkflowGraph(
|
|
id = "workflow-test",
|
|
start = StageId(start),
|
|
stages = stages.associate { StageId(it) to StageConfig() },
|
|
transitions = transitions,
|
|
)
|
|
|
|
private fun context(
|
|
currentSession: String = "s1",
|
|
currentStage: String,
|
|
): EvaluationContext {
|
|
return EvaluationContext(
|
|
sessionId = SessionId(currentSession),
|
|
currentStage = StageId(currentStage),
|
|
variables = emptyMap(),
|
|
)
|
|
}
|
|
|
|
private fun alwaysTrue() =
|
|
TransitionCondition { true }
|
|
|
|
@Suppress("UnusedPrivateMember")
|
|
private fun alwaysFalse() =
|
|
TransitionCondition { false }
|
|
}
|