返回 DeepSeek-Reasonix
orphan_recovery_test.go
根目录 / internal / turnevent / orphan_recovery_test.go
1 package turnevent
2
3 import (
4 "path/filepath"
5 "testing"
6
7 "reasonix/internal/event"
8 "reasonix/internal/provider"
9 )
10
11 func TestOrphanRecoveryDistinguishesPendingAndStartedAndIsIdempotent(t *testing.T) {
12 for _, started := range []bool{false, true} {
13 t.Run(map[bool]string{false: "pending", true: "started"}[started], func(t *testing.T) {
14 path := filepath.Join(t.TempDir(), "session.jsonl")
15 l, err := Open(path, "session")
16 if err != nil {
17 t.Fatal(err)
18 }
19 if _, err = l.Begin(); err != nil {
20 t.Fatal(err)
21 }
22 if _, _, err = l.Append(event.Event{Kind: event.ToolDispatch, Tool: event.Tool{ID: "call", Name: "write", RunState: provider.ToolRunPending}}, event.TurnInProgress); err != nil {
23 t.Fatal(err)
24 }
25 if started {
26 if _, _, err = l.Append(event.Event{Kind: event.ToolStarted, Tool: event.Tool{ID: "call", Name: "write", RunState: provider.ToolRunStarted, AttemptID: "attempt"}}, event.TurnInProgress); err != nil {
27 t.Fatal(err)
28 }
29 }
30 if err = l.Close(); err != nil {
31 t.Fatal(err)
32 }
33 l, err = Open(path, "session")
34 if err != nil {
35 t.Fatal(err)
36 }
37 records, err := l.EventsAfter(0)
38 if err != nil {
39 t.Fatal(err)
40 }
41 var state provider.ToolRunState
42 for _, r := range records {
43 if r.Kind == "tool_result" {
44 state = r.Event.Tool.RunState
45 }
46 }
47 want := provider.ToolRunCancelled
48 if started {
49 want = provider.ToolRunUnknown
50 }
51 if state != want {
52 t.Fatalf("recovered state=%s want=%s", state, want)
53 }
54 count := len(records)
55 _ = l.Close()
56 l, err = Open(path, "session")
57 if err != nil {
58 t.Fatal(err)
59 }
60 defer l.Close()
61 again, err := l.EventsAfter(0)
62 if err != nil || len(again) != count {
63 t.Fatalf("second reopen appended recovery events: %d -> %d err=%v", count, len(again), err)
64 }
65 })
66 }
67 }
68
68 lines GO