返回 DeepSeek-Reasonix
catalog_preview_test.go
根目录 / internal / session / catalog_preview_test.go
1 package session
2
3 import (
4 "context"
5 "encoding/json"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 bolt "go.etcd.io/bbolt"
12
13 "reasonix/internal/provider"
14 )
15
16 func TestCatalogPreviewUsesAuthoredTextBeforeTruncation(t *testing.T) {
17 const task = "修复左侧会话标题"
18 wrapped := "<response-language>" + strings.Repeat("internal preference ", 40) + "</response-language>\n<capability-route version=\"1\">tools</capability-route>\n" + task
19 for _, tc := range []struct{ name, content, raw, want string }{
20 {"legacy", wrapped, "", task},
21 {"raw", wrapped, "用户实际输入", "用户实际输入"},
22 {"raw markup is authored", wrapped, "<response-language>literal example</response-language>", "<response-language>literal example</response-language>"},
23 {"raw only", "", task, task},
24 } {
25 t.Run(tc.name, func(t *testing.T) {
26 message := provider.Message{Role: provider.RoleUser, Content: tc.content, RawContent: tc.raw, Origin: provider.MessageOriginUser}
27 if got := messagePreview(message); got != tc.want {
28 t.Fatalf("preview = %q, want %q", got, tc.want)
29 }
30 metadata := metadataFromProjection(Manifest{}, 0, Projection{Title: "My custom title", Messages: []provider.Message{
31 {Role: provider.RoleUser, Origin: provider.MessageOriginHost, Content: "host context"}, message,
32 }})
33 if metadata.Title != "My custom title" || metadata.Preview != tc.want {
34 t.Fatalf("metadata = %+v", metadata)
35 }
36 })
37 }
38 }
39
40 func TestCatalogPreviewSurvivesColdReopen(t *testing.T) {
41 dir := filepath.Join(t.TempDir(), "sessions", "preview")
42 s, err := CreateWithOptions(dir, "preview", OpenOptions{ExternalHistory: true})
43 if err != nil {
44 t.Fatal(err)
45 }
46 for i, message := range []provider.Message{
47 {ID: "host", Role: provider.RoleUser, Origin: provider.MessageOriginHost, Content: "host context"},
48 {ID: "user", Role: provider.RoleUser, Content: "<reasoning-language>" + strings.Repeat("internal", 80) + "</reasoning-language>\n实际请求"},
49 } {
50 payload, _ := json.Marshal(map[string]any{"message": message})
51 if _, err := s.Append(t.Context(), Batch{OperationID: string(rune('a' + i)), Events: []Event{{Kind: "message/complete", Payload: payload}}}); err != nil {
52 t.Fatal(err)
53 }
54 }
55 if _, err := s.Flush(t.Context()); err != nil {
56 t.Fatal(err)
57 }
58 if got := s.CatalogMetadata().Preview; got != "实际请求" {
59 t.Fatalf("live preview = %q", got)
60 }
61 if err := s.Close(t.Context()); err != nil {
62 t.Fatal(err)
63 }
64 // Simulate a pre-fix checkpoint: its truncated prefix has lost the task.
65 db, err := bolt.Open(filepath.Join(recoveryCacheDir(dir), recoveryDBName), 0o600, &bolt.Options{Timeout: time.Second})
66 if err != nil {
67 t.Fatal(err)
68 }
69 err = db.Update(func(tx *bolt.Tx) error {
70 bucket := tx.Bucket(recoveryCheckpointBucket)
71 for _, key := range [][]byte{recoveryCurrentKey, recoveryPreviousKey} {
72 if data := bucket.Get(key); data != nil {
73 var checkpoint recoveryCheckpoint
74 if err := decodeRecoveryValue(data, &checkpoint); err != nil {
75 return err
76 }
77 checkpoint.ProjectionVersion = 2
78 checkpoint.CatalogPreview = "<reasoning-language>internal..."
79 encoded, err := encodeRecoveryValue(checkpoint)
80 if err != nil {
81 return err
82 }
83 if err := bucket.Put(key, encoded); err != nil {
84 return err
85 }
86 }
87 }
88 return nil
89 })
90 closeErr := db.Close()
91 if err != nil {
92 t.Fatal(err)
93 }
94 if closeErr != nil {
95 t.Fatal(closeErr)
96 }
97 var stats RecoveryOpenStats
98 reopened, err := OpenWithOptions(dir, "preview", OpenOptions{ExternalHistory: true, ObserveRecovery: func(got RecoveryOpenStats) { stats = got }})
99 if err != nil {
100 t.Fatal(err)
101 }
102 t.Cleanup(func() { _ = reopened.Close(context.Background()) })
103 if stats.UsedCheckpoint {
104 t.Fatal("old display checkpoint was reused")
105 }
106 if got := reopened.CatalogMetadata().Preview; got != "实际请求" {
107 t.Fatalf("cold preview = %q", got)
108 }
109 }
110
111 func TestCatalogRejectsOldDisplayProjection(t *testing.T) {
112 dir := t.TempDir()
113 manifest := Manifest{SessionID: "old-preview", CreatedAt: time.Unix(1, 0)}
114 metadata := metadataFromProjection(manifest, 0, Projection{})
115 metadata.Version = 1
116 metadata.Preview = "<response-language>" + strings.Repeat("internal", 40)
117 if err := writeCatalogMetadata(dir, metadata); err != nil {
118 t.Fatal(err)
119 }
120 if _, err := readCatalogMetadata(dir, manifest, logRevision{}); err == nil {
121 t.Fatal("old truncated preview was reused")
122 }
123 }
124
125 func TestMessagePreviewDoesNotExposeHostProtocolBeforeHydration(t *testing.T) {
126 message := provider.Message{Role: provider.RoleUser, Origin: provider.MessageOriginHost,
127 Content: "<session-context version=\"1\">" + strings.Repeat("environment", 4000) + "</session-context>", RawContent: "host-only metadata"}
128 if got := messagePreview(message); got != "" {
129 t.Fatalf("host preview = %q", got)
130 }
131 message.Origin = provider.MessageOriginUser
132 message.RawContent = "用户引用的上下文"
133 if got := messagePreview(message); got != message.RawContent {
134 t.Fatalf("authored preview = %q", got)
135 }
136 message.Origin = ""
137 message.RawContent = ""
138 message.Content = "<session-context version=\"1\">\nThis host-generated snapshot supersedes every earlier session-context snapshot.\n" + strings.Repeat("environment", 4000)
139 if got := messagePreview(message); got != "" {
140 t.Fatalf("legacy host preview = %q", got)
141 }
142 }
143
143 lines GO