1package model
2
3import (
4 "testing"
5
6 "github.com/charmbracelet/crush/internal/permission"
7 "github.com/charmbracelet/crush/internal/ui/dialog"
8 "github.com/stretchr/testify/require"
9)
10
11// newTestUIForPermissions builds a UI with a chat, dialog overlay, and
12// common context sufficient to exercise handlePermissionNotification.
13func newTestUIForPermissions() *UI {
14 u := newTestUI()
15 u.dialog = dialog.NewOverlay()
16 return u
17}
18
19func TestHandlePermissionNotification_RemoteGrantClosesDialog(t *testing.T) {
20 t.Parallel()
21
22 u := newTestUIForPermissions()
23 perm := permission.PermissionRequest{
24 ID: "perm-1",
25 ToolCallID: "tool-call-X",
26 ToolName: "bash",
27 }
28 u.dialog.OpenDialogWithGrace(dialog.NewPermissions(u.com, perm))
29 require.True(t, u.dialog.ContainsDialog(dialog.PermissionsID))
30
31 u.handlePermissionNotification(permission.PermissionNotification{
32 ToolCallID: "tool-call-X",
33 Granted: true,
34 })
35
36 require.False(t, u.dialog.ContainsDialog(dialog.PermissionsID),
37 "granted notification should close matching permissions dialog")
38}
39
40func TestHandlePermissionNotification_RemoteDenyClosesDialog(t *testing.T) {
41 t.Parallel()
42
43 u := newTestUIForPermissions()
44 perm := permission.PermissionRequest{
45 ID: "perm-2",
46 ToolCallID: "tool-call-Y",
47 }
48 u.dialog.OpenDialogWithGrace(dialog.NewPermissions(u.com, perm))
49
50 u.handlePermissionNotification(permission.PermissionNotification{
51 ToolCallID: "tool-call-Y",
52 Denied: true,
53 })
54
55 require.False(t, u.dialog.ContainsDialog(dialog.PermissionsID),
56 "denied notification should close matching permissions dialog")
57}
58
59func TestHandlePermissionNotification_InitialPendingDoesNotClose(t *testing.T) {
60 t.Parallel()
61
62 u := newTestUIForPermissions()
63 perm := permission.PermissionRequest{
64 ID: "perm-3",
65 ToolCallID: "tool-call-Z",
66 }
67 u.dialog.OpenDialogWithGrace(dialog.NewPermissions(u.com, perm))
68
69 // The initial notification published by permission.Request is
70 // neither granted nor denied; it must not dismiss the dialog.
71 u.handlePermissionNotification(permission.PermissionNotification{
72 ToolCallID: "tool-call-Z",
73 })
74
75 require.True(t, u.dialog.ContainsDialog(dialog.PermissionsID),
76 "initial pending notification must not close the dialog")
77}
78
79func TestHandlePermissionNotification_DifferentToolCallIDDoesNotClose(t *testing.T) {
80 t.Parallel()
81
82 u := newTestUIForPermissions()
83 perm := permission.PermissionRequest{
84 ID: "perm-4",
85 ToolCallID: "tool-call-A",
86 }
87 u.dialog.OpenDialogWithGrace(dialog.NewPermissions(u.com, perm))
88
89 u.handlePermissionNotification(permission.PermissionNotification{
90 ToolCallID: "tool-call-B",
91 Granted: true,
92 })
93
94 require.True(t, u.dialog.ContainsDialog(dialog.PermissionsID),
95 "notification for unrelated tool call must not close the dialog")
96}