Files
MailUI4Agents/server/internal/repo/nearesthuman_test.go

120 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repo
import (
"context"
"testing"
"github.com/agentmail/gateway/internal/db"
)
// 权限询问的决策者必须是人Agent 收不到 SendToUser桥的 await 永不 resolve。
// 生产事故pi 把任务派给自己的另一条会话 → 那条会话要跑 bash → 权限邮件发给 "pi"
// → pi 不是人类用户 → 整条会话永久阻塞。
//
// 修法是顺着会话的邮件链上溯找最近的人类 —— 派活的人才是该点头的人。
func TestNearestHumanInThread(t *testing.T) {
setupTestDB(t)
ctx := context.Background()
mustExec(t, ctx, `INSERT INTO users (username, display_name, password_hash, role)
VALUES ('alice','Alice','x','user')`)
for _, a := range []string{"opencode", "dsh", "pi"} {
if err := CreateOrUpdateAgent(ctx, a, "s", "test", nil); err != nil {
t.Fatalf("注册 %s: %v", a, err)
}
}
t.Run("沿链上溯找到派活的人", func(t *testing.T) {
sid, err := CreateSession(ctx, nil, "opencode", "任务链", "/home")
if err != nil {
t.Fatalf("建会话: %v", err)
}
// alice → opencode → dshdsh 触发权限询问
m1, err := CreateMail(ctx, sid, nil, "alice", "", "opencode", "", "任务", "请帮忙", nil)
if err != nil {
t.Fatalf("封1: %v", err)
}
m2, err := CreateMail(ctx, sid, &m1, "opencode", "", "dsh", "", "转派", "你来看", nil)
if err != nil {
t.Fatalf("封2: %v", err)
}
if _, err := CreateMail(ctx, sid, &m2, "dsh", "", "opencode", "", "进展", "做了一半", nil); err != nil {
t.Fatalf("封3: %v", err)
}
for _, agent := range []string{"dsh", "opencode"} {
got, err := NearestHumanInThread(ctx, sid, agent)
if err != nil {
t.Fatalf("%s: %v", agent, err)
}
if got != "alice" {
t.Errorf("%s 触发权限时应路由到 alice得到 %q", agent, got)
}
}
})
t.Run("全 Agent 链返回空串", func(t *testing.T) {
// 没有人类参与的链条:调用方据此拒绝请求,而不是转给一个
// 对上下文一无所知的管理员。
sid, err := CreateSession(ctx, nil, "opencode", "纯 Agent", "/tmp")
if err != nil {
t.Fatalf("建会话: %v", err)
}
m1, err := CreateMail(ctx, sid, nil, "opencode", "", "dsh", "", "干活", "go", nil)
if err != nil {
t.Fatalf("封1: %v", err)
}
if _, err := CreateMail(ctx, sid, &m1, "dsh", "", "opencode", "", "好", "ok", nil); err != nil {
t.Fatalf("封2: %v", err)
}
got, err := NearestHumanInThread(ctx, sid, "dsh")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != "" {
t.Errorf("链上没有人类时应返回空串,得到 %q", got)
}
})
t.Run("skipAgent 是自己时不会把自己当人", func(t *testing.T) {
// pi 给自己的另一条会话派活正是事故场景:链上只有 pi 一个名字。
sid, err := CreateSession(ctx, nil, "pi", "自派", "/home")
if err != nil {
t.Fatalf("建会话: %v", err)
}
if _, err := CreateMail(ctx, sid, nil, "pi", "", "pi", "", "拆任务", "自己干", nil); err != nil {
t.Fatalf("封1: %v", err)
}
got, err := NearestHumanInThread(ctx, sid, "pi")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != "" {
t.Errorf("自派链上没有人类,应返回空串,得到 %q", got)
}
})
t.Run("空会话不报错", func(t *testing.T) {
sid, err := CreateSession(ctx, nil, "opencode", "空", "/tmp")
if err != nil {
t.Fatalf("建会话: %v", err)
}
got, err := NearestHumanInThread(ctx, sid, "opencode")
if err != nil {
t.Fatalf("空会话应返回空串而非报错,得到 err=%v", err)
}
if got != "" {
t.Errorf("空会话应返回空串,得到 %q", got)
}
})
}
func mustExec(t *testing.T, ctx context.Context, q string, args ...any) {
t.Helper()
if _, err := db.DB.ExecContext(ctx, q, args...); err != nil {
t.Fatalf("exec %s: %v", q, err)
}
}