返回 DeepSeek-Reasonix
testhelpers_test.go
根目录 / desktop / testhelpers_test.go
1 package main
2
3 import (
4 "os"
5 "path/filepath"
6 "strings"
7 "testing"
8 "time"
9
10 "reasonix/internal/config"
11 "reasonix/internal/control"
12 "reasonix/internal/provider"
13 )
14
15 // Test-only helpers for the desktop suite. They exercise production state
16 // through the same package internals, so they live here instead of in the
17 // shipped binary.
18
19 func waitNotRunning(t *testing.T, ctrl control.SessionAPI) {
20 t.Helper()
21 if waiter, ok := ctrl.(interface {
22 TurnIdleDone() (<-chan struct{}, bool)
23 }); ok {
24 if done, running := waiter.TurnIdleDone(); running {
25 select {
26 case <-done:
27 case <-time.After(30 * time.Second):
28 t.Fatalf("timed out waiting for controller idle boundary: %+v", ctrl.RuntimeStatus())
29 }
30 }
31 return
32 }
33
34 // Compatibility controllers may expose only SessionAPI. Keep a bounded
35 // fallback for those test doubles; production controllers use the explicit
36 // lifecycle boundary above.
37 deadline := time.Now().Add(30 * time.Second)
38 for ctrl.Running() {
39 if time.Now().After(deadline) {
40 t.Fatalf("timed out waiting for compatibility controller: %+v", ctrl.RuntimeStatus())
41 }
42 time.Sleep(10 * time.Millisecond)
43 }
44 }
45
46 // setSessionTitle sets (or, with an empty title, clears) a session's custom name.
47 func setSessionTitle(dir, sessionPath, title string) error {
48 sessionPath, _, err := validateSessionPath(dir, sessionPath)
49 if err != nil {
50 return err
51 }
52 key := filepath.Base(sessionPath)
53 return updateSessionTitles(dir, func(m map[string]string) bool {
54 title = strings.TrimSpace(title)
55 if title == "" {
56 if _, ok := m[key]; !ok {
57 return false
58 }
59 delete(m, key)
60 return true
61 }
62 if m[key] == title {
63 return false
64 }
65 m[key] = title
66 return true
67 })
68 }
69
70 // deleteSessionFile moves a session's .jsonl and file sidecars into the local
71 // trash. Title/display sidecars stay in place so trash previews and restores can
72 // preserve the user's labels.
73 func deleteSessionFile(dir, sessionPath string) error {
74 sessionPath, key, err := validateSessionPath(dir, sessionPath)
75 if err != nil {
76 return err
77 }
78 return trashSessionArtifacts(dir, sessionPath, key)
79 }
80
81 // loadTasks reads tasks from disk.
82 func (e *HeartbeatEngine) loadTasks() []HeartbeatTask {
83 snapshot, err := e.readConfigSnapshot()
84 if err != nil {
85 return nil
86 }
87 return snapshot.cfg.Tasks
88 }
89
90 // saveTasks writes tasks to disk atomically.
91 func (e *HeartbeatEngine) saveTasks(tasks []HeartbeatTask) error {
92 return e.writeTasks(tasks, heartbeatConfigSnapshot{}, false)
93 }
94
95 func historyMessages(msgs []provider.Message, resolveUserContent func(string) string) []HistoryMessage {
96 return historyMessagesWithPlannerDisplays(msgs, resolveUserContent, nil, nil)
97 }
98
99 func skillRootsView() []SkillRootView {
100 cwd, _ := os.Getwd()
101 cfg, _ := config.Load()
102 userCfg := config.LoadForEdit(config.UserConfigPath())
103 return skillRootsViewFrom(cwd, cfg, userCfg)
104 }
105
106 func mcpFailed(ctrl control.SessionAPI, name string) bool {
107 if ctrl == nil || ctrl.Host() == nil {
108 return false
109 }
110 for _, f := range ctrl.Host().Failures() {
111 if f.Name == name {
112 return true
113 }
114 }
115 return false
116 }
117
118 func saveExclusiveExportFiles(targets []string, payloads [][]byte) error {
119 return saveExclusiveExportPayloads(targets, len(payloads), func(index int) ([]byte, error) {
120 return payloads[index], nil
121 })
122 }
123
124 func pathWithinWorktree(path, worktreeRoot string) bool {
125 pathKey := canonicalRuntimeRoot(path)
126 rootKey := canonicalRuntimeRoot(worktreeRoot)
127 if pathKey == "" || rootKey == "" {
128 return false
129 }
130 if pathKey == rootKey {
131 return true
132 }
133 rel, err := filepath.Rel(rootKey, pathKey)
134 return err == nil && rel != "." && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
135 }
136
137 func buildScrollDiagnosticsZip(payload string) ([]byte, error) {
138 data, _, err := buildScrollDiagnosticsArchive(payload)
139 return data, err
140 }
141
141 lines GO