-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathplugin_trust_test.go
More file actions
138 lines (128 loc) · 4.53 KB
/
Copy pathplugin_trust_test.go
File metadata and controls
138 lines (128 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main
import (
"encoding/json"
"strings"
"testing"
tea "charm.land/bubbletea/v2"
)
// pluginTrustPromptEvent builds a real `plugin_trust_prompt` core event so the
// test exercises the actual event→modal wiring (not direct field assignment).
func pluginTrustPromptEvent(t *testing.T, plugins string) *coreEvent {
t.Helper()
raw, err := json.Marshal(map[string]any{
"plugins": json.RawMessage(plugins),
})
if err != nil {
t.Fatalf("marshal: %v", err)
}
return &coreEvent{Type: "plugin_trust_prompt", Raw: raw}
}
func TestPluginTrustPromptOpensModal(t *testing.T) {
s := initialSession()
s.ready = true
s.width, s.height = 80, 24
s.layout()
s.handleCoreEvent(pluginTrustPromptEvent(t,
`[{"name":"shady","version":"1.0.0","description":"Hook-heavy linter","path":"/ws/.catalyst-code/plugins/shady","decision":""}]`))
if s.modal.kind != modalPluginTrust {
t.Fatalf("kind=%v, want modalPluginTrust", s.modal.kind)
}
if s.pluginTrust == nil || len(s.pluginTrust.entries) != 1 {
t.Fatalf("pluginTrust=%+v, want 1 entry", s.pluginTrust)
}
if s.pluginTrust.decisions["shady"] != "" {
t.Fatalf("initial decision=%q, want undecided", s.pluginTrust.decisions["shady"])
}
// Render must include the plugin name and the apply row.
view := stripANSI(s.renderPluginTrustModal())
if !strings.Contains(view, "shady") || !strings.Contains(view, "Apply & close") {
t.Fatalf("render missing content: %q", view)
}
}
func TestPluginTrustCycleAndApply(t *testing.T) {
s := initialSession()
s.keybinds = defaultKeybinds() // isolate from user settings
s.ready = true
s.width, s.height = 80, 24
s.layout()
s.handleCoreEvent(pluginTrustPromptEvent(t,
`[{"name":"shady","version":"1.0.0","description":"x","path":"/p","decision":""},{"name":"linter","version":"2.0.0","description":"y","path":"/p","decision":"deny"}]`))
if s.modal.kind != modalPluginTrust {
t.Fatalf("kind=%v, want modalPluginTrust", s.modal.kind)
}
// Focus row 0 (shady) and cycle: undecided → trust.
s.modal.cursor = 0
s.handlePluginTrustKey(tea.KeyPressMsg{Code: tea.KeyEnter})
if got := s.pluginTrust.decisions["shady"]; got != "trust" {
t.Fatalf("after enter: shady=%q, want trust", got)
}
// Cycle again: trust → deny.
s.handlePluginTrustKey(tea.KeyPressMsg{Code: tea.KeyEnter})
if got := s.pluginTrust.decisions["shady"]; got != "deny" {
t.Fatalf("after second enter: shady=%q, want deny", got)
}
// Space on row 1 (linter, was deny) → deny → undecided.
s.modal.cursor = 1
s.handlePluginTrustKey(tea.KeyPressMsg{Code: tea.KeySpace})
if got := s.pluginTrust.decisions["linter"]; got != "" {
t.Fatalf("after space: linter=%q, want undecided", got)
}
// Space again: undecided → trust.
s.handlePluginTrustKey(tea.KeyPressMsg{Code: tea.KeySpace})
if got := s.pluginTrust.decisions["linter"]; got != "trust" {
t.Fatalf("after second space: linter=%q, want trust", got)
}
// Apply & close: only decided entries are sent to the core.
cw := &captureWriter{}
s.coreIn = cw
s.modal.cursor = s.pluginTrustRows() - 1 // apply row
s.handlePluginTrustKey(tea.KeyPressMsg{Code: tea.KeyEnter})
if s.modal.kind != modalNone {
t.Fatalf("modal still open after apply: kind=%v", s.modal.kind)
}
if len(cw.lines) != 1 {
t.Fatalf("expected one command sent, got %d", len(cw.lines))
}
var sent map[string]any
if err := json.Unmarshal([]byte(cw.lines[0]), &sent); err != nil {
t.Fatalf("unmarshal sent command: %v", err)
}
if sent["type"] != "plugin_trust_decisions" {
t.Fatalf("expected plugin_trust_decisions sent, got %+v", sent)
}
decisions, ok := sent["decisions"].(map[string]any)
if !ok {
t.Fatalf("decisions type=%T", sent["decisions"])
}
if decisions["shady"] != "deny" || decisions["linter"] != "trust" {
t.Fatalf("decisions=%v", decisions)
}
}
func TestPluginTrustEmptyPrompt(t *testing.T) {
s := initialSession()
s.ready = true
s.width, s.height = 80, 24
s.layout()
s.handleCoreEvent(pluginTrustPromptEvent(t, `[]`))
if s.modal.kind != modalNone {
t.Fatalf("empty prompt must not open a modal, kind=%v", s.modal.kind)
}
}
func TestPluginTrustSlashCommand(t *testing.T) {
s := initialSession()
s.ready = true
s.width, s.height = 80, 24
cw := &captureWriter{}
s.coreIn = cw
s.handleUserLine("/plugin-trust")
if len(cw.lines) != 1 {
t.Fatalf("expected one command sent, got %d", len(cw.lines))
}
var sent map[string]any
if err := json.Unmarshal([]byte(cw.lines[0]), &sent); err != nil {
t.Fatalf("unmarshal sent command: %v", err)
}
if sent["type"] != "plugin_trust_prompt" {
t.Fatalf("expected plugin_trust_prompt sent, got %+v", sent)
}
}