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
  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
package mockagent_test

import (
	"context"
	"io"
	"strings"
	"sync"
	"testing"
	"time"

	acp "github.com/coder/acp-go-sdk"

	"github.com/bots-garden/ori/internal/agent"
	"github.com/bots-garden/ori/internal/mockagent"
)

// panelHandler records everything the mock streams, and answers the
// permission request with a fixed choice.
type panelHandler struct {
	mu          sync.Mutex
	optionId    string
	texts       []string
	updateKinds []string
	permissions int
}

func (h *panelHandler) HandleSessionUpdate(_ context.Context, n acp.SessionNotification) {
	h.mu.Lock()
	defer h.mu.Unlock()
	switch {
	case n.Update.AgentMessageChunk != nil:
		h.updateKinds = append(h.updateKinds, "message")
		if t := n.Update.AgentMessageChunk.Content.Text; t != nil {
			h.texts = append(h.texts, t.Text)
		}
	case n.Update.AgentThoughtChunk != nil:
		h.updateKinds = append(h.updateKinds, "thought")
	case n.Update.ToolCall != nil:
		h.updateKinds = append(h.updateKinds, "tool_call")
	case n.Update.ToolCallUpdate != nil:
		h.updateKinds = append(h.updateKinds, "tool_call_update")
	case n.Update.Plan != nil:
		h.updateKinds = append(h.updateKinds, "plan")
	}
}

func (h *panelHandler) HandlePermissionRequest(_ context.Context, req acp.RequestPermissionRequest) (acp.RequestPermissionResponse, error) {
	h.mu.Lock()
	defer h.mu.Unlock()
	h.permissions++
	_ = req
	return acp.RequestPermissionResponse{Outcome: acp.NewRequestPermissionOutcomeSelected(acp.PermissionOptionId(h.optionId))}, nil
}

func (h *panelHandler) kinds() map[string]int {
	h.mu.Lock()
	defer h.mu.Unlock()
	counts := map[string]int{}
	for _, k := range h.updateKinds {
		counts[k]++
	}
	return counts
}

// connectPanel wires the mock agent to ori's real client code over pipes.
func connectPanel(t *testing.T, handler agent.Handler) *agent.Session {
	t.Helper()
	clientToAgentR, clientToAgentW := io.Pipe()
	agentToClientR, agentToClientW := io.Pipe()

	mock := mockagent.New()
	go func() { _ = mock.Run(clientToAgentR, agentToClientW) }()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	t.Cleanup(cancel)
	session, err := agent.Connect(ctx, clientToAgentW, agentToClientR, agent.Options{
		Cwd:     t.TempDir(),
		Handler: handler,
	})
	if err != nil {
		t.Fatalf("Connect to mock agent failed: %v", err)
	}
	return session
}

func TestScenarioStreamsEveryPanelFeature(t *testing.T) {
	handler := &panelHandler{optionId: "allow-once"}
	session := connectPanel(t, handler)

	stop, err := session.PromptText(context.Background(), "show me everything")
	if err != nil {
		t.Fatalf("PromptText returned an error: %v", err)
	}
	if stop != acp.StopReasonEndTurn {
		t.Errorf("stop reason = %q, want end_turn", stop)
	}

	counts := handler.kinds()
	for kind, want := range map[string]int{"thought": 1, "plan": 2, "tool_call": 2, "tool_call_update": 2} {
		if counts[kind] < want {
			t.Errorf("streamed %d %q updates, want at least %d (all: %v)", counts[kind], kind, want, counts)
		}
	}
	if handler.permissions != 1 {
		t.Errorf("permission requests = %d, want 1", handler.permissions)
	}
	if all := strings.Join(handler.texts, ""); !strings.Contains(all, "show me everything") {
		t.Errorf("final message %q does not echo the prompt", all)
	}
}

func TestScenarioStopsOnRejectedPermission(t *testing.T) {
	handler := &panelHandler{optionId: "reject-once"}
	session := connectPanel(t, handler)

	stop, err := session.PromptText(context.Background(), "try to write")
	if err != nil {
		t.Fatalf("PromptText returned an error: %v", err)
	}
	if stop != acp.StopReasonRefusal {
		t.Errorf("stop reason = %q, want refusal after a rejected permission", stop)
	}
	if counts := handler.kinds(); counts["tool_call"] != 1 {
		t.Errorf("tool calls = %d, want only the read call (no edit after rejection)", counts["tool_call"])
	}
}