From 4a42f57033bb58c3603422431832ba6fdddec703 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 22 Apr 2026 16:26:14 +0300 Subject: h7: fix agent toggle rewrite and hotkey collisions --- internal/ui/table_test.go | 80 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 11 deletions(-) (limited to 'internal/ui/table_test.go') diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 17049ac..9d0766e 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -815,28 +815,32 @@ func TestToggleAgentFilter(t *testing.T) { expect: []string{"project:home", "+agent"}, }, { - name: "switches +agent to -agent", - input: []string{"project:home", "+agent"}, - expect: []string{"project:home", "-agent"}, + name: "switches +agent to -agent in place", + input: []string{"project:home", "+agent", "status:pending"}, + expect: []string{"project:home", "-agent", "status:pending"}, }, { - name: "switches -agent to +agent", - input: []string{"project:home", "-agent"}, - expect: []string{"project:home", "+agent"}, + name: "switches -agent to +agent in place", + input: []string{"project:home", "-agent", "status:pending"}, + expect: []string{"project:home", "+agent", "status:pending"}, }, { - name: "normalizes contradictory agent filters", - input: []string{"+agent", "-agent", "status:pending"}, - expect: []string{"status:pending", "+agent"}, + name: "preserves complex filter structure", + input: []string{"(", "project:home", "or", "project:work", ")", "+agent", "status:pending"}, + expect: []string{"(", "project:home", "or", "project:work", ")", "-agent", "status:pending"}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := toggleAgentFilter(tc.input) + input := append([]string(nil), tc.input...) + got := toggleAgentFilter(input) if !reflect.DeepEqual(got, tc.expect) { t.Fatalf("toggleAgentFilter(%v) = %v, want %v", tc.input, got, tc.expect) } + if !reflect.DeepEqual(input, tc.input) { + t.Fatalf("toggleAgentFilter mutated input: got %v want %v", input, tc.input) + } }) } } @@ -908,7 +912,9 @@ func TestAgentFilterHotkeyCanBeRebound(t *testing.T) { if err != nil { t.Fatalf("New: %v", err) } - m.SetAgentFilterHotkey("7") + if err := m.SetAgentFilterHotkey("7"); err != nil { + t.Fatalf("SetAgentFilterHotkey: %v", err) + } mv, _ := (&m).Update(tea.KeyPressMsg{Code: '3', Text: "3"}) m = *mv.(*Model) @@ -931,6 +937,58 @@ func TestAgentFilterHotkeyCanBeRebound(t *testing.T) { } } +func TestAgentFilterHotkeyCollisionIsRejected(t *testing.T) { + tmp := t.TempDir() + taskPath := filepath.Join(tmp, "task") + + script := "#!/bin/sh\n" + + "if echo \"$@\" | grep -q export; then\n" + + " echo '{\"id\":1,\"uuid\":\"x\",\"description\":\"d\",\"status\":\"pending\",\"entry\":\"\",\"priority\":\"\",\"urgency\":0}'\n" + + "fi\n" + + if err := os.WriteFile(taskPath, []byte(script), 0o755); err != nil { + t.Fatal(err) + } + + origPath := os.Getenv("PATH") + os.Setenv("PATH", tmp+":"+origPath) + t.Cleanup(func() { os.Setenv("PATH", origPath) }) + + os.Setenv("TASKDATA", tmp) + os.Setenv("TASKRC", "/dev/null") + t.Cleanup(func() { + os.Unsetenv("TASKDATA") + os.Unsetenv("TASKRC") + }) + + m, err := New(nil, "firefox") + if err != nil { + t.Fatalf("New: %v", err) + } + + if err := m.SetAgentFilterHotkey("u"); err == nil { + t.Fatalf("expected collision for hotkey u") + } + if got := m.agentFilterHotkeyLabel(); got != "3" { + t.Fatalf("colliding hotkey changed label: got %q want %q", got, "3") + } + + mv, _ := (&m).Update(tea.KeyPressMsg{Code: 'u', Text: "u"}) + m = *mv.(*Model) + if !m.showUltra { + t.Fatalf("u no longer entered ultra mode after rejected hotkey") + } + if len(m.filters) != 0 { + t.Fatalf("u unexpectedly changed filters after rejected hotkey: %#v", m.filters) + } + + mv, _ = (&m).Update(tea.KeyPressMsg{Code: 'u', Text: "u"}) + m = *mv.(*Model) + if m.showUltra { + t.Fatalf("u no longer exited ultra mode after rejected hotkey") + } +} + func setupUltraTaskSet(t *testing.T, tmp string) string { taskPath := filepath.Join(tmp, "task") script := "#!/bin/sh\n" + -- cgit v1.2.3