| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "net/http" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | ) |
| 8 | |
| 9 | // ReclaimRemoteTabSession releases remoteTabMu and long-polls Serve for up to |
| 10 | // 20s, then re-locks the tab it captured earlier. The tab can close in the |
| 11 | // window between the command-target read and that capture, so the capture must |
| 12 | // prove the binding exists: dereferencing a missing entry panics inside a |
| 13 | // Wails binding and takes the window down instead of failing the button. |
| 14 | func TestReclaimObservationRejectsMissingOrReplacedBinding(t *testing.T) { |
| 15 | client := &http.Client{} |
| 16 | a := &App{remoteTabs: map[string]*remoteTab{}} |
| 17 | tab := &remoteTab{id: "remote-1", state: "ready", gen: 4, client: client, |
| 18 | runtime: remoteTabRuntimeState{revision: 7}, selectionRevision: 2} |
| 19 | a.remoteTabs[tab.id] = tab |
| 20 | |
| 21 | observed, err := a.observeRemoteTabForReclaim(tab.id, client) |
| 22 | if err != nil { |
| 23 | t.Fatalf("live binding was rejected: %v", err) |
| 24 | } |
| 25 | if observed.tab != tab || observed.gen != 4 || observed.runtimeRevision != 7 || observed.selectionRevision != 2 { |
| 26 | t.Fatalf("observation = %+v, want the live tab's fences", observed) |
| 27 | } |
| 28 | |
| 29 | // Closed between the command-target read and the snapshot. |
| 30 | delete(a.remoteTabs, tab.id) |
| 31 | closedObservation, err := a.observeRemoteTabForReclaim(tab.id, client) |
| 32 | if err == nil || !strings.Contains(err.Error(), "not connected") { |
| 33 | t.Fatalf("closed tab observation = %+v, %v; want a not-connected error", closedObservation, err) |
| 34 | } |
| 35 | if closedObservation.tab != nil { |
| 36 | t.Fatal("closed tab observation carried a binding forward") |
| 37 | } |
| 38 | |
| 39 | // Reconnected in the same window: a new client means the captured target |
| 40 | // belongs to a retired generation. |
| 41 | a.remoteTabs[tab.id] = tab |
| 42 | tab.client = &http.Client{} |
| 43 | if _, err := a.observeRemoteTabForReclaim(tab.id, client); err == nil { |
| 44 | t.Fatal("observation accepted a replaced client binding") |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // The public entry point reports the same disconnected error rather than |
| 49 | // panicking when the tab is gone. |
| 50 | func TestReclaimRemoteTabSessionRejectsClosedTab(t *testing.T) { |
| 51 | a := &App{remoteTabs: map[string]*remoteTab{}} |
| 52 | if err := a.ReclaimRemoteTabSession("missing"); err == nil { |
| 53 | t.Fatal("reclaim on a missing tab reported success") |
| 54 | } |
| 55 | } |
| 56 |