返回 DeepSeek-Reasonix
mock_provider_test.go
根目录 / internal / agent / testutil / mock_provider_test.go
1 package testutil
2
3 import (
4 "context"
5 "errors"
6 "testing"
7 "time"
8
9 "reasonix/internal/provider"
10 )
11
12 func TestMockProviderStreamHonorsCanceledContext(t *testing.T) {
13 ctx, cancel := context.WithCancel(context.Background())
14 cancel()
15
16 mp := NewMock("mock", Turn{Text: "hello"})
17 ch, err := mp.Stream(ctx, provider.Request{})
18 if !errors.Is(err, context.Canceled) {
19 t.Fatalf("Stream error = %v, want context.Canceled", err)
20 }
21 if ch != nil {
22 t.Fatal("Stream returned a channel for canceled context")
23 }
24 if got := mp.CallCount(); got != 0 {
25 t.Fatalf("CallCount = %d, want 0", got)
26 }
27 }
28
29 func TestMockProviderStreamStopsOnContextCancellation(t *testing.T) {
30 ctx, cancel := context.WithCancel(context.Background())
31 mp := NewMock("mock", Turn{
32 Text: "first",
33 ToolCalls: []provider.ToolCall{
34 {ID: "call-1", Name: "noop", Arguments: `{}`},
35 },
36 })
37
38 ch, err := mp.Stream(ctx, provider.Request{})
39 if err != nil {
40 t.Fatalf("Stream: %v", err)
41 }
42 if got := (<-ch).Text; got != "first" {
43 t.Fatalf("first chunk text = %q, want first", got)
44 }
45 cancel()
46
47 // Cancellation can race a send that is already committed: at most one
48 // data chunk may arrive after the cancel. The stream must then deliver a
49 // cancellation error and close.
50 inFlight := 0
51 for {
52 var chunk provider.Chunk
53 var ok bool
54 select {
55 case chunk, ok = <-ch:
56 case <-time.After(5 * time.Second):
57 t.Fatal("stream did not terminate after cancellation")
58 }
59 if !ok {
60 t.Fatal("stream closed without returning cancellation error")
61 }
62 if chunk.Type == provider.ChunkError {
63 if !errors.Is(chunk.Err, context.Canceled) {
64 t.Fatalf("cancellation error = %v, want context.Canceled", chunk.Err)
65 }
66 break
67 }
68 inFlight++
69 if inFlight > 1 {
70 t.Fatalf("stream kept sending data after cancellation (%d chunks)", inFlight)
71 }
72 }
73 select {
74 case _, ok := <-ch:
75 if ok {
76 t.Fatal("stream stayed open after cancellation error")
77 }
78 case <-time.After(5 * time.Second):
79 t.Fatal("stream did not close after the cancellation error")
80 }
81 }
82
82 lines GO