返回 DeepSeek-Reasonix
session_workspace_metadata_test.go
根目录 / desktop / session_workspace_metadata_test.go
1 package main
2
3 import (
4 "context"
5 "reflect"
6 "testing"
7
8 "reasonix/desktop/internal/workspacestate"
9 "reasonix/internal/session"
10 )
11
12 type workspaceInfoProbe struct{ ids []string }
13
14 type changingWorkspaceInfoProbe struct{ reads int }
15
16 func (p *changingWorkspaceInfoProbe) Stat(_ context.Context, ref session.SessionRef) (session.SessionInfo, error) {
17 p.reads++
18 sequence := uint64(1)
19 if p.reads > 2 && ref.SessionID == "a" {
20 sequence = 2
21 }
22 return session.SessionInfo{SessionID: ref.SessionID, EventSequence: sequence, MetadataStatus: session.MetadataReady}, nil
23 }
24
25 func TestWorkspaceMetadataSnapshotRejectsChangeDuringMaterialization(t *testing.T) {
26 probe := &changingWorkspaceInfoProbe{}
27 ids := []string{"a", "b"}
28 before, err := listWorkspaceSessionInfo(t.Context(), probe, ids)
29 if err != nil {
30 t.Fatal(err)
31 }
32 if workspaceSessionInfoUnchanged(t.Context(), probe, ids, before) {
33 t.Fatal("published mixed metadata after A changed while building the page")
34 }
35 stable, _ := listWorkspaceSessionInfo(t.Context(), probe, ids)
36 if !workspaceSessionInfoUnchanged(t.Context(), probe, ids, stable) {
37 t.Fatal("rejected unchanged metadata")
38 }
39 }
40
41 func (p *workspaceInfoProbe) Stat(_ context.Context, ref session.SessionRef) (session.SessionInfo, error) {
42 p.ids = append(p.ids, ref.SessionID)
43 return session.SessionInfo{SessionID: ref.SessionID, MetadataStatus: session.MetadataReady}, nil
44 }
45
46 func TestWorkspaceMetadataReadsOnlyRegisteredMembers(t *testing.T) {
47 probe := &workspaceInfoProbe{}
48 for _, ids := range [][]string{{"a", "b", "a"}, {"c"}, {"d", "e"}} {
49 if _, err := listWorkspaceSessionInfo(t.Context(), probe, ids); err != nil {
50 t.Fatal(err)
51 }
52 }
53 if !reflect.DeepEqual(probe.ids, []string{"a", "b", "c", "d", "e"}) {
54 t.Fatalf("repeated or unrelated reads: %v", probe.ids)
55 }
56 }
57
58 func TestWorkspacePendingMetadataIsNotBlank(t *testing.T) {
59 for _, status := range []string{session.MetadataPending, session.MetadataFailed} {
60 row := workspaceSessionRow("global", "old", session.SessionInfo{MetadataStatus: status}, true, false, nil)
61 if row.Blank {
62 t.Fatalf("%s metadata was classified as blank", status)
63 }
64 }
65 row := workspaceSessionRow("global", "missing", session.SessionInfo{}, false, false, nil)
66 if row.Blank {
67 t.Fatal("missing metadata was classified as blank")
68 }
69 row = workspaceSessionRow("global", "empty", session.SessionInfo{MetadataStatus: session.MetadataReady}, true, false, nil)
70 if !row.Blank {
71 t.Fatal("ready empty session should be blank")
72 }
73 row = workspaceSessionRow("global", "named", session.SessionInfo{MetadataStatus: session.MetadataReady, Title: "Saved title"}, true, false, nil)
74 if row.Blank {
75 t.Fatal("named session should not be hidden")
76 }
77 }
78
79 func TestCanonicalSessionTopicIdentityUsesTargetPresentation(t *testing.T) {
80 state := workspacestate.State{Presentation: map[string]workspacestate.Presentation{
81 "target": {TopicID: "topic-target", Title: "Target"},
82 }}
83 topicID, title := canonicalSessionTopicIdentity(state, "target")
84 if topicID != "topic-target" || title != "Target" {
85 t.Fatalf("identity = %q/%q, want target presentation", topicID, title)
86 }
87 topicID, title = canonicalSessionTopicIdentity(state, "missing")
88 if topicID != "canonical-missing" || title != "" {
89 t.Fatalf("fallback identity = %q/%q", topicID, title)
90 }
91 }
92
93 func TestCanonicalBindingUsesRenamedSessionTitleAndRuntimeIdentity(t *testing.T) {
94 isolateDesktopUserDirs(t)
95 app := NewApp()
96 t.Cleanup(app.closeSessionServices)
97 ws, err := app.ensureDesktopWorkspace(t.Context(), "global", "")
98 if err != nil {
99 t.Fatal(err)
100 }
101 runtime, err := app.desktopSessionService("").Create(t.Context(), session.CreateOptions{SessionID: "renamed-binding", Origin: session.SessionOriginNew})
102 if err != nil {
103 t.Fatal(err)
104 }
105 ref := runtime.Ref()
106 if err := app.workspaceRegistry().AttachSession(t.Context(), "", ws, ref.SessionID, ""); err != nil {
107 t.Fatal(err)
108 }
109 old := "Old fork title"
110 if err := app.workspaceRegistry().UpdatePresentation(t.Context(), []string{ref.SessionID}, &old, nil); err != nil {
111 t.Fatal(err)
112 }
113 if _, err := app.RenameSessionTarget(SessionSelector{Ref: &ref}, "Renamed B"); err != nil {
114 t.Fatal(err)
115 }
116 state, err := app.workspaceRegistry().Load(t.Context())
117 if err != nil {
118 t.Fatal(err)
119 }
120 tab := &WorkspaceTab{ID: "binding", SessionID: ref.SessionID, HistoricalSource: &SessionSourceRef{Path: "/fixture/old.jsonl"}}
121 app.tabs[tab.ID] = tab
122 if err := app.commitCanonicalSessionBinding(tab, nil, ref, state.Workspaces[ws], 0); err != nil {
123 t.Fatal(err)
124 }
125 if tab.HistoricalSource != nil {
126 t.Fatal("canonical binding retained the preparation action")
127 }
128 if tab.TopicTitle != "Renamed B" {
129 t.Fatalf("reopened title=%q", tab.TopicTitle)
130 }
131 snapshot := app.GetRuntimeStateSnapshot()
132 if len(snapshot.Topics) != 1 || snapshot.Topics[0].Node.Session == nil || snapshot.Topics[0].Node.Session.SessionID != ref.SessionID {
133 t.Fatalf("runtime lost canonical identity: %+v", snapshot.Topics)
134 }
135 }
136
136 lines GO