返回 DeepSeek-Reasonix
provider_fail_fast_test.go
根目录 / internal / agent / provider_fail_fast_test.go
1 package agent
2
3 import (
4 "context"
5 "errors"
6 "net"
7 "syscall"
8 "testing"
9 "time"
10
11 "reasonix/internal/agent/testutil"
12 "reasonix/internal/event"
13 "reasonix/internal/provider"
14 )
15
16 func TestProviderFailureReturnsWithoutWaitingOrRetrying(t *testing.T) {
17 for _, role := range []string{"main", "subagent", "planner"} {
18 for _, cause := range []error{
19 &net.OpError{Op: "dial", Net: "tcp", Err: syscall.ECONNREFUSED},
20 &provider.APIError{Status: 503},
21 &provider.APIError{Status: 429, RetryAfter: time.Hour},
22 &provider.APIError{Status: 409},
23 } {
24 t.Run(role+"/"+cause.Error(), func(t *testing.T) {
25 p := testutil.NewMock("unavailable", testutil.Turn{StreamError: cause}, testutil.Turn{Text: "must not retry"})
26 sink := &recordSink{}
27 ctx, cancel := context.WithCancel(withNoClosedLoop(t.Context()))
28 defer cancel()
29 a := New(p, echoRegistry(), NewSession(""), Options{}, event.FuncSink(func(e event.Event) {
30 sink.Emit(e)
31 if e.Kind == event.Retrying {
32 cancel() // A regression must fail without actually waiting for Retry-After.
33 }
34 }))
35 if role == "subagent" {
36 ctx = WithSubagentDepth(ctx, 1)
37 }
38 if role == "planner" {
39 ctx = context.WithValue(ctx, turnContextRoleKey{}, turnContextPlanner)
40 }
41 if err := a.Run(ctx, "go"); !errors.Is(err, cause) {
42 t.Fatalf("lost original failure: %v", err)
43 }
44 if p.CallCount() != 1 || len(sink.kinds(event.Retrying)) != 0 {
45 t.Fatalf("calls=%d retries=%+v", p.CallCount(), sink.kinds(event.Retrying))
46 }
47 })
48 }
49 }
50 }
51
51 lines GO