package main import ( "strings" "testing" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "sidekick/pkg/sidekick" ) // Since sidekick-tui/main.go uses global variables and is a tea.Model, // we test basic model behavior. func TestInitialModel(t *testing.T) { // For testing, sidekick.LoadConfig would fail or look in its default dir. // This ensures that the model can be initialized even without a config. m := initialModel() if m.agent == nil { t.Error("expected default agent to be initialized even without config") } if m.textarea.Value() != "" { t.Errorf("expected empty textarea, got %s", m.textarea.Value()) } if len(m.history) != 1 { t.Errorf("expected 1 initial message in history, got %d", len(m.history)) } if !strings.Contains(m.history[0].Content, "Sidekick initialized") { t.Error("expected initial message content to contain 'Sidekick initialized'") } } func TestHistoryStateInitialization(t *testing.T) { m := initialModel() if m.historyIndex != -1 { t.Errorf("expected historyIndex to be -1, got %d", m.historyIndex) } } func TestModelUpdate(t *testing.T) { m := initialModel() m.ready = true m.width = 80 m.height = 24 // Test a key message (Enter) m.textarea.SetValue("Hello") newModel, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEnter}) tm := newModel.(model) if tm.isThinking != true { t.Error("expected model to be in thinking state after Enter") } if tm.textarea.Value() != "" { t.Error("expected textarea to be reset after Enter") } if cmd == nil { t.Error("expected a command after Enter") } // Test an agent response message respModel, _ := tm.Update(agentResponseMsg{response: "Hi"}) rm := respModel.(model) if rm.isThinking != false { t.Error("expected model to stop thinking after response") } if len(rm.history) < 2 { t.Error("expected history list to grow after response") } } func TestMessageWrapping(t *testing.T) { m := initialModel() m.ready = true m.width = 10 m.height = 20 m.viewport = viewport.New(10, 5) // Very narrow longMsg := "This is a very long message that should be wrapped." respModel, _ := m.Update(agentResponseMsg{response: longMsg}) rm := respModel.(model) content := rm.viewport.View() if !strings.Contains(content, "\n") { t.Errorf("expected long message to be wrapped, but no newline found in viewport") } } func TestHistoryNavigation(t *testing.T) { m := initialModel() m.ready = true m.history = append(m.history, sidekick.Message{Role: sidekick.MessageRoleUser, Content: "First"}) m.history = append(m.history, sidekick.Message{Role: sidekick.MessageRoleAssistant, Content: "AI Response"}) m.history = append(m.history, sidekick.Message{Role: sidekick.MessageRoleUser, Content: "Second"}) // Test UP from empty draft m2, _ := m.Update(tea.KeyMsg{Type: tea.KeyUp}) tm2 := m2.(model) if tm2.textarea.Value() != "Second" { t.Errorf("expected 'Second', got '%s'", tm2.textarea.Value()) } if tm2.historyIndex != 1 { t.Errorf("expected historyIndex 1, got %d", tm2.historyIndex) } // Test UP again m3, _ := tm2.Update(tea.KeyMsg{Type: tea.KeyUp}) tm3 := m3.(model) if tm3.textarea.Value() != "First" { t.Errorf("expected 'First', got '%s'", tm3.textarea.Value()) } if tm3.historyIndex != 0 { t.Errorf("expected historyIndex 0, got %d", tm3.historyIndex) } // Test DOWN m4, _ := tm3.Update(tea.KeyMsg{Type: tea.KeyDown}) tm4 := m4.(model) if tm4.textarea.Value() != "Second" { t.Errorf("expected 'Second', got '%s'", tm4.textarea.Value()) } // Test DOWN to draft m5, _ := tm4.Update(tea.KeyMsg{Type: tea.KeyDown}) tm5 := m5.(model) if tm5.textarea.Value() != "" { t.Errorf("expected empty draft, got '%s'", tm5.textarea.Value()) } if tm5.historyIndex != -1 { t.Error("expected historyIndex to reset to -1") } }