返回 DeepSeek-Reasonix
background_runtime_test.go
根目录 / desktop / background_runtime_test.go
1 package main
2
3 import (
4 "testing"
5
6 "reasonix/internal/control"
7 "reasonix/internal/jobs"
8 "reasonix/internal/workspacelease"
9 )
10
11 type backgroundRuntimeController struct {
12 control.SessionAPI
13 status control.RuntimeStatus
14 jobs []jobs.View
15 lease workspacelease.State
16 cancelled []string
17 turnCancel int
18 }
19
20 func (c *backgroundRuntimeController) RuntimeStatus() control.RuntimeStatus { return c.status }
21 func (c *backgroundRuntimeController) Jobs() []jobs.View {
22 return append([]jobs.View(nil), c.jobs...)
23 }
24 func (c *backgroundRuntimeController) CancelJob(id string) bool {
25 for _, job := range c.jobs {
26 if job.ID == id {
27 c.cancelled = append(c.cancelled, id)
28 return true
29 }
30 }
31 return false
32 }
33 func (c *backgroundRuntimeController) Cancel() { c.turnCancel++ }
34 func (c *backgroundRuntimeController) WorkspaceLeaseState() workspacelease.State {
35 return c.lease
36 }
37
38 func TestBackgroundRuntimeAPIsKeepDetachedJobsActionable(t *testing.T) {
39 targetCtrl := &backgroundRuntimeController{
40 status: control.RuntimeStatus{BackgroundJobs: 1},
41 jobs: []jobs.View{{ID: "job-target", Kind: "bash", Label: "target tests", Status: "running", StartedAt: 1}},
42 }
43 focusedCtrl := &backgroundRuntimeController{
44 status: control.RuntimeStatus{BackgroundJobs: 1},
45 jobs: []jobs.View{{ID: "job-focused", Kind: "bash", Label: "focused tests", Status: "running", StartedAt: 2}},
46 }
47 target := &WorkspaceTab{ID: "target", TopicTitle: "Detached task", Ctrl: targetCtrl}
48 focused := &WorkspaceTab{ID: "focused", TopicTitle: "Focused task", Ctrl: focusedCtrl}
49 app := &App{
50 tabs: map[string]*WorkspaceTab{"focused": focused},
51 tabOrder: []string{"focused"},
52 activeTabID: "focused",
53 detachedSessions: map[string]*WorkspaceTab{"session-target": target},
54 }
55
56 work := app.ActiveWorkForTab("target")
57 if len(work.Jobs) != 1 || work.Jobs[0].ID != "job-target" {
58 t.Fatalf("ActiveWorkForTab(target) = %+v, want target job", work)
59 }
60 result, err := app.CancelJobsForTab("target", []string{"job-target", "job-target", "missing"})
61 if err != nil {
62 t.Fatalf("CancelJobsForTab: %v", err)
63 }
64 if len(result.Cancelled) != 1 || result.Cancelled[0] != "job-target" || len(result.NotRunning) != 1 || result.NotRunning[0] != "missing" {
65 t.Fatalf("CancelJobsForTab result = %+v", result)
66 }
67 if len(targetCtrl.cancelled) != 1 || len(focusedCtrl.cancelled) != 0 {
68 t.Fatalf("cancellation routing target=%v focused=%v", targetCtrl.cancelled, focusedCtrl.cancelled)
69 }
70
71 runtimes := app.BackgroundRuntimes()
72 if len(runtimes) != 2 {
73 t.Fatalf("BackgroundRuntimes = %+v, want visible and detached", runtimes)
74 }
75 var foundDetached bool
76 for _, runtime := range runtimes {
77 if runtime.TabID == "target" {
78 foundDetached = runtime.Detached && runtime.Title == "Detached task" && len(runtime.Jobs) == 1
79 }
80 }
81 if !foundDetached {
82 t.Fatalf("detached runtime missing from %+v", runtimes)
83 }
84 }
85
86 func TestCancelJobForLocalRuntimeByExplicitTab(t *testing.T) {
87 localCtrl := &backgroundRuntimeController{
88 status: control.RuntimeStatus{BackgroundJobs: 1},
89 jobs: []jobs.View{{ID: "job-local", Kind: "bash", Status: "running"}},
90 }
91 app := &App{
92 tabs: map[string]*WorkspaceTab{"local": {ID: "local", Ctrl: localCtrl}},
93 tabOrder: []string{"local"},
94 activeTabID: "local",
95 }
96
97 listed := app.JobsForTab("local")
98 if len(listed) != 1 || listed[0].ID != "job-local" {
99 t.Fatalf("JobsForTab(local) = %+v, want local job", listed)
100 }
101 cancelled, err := app.CancelJobForTab("local", "job-local")
102 if err != nil {
103 t.Fatalf("CancelJobForTab(local): %v", err)
104 }
105 if !cancelled || len(localCtrl.cancelled) != 1 || localCtrl.cancelled[0] != "job-local" {
106 t.Fatalf("local cancellation = %t, calls=%v; want local job cancelled", cancelled, localCtrl.cancelled)
107 }
108 }
109
110 func TestWorkspaceConflictIdentifiesExactLocalWriterWithoutIdentity(t *testing.T) {
111 root := t.TempDir()
112 waiterCtrl := &backgroundRuntimeController{
113 status: control.RuntimeStatus{Running: true},
114 lease: workspacelease.State{Waiting: true},
115 }
116 ownerCtrl := &backgroundRuntimeController{
117 status: control.RuntimeStatus{BackgroundJobs: 1},
118 jobs: []jobs.View{{ID: "owner-job", Kind: "bash", Status: "running"}},
119 lease: workspacelease.State{Acquired: true},
120 }
121 app := &App{
122 tabs: map[string]*WorkspaceTab{
123 "waiter": {ID: "waiter", WorkspaceRoot: root, Ctrl: waiterCtrl},
124 "owner": {ID: "owner", TopicTitle: "Writing task", WorkspaceRoot: root, Ctrl: ownerCtrl},
125 },
126 tabOrder: []string{"waiter", "owner"},
127 activeTabID: "waiter",
128 }
129
130 conflict := app.WorkspaceConflictForTab("waiter")
131 if conflict.State != "local" || conflict.OwnerTabID != "owner" || conflict.OwnerTitle != "Writing task" || !conflict.CanReveal {
132 t.Fatalf("WorkspaceConflictForTab = %+v, want exact local owner", conflict)
133 }
134 if len(conflict.OwnerWork.Jobs) != 1 || conflict.OwnerWork.Jobs[0].ID != "owner-job" {
135 t.Fatalf("owner work = %+v, want sanitized active work", conflict.OwnerWork)
136 }
137 }
138
138 lines GO