Skip to content

Commit ab3b5bf

Browse files
authored
test: cover denied permission outcomes (#51)
1 parent a0faf9a commit ab3b5bf

4 files changed

Lines changed: 71 additions & 5 deletions

File tree

codexadapter/adapter_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package codexadapter_test
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
67
"os"
78
"path/filepath"
89
"reflect"
910
"runtime"
1011
"strings"
1112
"testing"
13+
"time"
1214

1315
"github.com/hecatehq/acp-adapter-kit/acptest"
1416
"github.com/hecatehq/acp-adapter-kit/adaptertest"
@@ -655,6 +657,70 @@ printf '{"method":"item/completed","params":{"item":{"type":"agent_message","id"
655657
}
656658
}
657659

660+
func TestNewServerStopsCodexStreamWhenPermissionDenied(t *testing.T) {
661+
tests := []struct {
662+
name string
663+
option acptest.LiveClientOption
664+
wantReason string
665+
}{
666+
{
667+
name: "rejected",
668+
option: acptest.WithAutoRejectPermissions(),
669+
wantReason: "permission rejected for Run tests",
670+
},
671+
{
672+
name: "cancelled",
673+
option: acptest.WithAutoCancelPermissions(),
674+
wantReason: "permission cancelled for Run tests",
675+
},
676+
}
677+
for _, tt := range tests {
678+
t.Run(tt.name, func(t *testing.T) {
679+
installFakeCommand(t, "codex", `
680+
if [ "$1" != "exec" ]; then
681+
echo "unexpected command: $*" >&2
682+
exit 64
683+
fi
684+
printf '{"method":"permission/requested","params":{"toolCall":{"toolCallId":"tool-1","title":"Run tests","kind":"execute","rawInput":{"command":"go test ./..."}},"options":[{"optionId":"allow","name":"Allow","kind":"allow_once"},{"optionId":"reject","name":"Reject","kind":"reject_once"}]}}\n'
685+
printf '{"method":"item/completed","params":{"item":{"type":"agent_message","id":"msg-1","text":"should not continue"}}}\n'
686+
`)
687+
client := acptest.NewLiveClient(t, codexadapter.NewServer("test"), tt.option)
688+
client.Request("initialize", "initialize", map[string]any{}, time.Second)
689+
createdResponses := client.Request("new-session", "session/new", map[string]any{"cwd": t.TempDir()}, time.Second)
690+
var session struct {
691+
SessionID string `json:"sessionId"`
692+
}
693+
createdResponses[len(createdResponses)-1].ResultInto(t, &session)
694+
695+
responses := client.PromptText("prompt", session.SessionID, "hello", time.Second)
696+
if len(responses) < 3 {
697+
t.Fatalf("responses = %#v, want tool start + permission request + prompt error", responses)
698+
}
699+
permission := decodePermissionRequest(t, responses[1])
700+
if permission.ToolCall.ToolCallID != "tool-1" ||
701+
permission.ToolCall.Title != "Run tests" ||
702+
permission.ToolCall.Kind != "execute" {
703+
t.Fatalf("permission = %#v, want Codex stream permission request", permission)
704+
}
705+
for _, response := range responses {
706+
if response.Method != "session/update" {
707+
continue
708+
}
709+
update := decodeSessionUpdate(t, response)
710+
if update.Update.SessionUpdate == "agent_message_chunk" {
711+
t.Fatalf("responses = %#v, did not expect assistant continuation after denied permission", responses)
712+
}
713+
}
714+
final := responses[len(responses)-1]
715+
if final.Error == nil ||
716+
final.Error.Message != "prompt command failed" ||
717+
!strings.Contains(fmt.Sprint(final.Error.Data), tt.wantReason) {
718+
t.Fatalf("final response = %#v, want prompt command failed with %q", final, tt.wantReason)
719+
}
720+
})
721+
}
722+
}
723+
658724
func TestPromptCommandRequiresWorkspace(t *testing.T) {
659725
_, err := codexadapter.PromptCommand(commandbridge.Session{}, runtimeacp.PromptParams{
660726
Prompt: []runtimeacp.ContentBlock{{Type: "text", Text: "hello"}},

docs/TESTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ production-grade.
144144
command prompts, and prompt completion
145145
- source-shaped Codex stream fixtures for permission requests, including
146146
MCP `server/tool` permission labels, option alias/default handling,
147-
shell/tool lifecycle updates, reasoning chunks, usage, and terminal stop
148-
reasons
147+
shell/tool lifecycle updates, reasoning chunks, usage, terminal stop reasons,
148+
and live ACP client coverage for rejected and cancelled permission outcomes
149149
- auth-required error classification for native Codex failures, including the
150150
HTTP 401 "missing bearer or basic authentication" shape
151151
- opt-in real Codex CLI smoke coverage that requires an authenticated local

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require github.com/spf13/cobra v1.10.1 // indirect
77
require github.com/coder/acp-go-sdk v0.13.5 // indirect
88

99
require (
10-
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.6
10+
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.7
1111
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1212
github.com/spf13/pflag v1.0.9 // indirect
1313
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/coder/acp-go-sdk v0.13.5 h1:LI9jq5xon7xslaYlnoktvTVyDlE37yIk2daT7N9ASYk=
22
github.com/coder/acp-go-sdk v0.13.5/go.mod h1:yKzM/3R9uELp4+nBAwwtkS0aN1FOFjo11CNPy37yFko=
33
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
4-
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.6 h1:udz5nA6Nms6m0DwPzWQJ9SU/gYrHSLAPj1+xQjT4Wi8=
5-
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.6/go.mod h1:7Wl6fSNPnS2t7EgrLspkEQ5YGjp+yaStebHEHIRjqD8=
4+
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.7 h1:i3yl7JhF5JEF10gpXaslnLm9I4aC2+UUyIrSpP7Ked8=
5+
github.com/hecatehq/acp-adapter-kit v0.1.0-alpha.7/go.mod h1:7Wl6fSNPnS2t7EgrLspkEQ5YGjp+yaStebHEHIRjqD8=
66
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
77
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
88
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=

0 commit comments

Comments
 (0)