diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/input/KeyResolverTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/input/KeyResolverTest.kt index 8cb51a7a..8447ef49 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/input/KeyResolverTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/input/KeyResolverTest.kt @@ -8,110 +8,110 @@ import kotlin.test.assertNull class KeyResolverTest { @Test - fun `n in None mode resolves to OpenNewSessionPrompt`() { - val result = KeyResolver.resolve(KeyEvent.NewSession, InputMode.None, "") + fun `n in ROUTER mode resolves to OpenNewSessionPrompt`() { + val result = KeyResolver.resolve(KeyEvent.NewSession, InputMode.ROUTER, "") assertEquals(Action.OpenNewSessionPrompt, result) } @Test - fun `command letters in input mode resolve to null`() { - val result = KeyResolver.resolve(KeyEvent.NewSession, InputMode.WorkflowId, "") + fun `command letters in text input mode resolve to null`() { + val result = KeyResolver.resolve(KeyEvent.NewSession, InputMode.FILTER, "") assertNull(result) } @Test - fun `CharInput in input mode resolves to AppendChar`() { - val result = KeyResolver.resolve(KeyEvent.CharInput('a'), InputMode.WorkflowId, "") + fun `CharInput in text input mode resolves to AppendChar`() { + val result = KeyResolver.resolve(KeyEvent.CharInput('a'), InputMode.FILTER, "") assertEquals(Action.AppendChar('a'), result) } @Test fun `Enter on blank input resolves to null`() { - val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.WorkflowId, "") + val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.FILTER, "") assertNull(result) } @Test fun `Enter on non-blank input resolves to SubmitInput`() { - val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.WorkflowId, "test") + val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.FILTER, "test") assertEquals(Action.SubmitInput, result) } @Test - fun `Escape in input mode resolves to CancelInput`() { - val result = KeyResolver.resolve(KeyEvent.Escape, InputMode.WorkflowId, "") + fun `Escape in text input mode resolves to CancelInput`() { + val result = KeyResolver.resolve(KeyEvent.Escape, InputMode.FILTER, "") assertEquals(Action.CancelInput, result) } @Test - fun `Backspace in input mode resolves to Backspace action`() { - val result = KeyResolver.resolve(KeyEvent.Backspace, InputMode.WorkflowId, "") + fun `Backspace in text input mode resolves to Backspace action`() { + val result = KeyResolver.resolve(KeyEvent.Backspace, InputMode.FILTER, "") assertEquals(Action.Backspace, result) } @Test - fun `Quit in None mode resolves to Quit`() { - val result = KeyResolver.resolve(KeyEvent.Quit, InputMode.None, "") + fun `Quit in ROUTER mode resolves to Quit`() { + val result = KeyResolver.resolve(KeyEvent.Quit, InputMode.ROUTER, "") assertEquals(Action.Quit, result) } @Test - fun `Quit in input mode resolves to null`() { - val result = KeyResolver.resolve(KeyEvent.Quit, InputMode.WorkflowId, "") + fun `Quit in text input mode resolves to null`() { + val result = KeyResolver.resolve(KeyEvent.Quit, InputMode.FILTER, "") assertNull(result) } @Test - fun `Cancel in None mode resolves to CancelSelectedSession`() { - val result = KeyResolver.resolve(KeyEvent.Cancel, InputMode.None, "") + fun `Cancel in ROUTER mode resolves to CancelSelectedSession`() { + val result = KeyResolver.resolve(KeyEvent.Cancel, InputMode.ROUTER, "") assertEquals(Action.CancelSelectedSession, result) } @Test - fun `Approve in None mode resolves to ApproveActive`() { - val result = KeyResolver.resolve(KeyEvent.Approve, InputMode.None, "") + fun `Approve in ROUTER mode resolves to ApproveActive`() { + val result = KeyResolver.resolve(KeyEvent.Approve, InputMode.ROUTER, "") assertEquals(Action.ApproveActive, result) } @Test - fun `Reject in None mode resolves to RejectActive`() { - val result = KeyResolver.resolve(KeyEvent.Reject, InputMode.None, "") + fun `Reject in ROUTER mode resolves to RejectActive`() { + val result = KeyResolver.resolve(KeyEvent.Reject, InputMode.ROUTER, "") assertEquals(Action.RejectActive, result) } @Test - fun `Steer in None mode resolves to OpenSteeringPrompt`() { - val result = KeyResolver.resolve(KeyEvent.Steer, InputMode.None, "") + fun `Steer in ROUTER mode resolves to OpenSteeringPrompt`() { + val result = KeyResolver.resolve(KeyEvent.Steer, InputMode.ROUTER, "") assertEquals(Action.OpenSteeringPrompt, result) } @Test - fun `NavUp in None mode resolves to NavigateUp`() { - val result = KeyResolver.resolve(KeyEvent.NavUp, InputMode.None, "") + fun `NavUp in ROUTER mode resolves to NavigateUp`() { + val result = KeyResolver.resolve(KeyEvent.NavUp, InputMode.ROUTER, "") assertEquals(Action.NavigateUp, result) } @Test - fun `NavDown in None mode resolves to NavigateDown`() { - val result = KeyResolver.resolve(KeyEvent.NavDown, InputMode.None, "") + fun `NavDown in ROUTER mode resolves to NavigateDown`() { + val result = KeyResolver.resolve(KeyEvent.NavDown, InputMode.ROUTER, "") assertEquals(Action.NavigateDown, result) } @Test - fun `Filter in None mode resolves to OpenFilter`() { - val result = KeyResolver.resolve(KeyEvent.Filter, InputMode.None, "") + fun `Filter in ROUTER mode resolves to OpenFilter`() { + val result = KeyResolver.resolve(KeyEvent.Filter, InputMode.ROUTER, "") assertEquals(Action.OpenFilter, result) } @Test fun `Enter on whitespace-only input resolves to null`() { - val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.WorkflowId, " ") + val result = KeyResolver.resolve(KeyEvent.Enter, InputMode.FILTER, " ") assertNull(result) } @Test - fun `CharInput in None mode resolves to null`() { - val result = KeyResolver.resolve(KeyEvent.CharInput('x'), InputMode.None, "") + fun `CharInput in ROUTER mode resolves to null`() { + val result = KeyResolver.resolve(KeyEvent.CharInput('x'), InputMode.ROUTER, "") assertNull(result) } } diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/input/TambouiKeyMapperTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/input/TambouiKeyMapperTest.kt index a6257a31..4a7defc4 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/input/TambouiKeyMapperTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/input/TambouiKeyMapperTest.kt @@ -14,27 +14,27 @@ class TambouiKeyMapperTest { // --- Ctrl-C always → Quit --- @Test - fun `ctrl-c maps to Quit in InputMode None`() { + fun `ctrl-c maps to Quit in InputMode ROUTER`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.CTRL, 'c'.code) - assertEquals(KeyEvent.Quit, mapKey(event, InputMode.None)) + assertEquals(KeyEvent.Quit, mapKey(event, InputMode.ROUTER)) } @Test - fun `ctrl-c maps to Quit in InputMode Filter`() { + fun `ctrl-c maps to Quit in InputMode FILTER`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.CTRL, 'c'.code) - assertEquals(KeyEvent.Quit, mapKey(event, InputMode.Filter)) + assertEquals(KeyEvent.Quit, mapKey(event, InputMode.FILTER)) } @Test - fun `ctrl-c maps to Quit in InputMode WorkflowId`() { + fun `ctrl-c maps to Quit in InputMode NAVIGATE`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.CTRL, 'c'.code) - assertEquals(KeyEvent.Quit, mapKey(event, InputMode.WorkflowId)) + assertEquals(KeyEvent.Quit, mapKey(event, InputMode.NAVIGATE)) } @Test - fun `ctrl-c maps to Quit in InputMode SteeringNote`() { + fun `ctrl-c maps to Quit in InputMode STEER`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.CTRL, 'c'.code) - assertEquals(KeyEvent.Quit, mapKey(event, InputMode.SteeringNote)) + assertEquals(KeyEvent.Quit, mapKey(event, InputMode.STEER)) } // --- Other Ctrl/Alt → null --- @@ -42,166 +42,166 @@ class TambouiKeyMapperTest { @Test fun `ctrl modifier non-ctrlC maps to null`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.CTRL, 'a'.code) - assertNull(mapKey(event, InputMode.None)) + assertNull(mapKey(event, InputMode.ROUTER)) } @Test fun `alt modifier maps to null`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.ALT, 'q'.code) - assertNull(mapKey(event, InputMode.None)) + assertNull(mapKey(event, InputMode.ROUTER)) } - // --- KeyCode mappings in InputMode.None --- + // --- KeyCode mappings in InputMode.ROUTER --- @Test - fun `UP maps to NavUp in InputMode None`() { - assertEquals(KeyEvent.NavUp, mapKey(TambouiKeyEvent.ofKey(KeyCode.UP), InputMode.None)) + fun `UP maps to NavUp in InputMode ROUTER`() { + assertEquals(KeyEvent.NavUp, mapKey(TambouiKeyEvent.ofKey(KeyCode.UP), InputMode.ROUTER)) } @Test - fun `DOWN maps to NavDown in InputMode None`() { - assertEquals(KeyEvent.NavDown, mapKey(TambouiKeyEvent.ofKey(KeyCode.DOWN), InputMode.None)) + fun `DOWN maps to NavDown in InputMode ROUTER`() { + assertEquals(KeyEvent.NavDown, mapKey(TambouiKeyEvent.ofKey(KeyCode.DOWN), InputMode.ROUTER)) } @Test - fun `ENTER maps to Enter in InputMode None`() { - assertEquals(KeyEvent.Enter, mapKey(TambouiKeyEvent.ofKey(KeyCode.ENTER), InputMode.None)) + fun `ENTER maps to Enter in InputMode ROUTER`() { + assertEquals(KeyEvent.Enter, mapKey(TambouiKeyEvent.ofKey(KeyCode.ENTER), InputMode.ROUTER)) } @Test - fun `ESCAPE maps to Escape in InputMode None`() { - assertEquals(KeyEvent.Escape, mapKey(TambouiKeyEvent.ofKey(KeyCode.ESCAPE), InputMode.None)) + fun `ESCAPE maps to Escape in InputMode ROUTER`() { + assertEquals(KeyEvent.Escape, mapKey(TambouiKeyEvent.ofKey(KeyCode.ESCAPE), InputMode.ROUTER)) } @Test - fun `BACKSPACE maps to Backspace in InputMode None`() { - assertEquals(KeyEvent.Backspace, mapKey(TambouiKeyEvent.ofKey(KeyCode.BACKSPACE), InputMode.None)) + fun `BACKSPACE maps to Backspace in InputMode ROUTER`() { + assertEquals(KeyEvent.Backspace, mapKey(TambouiKeyEvent.ofKey(KeyCode.BACKSPACE), InputMode.ROUTER)) } @Test - fun `TAB maps to Filter in InputMode None`() { - assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofKey(KeyCode.TAB), InputMode.None)) + fun `TAB maps to Filter in InputMode ROUTER`() { + assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofKey(KeyCode.TAB), InputMode.ROUTER)) } - // --- KeyCode mappings in InputMode.Filter --- + // --- KeyCode mappings in InputMode.FILTER --- @Test - fun `UP maps to NavUp in InputMode Filter`() { - assertEquals(KeyEvent.NavUp, mapKey(TambouiKeyEvent.ofKey(KeyCode.UP), InputMode.Filter)) + fun `UP maps to NavUp in InputMode FILTER`() { + assertEquals(KeyEvent.NavUp, mapKey(TambouiKeyEvent.ofKey(KeyCode.UP), InputMode.FILTER)) } @Test - fun `DOWN maps to NavDown in InputMode Filter`() { - assertEquals(KeyEvent.NavDown, mapKey(TambouiKeyEvent.ofKey(KeyCode.DOWN), InputMode.Filter)) + fun `DOWN maps to NavDown in InputMode FILTER`() { + assertEquals(KeyEvent.NavDown, mapKey(TambouiKeyEvent.ofKey(KeyCode.DOWN), InputMode.FILTER)) } @Test - fun `ENTER maps to Enter in InputMode Filter`() { - assertEquals(KeyEvent.Enter, mapKey(TambouiKeyEvent.ofKey(KeyCode.ENTER), InputMode.Filter)) + fun `ENTER maps to Enter in InputMode FILTER`() { + assertEquals(KeyEvent.Enter, mapKey(TambouiKeyEvent.ofKey(KeyCode.ENTER), InputMode.FILTER)) } @Test - fun `ESCAPE maps to Escape in InputMode Filter`() { - assertEquals(KeyEvent.Escape, mapKey(TambouiKeyEvent.ofKey(KeyCode.ESCAPE), InputMode.Filter)) + fun `ESCAPE maps to Escape in InputMode FILTER`() { + assertEquals(KeyEvent.Escape, mapKey(TambouiKeyEvent.ofKey(KeyCode.ESCAPE), InputMode.FILTER)) } @Test - fun `BACKSPACE maps to Backspace in InputMode Filter`() { - assertEquals(KeyEvent.Backspace, mapKey(TambouiKeyEvent.ofKey(KeyCode.BACKSPACE), InputMode.Filter)) + fun `BACKSPACE maps to Backspace in InputMode FILTER`() { + assertEquals(KeyEvent.Backspace, mapKey(TambouiKeyEvent.ofKey(KeyCode.BACKSPACE), InputMode.FILTER)) } @Test - fun `TAB maps to Filter in InputMode Filter`() { - assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofKey(KeyCode.TAB), InputMode.Filter)) + fun `TAB maps to Filter in InputMode FILTER`() { + assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofKey(KeyCode.TAB), InputMode.FILTER)) } - // --- Semantic intents in InputMode.None --- + // --- Semantic intents in InputMode.ROUTER --- @Test - fun `q maps to Quit in InputMode None`() { - assertEquals(KeyEvent.Quit, mapKey(TambouiKeyEvent.ofChar('q'), InputMode.None)) + fun `q maps to Quit in InputMode ROUTER`() { + assertEquals(KeyEvent.Quit, mapKey(TambouiKeyEvent.ofChar('q'), InputMode.ROUTER)) } @Test - fun `n maps to NewSession in InputMode None`() { - assertEquals(KeyEvent.NewSession, mapKey(TambouiKeyEvent.ofChar('n'), InputMode.None)) + fun `n maps to NewSession in InputMode ROUTER`() { + assertEquals(KeyEvent.NewSession, mapKey(TambouiKeyEvent.ofChar('n'), InputMode.ROUTER)) } @Test - fun `c maps to Cancel in InputMode None`() { - assertEquals(KeyEvent.Cancel, mapKey(TambouiKeyEvent.ofChar('c'), InputMode.None)) + fun `c maps to Cancel in InputMode ROUTER`() { + assertEquals(KeyEvent.Cancel, mapKey(TambouiKeyEvent.ofChar('c'), InputMode.ROUTER)) } @Test - fun `a maps to Approve in InputMode None`() { - assertEquals(KeyEvent.Approve, mapKey(TambouiKeyEvent.ofChar('a'), InputMode.None)) + fun `a maps to Approve in InputMode ROUTER`() { + assertEquals(KeyEvent.Approve, mapKey(TambouiKeyEvent.ofChar('a'), InputMode.ROUTER)) } @Test - fun `r maps to Reject in InputMode None`() { - assertEquals(KeyEvent.Reject, mapKey(TambouiKeyEvent.ofChar('r'), InputMode.None)) + fun `r maps to Reject in InputMode ROUTER`() { + assertEquals(KeyEvent.Reject, mapKey(TambouiKeyEvent.ofChar('r'), InputMode.ROUTER)) } @Test - fun `s maps to Steer in InputMode None`() { - assertEquals(KeyEvent.Steer, mapKey(TambouiKeyEvent.ofChar('s'), InputMode.None)) + fun `s maps to Steer in InputMode ROUTER`() { + assertEquals(KeyEvent.Steer, mapKey(TambouiKeyEvent.ofChar('s'), InputMode.ROUTER)) } @Test - fun `slash maps to Filter in InputMode None`() { - assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofChar('/'), InputMode.None)) + fun `slash maps to Filter in InputMode ROUTER`() { + assertEquals(KeyEvent.Filter, mapKey(TambouiKeyEvent.ofChar('/'), InputMode.ROUTER)) } @Test - fun `other char maps to CharInput in InputMode None`() { - assertEquals(KeyEvent.CharInput('x'), mapKey(TambouiKeyEvent.ofChar('x'), InputMode.None)) + fun `other char maps to CharInput in InputMode ROUTER`() { + assertEquals(KeyEvent.CharInput('x'), mapKey(TambouiKeyEvent.ofChar('x'), InputMode.ROUTER)) } - // --- Regression: semantic chars map to CharInput in non-None modes --- + // --- Regression: semantic chars map to CharInput in non-ROUTER modes --- @Test - fun `q maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.WorkflowId)) + fun `q maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.NAVIGATE)) } @Test - fun `n maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('n'), mapKey(TambouiKeyEvent.ofChar('n'), InputMode.WorkflowId)) + fun `n maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('n'), mapKey(TambouiKeyEvent.ofChar('n'), InputMode.NAVIGATE)) } @Test - fun `c maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('c'), mapKey(TambouiKeyEvent.ofChar('c'), InputMode.WorkflowId)) + fun `c maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('c'), mapKey(TambouiKeyEvent.ofChar('c'), InputMode.NAVIGATE)) } @Test - fun `a maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('a'), mapKey(TambouiKeyEvent.ofChar('a'), InputMode.WorkflowId)) + fun `a maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('a'), mapKey(TambouiKeyEvent.ofChar('a'), InputMode.NAVIGATE)) } @Test - fun `r maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('r'), mapKey(TambouiKeyEvent.ofChar('r'), InputMode.WorkflowId)) + fun `r maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('r'), mapKey(TambouiKeyEvent.ofChar('r'), InputMode.NAVIGATE)) } @Test - fun `s maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('s'), mapKey(TambouiKeyEvent.ofChar('s'), InputMode.WorkflowId)) + fun `s maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('s'), mapKey(TambouiKeyEvent.ofChar('s'), InputMode.NAVIGATE)) } @Test - fun `slash maps to CharInput in InputMode WorkflowId`() { - assertEquals(KeyEvent.CharInput('/'), mapKey(TambouiKeyEvent.ofChar('/'), InputMode.WorkflowId)) + fun `slash maps to CharInput in InputMode NAVIGATE`() { + assertEquals(KeyEvent.CharInput('/'), mapKey(TambouiKeyEvent.ofChar('/'), InputMode.NAVIGATE)) } @Test - fun `q maps to CharInput in InputMode SteeringNote`() { - assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.SteeringNote)) + fun `q maps to CharInput in InputMode STEER`() { + assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.STEER)) } @Test - fun `q maps to CharInput in InputMode Filter`() { - assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.Filter)) + fun `q maps to CharInput in InputMode FILTER`() { + assertEquals(KeyEvent.CharInput('q'), mapKey(TambouiKeyEvent.ofChar('q'), InputMode.FILTER)) } // --- ISO control chars in CHAR event → null --- @@ -209,12 +209,12 @@ class TambouiKeyMapperTest { @Test fun `ESC codepoint as CHAR event maps to null`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.NONE, 0x1B) - assertNull(mapKey(event, InputMode.None)) + assertNull(mapKey(event, InputMode.ROUTER)) } @Test fun `NUL codepoint as CHAR event maps to null`() { val event = TambouiKeyEvent(KeyCode.CHAR, KeyModifiers.NONE, 0x00) - assertNull(mapKey(event, InputMode.None)) + assertNull(mapKey(event, InputMode.ROUTER)) } } diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/ApprovalReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/ApprovalReducerTest.kt index d4c4747a..c4d2fe6d 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/ApprovalReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/ApprovalReducerTest.kt @@ -29,7 +29,7 @@ class ApprovalReducerTest { private fun reduce( approval: ApprovalState = ApprovalState(), - inputMode: InputMode = InputMode.None, + inputMode: InputMode = InputMode.ROUTER, action: Action, ) = ApprovalReducer.reduce(approval, inputMode, action) @@ -72,7 +72,7 @@ class ApprovalReducerTest { fun `SubmitInput in SteeringNote mode clears active approval`() { val (state, effects) = reduce( approval = ApprovalState(active = activeInfo), - inputMode = InputMode.SteeringNote, + inputMode = InputMode.STEER, action = Action.SubmitInput, ) assertNull(state.active) @@ -83,7 +83,7 @@ class ApprovalReducerTest { fun `SubmitInput in non-SteeringNote mode is no-op`() { val (state, _) = reduce( approval = ApprovalState(active = activeInfo), - inputMode = InputMode.WorkflowId, + inputMode = InputMode.NAVIGATE, action = Action.SubmitInput, ) assertEquals(activeInfo, state.active) diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/InputReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/InputReducerTest.kt index ae9c7c9f..cdebe328 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/InputReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/InputReducerTest.kt @@ -4,15 +4,15 @@ import com.correx.apps.server.protocol.ApprovalDecision import com.correx.apps.server.protocol.ClientMessage import com.correx.apps.tui.input.Action import com.correx.apps.tui.state.ApprovalInfo +import com.correx.apps.tui.state.ApprovalState import com.correx.apps.tui.state.InputMode -import com.correx.apps.tui.state.InputState +import com.correx.apps.tui.state.TuiState import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test class InputReducerTest { - private val noApproval: ApprovalInfo? = null private val activeApproval = ApprovalInfo( requestId = "req-1", sessionId = "sess-1", @@ -22,68 +22,83 @@ class InputReducerTest { preview = null, ) - private fun reduce(input: InputState = InputState(), approval: ApprovalInfo? = noApproval, action: Action) = - InputReducer.reduce(input, activeApproval = approval, action = action) + private fun reduce( + state: TuiState = TuiState(), + action: Action, + ) = InputReducer.reduce(state, action) @Test - fun `OpenNewSessionPrompt sets mode to WorkflowId with empty text`() { + fun `OpenNewSessionPrompt sets mode to ROUTER with empty buffer`() { val (state, effects) = reduce(action = Action.OpenNewSessionPrompt) - assertEquals(InputMode.WorkflowId, state.mode) - assertEquals("", state.text) + assertEquals(InputMode.ROUTER, state.inputMode) + assertEquals("", state.inputBuffer) assertTrue(effects.isEmpty()) } @Test - fun `OpenSteeringPrompt sets mode to SteeringNote when approval active`() { - val (state, effects) = reduce(approval = activeApproval, action = Action.OpenSteeringPrompt) - assertEquals(InputMode.SteeringNote, state.mode) - assertEquals("", state.text) + fun `OpenSteeringPrompt sets mode to STEER when approval active`() { + val initial = TuiState(approval = ApprovalState(active = activeApproval)) + val (state, effects) = reduce(state = initial, action = Action.OpenSteeringPrompt) + assertEquals(InputMode.STEER, state.inputMode) + assertEquals("", state.inputBuffer) assertTrue(effects.isEmpty()) } @Test fun `OpenSteeringPrompt is no-op when no active approval`() { - val initial = InputState() - val (state, effects) = reduce(input = initial, approval = null, action = Action.OpenSteeringPrompt) + val initial = TuiState() + val (state, effects) = reduce(state = initial, action = Action.OpenSteeringPrompt) assertEquals(initial, state) assertTrue(effects.isEmpty()) } @Test - fun `AppendChar appends character to text`() { - val (state, effects) = reduce(input = InputState(InputMode.WorkflowId, "ab"), action = Action.AppendChar('c')) - assertEquals("abc", state.text) + fun `AppendChar appends character to buffer`() { + val (state, effects) = reduce( + state = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "ab"), + action = Action.AppendChar('c'), + ) + assertEquals("abc", state.inputBuffer) assertTrue(effects.isEmpty()) } @Test fun `Backspace drops last character`() { - val (state, effects) = reduce(input = InputState(InputMode.WorkflowId, "abc"), action = Action.Backspace) - assertEquals("ab", state.text) - assertTrue(effects.isEmpty()) - } - - @Test - fun `Backspace on empty text stays empty`() { - val (state, _) = reduce(input = InputState(InputMode.WorkflowId, ""), action = Action.Backspace) - assertEquals("", state.text) - } - - @Test - fun `CancelInput resets to None with empty text`() { - val (state, effects) = reduce(input = InputState(InputMode.WorkflowId, "hello"), action = Action.CancelInput) - assertEquals(InputMode.None, state.mode) - assertEquals("", state.text) - assertTrue(effects.isEmpty()) - } - - @Test - fun `SubmitInput in WorkflowId mode emits StartSession effect`() { val (state, effects) = reduce( - input = InputState(InputMode.WorkflowId, "my-workflow"), + state = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "abc"), + action = Action.Backspace, + ) + assertEquals("ab", state.inputBuffer) + assertTrue(effects.isEmpty()) + } + + @Test + fun `Backspace on empty buffer stays empty`() { + val (state, _) = reduce( + state = TuiState(inputMode = InputMode.ROUTER, inputBuffer = ""), + action = Action.Backspace, + ) + assertEquals("", state.inputBuffer) + } + + @Test + fun `CancelInput resets to ROUTER mode with empty buffer`() { + val (state, effects) = reduce( + state = TuiState(inputMode = InputMode.FILTER, inputBuffer = "hello"), + action = Action.CancelInput, + ) + assertEquals(InputMode.ROUTER, state.inputMode) + assertEquals("", state.inputBuffer) + assertTrue(effects.isEmpty()) + } + + @Test + fun `SubmitInput in ROUTER mode emits StartSession effect`() { + val (state, effects) = reduce( + state = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "my-workflow"), action = Action.SubmitInput, ) - assertEquals(InputMode.None, state.mode) + assertEquals(InputMode.ROUTER, state.inputMode) assertEquals(1, effects.size) val effect = effects[0] as Effect.SendWs val msg = effect.message as ClientMessage.StartSession @@ -91,23 +106,26 @@ class InputReducerTest { } @Test - fun `SubmitInput in WorkflowId mode with blank text emits no effect`() { + fun `SubmitInput in ROUTER mode with blank text emits no effect`() { val (state, effects) = reduce( - input = InputState(InputMode.WorkflowId, " "), + state = TuiState(inputMode = InputMode.ROUTER, inputBuffer = " "), action = Action.SubmitInput, ) - assertEquals(InputMode.None, state.mode) + assertEquals(InputMode.ROUTER, state.inputMode) assertTrue(effects.isEmpty()) } @Test - fun `SubmitInput in SteeringNote mode with active approval emits ApprovalResponse STEER`() { + fun `SubmitInput in STEER mode with active approval emits ApprovalResponse STEER`() { val (state, effects) = reduce( - input = InputState(InputMode.SteeringNote, "steer this way"), - approval = activeApproval, + state = TuiState( + inputMode = InputMode.STEER, + inputBuffer = "steer this way", + approval = ApprovalState(active = activeApproval), + ), action = Action.SubmitInput, ) - assertEquals(InputMode.None, state.mode) + assertEquals(InputMode.ROUTER, state.inputMode) assertEquals(1, effects.size) val effect = effects[0] as Effect.SendWs val msg = effect.message as ClientMessage.ApprovalResponse @@ -117,12 +135,12 @@ class InputReducerTest { } @Test - fun `SubmitInput in Filter mode clears input with no effects`() { + fun `SubmitInput in FILTER mode clears input with no effects`() { val (state, effects) = reduce( - input = InputState(InputMode.Filter, "myfilter"), + state = TuiState(inputMode = InputMode.FILTER, inputBuffer = "myfilter"), action = Action.SubmitInput, ) - assertEquals(InputMode.None, state.mode) + assertEquals(InputMode.ROUTER, state.inputMode) assertTrue(effects.isEmpty()) } } diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt index b2703eda..6f7ca2e0 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt @@ -3,7 +3,6 @@ package com.correx.apps.tui.reducer import com.correx.apps.server.protocol.ClientMessage import com.correx.apps.tui.input.Action import com.correx.apps.tui.state.InputMode -import com.correx.apps.tui.state.InputState import com.correx.apps.tui.state.TuiState import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -14,11 +13,11 @@ class RootReducerTest { private val fixedClock: () -> Long = { 1000L } @Test - fun `SubmitInput WorkflowId emits StartSession effect and resets input`() { - val initial = TuiState(input = InputState(InputMode.WorkflowId, "my-workflow")) + fun `SubmitInput in ROUTER mode emits StartSession effect and resets buffer`() { + val initial = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "my-workflow") val (state, effects) = RootReducer.reduce(initial, Action.SubmitInput, fixedClock) - assertEquals(InputMode.None, state.input.mode) - assertEquals("", state.input.text) + assertEquals(InputMode.ROUTER, state.inputMode) + assertEquals("", state.inputBuffer) val wsEffects = effects.filterIsInstance() assertEquals(1, wsEffects.size) val msg = wsEffects[0].message as ClientMessage.StartSession @@ -31,7 +30,8 @@ class RootReducerTest { val (state, effects) = RootReducer.reduce(initial, Action.Connected, fixedClock) assertTrue(state.connection.connected) assertTrue(effects.isEmpty()) - assertEquals(initial.input, state.input) + assertEquals(initial.inputMode, state.inputMode) + assertEquals(initial.inputBuffer, state.inputBuffer) assertEquals(initial.sessions, state.sessions) assertEquals(initial.approval, state.approval) assertEquals(initial.provider, state.provider) @@ -51,15 +51,15 @@ class RootReducerTest { ) val (state2, _) = RootReducer.reduce(state1, Action.ServerEventReceived(startMsg2), fixedClock) // selected is s1 (first); navigating down should move to s2 - val (state3, _) = RootReducer.reduce(state2, Action.NavigateDown, fixedClock) + val (state3, _) = RootReducer.reduce(state2.copy(inputMode = InputMode.NAVIGATE), Action.NavigateDown, fixedClock) assertEquals("s2", state3.sessions.selectedId) } @Test - fun `AppendChar followed by Backspace returns to original text`() { - val initial = TuiState(input = InputState(InputMode.WorkflowId, "ab")) + fun `AppendChar followed by Backspace returns to original buffer`() { + val initial = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "ab") val (s1, _) = RootReducer.reduce(initial, Action.AppendChar('c'), fixedClock) val (s2, _) = RootReducer.reduce(s1, Action.Backspace, fixedClock) - assertEquals("ab", s2.input.text) + assertEquals("ab", s2.inputBuffer) } } diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt index 22f1c976..a80654da 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt @@ -27,7 +27,7 @@ class SessionsReducerTest { private fun reduce( sessions: SessionsState = SessionsState(), - inputMode: InputMode = InputMode.None, + inputMode: InputMode = InputMode.ROUTER, inputText: String = "", action: Action, ) = SessionsReducer.reduce(sessions, inputMode, inputText, action, fixedClock) @@ -35,7 +35,7 @@ class SessionsReducerTest { @Test fun `NavigateUp wraps from top to bottom`() { val s = SessionsState(sessions = listOf(session("a"), session("b"), session("c")), selectedId = "a") - val (state, effects) = reduce(sessions = s, action = Action.NavigateUp) + val (state, effects) = reduce(sessions = s, inputMode = InputMode.NAVIGATE, action = Action.NavigateUp) assertEquals("c", state.selectedId) assertTrue(effects.isEmpty()) } @@ -43,14 +43,14 @@ class SessionsReducerTest { @Test fun `NavigateDown advances selection`() { val s = SessionsState(sessions = listOf(session("a"), session("b")), selectedId = "a") - val (state, _) = reduce(sessions = s, action = Action.NavigateDown) + val (state, _) = reduce(sessions = s, inputMode = InputMode.NAVIGATE, action = Action.NavigateDown) assertEquals("b", state.selectedId) } @Test fun `NavigateDown wraps from bottom to top`() { val s = SessionsState(sessions = listOf(session("a"), session("b")), selectedId = "b") - val (state, _) = reduce(sessions = s, action = Action.NavigateDown) + val (state, _) = reduce(sessions = s, inputMode = InputMode.NAVIGATE, action = Action.NavigateDown) assertEquals("a", state.selectedId) } @@ -58,7 +58,7 @@ class SessionsReducerTest { fun `SubmitInput in Filter mode copies inputText into filter`() { val s = SessionsState() val (state, effects) = reduce( - sessions = s, inputMode = InputMode.Filter, inputText = "myfilter", action = Action.SubmitInput, + sessions = s, inputMode = InputMode.FILTER, inputText = "myfilter", action = Action.SubmitInput, ) assertEquals("myfilter", state.filter) assertTrue(effects.isEmpty()) @@ -68,7 +68,7 @@ class SessionsReducerTest { fun `SubmitInput in Filter mode with empty text clears filter`() { val s = SessionsState(filter = "old") val (state, effects) = reduce( - sessions = s, inputMode = InputMode.Filter, inputText = "", action = Action.SubmitInput, + sessions = s, inputMode = InputMode.FILTER, inputText = "", action = Action.SubmitInput, ) assertEquals("", state.filter) assertTrue(effects.isEmpty()) @@ -77,7 +77,7 @@ class SessionsReducerTest { @Test fun `CancelInput in Filter mode clears filter`() { val s = SessionsState(filter = "something") - val (state, effects) = reduce(sessions = s, inputMode = InputMode.Filter, action = Action.CancelInput) + val (state, effects) = reduce(sessions = s, inputMode = InputMode.FILTER, action = Action.CancelInput) assertEquals("", state.filter) assertTrue(effects.isEmpty()) } @@ -85,7 +85,7 @@ class SessionsReducerTest { @Test fun `CancelInput in non-Filter mode is no-op`() { val s = SessionsState(filter = "something") - val (state, _) = reduce(sessions = s, inputMode = InputMode.WorkflowId, action = Action.CancelInput) + val (state, _) = reduce(sessions = s, inputMode = InputMode.ROUTER, action = Action.CancelInput) assertEquals("something", state.filter) } @@ -96,7 +96,8 @@ class SessionsReducerTest { assertEquals(1, state.sessions.size) assertEquals("s1", state.sessions[0].id) assertEquals("s1", state.selectedId) - assertTrue(effects.isEmpty()) + assertEquals(1, effects.size) + assertTrue(effects[0] is Effect.ConnectSession) } @Test