Files
cthulhu/internal/tentacle/remote_capture_test.go
Chase CichorzandClaude Opus 5.5 2d5327adf4 Add Remote controller capture IPC to the host and both SDKs
remote.focus lets a controller keyboard-capture lease ask core to enter
or leave remote focus; the reply acknowledges admission only. Core
reports the actual state with remote.focus events and forwards routed
transitions as remote.input events carrying the source keycode and, on
fresh presses, a canonical key name. A full focus mailbox revokes the
lease; revocation drains pending focus work. No native capture owner is
attached yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:06:38 -05:00

148 lines
6.1 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package tentacle
import (
"encoding/json"
"errors"
"strings"
"testing"
"git.stormux.org/storm/cthulhu/internal/tentaclewire"
)
func focusRequest(lease tentaclewire.RemoteLease, sequence uint64, remote bool) tentaclewire.RemoteFocusRequest {
return tentaclewire.RemoteFocusRequest{RemoteOperation: tentaclewire.RemoteOperation{Lease: lease.Lease, Epoch: lease.Epoch, Sequence: sequence}, Remote: remote}
}
func nextSDKEvent(t *testing.T, session *Session) tentaclewire.SessionEvent {
t.Helper()
var event tentaclewire.SessionEvent
if err := json.Unmarshal(invokeSessionSDK(t, session, "event", nil), &event); err != nil {
t.Fatal(err)
}
return event
}
func TestRemoteCaptureSDKFocusAndInputEvents(t *testing.T) {
for _, language := range []string{"go", "python"} {
t.Run(language, func(t *testing.T) {
manager := test_manager(t, DefaultLimits(), nil)
session := startSessionSDK(t, manager, language, "capture", "keyboard-capture")
lease := speech_fixture_lease(t, session, "controller", []string{"remote-present", "keyboard-capture"})
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 1, true)); code != -32601 {
t.Fatal("focus admitted without capture owner", code)
}
deliveries := session.EnableRemoteCapture()
if session.EnableRemoteCapture() != deliveries {
t.Fatal("attachment created another capture mailbox")
}
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 1, true)); code != 0 {
t.Fatal("valid focus request refused", code)
}
delivery := <-deliveries
if !delivery.Remote || delivery.Lease != lease || delivery.Sequence != 1 || delivery.Context.Err() != nil {
t.Fatal("focus delivery changed identity or lifetime", delivery)
}
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 1, false)); code != -32001 {
t.Fatal("replayed focus sequence accepted", code)
}
if err := session.PublishRemoteFocus(lease, tentaclewire.RemoteFocusRemote, "requested"); err != nil {
t.Fatal(err)
}
if event := nextSDKEvent(t, session); event.Kind != "remote.focus" || event.Focus != "remote" || event.Reason != "requested" || event.Lease != lease.Lease {
t.Fatal("bad focus event", event)
}
keys := []tentaclewire.RemoteCapturedKey{
{Code: 50, Name: "Shift_L", Pressed: true},
{Code: 38, Name: "a", Pressed: true},
{Code: 38, Pressed: true, Repeat: true},
{Code: 38},
{Code: 50},
}
for _, key := range keys {
if err := session.PublishRemoteInput(lease, key); err != nil {
t.Fatal(err)
}
}
for _, key := range keys {
event := nextSDKEvent(t, session)
if event.Kind != "remote.input" || event.Input == nil || *event.Input != key || event.Epoch != lease.Epoch {
t.Fatal("captured key changed in transit", event)
}
}
for _, invalid := range []tentaclewire.RemoteCapturedKey{
{Code: 7, Name: "a", Pressed: true},
{Code: 38, Name: "A", Pressed: true},
{Code: 38, Name: "a"}, // Release carries no name.
{Code: 38, Repeat: true},
} {
if !errors.Is(session.PublishRemoteInput(lease, invalid), ErrPermission) {
t.Fatal("invalid captured key published", invalid)
}
}
if !errors.Is(session.PublishRemoteFocus(lease, "remote", "network"), ErrPermission) {
t.Fatal("unknown focus reason published")
}
session.RevokeRemote()
if event := nextSDKEvent(t, session); event.Kind != "remote.revoked" {
t.Fatal("missing revocation", event)
}
if !errors.Is(session.PublishRemoteInput(lease, keys[0]), ErrPermission) || !errors.Is(session.PublishRemoteFocus(lease, "local", "local"), ErrPermission) {
t.Fatal("retired lease published capture events")
}
})
}
}
func TestRemoteCaptureSDKPermissionAndParameters(t *testing.T) {
for _, language := range []string{"go", "python"} {
t.Run(language, func(t *testing.T) {
manager := test_manager(t, DefaultLimits(), nil)
session := startSessionSDK(t, manager, language, "capture", "keyboard-capture", "keyboard-inject")
deliveries := session.EnableRemoteCapture()
lease := speech_fixture_lease(t, session, "controller", []string{"remote-present"})
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 1, true)); code != -32001 {
t.Fatal("speech-only controller requested capture", code)
}
if !errors.Is(session.PublishRemoteInput(lease, tentaclewire.RemoteCapturedKey{Code: 38, Name: "a", Pressed: true}), ErrPermission) {
t.Fatal("input published without keyboard-capture")
}
lease = speech_fixture_lease(t, session, "controlled", []string{"keyboard-inject"})
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 1, true)); code != -32001 {
t.Fatal("controlled role requested capture", code)
}
lease = speech_fixture_lease(t, session, "controller", []string{"keyboard-capture"})
data, _ := json.Marshal(focusRequest(lease, 1, true))
for _, raw := range []string{
strings.Replace(string(data), `"remote":true`, `"remote":null`, 1),
strings.Replace(string(data), `,"remote":true`, ``, 1),
strings.Replace(string(data), `"sequence":1`, `"sequence":1,"origin":"local"`, 1),
} {
if _, code := remoteCall(t, session, "remote.focus", json.RawMessage(raw)); code != -32602 {
t.Fatal("invalid focus parameters admitted", code, raw)
}
}
if len(deliveries) != 0 {
t.Fatal("denied focus reached the owner")
}
for sequence := uint64(1); sequence <= tentaclewire.RemoteFocusQueueSize; sequence++ {
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, sequence, sequence%2 == 1)); code != 0 {
t.Fatal("focus request refused before saturation", code)
}
}
if _, code := remoteCall(t, session, "remote.focus", focusRequest(lease, 99, false)); code != -32002 {
t.Fatal("full focus queue did not fail", code)
}
if _, err := session.RemoteContext(lease, "keyboard-capture"); err == nil {
t.Fatal("full focus queue left capture consent active")
}
if len(deliveries) != 0 {
t.Fatal("revocation did not drain pending focus work")
}
})
}
}