返回 DeepSeek-Reasonix
session_bundle_heads_test.go
根目录 / internal / doctor / session_bundle_heads_test.go
1 package doctor
2
3 import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10
11 "reasonix/internal/agent"
12 "reasonix/internal/provider"
13 "reasonix/internal/store"
14 )
15
16 func TestWriteSessionBundleListsSessionLogHeads(t *testing.T) {
17 home := t.TempDir()
18 t.Setenv("REASONIX_HOME", home)
19 dir := filepath.Join(home, "projects", "workspace", "sessions")
20 if err := os.MkdirAll(dir, 0o755); err != nil {
21 t.Fatal(err)
22 }
23 path := filepath.Join(dir, "log-session.jsonl")
24 sess := agent.NewSession("system")
25 sess.Add(provider.Message{Role: provider.RoleUser, Content: "shared question"})
26 sess.Add(provider.Message{Role: provider.RoleAssistant, Content: "shared answer"})
27 if err := sess.Save(path); err != nil {
28 t.Fatal(err)
29 }
30 fork, err := sess.ForkHead(path, sess.Snapshot()[2].ID, agent.HeadKindFork, "alt")
31 if err != nil {
32 t.Fatal(err)
33 }
34 sess.Add(provider.Message{Role: provider.RoleUser, Content: "alt question"})
35 if err := sess.Save(path); err != nil {
36 t.Fatal(err)
37 }
38
39 out := filepath.Join(t.TempDir(), "diag.zip")
40 if _, err := WriteSessionBundle(SessionBundleOptions{Version: "test-version", SessionRef: path, OutputPath: out, Now: time.Unix(1700000000, 0)}); err != nil {
41 t.Fatalf("WriteSessionBundle: %v", err)
42 }
43 files := zipFiles(t, out)
44 var manifest SessionBundleManifest
45 if err := json.Unmarshal(files["manifest.json"], &manifest); err != nil {
46 t.Fatalf("manifest JSON: %v", err)
47 }
48 if strings.Contains(string(files["manifest.json"]), home) {
49 t.Fatalf("manifest leaked REASONIX_HOME path:\n%s", files["manifest.json"])
50 }
51 if len(manifest.Sessions) != 1 {
52 t.Fatalf("manifest sessions = %+v, want the log alone: heads are not chain members", manifest.Sessions)
53 }
54 entry := manifest.Sessions[0]
55 if entry.LogFormat != 2 || entry.SelectedHead != fork || len(entry.Heads) != 2 {
56 t.Fatalf("manifest entry = %+v", entry)
57 }
58 main, alt := entry.Heads[0], entry.Heads[1]
59 if main.ID != agent.SessionMainHead || main.Kind != agent.HeadKindMain || !main.Covered || main.Selected || main.MessageCount != 3 {
60 t.Fatalf("main head = %+v", main)
61 }
62 if alt.ID != fork || alt.Kind != agent.HeadKindFork || alt.Name != "alt" || !alt.Selected || alt.Covered || alt.ParentHead != agent.SessionMainHead || alt.MessageCount != 4 {
63 t.Fatalf("fork head = %+v", alt)
64 }
65 if len(manifest.Missing) != 0 {
66 t.Fatalf("missing = %v, want none", manifest.Missing)
67 }
68 wantLog := filepath.ToSlash(filepath.Join("sessions", safeBundleName(agent.BranchID(path)), filepath.Base(store.SessionEventLog(path))))
69 if _, ok := files[wantLog]; !ok {
70 t.Fatalf("bundle lacks the event log %q; have %v", wantLog, keysOf(files))
71 }
72 }
73
74 func keysOf(files map[string][]byte) []string {
75 out := make([]string, 0, len(files))
76 for name := range files {
77 out = append(out, name)
78 }
79 return out
80 }
81
81 lines GO