返回 DeepSeek-Reasonix
background_attention_test.go
根目录 / desktop / background_attention_test.go
1 package main
2
3 import (
4 "context"
5 "testing"
6 "time"
7
8 "reasonix/internal/control"
9 "reasonix/internal/event"
10 )
11
12 func TestDetachedAskPublishesGlobalPendingInteraction(t *testing.T) {
13 isolateDesktopUserDirs(t)
14 dir := t.TempDir()
15 path := writeTopicSessionWithPrompt(t, dir, "a.jsonl", "topic-a", "Conversation A", "", "fixture", time.Now())
16 a := NewApp()
17 a.ctx = context.Background()
18 pending := make(chan RuntimeSessionState, 1)
19 a.runtimeEvents.emit = func(_ context.Context, name string, payload ...any) {
20 if name != "runtime-state:changed" {
21 return
22 }
23 for _, session := range payload[0].(RuntimeStateProjection).Sessions {
24 if !session.Open && len(session.State.Interactions) > 0 {
25 select {
26 case pending <- session:
27 default:
28 }
29 }
30 }
31 }
32 sink := &tabEventSink{tabID: "A", app: a, ctx: a.ctx}
33 runner := &blockingRunner{started: make(chan struct{}), release: make(chan struct{})}
34 ctrl := control.New(control.Options{Runner: runner, SessionDir: dir, SessionPath: path, Sink: sink})
35 defer ctrl.Close()
36 tab := &WorkspaceTab{ID: "A", Scope: "global", TopicID: "topic-a", TopicTitle: "Conversation A", SessionPath: path, Ctrl: ctrl, sink: sink, Ready: true}
37 a.tabs[tab.ID] = tab
38 a.activeTabID = tab.ID
39 ctrl.Submit("fixture turn")
40 <-runner.started
41 if !a.detachRuntimeForReplacement(tab) {
42 t.Fatal("could not detach running A")
43 }
44 a.mu.Lock()
45 delete(a.tabs, "A")
46 a.tabs["B"] = &WorkspaceTab{ID: "B"}
47 a.activeTabID = "B"
48 a.mu.Unlock()
49 if sink.context() != nil {
50 t.Fatal("detached transcript must stay disconnected")
51 }
52 ctx, cancel := context.WithCancel(t.Context())
53 done := make(chan struct{})
54 go func() {
55 defer close(done)
56 _, _ = ctrl.Ask(ctx, []event.AskQuestion{{ID: "choice", Prompt: "Choose"}})
57 }()
58 defer func() {
59 cancel()
60 <-done
61 close(runner.release)
62 waitNotRunning(t, ctrl)
63 a.mu.RLock()
64 detached := a.detachedSessions[sessionRuntimeKey(path)]
65 a.mu.RUnlock()
66 a.quiesceTabAutosave(detached)
67 }()
68 select {
69 case session := <-pending:
70 prompt := session.State.Interactions[0]
71 if session.TabID == "B" || session.SessionPath != path || prompt.Kind != "ask" || prompt.RequestID == "" || prompt.TurnID == "" {
72 t.Fatalf("wrong background prompt identity: %+v", session)
73 }
74 case <-time.After(3 * time.Second):
75 t.Fatal("background Ask was not pushed without reopening A")
76 }
77 a.mu.RLock()
78 active := a.activeTabID
79 a.mu.RUnlock()
80 if active != "B" {
81 t.Fatalf("background Ask changed active tab to %q", active)
82 }
83 }
84
84 lines GO