nandi/oripublic Fork 0
2434cc7fb724f02b34c0189dfd5fb30c1a8a68e3
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

mockagent_test.go · 126 lines · 3.7 KBGo Blame HistoryRaw
✨ Introduce new feature(s): ACP web client — Go backend (agent, bridge, httpserver, mockagent) + React SPA (Zed-like agent panel), tests, quality gate PASS 2434cc7 k33g yesterday1package mockagent_test
2
3import (
4 "context"
5 "io"
6 "strings"
7 "sync"
8 "testing"
9 "time"
10
11 acp "github.com/coder/acp-go-sdk"
12
13 "github.com/bots-garden/ori/internal/agent"
14 "github.com/bots-garden/ori/internal/mockagent"
15)
16
17// panelHandler records everything the mock streams, and answers the
18// permission request with a fixed choice.
19type panelHandler struct {
20 mu sync.Mutex
21 optionId string
22 texts []string
23 updateKinds []string
24 permissions int
25}
26
27func (h *panelHandler) HandleSessionUpdate(_ context.Context, n acp.SessionNotification) {
28 h.mu.Lock()
29 defer h.mu.Unlock()
30 switch {
31 case n.Update.AgentMessageChunk != nil:
32 h.updateKinds = append(h.updateKinds, "message")
33 if t := n.Update.AgentMessageChunk.Content.Text; t != nil {
34 h.texts = append(h.texts, t.Text)
35 }
36 case n.Update.AgentThoughtChunk != nil:
37 h.updateKinds = append(h.updateKinds, "thought")
38 case n.Update.ToolCall != nil:
39 h.updateKinds = append(h.updateKinds, "tool_call")
40 case n.Update.ToolCallUpdate != nil:
41 h.updateKinds = append(h.updateKinds, "tool_call_update")
42 case n.Update.Plan != nil:
43 h.updateKinds = append(h.updateKinds, "plan")
44 }
45}
46
47func (h *panelHandler) HandlePermissionRequest(_ context.Context, req acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
48 h.mu.Lock()
49 defer h.mu.Unlock()
50 h.permissions++
51 _ = req
52 return acp.RequestPermissionResponse{Outcome: acp.NewRequestPermissionOutcomeSelected(acp.PermissionOptionId(h.optionId))}, nil
53}
54
55func (h *panelHandler) kinds() map[string]int {
56 h.mu.Lock()
57 defer h.mu.Unlock()
58 counts := map[string]int{}
59 for _, k := range h.updateKinds {
60 counts[k]++
61 }
62 return counts
63}
64
65// connectPanel wires the mock agent to ori's real client code over pipes.
66func connectPanel(t *testing.T, handler agent.Handler) *agent.Session {
67 t.Helper()
68 clientToAgentR, clientToAgentW := io.Pipe()
69 agentToClientR, agentToClientW := io.Pipe()
70
71 mock := mockagent.New()
72 go func() { _ = mock.Run(clientToAgentR, agentToClientW) }()
73
74 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
75 t.Cleanup(cancel)
76 session, err := agent.Connect(ctx, clientToAgentW, agentToClientR, agent.Options{
77 Cwd: t.TempDir(),
78 Handler: handler,
79 })
80 if err != nil {
81 t.Fatalf("Connect to mock agent failed: %v", err)
82 }
83 return session
84}
85
86func TestScenarioStreamsEveryPanelFeature(t *testing.T) {
87 handler := &panelHandler{optionId: "allow-once"}
88 session := connectPanel(t, handler)
89
90 stop, err := session.PromptText(context.Background(), "show me everything")
91 if err != nil {
92 t.Fatalf("PromptText returned an error: %v", err)
93 }
94 if stop != acp.StopReasonEndTurn {
95 t.Errorf("stop reason = %q, want end_turn", stop)
96 }
97
98 counts := handler.kinds()
99 for kind, want := range map[string]int{"thought": 1, "plan": 2, "tool_call": 2, "tool_call_update": 2} {
100 if counts[kind] < want {
101 t.Errorf("streamed %d %q updates, want at least %d (all: %v)", counts[kind], kind, want, counts)
102 }
103 }
104 if handler.permissions != 1 {
105 t.Errorf("permission requests = %d, want 1", handler.permissions)
106 }
107 if all := strings.Join(handler.texts, ""); !strings.Contains(all, "show me everything") {
108 t.Errorf("final message %q does not echo the prompt", all)
109 }
110}
111
112func TestScenarioStopsOnRejectedPermission(t *testing.T) {
113 handler := &panelHandler{optionId: "reject-once"}
114 session := connectPanel(t, handler)
115
116 stop, err := session.PromptText(context.Background(), "try to write")
117 if err != nil {
118 t.Fatalf("PromptText returned an error: %v", err)
119 }
120 if stop != acp.StopReasonRefusal {
121 t.Errorf("stop reason = %q, want refusal after a rejected permission", stop)
122 }
123 if counts := handler.kinds(); counts["tool_call"] != 1 {
124 t.Errorf("tool calls = %d, want only the read call (no edit after rejection)", counts["tool_call"])
125 }
126}