Gim/internal/motion/command_test.go
Hayden Hargreaves a01369f407
All checks were successful
Run Test Suite / test (push) Successful in 42s
fix: cleaned up the testing mocking. New single module
2026-03-19 17:31:53 -07:00

377 lines
10 KiB
Go

package motion
import (
"testing"
"git.gophernest.net/azpect/TextEditor/internal/action"
"git.gophernest.net/azpect/TextEditor/internal/core"
)
// ==================================================
// MoveCommandHistoryUp Tests
// ==================================================
func TestMoveCommandHistoryUp(t *testing.T) {
t.Run("navigate up from start", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 0,
}
action := MoveCommandHistoryUp{}
action.Execute(m)
// Should load first command in history
if m.Command() != "cmd3" {
t.Errorf("Command = %q, want %q", m.Command(), "cmd3")
}
// Cursor should move to end of command
if m.CommandCursor() != len("cmd3") {
t.Errorf("CommandCursor = %d, want %d", m.CommandCursor(), len("cmd3"))
}
// History cursor should advance
if m.CommandHistoryCursor() != 1 {
t.Errorf("CommandHistoryCursor = %d, want 1", m.CommandHistoryCursor())
}
})
t.Run("navigate up multiple times", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 0,
}
action := MoveCommandHistoryUp{}
// First up
action.Execute(m)
if m.Command() != "cmd3" {
t.Errorf("After 1st up: Command = %q, want 'cmd3'", m.Command())
}
// Second up
action.Execute(m)
if m.Command() != "cmd2" {
t.Errorf("After 2nd up: Command = %q, want 'cmd2'", m.Command())
}
// Third up
action.Execute(m)
if m.Command() != "cmd1" {
t.Errorf("After 3rd up: Command = %q, want 'cmd1'", m.Command())
}
if m.CommandHistoryCursor() != 3 {
t.Errorf("CommandHistoryCursor = %d, want 3", m.CommandHistoryCursor())
}
})
t.Run("cannot navigate past end of history", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 3, // Already at end
}
action := MoveCommandHistoryUp{}
action.Execute(m)
// Should not change command (still showing cmd1)
if m.Command() != "" {
t.Errorf("Command = %q, want empty (should not go past end)", m.Command())
}
// Cursor should not advance
if m.CommandHistoryCursor() != 3 {
t.Errorf("CommandHistoryCursor = %d, want 3 (should not advance)", m.CommandHistoryCursor())
}
})
t.Run("empty history does nothing", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "current",
CommandHistoryList: []string{},
CommandHistoryCur: 0,
}
action := MoveCommandHistoryUp{}
action.Execute(m)
// Command should not change
if m.Command() != "current" {
t.Errorf("Command = %q, want 'current' (no history to navigate)", m.Command())
}
if m.CommandHistoryCursor() != 0 {
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
}
})
t.Run("command cursor moves to end", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandCursorVal: 0,
CommandHistoryList: []string{"long command here"},
CommandHistoryCur: 0,
}
action := MoveCommandHistoryUp{}
action.Execute(m)
expectedLen := len("long command here")
if m.CommandCursor() != expectedLen {
t.Errorf("CommandCursor = %d, want %d (end of command)", m.CommandCursor(), expectedLen)
}
})
}
// ==================================================
// MoveCommandHistoryDown Tests
// ==================================================
func TestMoveCommandHistoryDown(t *testing.T) {
t.Run("navigate down from middle of history", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "cmd2",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 2, // At cmd2
}
action := MoveCommandHistoryDown{}
action.Execute(m)
// Should load cmd3 (more recent)
if m.Command() != "cmd3" {
t.Errorf("Command = %q, want 'cmd3'", m.Command())
}
// Cursor should be at 1
if m.CommandHistoryCursor() != 1 {
t.Errorf("CommandHistoryCursor = %d, want 1", m.CommandHistoryCursor())
}
})
t.Run("navigate down to empty command", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "cmd3",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 1, // At first command
}
action := MoveCommandHistoryDown{}
action.Execute(m)
// Should clear command (back to current input)
if m.Command() != "" {
t.Errorf("Command = %q, want empty (back to current)", m.Command())
}
if m.CommandCursor() != 0 {
t.Errorf("CommandCursor = %d, want 0", m.CommandCursor())
}
if m.CommandHistoryCursor() != 0 {
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
}
})
t.Run("navigate down from cursor 0 does nothing", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 0, // Already at bottom
}
action := MoveCommandHistoryDown{}
action.Execute(m)
// Should clear command
if m.Command() != "" {
t.Errorf("Command = %q, want empty", m.Command())
}
if m.CommandHistoryCursor() != 0 {
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
}
})
t.Run("command cursor moves to end", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandCursorVal: 0,
CommandHistoryList: []string{"cmd3", "long command here"},
CommandHistoryCur: 2,
}
action := MoveCommandHistoryDown{}
action.Execute(m)
expectedLen := len("cmd3")
if m.CommandCursor() != expectedLen {
t.Errorf("CommandCursor = %d, want %d (end of command)", m.CommandCursor(), expectedLen)
}
})
}
// ==================================================
// Integration Tests
// ==================================================
func TestCommandHistoryIntegration(t *testing.T) {
t.Run("up down up sequence", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
CommandHistoryCur: 0,
}
upAction := MoveCommandHistoryUp{}
downAction := MoveCommandHistoryDown{}
// Up twice
upAction.Execute(m) // cursor=1, cmd=cmd3
upAction.Execute(m) // cursor=2, cmd=cmd2
// Down once
downAction.Execute(m) // cursor=1, cmd=cmd3
// Up again
upAction.Execute(m) // cursor=2, cmd=cmd2
if m.Command() != "cmd2" {
t.Errorf("Command = %q, want 'cmd2'", m.Command())
}
if m.CommandHistoryCursor() != 2 {
t.Errorf("CommandHistoryCursor = %d, want 2", m.CommandHistoryCursor())
}
})
t.Run("navigate up past end does not crash", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd1", "cmd2"},
CommandHistoryCur: 0,
}
upAction := MoveCommandHistoryUp{}
// Navigate to end
upAction.Execute(m) // cursor = 1, cmd = cmd1
upAction.Execute(m) // cursor = 2, cmd = cmd2
// Try to go past end
upAction.Execute(m) // Should do nothing
if m.CommandHistoryCursor() != 2 {
t.Errorf("CommandHistoryCursor = %d, want 2 (should stay at end)", m.CommandHistoryCursor())
}
})
t.Run("navigate down past bottom does not crash", func(t *testing.T) {
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: []string{"cmd1"},
CommandHistoryCur: 0,
}
downAction := MoveCommandHistoryDown{}
// Try to go down from bottom
downAction.Execute(m)
if m.CommandHistoryCursor() != 0 {
t.Errorf("CommandHistoryCursor = %d, want 0 (should stay at bottom)", m.CommandHistoryCursor())
}
// Try again
downAction.Execute(m)
if m.CommandHistoryCursor() != 0 {
t.Errorf("CommandHistoryCursor = %d, want 0 (should stay at bottom)", m.CommandHistoryCursor())
}
})
}
// ==================================================
// Long History Tests
// ==================================================
func TestCommandHistoryWithLongHistory(t *testing.T) {
t.Run("navigate through 20 commands", func(t *testing.T) {
history := make([]string, 20)
for i := 0; i < 20; i++ {
history[i] = string(rune('A' + i))
}
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: history,
CommandHistoryCur: 0,
}
upAction := MoveCommandHistoryUp{}
// Navigate to 10th command
for i := 0; i < 10; i++ {
upAction.Execute(m)
}
want := string(rune('A' + 9))
if m.Command() != want {
t.Errorf("Command after 10 ups = %q, want %q", m.Command(), want)
}
if m.CommandHistoryCursor() != 10 {
t.Errorf("CommandHistoryCursor = %d, want 10", m.CommandHistoryCursor())
}
})
t.Run("navigate to very end of long history", func(t *testing.T) {
history := make([]string, 50)
for i := 0; i < 50; i++ {
history[i] = string(rune('0' + (i % 10)))
}
m := &action.MockModel{
ModeVal: core.CommandMode,
CommandVal: "",
CommandHistoryList: history,
CommandHistoryCur: 0,
}
upAction := MoveCommandHistoryUp{}
// Navigate all the way to the end
for i := 0; i < 100; i++ { // Try to go past end
upAction.Execute(m)
}
// Should be at position 50 (end of 50-item array)
if m.CommandHistoryCursor() != 50 {
t.Errorf("CommandHistoryCursor = %d, want 50 (at end)", m.CommandHistoryCursor())
}
// Should be showing last command
want := string(rune('0' + 49%10))
if m.Command() != want {
t.Errorf("Command = %q, want %q (last in history)", m.Command(), want)
}
})
}