9 "github.com/charmbracelet/crush/internal/config"
10 "github.com/charmbracelet/crush/internal/permission"
11 "github.com/charmbracelet/crush/internal/pubsub"
12 "github.com/charmbracelet/crush/internal/shell"
13 "github.com/stretchr/testify/require"
16type mockBashPermissionService struct {
17 *pubsub.Broker[permission.PermissionRequest]
20func (m *mockBashPermissionService) Request(ctx context.Context, req permission.CreatePermissionRequest) (bool, error) {
24func (m *mockBashPermissionService) Grant(req permission.PermissionRequest) {}
26func (m *mockBashPermissionService) Deny(req permission.PermissionRequest) {}
28func (m *mockBashPermissionService) GrantPersistent(req permission.PermissionRequest) {}
30func (m *mockBashPermissionService) AutoApproveSession(sessionID string) {}
32func (m *mockBashPermissionService) SetSkipRequests(skip bool) {}
34func (m *mockBashPermissionService) SkipRequests() bool {
38func (m *mockBashPermissionService) SubscribeNotifications(ctx context.Context) <-chan pubsub.Event[permission.PermissionNotification] {
39 return make(<-chan pubsub.Event[permission.PermissionNotification])
42func TestBashTool_DefaultAutoBackgroundThreshold(t *testing.T) {
43 workingDir := t.TempDir()
44 tool := newBashToolForTest(workingDir)
45 ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session")
47 resp := runBashTool(t, tool, ctx, BashParams{
48 Description: "default threshold",
52 require.False(t, resp.IsError)
53 var meta BashResponseMetadata
54 require.NoError(t, json.Unmarshal([]byte(resp.Metadata), &meta))
55 require.False(t, meta.Background)
56 require.Empty(t, meta.ShellID)
57 require.Contains(t, meta.Output, "done")
60func TestBashTool_CustomAutoBackgroundThreshold(t *testing.T) {
61 workingDir := t.TempDir()
62 tool := newBashToolForTest(workingDir)
63 ctx := context.WithValue(context.Background(), SessionIDContextKey, "test-session")
65 resp := runBashTool(t, tool, ctx, BashParams{
66 Description: "custom threshold",
67 Command: "sleep 1.5 && echo done",
68 AutoBackgroundAfter: 1,
71 require.False(t, resp.IsError)
72 var meta BashResponseMetadata
73 require.NoError(t, json.Unmarshal([]byte(resp.Metadata), &meta))
74 require.True(t, meta.Background)
75 require.NotEmpty(t, meta.ShellID)
76 require.Contains(t, resp.Content, "moved to background")
78 bgManager := shell.GetBackgroundShellManager()
79 require.NoError(t, bgManager.Kill(meta.ShellID))
82func newBashToolForTest(workingDir string) fantasy.AgentTool {
83 permissions := &mockBashPermissionService{Broker: pubsub.NewBroker[permission.PermissionRequest]()}
84 attribution := &config.Attribution{TrailerStyle: config.TrailerStyleNone}
85 return NewBashTool(permissions, workingDir, attribution, "test-model")
88func runBashTool(t *testing.T, tool fantasy.AgentTool, ctx context.Context, params BashParams) fantasy.ToolResponse {
91 input, err := json.Marshal(params)
92 require.NoError(t, err)
94 call := fantasy.ToolCall{
100 resp, err := tool.Run(ctx, call)
101 require.NoError(t, err)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list