ai-agent/internal/tui/model_completion_test.go
admin 8dc496b626
Some checks failed
CI / test (push) Has been cancelled
Release / release (push) Failing after 4m36s
first commit
2026-03-08 15:40:34 +07:00

204 lines
6.0 KiB
Go

package tui
import "testing"
func TestTriggerCompletion(t *testing.T) {
t.Run("slash_triggers_command", func(t *testing.T) {
m := newTestModel(t)
m.triggerCompletion("/")
if !m.isCompletionActive() {
t.Error("/ should activate completion")
}
if m.completionState.Kind != "command" {
t.Errorf("expected kind 'command', got %q", m.completionState.Kind)
}
if m.overlay != OverlayCompletion {
t.Errorf("expected OverlayCompletion, got %d", m.overlay)
}
if len(m.completionState.AllItems) == 0 {
t.Error("should have completion items for /")
}
})
t.Run("at_triggers_attachments_with_multiselect", func(t *testing.T) {
m := newTestModel(t)
m.triggerCompletion("@")
// @ triggers agent/file completion.
// It may or may not find matches depending on agents + cwd.
// If agents exist, it should activate.
if m.isCompletionActive() {
if m.completionState.Kind != "attachments" {
t.Errorf("expected kind 'attachments', got %q", m.completionState.Kind)
}
if m.completionState.Selected == nil {
t.Error("attachments should initialize Selected map")
}
}
})
t.Run("hash_triggers_skills", func(t *testing.T) {
m := newTestModel(t)
m.triggerCompletion("#")
if !m.isCompletionActive() {
t.Error("# should activate completion for skills")
}
if m.completionState.Kind != "skills" {
t.Errorf("expected kind 'skills', got %q", m.completionState.Kind)
}
if m.completionState.Selected == nil {
t.Error("skills should initialize Selected map")
}
})
t.Run("no_matches_stays_inactive", func(t *testing.T) {
m := newTestModel(t)
m.triggerCompletion("/zzzznonexistent")
if m.isCompletionActive() {
t.Error("should not activate with no matches")
}
})
t.Run("plain_text_no_trigger", func(t *testing.T) {
m := newTestModel(t)
m.triggerCompletion("hello")
if m.isCompletionActive() {
t.Error("plain text should not trigger completion")
}
})
}
func TestAcceptCompletion(t *testing.T) {
t.Run("single_select", func(t *testing.T) {
m := newTestModel(t)
items := []Completion{
{Label: "/help", Insert: "/help "},
{Label: "/clear", Insert: "/clear "},
}
m.completionState = newCompletionState("command", items, false)
m.overlay = OverlayCompletion
m.completionState.Index = 0
m.acceptCompletion()
if m.input.Value() != "/help " {
t.Errorf("expected '/help ', got %q", m.input.Value())
}
if m.isCompletionActive() {
t.Error("should be inactive after accept")
}
if m.overlay != OverlayNone {
t.Error("overlay should be OverlayNone")
}
})
t.Run("multi_select_with_selections", func(t *testing.T) {
m := newTestModel(t)
items := []Completion{
{Label: "@a", Insert: "@a "},
{Label: "@b", Insert: "@b "},
{Label: "@c", Insert: "@c "},
}
m.completionState = newCompletionState("attachments", items, true)
m.overlay = OverlayCompletion
m.completionState.Index = 0
m.completionState.Selected[1] = true
m.acceptCompletion()
if m.input.Value() != "@b " {
t.Errorf("expected '@b ', got %q", m.input.Value())
}
if m.isCompletionActive() {
t.Error("should be inactive after accept")
}
})
t.Run("multi_select_empty_fallback", func(t *testing.T) {
m := newTestModel(t)
items := []Completion{
{Label: "@x", Insert: "@x "},
{Label: "@y", Insert: "@y "},
}
m.completionState = newCompletionState("attachments", items, true)
m.overlay = OverlayCompletion
m.completionState.Index = 1
m.acceptCompletion()
if m.input.Value() != "@y " {
t.Errorf("expected '@y ' as fallback, got %q", m.input.Value())
}
})
t.Run("inactive_noop", func(t *testing.T) {
m := newTestModel(t)
m.completionState = nil
m.input.SetValue("original")
m.acceptCompletion()
if m.input.Value() != "original" {
t.Errorf("inactive accept should be noop, got %q", m.input.Value())
}
})
}
func TestCloseCompletion(t *testing.T) {
m := newTestModel(t)
items := []Completion{{Label: "test"}}
m.completionState = newCompletionState("command", items, true)
m.completionState.Index = 5
m.completionState.Selected[0] = true
m.overlay = OverlayCompletion
m.closeCompletion()
if m.isCompletionActive() {
t.Error("completionState should be nil")
}
if m.overlay != OverlayNone {
t.Errorf("overlay should be OverlayNone, got %d", m.overlay)
}
}
func TestFilterCompletions(t *testing.T) {
items := []Completion{
{Label: "/help"},
{Label: "/clear"},
{Label: "/model"},
}
t.Run("empty_query_returns_all", func(t *testing.T) {
filtered := FilterCompletions(items, "")
if len(filtered) != 3 {
t.Errorf("expected 3, got %d", len(filtered))
}
})
t.Run("filters_by_substring", func(t *testing.T) {
filtered := FilterCompletions(items, "el")
if len(filtered) != 2 {
t.Errorf("expected 2 (help, model), got %d", len(filtered))
}
})
t.Run("case_insensitive", func(t *testing.T) {
filtered := FilterCompletions(items, "HELP")
if len(filtered) != 1 {
t.Errorf("expected 1, got %d", len(filtered))
}
})
t.Run("no_match", func(t *testing.T) {
filtered := FilterCompletions(items, "zzz")
if len(filtered) != 0 {
t.Errorf("expected 0, got %d", len(filtered))
}
})
}