返回 DeepSeek-Reasonix
session_catalog_published_runtime_test.go
根目录 / desktop / session_catalog_published_runtime_test.go
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "sync"
7 "sync/atomic"
8 "testing"
9 "time"
10
11 "reasonix/internal/event"
12 "reasonix/internal/provider"
13 )
14
15 type publishedSidebarController struct {
16 bindingRuntimeReader
17 published atomic.Pointer[event.RuntimeStateSnapshot]
18 }
19
20 func (c *publishedSidebarController) PublishedRuntimeStateSnapshot() event.RuntimeStateSnapshot {
21 return *c.published.Load()
22 }
23
24 func (c *publishedSidebarController) SessionPath() string { return "active-session.jsonl" }
25
26 func TestProjectListReadsPublishedStateWhileOtherProjectSamplingBlocks(t *testing.T) {
27 for _, indexed := range []bool{false, true} {
28 for _, detached := range []bool{false, true} {
29 t.Run(testBoolName("catalog", indexed)+"/"+testBoolName("detached", detached), func(t *testing.T) {
30 app, dsh, refs := canonicalOrganizationFixture(t, "dsh-history")
31 history, ok := app.desktopSessionService("").Runtime(refs["dsh-history"])
32 if !ok {
33 t.Fatal("history fixture has no runtime")
34 }
35 appendSessionTestMessage(t, history, "history-user", provider.Message{ID: "history-user", Role: provider.RoleUser, Content: "Existing history"})
36 active := t.TempDir()
37 if err := addProject(active, "Active project"); err != nil {
38 t.Fatal(err)
39 }
40 if indexed {
41 dir := desktopSessionDir(dsh)
42 if err := os.MkdirAll(dir, 0o755); err != nil {
43 t.Fatal(err)
44 }
45 installSessionCatalogForTest(t, app, dir, "project", dsh)
46 }
47 ctrl := &publishedSidebarController{bindingRuntimeReader: bindingRuntimeReader{entered: make(chan struct{}), release: make(chan struct{})}}
48 release := sync.OnceFunc(func() { close(ctrl.release) })
49 defer release()
50 ctrl.published.Store(&event.RuntimeStateSnapshot{SchemaVersion: 1, RuntimeEpoch: "active", Revision: 1, Phase: "executing", Running: true})
51 tab := &WorkspaceTab{ID: "active", Scope: "project", WorkspaceRoot: active, TopicID: "active-topic", SessionPath: "active-session.jsonl", Ctrl: ctrl}
52 if detached {
53 app.detachedSessions["active"] = tab
54 } else {
55 app.tabs["active"] = tab
56 }
57 // Keep a real refresh call outstanding throughout the list query.
58 refreshed := make(chan struct{})
59 go func() { ctrl.RuntimeStateSnapshot(); close(refreshed) }()
60 <-ctrl.entered
61 done := make(chan error, 1)
62 go func() {
63 defer close(done)
64 page, err := app.ListProjectTopics(ProjectTopicPageRequest{Scope: "project", WorkspaceRoot: dsh, Limit: 50})
65 if err == nil && (len(page.Items) != 1 || page.Items[0].Session == nil || *page.Items[0].Session != refs["dsh-history"]) {
66 err = fmt.Errorf("list lost existing DSH history: %+v", page.Items)
67 }
68 done <- err
69 }()
70 defer func() {
71 release()
72 <-refreshed
73 for range done {
74 }
75 }()
76 select {
77 case err := <-done:
78 if err != nil {
79 t.Fatal(err)
80 }
81 case <-time.After(5 * time.Second):
82 t.Fatal("DSH list waited on the other project's runtime refresh")
83 }
84 for i, phase := range []string{"executing", "cancelling", "idle"} {
85 ctrl.published.Store(&event.RuntimeStateSnapshot{SchemaVersion: 1, RuntimeEpoch: "active", Revision: uint64(i + 1), Phase: phase, Running: phase != "idle", CancelRequested: phase == "cancelling"})
86 projection := app.GetRuntimeStateSnapshot()
87 if len(projection.Sessions) != 1 || projection.Sessions[0].State.Phase != phase || projection.Sessions[0].Open == detached {
88 t.Fatalf("runtime projection lost committed state or binding: %+v", projection)
89 }
90 }
91 })
92 }
93 }
94 }
95
96 func testBoolName(name string, value bool) string {
97 if value {
98 return name + "-yes"
99 }
100 return name + "-no"
101 }
102
102 lines GO