96 lines
3.3 KiB
Go
96 lines
3.3 KiB
Go
package tentacle
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFramingBoundsAndRequiresNewline(t *testing.T) {
|
|
for _, testCase := range []struct {
|
|
name string
|
|
input string
|
|
want error
|
|
}{
|
|
{"valid", strings.Repeat("x", MaxFrameBytes) + "\n", nil},
|
|
{"oversized", strings.Repeat("x", MaxFrameBytes+1) + "\n", ErrLimit},
|
|
{"unterminated", `{}`, io.ErrUnexpectedEOF},
|
|
{"empty", "", io.EOF},
|
|
} {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
_, err := read_frame(bufio.NewReaderSize(strings.NewReader(testCase.input), 32))
|
|
if !errors.Is(err, testCase.want) {
|
|
t.Fatalf("got %v, want %v", err, testCase.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResponseValidation(t *testing.T) {
|
|
for _, valid := range []string{
|
|
`{"jsonrpc":"2.0","id":"1","result":null}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":{"text":"é猫😀é"}}`,
|
|
`{"jsonrpc":"2.0","id":"1","error":{"code":-32601,"message":"Unknown method"}}`,
|
|
} {
|
|
if _, err := decode_response([]byte(valid)); err != nil {
|
|
t.Errorf("valid response rejected: %v", err)
|
|
}
|
|
}
|
|
for _, invalid := range []string{
|
|
`[]`, `null`, `{}`, `{"jsonrpc":"2.0","id":"1"}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":null,"error":null}`,
|
|
`{"jsonrpc":"2.0","id":"1","error":null}`,
|
|
`{"jsonrpc":"2.0","id":1,"result":null}`,
|
|
`{"jsonrpc":"1.0","id":"1","result":null}`,
|
|
`{"jsonrpc":"2.0","id":"1","id":"2","result":null}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":{"x":0,"x":1}}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":null,"extra":1}`,
|
|
`{"jsonrpc":"2.0","id":"1","error":{"message":"missing code"}}`,
|
|
`{"jsonrpc":"2.0","id":"1","error":{"code":null,"message":"null code"}}`,
|
|
`{"jsonrpc":"2.0","id":"1","error":{"code":1,"message":null}}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":"` + string([]byte{0xff}) + `"}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":` + strings.Repeat("[", 40) + "0" + strings.Repeat("]", 40) + `}`,
|
|
`{"jsonrpc":"2.0","id":"1","result":null} {}`,
|
|
} {
|
|
if _, err := decode_response([]byte(invalid)); err == nil {
|
|
t.Errorf("invalid response accepted: %q", invalid)
|
|
}
|
|
}
|
|
if strings.Contains((&RPCError{Code: 1, Message: "private text"}).Error(), "private") {
|
|
t.Fatal("remote text leaked through Error")
|
|
}
|
|
}
|
|
|
|
func FuzzResponseDecoder(f *testing.F) {
|
|
f.Add([]byte(`{"jsonrpc":"2.0","id":"1","result":null}`))
|
|
f.Add([]byte(`{"jsonrpc":"2.0","id":"1","error":{"code":1,"message":"bad"}}`))
|
|
f.Fuzz(func(t *testing.T, data []byte) {
|
|
if len(data) > MaxFrameBytes {
|
|
t.Skip()
|
|
}
|
|
_, _ = decode_response(data)
|
|
_, _ = read_frame(bufio.NewReader(bytes.NewReader(data)))
|
|
})
|
|
}
|
|
|
|
func TestSettingsRegistrationRequiresOneInteractiveCommand(t *testing.T) {
|
|
command := Command{ID: "settings", Title: "Settings", Settings: true, Interaction: true, Presentation: true}
|
|
registration := Registration{Protocol: Version{Major: 1}, Session: "test", Commands: []Command{command}}
|
|
if err := validate_registration(registration, "test"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
registration.Commands[0].Interaction = false
|
|
if err := validate_registration(registration, "test"); err == nil {
|
|
t.Fatal("accepted a settings handler without interaction")
|
|
}
|
|
registration.Commands[0] = command
|
|
command.ID = "other-settings"
|
|
registration.Commands = append(registration.Commands, command)
|
|
if err := validate_registration(registration, "test"); err == nil {
|
|
t.Fatal("accepted two settings entry points")
|
|
}
|
|
}
|