1package core
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/bnema/zut/packages/provider"
10)
11
12func TestListSessionPathsContextScopesAndValidatesBuckets(t *testing.T) {
13 root := t.TempDir()
14 cwdA := filepath.Join(t.TempDir(), "workspace-a")
15 cwdB := filepath.Join(t.TempDir(), "workspace-b")
16
17 newSession := func(cwd, text string) *Session {
18 t.Helper()
19 s, err := NewSession(root, cwd, "provider", "model", "test")
20 if err != nil {
21 t.Fatal(err)
22 }
23 if err := s.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: text}}}); err != nil {
24 t.Fatal(err)
25 }
26 if err := s.Close(); err != nil {
27 t.Fatal(err)
28 }
29 return s
30 }
31
32 a := newSession(cwdA, "from a")
33 b := newSession(cwdB, "from b")
34
35 if got := ListSessionPathsContext(context.Background(), root, cwdA, false); len(got) != 1 || got[0] != a.Path {
36 t.Fatalf("current scope = %q, want %q", got, a.Path)
37 }
38 got := ListSessionPathsContext(context.Background(), root, cwdA, true)
39 if len(got) != 2 || !containsPath(got, a.Path) || !containsPath(got, b.Path) {
40 t.Fatalf("all scope = %q, want both valid sessions", got)
41 }
42
43 invalidDir := filepath.Join(root, "sessions", "not-a-cwd-bucket")
44 if err := os.MkdirAll(invalidDir, 0o755); err != nil {
45 t.Fatal(err)
46 }
47 if err := os.WriteFile(filepath.Join(invalidDir, "invalid.jsonl"), []byte("{}\n"), 0o644); err != nil {
48 t.Fatal(err)
49 }
50 wrongBucket := filepath.Join(root, "sessions", "0123456789abcdef")
51 if err := os.MkdirAll(wrongBucket, 0o755); err != nil {
52 t.Fatal(err)
53 }
54 contents, err := os.ReadFile(a.Path)
55 if err != nil {
56 t.Fatal(err)
57 }
58 if err := os.WriteFile(filepath.Join(wrongBucket, "wrong.jsonl"), contents, 0o644); err != nil {
59 t.Fatal(err)
60 }
61 if got := ListSessionPathsContext(context.Background(), root, cwdA, true); len(got) != 2 {
62 t.Fatalf("all scope included invalid session paths: %q", got)
63 }
64}
65
66func TestManagedSessionMetaRejectsWrongBucket(t *testing.T) {
67 root := t.TempDir()
68 cwd := t.TempDir()
69 session, err := NewSession(root, cwd, "provider", "model", "test")
70 if err != nil {
71 t.Fatal(err)
72 }
73 if err := session.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "keep this session"}}}); err != nil {
74 t.Fatal(err)
75 }
76 if err := session.Close(); err != nil {
77 t.Fatal(err)
78 }
79 if _, err := ManagedSessionMeta(context.Background(), root, session.Path); err != nil {
80 t.Fatalf("managed session metadata: %v", err)
81 }
82 wrongPath := filepath.Join(root, "sessions", "0123456789abcdef", "moved.jsonl")
83 if err := os.MkdirAll(filepath.Dir(wrongPath), 0o755); err != nil {
84 t.Fatal(err)
85 }
86 contents, err := os.ReadFile(session.Path)
87 if err != nil {
88 t.Fatal(err)
89 }
90 if err := os.WriteFile(wrongPath, contents, 0o644); err != nil {
91 t.Fatal(err)
92 }
93 if _, err := ManagedSessionMeta(context.Background(), root, wrongPath); err == nil {
94 t.Fatal("wrong-bucket session was accepted")
95 }
96}
97
98func TestReadSessionSearchSegmentsIncludesOnlyUserAssistantText(t *testing.T) {
99 root := t.TempDir()
100 cwd := t.TempDir()
101 s, err := NewSession(root, cwd, "provider", "model", "test")
102 if err != nil {
103 t.Fatal(err)
104 }
105 for _, msg := range []provider.Message{
106 {Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "find this user sentence"}}},
107 {Role: provider.RoleAssistant, Content: []provider.Content{provider.TextBlock{Text: "find this assistant sentence"}, provider.ToolCallBlock{ID: "call", Name: "secret-tool"}}},
108 {Role: provider.RoleTool, Content: []provider.Content{provider.ToolResultBlock{CallID: "call", Content: []provider.Content{provider.TextBlock{Text: "never expose tool result"}}}}},
109 {Role: provider.RoleDeveloper, Content: []provider.Content{provider.TextBlock{Text: "never expose developer text"}}},
110 } {
111 if err := s.AppendMessage(msg); err != nil {
112 t.Fatal(err)
113 }
114 }
115 if err := s.Close(); err != nil {
116 t.Fatal(err)
117 }
118
119 segments, err := ReadSessionSearchSegments(context.Background(), s.Path)
120 if err != nil {
121 t.Fatal(err)
122 }
123 if len(segments) != 2 {
124 t.Fatalf("segments = %#v, want exactly user and assistant text", segments)
125 }
126 if segments[0].Text != "find this user sentence" || segments[1].Text != "find this assistant sentence" {
127 t.Fatalf("segments = %#v", segments)
128 }
129}
130
131func containsPath(paths []string, want string) bool {
132 for _, path := range paths {
133 if path == want {
134 return true
135 }
136 }
137 return false
138}