返回 DeepSeek-Reasonix
remote_tab_rejection_paths_test.go
根目录 / desktop / remote_tab_rejection_paths_test.go
1 package main
2
3 import (
4 "encoding/json"
5 "errors"
6 "io"
7 "net/http"
8 "strings"
9 "testing"
10 )
11
12 func TestRemoteResumeFailurePathsPublishOneRestoredSnapshot(t *testing.T) {
13 for _, kind := range []string{"http", "busy", "listing", "notfound", "transport"} {
14 t.Run(kind, func(t *testing.T) {
15 isolateDesktopUserDirs(t)
16 const oldPath, targetPath = "/sessions/old.jsonl", "/sessions/target.jsonl"
17 client := &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
18 code, body := http.StatusConflict, "rejected"
19 if kind == "busy" {
20 body = "while a turn is running"
21 }
22 if kind == "listing" {
23 code = http.StatusInternalServerError
24 }
25 if kind == "notfound" {
26 code, body = http.StatusOK, `[]`
27 }
28 if kind == "transport" {
29 if req.URL.Path == "/resume" {
30 return nil, errors.New("response lost")
31 }
32 code, body = http.StatusOK, `[{"name":"old","path":"/sessions/old.jsonl","title":"Old","current":true}]`
33 }
34 return &http.Response{StatusCode: code, Header: make(http.Header), Body: io.NopCloser(strings.NewReader(body)), Request: req}, nil
35 })}
36 tab := &remoteTab{id: "remote-1", state: "ready", client: client, base: "http://fixture.invalid", gen: 7, selectionRevision: 9,
37 session: remoteTabSessionState{name: "target", path: targetPath}, topicTitle: "Target",
38 routing: remoteTabSessionRouting{currentPath: targetPath, pathRevision: 11, running: map[string]bool{}},
39 }
40 oldPending := json.RawMessage(`{"kind":"approval_request","callId":"old"}`)
41 previous := &remoteTabOpenSelection{session: remoteTabSessionState{name: "old", path: oldPath}, topicTitle: "Old", currentPath: oldPath, revision: 9,
42 pending: map[string]json.RawMessage{"old": oldPending}, runtime: remoteTabRuntimeState{running: true, cancellable: true, revision: 3},
43 }
44 a := &App{remoteTabs: map[string]*remoteTab{tab.id: tab}}
45 failures := 0
46 a.remoteEventHook = func(_ string, payload any) {
47 state, ok := payload.(RemoteTabStateView)
48 if !ok || state.Error == "" {
49 return
50 }
51 failures++
52 a.remoteTabMu.Lock()
53 defer a.remoteTabMu.Unlock()
54 if tab.session.name != "old" || tab.session.path != oldPath || tab.routing.currentPath != oldPath || tab.topicTitle != "Old" ||
55 !tab.runtime.running || !tab.runtime.cancellable || string(tab.pendingEvents["old"]) != string(oldPending) || tab.err != state.Error {
56 t.Errorf("failure exposed a partial identity/runtime/prompt restore: session=%+v route=%q title=%q runtime=%+v error=%q", tab.session, tab.routing.currentPath, tab.topicTitle, tab.runtime, tab.err)
57 }
58 }
59 path := targetPath
60 if kind == "listing" || kind == "notfound" {
61 path = ""
62 }
63 a.resumeRemoteTabSessionPathForOpenSelection(tab.id, "target", path, "Target", 9, previous)
64 if failures != 1 {
65 t.Fatalf("failure publications = %d, want 1", failures)
66 }
67 })
68 }
69 }
70
71 func TestRemoteRejectedResumePreservesProbedAuthoritativeSelection(t *testing.T) {
72 isolateDesktopUserDirs(t)
73 const previousPath = "/sessions/previous.jsonl"
74 const targetPath = "/sessions/target.jsonl"
75 const authoritativePath = "/sessions/authoritative.jsonl"
76 client := &http.Client{}
77 tab := &remoteTab{
78 id: "remote-1", state: "ready", client: client, gen: 7, selectionRevision: 11,
79 session: remoteTabSessionState{name: "previous", path: previousPath}, topicTitle: "Previous",
80 routing: remoteTabSessionRouting{currentPath: previousPath, running: map[string]bool{}},
81 }
82 a := &App{remoteTabs: map[string]*remoteTab{tab.id: tab}}
83 previous := &remoteTabOpenSelection{
84 session: tab.session, topicTitle: tab.topicTitle, currentPath: previousPath, revision: tab.selectionRevision,
85 }
86 route := a.beginRemoteTabProvisionalResume(tab.id, tab, client, tab.gen, targetPath)
87 route.previousSelection = previous
88 handled := a.reconcileRemoteTabRejectedResume(
89 tab.id, tab, client, tab.gen, route,
90 serveSessionEntry{Name: "authoritative", Path: authoritativePath, Title: "Authoritative"},
91 errors.New("resume response lost"),
92 )
93 if !handled {
94 t.Fatal("authoritative reconciliation requested stale rollback")
95 }
96 a.remoteTabMu.Lock()
97 gotPath, gotSession, gotTitle := tab.routing.currentPath, tab.session.path, tab.topicTitle
98 a.remoteTabMu.Unlock()
99 if gotPath != authoritativePath || gotSession != authoritativePath || gotTitle != "Authoritative" {
100 t.Fatalf("ambiguous resume restored stale selection: route/session/title = %q/%q/%q", gotPath, gotSession, gotTitle)
101 }
102 }
103
103 lines GO