package eino import ( "context" "strings" "testing" "time" "github.com/cloudwego/eino/schema" ) // 专家超时:卡死的专家应在 timeout 附近作为"观察"跳过(err=nil,不中断协调), // 而非无限阻塞 lead。 func TestSpecialist_TimeoutSkips(t *testing.T) { o := newOrch(&fakeLLM{}, kbTool(nil), &fakeSink{}, &fakeExec{}) st := &specialistTool{ name: "分析专家", tr: o.tracer("t1"), timeout: 50 * time.Millisecond, info: &schema.ToolInfo{Name: "分析专家"}, run: func(ctx context.Context, _ string) (string, error) { <-ctx.Done() // 模拟卡死:直到超时被取消 return "", ctx.Err() }, } start := time.Now() out, err := st.InvokableRun(context.Background(), `{"brief":"干活"}`) elapsed := time.Since(start) if err != nil { t.Fatalf("专家超时应作为观察返回、err=nil(不中断协调),got err=%v", err) } if !strings.Contains(out, "超时") { t.Errorf("应返回超时观察,got %q", out) } if elapsed > 2*time.Second { t.Errorf("应在超时(50ms)附近返回,实际耗时 %v(疑似未生效)", elapsed) } } // 正常专家不受超时影响。 func TestSpecialist_NormalWithinTimeout(t *testing.T) { o := newOrch(&fakeLLM{}, kbTool(nil), &fakeSink{}, &fakeExec{}) st := &specialistTool{ name: "快专家", tr: o.tracer("t1"), timeout: time.Second, info: &schema.ToolInfo{Name: "快专家"}, run: func(_ context.Context, brief string) (string, error) { return "结论:" + brief, nil }, } out, err := st.InvokableRun(context.Background(), `{"brief":"分析X"}`) if err != nil || !strings.Contains(out, "结论:分析X") { t.Fatalf("正常专家应返回结论,got out=%q err=%v", out, err) } }