134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"reasonix/internal/bot"
|
|
"reasonix/internal/event"
|
|
)
|
|
|
|
type desktopForwardTestAdapter struct {
|
|
platform bot.Platform
|
|
name string
|
|
messages chan bot.InboundMessage
|
|
entered chan struct{}
|
|
release chan struct{}
|
|
sent chan bot.OutboundMessage
|
|
once sync.Once
|
|
}
|
|
|
|
func newDesktopForwardTestAdapter() *desktopForwardTestAdapter {
|
|
return &desktopForwardTestAdapter{
|
|
platform: bot.PlatformFeishu,
|
|
name: "forward-test",
|
|
messages: make(chan bot.InboundMessage),
|
|
entered: make(chan struct{}),
|
|
release: make(chan struct{}),
|
|
sent: make(chan bot.OutboundMessage, 8),
|
|
}
|
|
}
|
|
|
|
func (a *desktopForwardTestAdapter) Platform() bot.Platform { return a.platform }
|
|
func (a *desktopForwardTestAdapter) Name() string { return a.name }
|
|
func (a *desktopForwardTestAdapter) Start(context.Context) error {
|
|
return nil
|
|
}
|
|
func (a *desktopForwardTestAdapter) Stop() error { return nil }
|
|
func (a *desktopForwardTestAdapter) SendTyping(context.Context, string) error {
|
|
return nil
|
|
}
|
|
func (a *desktopForwardTestAdapter) Messages() <-chan bot.InboundMessage {
|
|
return a.messages
|
|
}
|
|
func (a *desktopForwardTestAdapter) Send(ctx context.Context, msg bot.OutboundMessage) (bot.SendResult, error) {
|
|
a.once.Do(func() { close(a.entered) })
|
|
select {
|
|
case <-a.release:
|
|
case <-ctx.Done():
|
|
return bot.SendResult{}, ctx.Err()
|
|
}
|
|
a.sent <- msg
|
|
return bot.SendResult{MessageID: "sent"}, nil
|
|
}
|
|
|
|
func newDesktopForwardTestRuntime(adapter bot.Adapter) *desktopBotRuntime {
|
|
gw := bot.NewGatewayWithAdapterBindings(bot.GatewayConfig{}, []bot.AdapterBinding{{
|
|
ID: "feishu-lark",
|
|
Domain: "lark",
|
|
Platform: bot.PlatformFeishu,
|
|
Adapter: adapter,
|
|
}}, slog.New(slog.NewTextHandler(io.Discard, nil)))
|
|
return &desktopBotRuntime{gw: gw}
|
|
}
|
|
|
|
func TestBotEventForwarderDoesNotBlockEventEmissionOnSlowSend(t *testing.T) {
|
|
adapter := newDesktopForwardTestAdapter()
|
|
forwarder := newBotEventForwarder(newDesktopForwardTestRuntime(adapter), []botForwardTarget{{
|
|
ConnID: "feishu-lark",
|
|
Domain: "lark",
|
|
ChatID: "oc-group-1",
|
|
ChatType: bot.ChatGroup,
|
|
}})
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
forwarder.Emit(event.Event{Kind: event.Text, Text: strings.Repeat("x", 400)})
|
|
close(done)
|
|
}()
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(500 * time.Millisecond):
|
|
t.Fatal("Emit blocked behind slow bot send")
|
|
}
|
|
select {
|
|
case <-adapter.entered:
|
|
case <-time.After(500 * time.Millisecond):
|
|
t.Fatal("adapter send did not start")
|
|
}
|
|
|
|
forwarder.Close()
|
|
close(adapter.release)
|
|
select {
|
|
case <-adapter.sent:
|
|
case <-time.After(500 * time.Millisecond):
|
|
t.Fatal("queued bot message was not sent after adapter release")
|
|
}
|
|
}
|
|
|
|
func TestBotEventForwarderApprovalNoticeDoesNotExposeReplyID(t *testing.T) {
|
|
adapter := newDesktopForwardTestAdapter()
|
|
close(adapter.release)
|
|
forwarder := newBotEventForwarder(newDesktopForwardTestRuntime(adapter), []botForwardTarget{{
|
|
ConnID: "feishu-lark",
|
|
Domain: "lark",
|
|
ChatID: "oc-group-1",
|
|
ChatType: bot.ChatGroup,
|
|
}})
|
|
|
|
forwarder.Emit(event.Event{Kind: event.ApprovalRequest, Approval: event.Approval{
|
|
ID: "approval-1",
|
|
Tool: "shell",
|
|
Subject: "run command",
|
|
}})
|
|
forwarder.Close()
|
|
|
|
select {
|
|
case msg := <-adapter.sent:
|
|
if strings.Contains(msg.Text, "approval-1") || strings.Contains(msg.Text, "/approve") {
|
|
t.Fatalf("approval notice exposed unusable reply routing: %q", msg.Text)
|
|
}
|
|
if !strings.Contains(msg.Text, "桌面") {
|
|
t.Fatalf("approval notice = %q, want desktop guidance", msg.Text)
|
|
}
|
|
case <-time.After(500 * time.Millisecond):
|
|
t.Fatal("approval notice was not sent")
|
|
}
|
|
}
|