返回 DeepSeek-Reasonix
native_session_test.go
根目录 / internal / control / native_session_test.go
1 package control
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "os"
8 "path/filepath"
9 "testing"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/agent/testutil"
13 "reasonix/internal/event"
14 "reasonix/internal/provider"
15 "reasonix/internal/session"
16 "reasonix/internal/tool"
17 "reasonix/internal/transcript"
18 )
19
20 func TestNativeResumePrefersCompletedCanonicalCutover(t *testing.T) {
21 root := t.TempDir()
22 path := filepath.Join(root, "old.jsonl")
23 if err := os.WriteFile(path, []byte("{\"role\":\"user\",\"content\":\"stale\"}\n"), 0o600); err != nil {
24 t.Fatal(err)
25 }
26 service, err := session.NewService("local", session.NewFilesystemPersistence(filepath.Join(root, "canonical")))
27 if err != nil {
28 t.Fatal(err)
29 }
30 runtime, err := service.Create(t.Context(), session.CreateOptions{SessionID: agent.BranchID(path)})
31 if err != nil {
32 t.Fatal(err)
33 }
34 payload, _ := json.Marshal(map[string]any{"message": provider.Message{ID: "newer", Role: provider.RoleUser, Content: "new canonical work"}})
35 if _, err := runtime.Session().Append(t.Context(), session.Batch{OperationID: "newer", Events: []session.Event{{Kind: "message/complete", Payload: payload}}}); err != nil {
36 t.Fatal(err)
37 }
38 exec := agent.New(nil, tool.NewRegistry(), agent.NewSession(""), agent.Options{}, event.Discard)
39 c := newOwnedTestController(t, Options{Executor: exec, Sink: event.Discard, SessionService: service, NativeLegacySession: true})
40 loaded, err := agent.LoadSession(path)
41 if err != nil {
42 t.Fatal(err)
43 }
44 if err := c.ResumeNativeSession(loaded, path); err != nil {
45 t.Fatal(err)
46 }
47 if !c.UsesExclusiveSession() || len(c.History()) != 1 || c.History()[0].Content != "new canonical work" {
48 t.Fatalf("resumed stale source: %+v", c.History())
49 }
50 }
51
52 func TestNativeLegacyResumeContinueAndNew(t *testing.T) {
53 root := t.TempDir()
54 path := filepath.Join(root, "sessions", "old.jsonl")
55 if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
56 t.Fatal(err)
57 }
58 original := []byte("{\"role\":\"system\",\"content\":\"system\"}\n{\"role\":\"user\",\"content\":\"old question\"}\n{\"role\":\"assistant\",\"content\":\"old answer\"}\n")
59 if err := os.WriteFile(path, original, 0o600); err != nil {
60 t.Fatal(err)
61 }
62 loaded, err := agent.LoadSession(path)
63 if err != nil {
64 t.Fatal(err)
65 }
66 service, err := session.NewService("local", session.NewFilesystemPersistence(filepath.Join(root, "canonical")))
67 if err != nil {
68 t.Fatal(err)
69 }
70 exec := agent.New(testutil.NewMock("test", testutil.Turn{Text: "continued answer"}), tool.NewRegistry(), agent.NewSession("system"), agent.Options{}, event.Discard)
71 c := newOwnedTestController(t, Options{Runner: exec, Executor: exec, Sink: event.Discard, SystemPrompt: "system", SessionDir: filepath.Dir(path), SessionService: service, NativeLegacySession: true})
72 c.Resume(loaded, path)
73 if got, err := os.ReadFile(path); err != nil || !bytes.Equal(got, original) {
74 t.Fatalf("resume rewrote source: %v", err)
75 }
76 if _, err := os.Stat(sessionDirectory(path)); !os.IsNotExist(err) {
77 t.Fatalf("resume created canonical copy: %v", err)
78 }
79 follow, err := c.TranscriptFollow(t.Context(), transcript.FollowRequest{})
80 if err != nil {
81 t.Fatal(err)
82 }
83 if err := c.RunTurn(t.Context(), "continue"); err != nil {
84 t.Fatal(err)
85 }
86 if err := c.Snapshot(); err != nil {
87 t.Fatal(err)
88 }
89 next, err := c.TranscriptFollow(t.Context(), transcript.FollowRequest{Subscription: follow.Subscription, AfterRevision: follow.Snapshot.ProjectionRevision})
90 if err != nil || len(next.Changes) == 0 {
91 t.Fatalf("legacy follow lost live changes: %+v %v", next, err)
92 }
93 _, _ = c.TranscriptFollow(context.Background(), transcript.FollowRequest{Subscription: follow.Subscription, Close: true})
94 reopened, err := agent.LoadSession(path)
95 if err != nil {
96 t.Fatal(err)
97 }
98 messages := reopened.Snapshot()
99 if last := messages[len(messages)-1]; last.Role != provider.RoleAssistant || last.Content != "continued answer" {
100 t.Fatalf("continued message missing: %+v", messages)
101 }
102 if _, err := os.Stat(sessionDirectory(path)); !os.IsNotExist(err) {
103 t.Fatalf("continuation created canonical copy: %v", err)
104 }
105 if err := c.NewSession(); err != nil {
106 t.Fatal(err)
107 }
108 if !c.UsesExclusiveSession() || c.NativeLegacySession() {
109 t.Fatal("new session retained legacy backend")
110 }
111 if _, bound := c.SessionRef(); !bound {
112 t.Fatal("new session has no canonical identity")
113 }
114 }
115
116 func TestNativeDAGHeadIsNotReplacedByAnotherHeadsStore(t *testing.T) {
117 root := t.TempDir()
118 path := filepath.Join(root, "branches.jsonl")
119 loaded := agent.NewSession("system")
120 loaded.Add(provider.Message{Role: provider.RoleUser, Content: "shared"})
121 if err := loaded.Save(path); err != nil {
122 t.Fatal(err)
123 }
124 if _, err := loaded.ForkHead(path, loaded.Snapshot()[1].ID, agent.HeadKindFork, "alternate"); err != nil {
125 t.Fatal(err)
126 }
127 loaded.Add(provider.Message{Role: provider.RoleAssistant, Content: "alternate answer"})
128 if err := loaded.Save(path); err != nil {
129 t.Fatal(err)
130 }
131 service, err := session.NewService("local", session.NewFilesystemPersistence(filepath.Join(root, "canonical")))
132 if err != nil {
133 t.Fatal(err)
134 }
135 if _, err := service.Create(t.Context(), session.CreateOptions{SessionID: agent.BranchID(path)}); err != nil {
136 t.Fatal(err)
137 }
138 exec := agent.New(nil, tool.NewRegistry(), agent.NewSession(""), agent.Options{}, event.Discard)
139 c := newOwnedTestController(t, Options{Executor: exec, Sink: event.Discard, SessionService: service, NativeLegacySession: true})
140 if err := c.ResumeNativeSession(loaded, path); err != nil {
141 t.Fatal(err)
142 }
143 if !c.NativeLegacySession() || c.History()[2].Content != "alternate answer" {
144 t.Fatal("a path alias replaced the requested DAG head")
145 }
146 }
147
147 lines GO