返回 DeepSeek-Reasonix
session_checkpoint_test.go
根目录 / internal / agent / session_checkpoint_test.go
1 package agent
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "sync"
8 "testing"
9
10 "reasonix/internal/event"
11 "reasonix/internal/provider"
12 "reasonix/internal/tool"
13 )
14
15 type recordingCheckpointer struct {
16 mu sync.Mutex
17 boundaries []SessionCheckpointBoundary
18 fail SessionCheckpointBoundary
19 }
20
21 func (c *recordingCheckpointer) CheckpointSession(_ context.Context, boundary SessionCheckpointBoundary) error {
22 c.mu.Lock()
23 defer c.mu.Unlock()
24 c.boundaries = append(c.boundaries, boundary)
25 if boundary == c.fail {
26 return errors.New("checkpoint unavailable")
27 }
28 return nil
29 }
30
31 type checkpointTool struct{ calls int }
32
33 func (t *checkpointTool) Name() string { return "checkpoint_tool" }
34 func (t *checkpointTool) Description() string { return "test" }
35 func (t *checkpointTool) Schema() json.RawMessage { return json.RawMessage(`{"type":"object"}`) }
36 func (t *checkpointTool) ReadOnly() bool { return true }
37 func (t *checkpointTool) Execute(context.Context, json.RawMessage) (string, error) {
38 t.calls++
39 return "ok", nil
40 }
41
42 func TestModelCheckpointFailurePreventsProviderDispatch(t *testing.T) {
43 providerStub := &scriptedProvider{name: "model", turns: [][]provider.Chunk{{{Type: provider.ChunkDone}}}}
44 checkpointer := &recordingCheckpointer{fail: CheckpointBeforeModel}
45 agent := New(providerStub, tool.NewRegistry(), NewSession("sys"), Options{SessionCheckpointer: checkpointer}, event.Discard)
46
47 if err := agent.Run(t.Context(), "hello"); err == nil || providerStub.call != 0 {
48 t.Fatalf("Run error/provider calls = %v/%d", err, providerStub.call)
49 }
50 }
51
52 func TestToolCheckpointFailurePreventsToolBody(t *testing.T) {
53 target := &checkpointTool{}
54 registry := tool.NewRegistry()
55 registry.Add(target)
56 providerStub := &scriptedProvider{name: "model", turns: [][]provider.Chunk{{
57 toolCallChunk("call-1", target.Name(), `{}`),
58 {Type: provider.ChunkDone},
59 }, {
60 {Type: provider.ChunkText, Text: "tool did not run"},
61 {Type: provider.ChunkDone},
62 }}}
63 checkpointer := &recordingCheckpointer{fail: CheckpointBeforeTopTool}
64 agent := New(providerStub, registry, NewSession("sys"), Options{SessionCheckpointer: checkpointer}, event.Discard)
65
66 if err := agent.Run(t.Context(), "run it"); err != nil {
67 t.Fatal(err)
68 }
69 if target.calls != 0 {
70 t.Fatalf("tool body ran %d times", target.calls)
71 }
72 if providerStub.call != 2 {
73 t.Fatalf("provider calls = %d, want 2", providerStub.call)
74 }
75 }
76
77 var _ tool.Tool = (*checkpointTool)(nil)
78
78 lines GO