// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "bufio" "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" "encoding/json" "io" "log/slog" "math/big" "net" "os" "path/filepath" "strings" "testing" "time" "git.stormux.org/storm/cthulhu/internal/atspi" "git.stormux.org/storm/cthulhu/internal/config" "git.stormux.org/storm/cthulhu/internal/input" "git.stormux.org/storm/cthulhu/internal/interaction" "git.stormux.org/storm/cthulhu/internal/review" "git.stormux.org/storm/cthulhu/internal/tentacle" "git.stormux.org/storm/cthulhu/internal/tentaclewire" ) // This uses the source-built external executable and real host/SDK IPC. Only // OpenLocal submissions are synthetic; it cannot prove physical-source input. func remote_command_fixture(t *testing.T, setup ...func(*tentacleRuntime)) (*tentacleRuntime, *consentTestForms, input.Command) { t.Helper() return remote_command_fixture_grants(t, []string{"output-observe", "remote-present"}, setup...) } func remote_command_fixture_grants(t *testing.T, grants []string, setup ...func(*tentacleRuntime)) (*tentacleRuntime, *consentTestForms, input.Command) { t.Helper() program := os.Getenv("CTHULHU_REMOTE_BINARY") if program == "" { t.Skip("requires the separately built Remote executable") } directory := t.TempDir() spec := tentacle.Spec{ID: "remote", Program: []string{program, "--data-dir", filepath.Join(directory, "state")}, Presentation: true, Interaction: true, SessionEvents: true, RemoteGrants: grants} data, _ := json.Marshal([]tentacle.Spec{spec}) launchPath := filepath.Join(directory, "launch.json") if err := os.WriteFile(launchPath, data, 0600); err != nil { t.Fatal(err) } bindings := &tentacleBindingRecorder{} runtime := newTentacleRuntime(slog.New(slog.DiscardHandler), bindings, launchPath, "") runtime.output = tentacleOutputFixture(t) runtime.remotePolicy = tentacle.NewRemotePolicy() runtime.output.gate.remotePolicy = runtime.remotePolicy runtime.newManager = func() (*tentacle.Manager, error) { return tentacle.NewManager(tentacle.DefaultLimits(), nil) } forms := &consentTestForms{requests: make(chan consentFormRequest, 1)} runtime.forms = forms for _, configure := range setup { configure(runtime) } ctx, cancel := context.WithCancel(t.Context()) t.Cleanup(func() { cancel() stopCtx, stopCancel := context.WithTimeout(context.Background(), 5*time.Second) defer stopCancel() if err := runtime.Stop(stopCtx); err != nil { t.Error(err) } }) if err := runtime.Start(ctx); err != nil { t.Fatal(err) } var command input.Command waitTentacle(t, func() bool { runtime.mutex.Lock() defer runtime.mutex.Unlock() for id, registered := range runtime.commands { if registered.id == "remote" { command = id return true } } return false }) runtime.Invoke(ctx, command) return runtime, forms, command } func next_remote_form(t *testing.T, forms *consentTestForms) consentFormRequest { t.Helper() select { case request := <-forms.requests: if request.ctx.Err() != nil { t.Fatal("form already cancelled") } if _, err := interaction.NewSession(request.form); err != nil { t.Fatal("invalid form", err) } return request case <-time.After(5 * time.Second): t.Fatal("Remote did not present its next local form") return consentFormRequest{} } } func select_remote_relay(t *testing.T, forms *consentTestForms, address, role string) consentFormRequest { t.Helper() selection := next_remote_form(t, forms) if selection.form.Title != "Remote" || selection.form.Elements[0].ID != "cancel" { t.Fatal("missing compact connection form") } selectionSession, _ := interaction.NewSession(selection.form) if effect := selectionSession.Handle(interaction.Event{Key: interaction.Enter}); effect.Activated != "cancel" { t.Fatal("empty entries blocked Cancel") } for _, element := range selection.form.Elements { if element.ID == "channel-key" && (!element.Secret || element.Text != "") { t.Fatal("key unmasked or prefilled") } // The only Tentacle checkbox is remembering the host and key; // access is chosen solely in the reader-owned approval form. if element.Kind == interaction.CheckBox && element.ID != "remember" { t.Fatal("permissions duplicated") } } selection.reply <- internalUIResult{Activated: "options", Values: map[string]interaction.Value{"host": {Text: address}, "role": {Selected: role}}} options := next_remote_form(t, forms) if options.form.Title != "Remote options" { t.Fatal("missing options") } return options } func approve_remote_form(t *testing.T, forms *consentTestForms, consent consentFormRequest, role string, extra ...string) consentFormRequest { t.Helper() if !strings.Contains(consent.form.Title, "Remote access for remote") { t.Fatal("missing reader approval") } for _, element := range consent.form.Elements { if element.Checked { t.Fatal("permission preselected") } } grant := "remote-present" if role == "controlled" { grant = "output-observe" } values := map[string]interaction.Value{grant: {Checked: true}} for _, name := range extra { values[name] = interaction.Value{Checked: true} } consent.reply <- internalUIResult{Activated: "approve", Values: values} progress := next_remote_form(t, forms) if progress.form.Title != "Connecting Remote" { t.Fatal("approval did not start connection", progress.form.Title) } return progress } func remote_consent_listener(t *testing.T) net.Listener { t.Helper() key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } template := &x509.Certificate{SerialNumber: big.NewInt(1), NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}} der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) if err != nil { t.Fatal(err) } listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{Certificates: []tls.Certificate{{Certificate: [][]byte{der}, PrivateKey: key}}, MinVersion: tls.VersionTLS12}) if err != nil { t.Fatal(err) } t.Cleanup(func() { listener.Close() }) go func() { for { connection, err := listener.Accept() if err != nil { return } go func() { defer connection.Close() connection.SetDeadline(time.Now().Add(5 * time.Second)) io.Copy(io.Discard, connection) }() } }() return listener } func prepare_remote_consent(t *testing.T, forms *consentTestForms, address, role string) consentFormRequest { t.Helper() options := select_remote_relay(t, forms, address, role) options.reply <- internalUIResult{Activated: "inspect-certificate"} next_remote_form(t, forms) // Cancellable progress. review := next_remote_form(t, forms) // Poll replaces progress automatically. if review.form.Title != "Review Remote relay certificate" { t.Fatal("inspection did not advance") } review.reply <- internalUIResult{Activated: "trust-once"} options = next_remote_form(t, forms) options.reply <- internalUIResult{Activated: "connect", Values: map[string]interaction.Value{"channel-key": {Text: "synthetic-fixture"}}} next_remote_form(t, forms) // Verified preflight; no lease/key sent. return next_remote_form(t, forms) } func TestRemoteConnectionCommandConsentDenialAndPolicyCancellation(t *testing.T) { for _, mode := range []string{"deny", "empty", "lock", "handoff"} { t.Run(mode, func(t *testing.T) { runtime, forms, command := remote_command_fixture(t) listener := remote_consent_listener(t) consent := prepare_remote_consent(t, forms, listener.Addr().String(), "controller") switch mode { case "deny": consent.reply <- internalUIResult{Cancelled: true} case "empty": consent.reply <- internalUIResult{Activated: "approve"} case "lock", "handoff": runtime.remotePolicy.SetAvailable(false) runtime.remotePolicy.SetAvailable(true) } waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) select { case form := <-forms.requests: t.Fatal("denied consent opened connection controls", form.form.Title) default: } // The command must recover after a denied/cancelled review, without // keeping an awaiting flag or reviving the previous approval. runtime.Invoke(t.Context(), command) form := next_remote_form(t, forms) if form.form.Title != "Remote" { t.Fatal("stale setup survived cancellation") } form.reply <- internalUIResult{Cancelled: true} waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) }) } } func TestRemoteConnectionCommandCancelsInspectionOnHandoff(t *testing.T) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer listener.Close() accepted := make(chan struct{}) closed := make(chan struct{}) go func() { connection, err := listener.Accept() if err != nil { return } defer connection.Close() connection.SetDeadline(time.Now().Add(5 * time.Second)) close(accepted) io.Copy(io.Discard, connection) close(closed) }() runtime, forms, _ := remote_command_fixture(t) credentials := select_remote_relay(t, forms, listener.Addr().String(), "controller") credentials.reply <- internalUIResult{Activated: "inspect-certificate"} inspection := next_remote_form(t, forms) select { case <-accepted: case <-time.After(5 * time.Second): t.Fatal("inspection did not open its socket") } runtime.remotePolicy.SetAvailable(false) runtime.remotePolicy.SetAvailable(true) select { case <-inspection.ctx.Done(): case <-time.After(time.Second): t.Fatal("handoff retained the inspection form") } select { case <-closed: case <-time.After(time.Second): t.Fatal("handoff retained a pre-lease inspection socket") } waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) } func TestRemoteConnectionCommandPrivateRelay(t *testing.T) { for _, test := range []struct { role, ending string playback bool }{ {"controller", "disconnect", false}, {"controlled", "disconnect", false}, {"controller", "lock", false}, {"controlled", "lock", false}, {"controller", "handoff", false}, {"controlled", "handoff", false}, {"controller", "disable", false}, {"controlled", "disable", false}, {"controller", "disconnect", true}, {"controller", "lock", true}, {"controller", "handoff", true}, {"controller", "disable", true}, {"controller", "backend-failure", true}, {"controlled", "missing-helper", false}, } { role, ending := test.role, test.ending keyboard := role == "controlled" && (ending == "disconnect" || ending == "missing-helper") name := role + "/" + ending if test.playback { name += "/production-speech" } t.Run(name, func(t *testing.T) { remoteText := " " + strings.Repeat("é\t世界 ", 1500) + " " key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } template := &x509.Certificate{SerialNumber: big.NewInt(1), NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}} der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) if err != nil { t.Fatal(err) } listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{Certificates: []tls.Certificate{{Certificate: [][]byte{der}, PrivateKey: key}}, MinVersion: tls.VersionTLS12}) if err != nil { t.Fatal(err) } defer listener.Close() probed, joined, closed := make(chan struct{}), make(chan struct{}), make(chan struct{}) speechReady := make(chan struct{}) relayOutput := make(chan []byte, 64) go func() { probe, err := listener.Accept() if err != nil { return } probe.SetDeadline(time.Now().Add(5 * time.Second)) data, _ := io.ReadAll(probe) probe.Close() if len(data) != 0 { t.Error("certificate inspection sent application data") } close(probed) preflight, err := listener.Accept() if err != nil { return } preflight.SetDeadline(time.Now().Add(5 * time.Second)) preflightData, _ := io.ReadAll(preflight) preflight.Close() if len(preflightData) != 0 { t.Error("preflight sent application data before approval") } connection, err := listener.Accept() if err != nil { return } defer connection.Close() connection.SetDeadline(time.Now().Add(10 * time.Second)) reader := bufio.NewReader(connection) if _, err := reader.ReadBytes('\n'); err != nil { return } line, err := reader.ReadBytes('\n') var join struct { Type string `json:"type"` Channel string `json:"channel"` ConnectionType string `json:"connection_type"` } wantRole := "master" if role == "controlled" { wantRole = "slave" } if err != nil || json.Unmarshal(line, &join) != nil || join.Type != "join" || join.Channel != "synthetic-fixture" || join.ConnectionType != wantRole { t.Error("invalid relay join or wrong selected role") return } peerRole := "master" if role == "controller" { peerRole = "slave" } io.WriteString(connection, `{"type":"channel_joined","channel":"synthetic-fixture","clients":[{"id":7,"connection_type":"`+peerRole+`"}]}`+"\n") close(joined) if keyboard { select { case <-speechReady: case <-t.Context().Done(): return } // NVDA and Cthulhu controllers use these same key frames. for _, pressed := range []bool{true, false} { json.NewEncoder(connection).Encode(map[string]any{"type": "key", "origin": 7, "vk_code": 65, "extended": false, "pressed": pressed}) } } if role == "controller" { select { case <-speechReady: case <-t.Context().Done(): return } json.NewEncoder(connection).Encode(map[string]any{"type": "speak", "origin": 7, "priority": 2, "sequence": []any{ []any{"CharacterModeCommand", map[string]any{"state": true}}, []any{"LangChangeCommand", map[string]any{"lang": "fr"}}, remoteText, []any{"EndUtteranceCommand", map[string]any{}}, }}) } for { frame, err := reader.ReadBytes('\n') if err != nil { break } relayOutput <- frame } close(closed) }() var server *isolatedVoiceServer var voice *remoteSpeechVoice var rendered isolatedVoiceUtterance var configure []func(*tentacleRuntime) if test.playback { speaker, fixture := isolated_voice_fixture(t) server = fixture configure = append(configure, func(runtime *tentacleRuntime) { worker := runtime.output.speech worker.speaker = speaker if err := worker.Start(t.Context()); err != nil { t.Fatal(err) } t.Cleanup(func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() if err := worker.Stop(ctx); err != nil { t.Error(err) } }) }) } grants := []string{"output-observe", "remote-present"} var keyLog string if keyboard { grants = append(grants, "keyboard-inject") helper, log := keyboard_owner_helper(t) keyLog = log if ending == "missing-helper" { helper = filepath.Join(t.TempDir(), "absent") } configure = append(configure, func(runtime *tentacleRuntime) { runtime.remoteInputHelper = helper }) } runtime, forms, shortcut := remote_command_fixture_grants(t, grants, configure...) if role == "controlled" { worker := runtime.output.speech worker.speaker = &observationSpeaker{fakeSpeaker{spoken: make(chan string, 64)}} worker.textTransform = func(text string) string { return strings.ReplaceAll(text, "colour", "color") } worker.reading = func() config.ReadingSettings { return runtime.output.live.Load().Reading } if err := worker.Start(t.Context()); err != nil { t.Fatal(err) } t.Cleanup(func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() if err := worker.Stop(ctx); err != nil { t.Error(err) } }) } // Keep the explicit mailbox assembly fixture alongside production // playback, which is attached by the actual runtime startup path. var deliveries <-chan tentacle.RemoteSpeechDelivery if role == "controller" && !test.playback { runtime.mutex.Lock() for _, command := range runtime.commands { if command.id == "remote" { deliveries = command.session.EnableRemoteSpeech() break } } runtime.mutex.Unlock() if deliveries == nil { t.Fatal("Remote fixture session unavailable") } } credentials := select_remote_relay(t, forms, listener.Addr().String(), role) credentials.reply <- internalUIResult{Activated: "inspect-certificate"} inspection := next_remote_form(t, forms) select { case <-probed: case <-time.After(5 * time.Second): t.Fatal("certificate probe did not finish") } inspection = next_remote_form(t, forms) if inspection.form.Title != "Review Remote relay certificate" { t.Fatal("certificate result was not automatic") } inspection.reply <- internalUIResult{Activated: "trust-once"} credentials = next_remote_form(t, forms) credentials.reply <- internalUIResult{Activated: "connect", Values: map[string]interaction.Value{"channel-key": {Text: "synthetic-fixture"}}} next_remote_form(t, forms) // Verified certificate progress. consent := next_remote_form(t, forms) var extra []string if keyboard { extra = append(extra, "keyboard-inject") } status := approve_remote_form(t, forms, consent, role, extra...) select { case <-joined: case <-time.After(5 * time.Second): t.Fatal("Remote did not join the private relay") } if status.form.Title != "Connecting Remote" { t.Fatal("missing connecting form") } // Open has already succeeded before the relay can join. Return to // ordinary desktop work without holding either runtime slot or form. // Poll returns to the desktop after the verified join. 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") } for _, closeMode := range []string{"close", "escape"} { runtime.Invoke(t.Context(), shortcut) reopened := next_remote_form(t, forms) if reopened.form.Title != "Remote status" { t.Fatal("status reopening requested new consent") } reply := internalUIResult{Activated: "desktop"} if closeMode == "escape" { reply = internalUIResult{Cancelled: true} } reopened.reply <- reply waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 && len(runtime.interactionSlots) == 0 }) select { case <-closed: t.Fatal(closeMode + " disconnected the live lease") default: } } if test.playback { waitTentacle(t, func() bool { worker := runtime.output.speech worker.mutex.Lock() defer worker.mutex.Unlock() return worker.activeDone == nil && len(worker.pending) == 0 }) if ending == "backend-failure" { server.mutex.Lock() server.queryReplies = map[string]string{"GET LANGUAGE": "400 Rejected\r\n"} server.mutex.Unlock() } } close(speechReady) if ending == "missing-helper" { remote_stream_wait(t, closed) if _, err := os.Stat(keyLog); !os.IsNotExist(err) { t.Fatal("missing helper unexpectedly delivered input", err) } return } if keyboard { fixture := &keyboardOwnerFixture{log: keyLog} fixture.wait(t, func() bool { effects := fixture.effects() return strings.Contains(effects, " a true\n") && strings.Contains(effects, " a false\n") }) } if ending == "backend-failure" { remote_stream_wait(t, closed) // Backend failure retires the lease, while the process and // mailbox owner remain usable for an explicit new connection. runtime.Invoke(t.Context(), shortcut) selection := next_remote_form(t, forms) if selection.form.Title == "Remote status" { // An ended attempt remains inspectable until explicitly // closed, as with ordinary transport failure. selection.reply <- internalUIResult{Activated: "cancel"} waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) runtime.Invoke(t.Context(), shortcut) selection = next_remote_form(t, forms) } if selection.form.Title != "Remote" { t.Fatal("backend failure retained a connected workflow") } selection.reply <- internalUIResult{Cancelled: true} waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) server.mutex.Lock() for _, connection := range server.connections { if connection.priority == "TEXT" && connection.pending != "" { t.Error("failed rendering exposed a speech prefix") } } server.mutex.Unlock() return } select { case <-closed: t.Fatal("return to desktop disconnected the relay") default: } var delivery tentacle.RemoteSpeechDelivery if test.playback { utterance := server.next_utterance(t) for utterance.priority == "MESSAGE" { // Returning to desktop produces local status on its ordinary // client. The production Remote voice must stay separate. utterance = server.next_utterance(t) } rendered = utterance if utterance.text != remoteText || utterance.language != "fr" || utterance.spelling != "on" || utterance.ssml != "off" || utterance.priority != "TEXT" { t.Fatal("production owner lost literal speech, metadata or local priority") } speaker := runtime.output.speech.speaker.(*configuredSpeaker) speaker.mu.RLock() voice = speaker.remote speaker.mu.RUnlock() if voice == nil { t.Fatal("production owner did not acquire isolated synthesis") } } else if role == "controller" { select { case delivery = <-deliveries: case <-time.After(5 * time.Second): t.Fatal("split relay speech did not reach the reader owner") } var text strings.Builder ends := 0 for _, speech := range delivery.Speeches { if !tentaclewire.ValidRemoteSpeech(speech) || speech.Priority != 2 { t.Fatal("invalid speech fragment or changed priority") } for _, part := range speech.Parts { if part.End { ends++ } else { if !part.Characters || part.Language != "fr" { t.Fatal("split speech lost character/language state") } text.WriteString(part.Text) } } } if len(delivery.Speeches) < 2 || text.String() != remoteText || ends != 1 { t.Fatal("reader owner received partial or altered utterance") } select { case <-deliveries: t.Fatal("one relay utterance became multiple deliveries") default: } delivery.Release() } else { // 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) if strings.Contains(string(encoded), "Connected to the other computer.") { break } if time.Now().After(deadline) { t.Fatal("peer did not become ready") } 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"}, {Text: "password", Role: "password text", origin: speechOriginLocal}, {Text: "local status", origin: speechOriginStatus}, {Text: "colour " + remoteText, origin: speechOriginLocal}, } { if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } } request := document_observation_request(t, "read-all", review.Result{ ReadAllContext: t.Context(), ReadAllLines: []atspi.DocumentLine{{Contents: []atspi.DocumentContent{{ Snapshot: atspi.Snapshot{Role: "text"}, Text: "First colour. Second colour.", }}}}, }) if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } request = current_line_observation_request(t, review.Result{CurrentLineBrowse: true, DocumentCaretText: "Current colour.", DocumentPosition: review.DocumentPosition{Snapshot: atspi.Snapshot{Role: "static"}}, DocumentCaretContext: []atspi.Snapshot{{Role: "static"}, {Role: "paragraph"}, {Role: "document web"}}}) if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } // A joined line can contain a private neighboring fragment even // when its caret source and the line's presentation are ordinary. caret := atspi.Snapshot{Role: "static"} privateLine := atspi.DocumentLine{Contents: []atspi.DocumentContent{{Snapshot: caret, Text: "private joined text"}}, SourceContexts: [][]atspi.Snapshot{{caret}, {{Role: "static"}, {Role: "password text"}}}} for _, kind := range []string{"caret", "read-all", "current-line"} { request := document_observation_request(t, kind, review.Result{ Moved: true, DocumentCaretCommand: review.CaretNextLine, DocumentPosition: review.DocumentPosition{Snapshot: caret}, DocumentCaretText: "private joined text", DocumentCaretContext: []atspi.Snapshot{caret, {Role: "document web"}}, DocumentCaretLine: &privateLine, ReadAllContext: t.Context(), ReadAllLines: []atspi.DocumentLine{privateLine}, }) if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } } for _, private := range []bool{false, true} { result := native_current_line_result() if private { result.DocumentCaretText = "private native text" result.DocumentCaretContext[1].Role = "password text" } if err := worker.EnqueueAndWait(t.Context(), current_line_observation_request(t, result)); err != nil { t.Fatal(err) } } for _, kind := range []string{"object", "line"} { for _, privacy := range []string{"ordinary", "snapshot-ancestor", "stale"} { request := review_observation_request(t, kind, review_observation_result(t, kind, privacy)) if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } } } for _, privacy := range []string{"ordinary", "snapshot-ancestor", "missing", "stale"} { if err := worker.EnqueueAndWait(t.Context(), where_am_i_observation_request(t, privacy)); err != nil { t.Fatal(err) } } for _, privacy := range []string{"ordinary", "ancestor", "changed-text", "failure"} { if err := worker.EnqueueAndWait(t.Context(), native_where_am_i_request(t, privacy)); err != nil { t.Fatal(err) } } list := atspi.Snapshot{BusName: ":1.42", Path: "/relay-list", Role: "list", Name: "Relay items"} privateList := list privateList.Path, privateList.Name = "/private-list", "Private relay label" for _, request := range []speechRequest{ {origin: speechOriginLocal, Text: "Relay inside", Context: []atspi.Snapshot{list}}, {origin: speechOriginLocal, Text: "Relay outside", Context: []atspi.Snapshot{}}, {origin: speechOriginSecret, Text: "Private relay content", Context: []atspi.Snapshot{privateList}}, {origin: speechOriginLocal, Text: "Relay resumed", Context: []atspi.Snapshot{}}, } { if err := worker.EnqueueAndWait(t.Context(), request); err != nil { t.Fatal(err) } } worker.InterruptForUser() var received strings.Builder ends, frames := 0, 0 readOutput: for { select { case frame := <-relayOutput: var message struct { Type string `json:"type"` Sequence []json.RawMessage `json:"sequence"` } if err := json.Unmarshal(frame, &message); err != nil { t.Fatal(err) } if message.Type == "cancel" { break readOutput } if message.Type != "speak" { t.Fatal("unexpected observed relay message", message.Type) } frames++ for _, raw := range message.Sequence { var text string if json.Unmarshal(raw, &text) == nil { received.WriteString(text) } else if strings.Contains(string(raw), "EndUtteranceCommand") { ends++ } else { t.Fatal("unexpected observed speech command", string(raw)) } } case <-time.After(5 * time.Second): t.Fatal("reader output did not reach the relay in order") } } if received.String() != "color "+remoteText+"First color.Second color.Current color.Native color.Review color.staticFlat color. Review color.Review color.staticNative color.textRelay items listRelay insideLeaving Relay items list.Relay outsideRelay resumed" || frames < 18 || ends != 17 { t.Fatalf("observation leaked private text or lost resolved output/boundaries: frames=%d ends=%d suffix=%q", frames, ends, received.String()[max(0, received.Len()-160):]) } } // 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 status" { t.Fatal("reopening lost the approved connection") } status.reply <- internalUIResult{Activated: "disconnect"} 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("background connection retained the relay after " + ending) } waitTentacle(t, func() bool { return len(runtime.resultSlots) == 0 }) if test.playback { remote_stream_wait(t, voice.closed) server.mutex.Lock() pending := server.connections[rendered.id].pending server.mutex.Unlock() if pending != "" { t.Fatal("revoked production speech survived backend cleanup") } } else 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") } } }) } }