Allow Remote sessions to outlive connection forms
This commit is contained in:
1 parent
b60cf5d5f2
commit
b4896a2eb4
10 files changed
+233
-16
No files matched your search
@@ -222,8 +222,14 @@ func TestRemoteConnectionCommandCancelsInspectionOnHandoff(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
for _, role := range []string{"controller", "controlled"} {
|
||||
t.Run(role, func(t *testing.T) {
|
||||
for _, test := range []struct{ role, ending string }{
|
||||
{"controller", "disconnect"}, {"controlled", "disconnect"},
|
||||
{"controller", "lock"}, {"controlled", "lock"},
|
||||
{"controller", "handoff"}, {"controlled", "handoff"},
|
||||
{"controller", "disable"}, {"controlled", "disable"},
|
||||
} {
|
||||
role, ending := test.role, test.ending
|
||||
t.Run(role+"/"+ending, func(t *testing.T) {
|
||||
remoteText := " " + strings.Repeat("é\x00世界 ", 1500) + " "
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
@@ -299,7 +305,7 @@ func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
}
|
||||
close(closed)
|
||||
}()
|
||||
runtime, forms, _ := remote_command_fixture(t)
|
||||
runtime, forms, shortcut := remote_command_fixture(t)
|
||||
if role == "controlled" {
|
||||
worker := runtime.output.speech
|
||||
worker.speaker = &fakeSpeaker{spoken: make(chan string, 16)}
|
||||
@@ -356,6 +362,18 @@ func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
if status.form.Title != "Remote relay status" {
|
||||
t.Fatal("missing connection status")
|
||||
}
|
||||
// Open has already succeeded before the relay can join. Return to
|
||||
// ordinary desktop work without holding either runtime slot or form.
|
||||
status.reply <- internalUIResult{Activated: "desktop"}
|
||||
waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 && len(runtime.interactionSlots) == 0 })
|
||||
if status.ctx.Err() == nil {
|
||||
t.Fatal("background connection retained its form context")
|
||||
}
|
||||
select {
|
||||
case <-closed:
|
||||
t.Fatal("return to desktop disconnected the relay")
|
||||
default:
|
||||
}
|
||||
var delivery tentacle.RemoteSpeechDelivery
|
||||
if role == "controller" {
|
||||
select {
|
||||
@@ -393,6 +411,8 @@ func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
// Joining at the relay is earlier than the Tentacle admitting the
|
||||
// peer. Poll status before testing actual output rather than
|
||||
// depending on a scheduling delay or replaying pre-peer output.
|
||||
runtime.Invoke(t.Context(), shortcut)
|
||||
status = next_remote_form(t, forms)
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
for {
|
||||
encoded, _ := json.Marshal(status.form)
|
||||
@@ -405,6 +425,8 @@ func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
status.reply <- internalUIResult{Activated: "refresh"}
|
||||
status = next_remote_form(t, forms)
|
||||
}
|
||||
status.reply <- internalUIResult{Activated: "desktop"}
|
||||
waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 })
|
||||
worker := runtime.output.speech
|
||||
for _, request := range []speechRequest{
|
||||
{Text: "private form", Cause: "internal-ui"},
|
||||
@@ -455,15 +477,40 @@ func TestRemoteConnectionCommandPrivateRelay(t *testing.T) {
|
||||
t.Fatal("observation leaked private text or lost resolved output/boundaries")
|
||||
}
|
||||
}
|
||||
status.reply <- internalUIResult{Activated: "cancel"}
|
||||
// Reopening returns to this attempt, with no second consent or
|
||||
// saved-key read. Lifecycle retirement must work with no form open.
|
||||
switch ending {
|
||||
case "disconnect":
|
||||
runtime.Invoke(t.Context(), shortcut)
|
||||
status = next_remote_form(t, forms)
|
||||
if status.form.Title != "Remote relay status" {
|
||||
t.Fatal("reopening lost the approved connection")
|
||||
}
|
||||
status.reply <- internalUIResult{Activated: "cancel"}
|
||||
case "lock":
|
||||
runtime.output.gate.SetLocked(true, nil, nil, nil, nil, &brailleWorker{})
|
||||
runtime.output.gate.SetLocked(false, nil, nil, nil, nil, &brailleWorker{})
|
||||
case "handoff":
|
||||
runtime.output.gate.SetSuspended(true)
|
||||
runtime.output.gate.SetSuspended(false)
|
||||
case "disable":
|
||||
runtime.Disable()
|
||||
}
|
||||
select {
|
||||
case <-closed:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Disconnect and close retained the relay")
|
||||
t.Fatal("background connection retained the relay after " + ending)
|
||||
}
|
||||
waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 })
|
||||
if role == "controller" && delivery.Context.Err() == nil {
|
||||
t.Fatal("Disconnect retained remote delivery authority")
|
||||
if role == "controller" {
|
||||
// Socket cancellation precedes the final sequenced lease close.
|
||||
// A reopened status interaction did not issue the original consent,
|
||||
// so its result-slot completion is not the lease-close barrier.
|
||||
select {
|
||||
case <-delivery.Context.Done():
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("Disconnect retained remote delivery authority")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user