返回 DeepSeek-Reasonix
remote_listing_identity_test.go
根目录 / desktop / remote_listing_identity_test.go
1 package main
2
3 import (
4 "encoding/json"
5 "strings"
6 "testing"
7 )
8
9 // remoteListingTab installs one ready remote tab bound to route, with the given
10 // serve listing behind it, and returns the app plus the live tab.
11 func remoteListingTab(t *testing.T, fs *fakeServe, route string) (*App, *remoteTab) {
12 t.Helper()
13 client, _ := remoteSessionTestClient(t, fs)
14 tab := &remoteTab{
15 id: "remote-1", ref: RemoteTabRef{HostID: "box", Workspace: "~/app"}, state: "ready",
16 client: client, base: fs.server.URL, gen: 3, topicTitle: "Fresh topic",
17 routing: remoteTabSessionRouting{currentPath: route, running: map[string]bool{}},
18 }
19 if _, sessionID := remoteSessionRouteIdentity(route); sessionID != "" {
20 tab.session.sessionID = sessionID
21 } else {
22 tab.session.path = route
23 }
24 return &App{remoteTabs: map[string]*remoteTab{tab.id: tab}}, tab
25 }
26
27 // The synthetic current row stands in for a foreground session /sessions does
28 // not list yet. Built from an identity route it must expose that identity as
29 // sessionId: reporting it as Path makes the resume body ask Serve to resolve
30 // "session-id:X" as a filesystem path, which fails with 400.
31 func TestSyntheticCurrentRowCarriesIdentityAsSessionID(t *testing.T) {
32 seedBridgeTestHost(t, "box")
33 fs := newFakeServe(t, "s3cret", nil)
34 a, _ := remoteListingTab(t, fs, remoteSessionIDRoutePrefix+"fresh-id")
35
36 sessions, err := a.RemoteProjectSessions("box", "~/app")
37 if err != nil {
38 t.Fatal(err)
39 }
40 if len(sessions) != 1 || !sessions[0].Current {
41 t.Fatalf("sessions = %+v, want one synthetic current row", sessions)
42 }
43 row := sessions[0]
44 if row.SessionID != "fresh-id" || row.Path != "" {
45 t.Fatalf("synthetic row identity = sessionId:%q path:%q, want the identity route as sessionId", row.SessionID, row.Path)
46 }
47
48 // The row round-trips through the click path: the frontend hands its
49 // fields back verbatim, and the resume body must name the identity.
50 body, err := remoteSessionResumeBody(serveSessionEntry{Name: row.Name, Path: row.Path, SessionID: row.SessionID})
51 if err != nil {
52 t.Fatal(err)
53 }
54 var request map[string]string
55 if err := json.Unmarshal(body, &request); err != nil {
56 t.Fatal(err)
57 }
58 if request["sessionId"] != "fresh-id" || request["path"] != "" {
59 t.Fatalf("resume body = %s, want the identity in sessionId and an empty path", body)
60 }
61 }
62
63 // A legacy path route keeps its path: only identity routes move to sessionId.
64 func TestSyntheticCurrentRowKeepsLegacyPathRoute(t *testing.T) {
65 seedBridgeTestHost(t, "box")
66 fs := newFakeServe(t, "s3cret", nil)
67 a, _ := remoteListingTab(t, fs, "/sessions/legacy.jsonl")
68
69 sessions, err := a.RemoteProjectSessions("box", "~/app")
70 if err != nil {
71 t.Fatal(err)
72 }
73 if len(sessions) != 1 || sessions[0].Path != "/sessions/legacy.jsonl" || sessions[0].SessionID != "" {
74 t.Fatalf("legacy synthetic row = %+v, want the path preserved", sessions)
75 }
76 }
77
78 // An ID-only selection (the synthetic identity row has no name) must resume by
79 // the committed identity instead of falling through to a listing lookup keyed
80 // on the empty name, which reports "not found".
81 func TestResumeAcceptsCommittedIdentityWithoutAName(t *testing.T) {
82 seedBridgeTestHost(t, "box")
83 fs := newFakeServe(t, "s3cret", []serveSessionEntry{{Name: "other", Path: "/other.jsonl", Current: true}})
84 a, tab := remoteListingTab(t, fs, remoteSessionIDRoutePrefix+"committed-id")
85
86 if !a.resumeRemoteTabSessionPathForOpenSelection(tab.id, "", "", "", 0, nil) {
87 t.Fatal("ID-only selection was rejected")
88 }
89 if got := fs.resumedSessionID(); got != "committed-id" {
90 t.Fatalf("resume carried sessionId %q, want the committed identity", got)
91 }
92 a.remoteTabMu.Lock()
93 state, route, title, errText := tab.state, tab.routing.currentPath, tab.topicTitle, tab.err
94 a.remoteTabMu.Unlock()
95 if state != "ready" || errText != "" {
96 t.Fatalf("ID-only resume state/err = %q/%q, want a clean ready tab", state, errText)
97 }
98 if route != remoteSessionIDRoutePrefix+"committed-id" {
99 t.Fatalf("resumed route = %q, want the committed identity route", route)
100 }
101 if strings.TrimSpace(title) == "" {
102 t.Fatal("ID-only resume left the tab without a display title")
103 }
104 }
105
105 lines GO