Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions json.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,21 +194,21 @@ func FormatJSONSimpleValue(formatter Formatter, value spanner.GenericColumnValue
func validateRawJSONValue(code sppb.TypeCode, value *structpb.Value) (string, error) {
stringValue, ok := value.GetKind().(*structpb.Value_StringValue)
if !ok {
return "", fmt.Errorf("invalid %s JSON payload kind %T: want string value", code, value.GetKind())
return "", fmt.Errorf("%w: invalid %s JSON payload kind %T: want string value", ErrMalformedWire, code, value.GetKind())
}

switch code {
case sppb.TypeCode_INT64, sppb.TypeCode_ENUM:
trimmed := strings.TrimSpace(stringValue.StringValue)
if !json.Valid([]byte(stringValue.StringValue)) {
return "", fmt.Errorf("invalid %s JSON payload %q", code, stringValue.StringValue)
return "", fmt.Errorf("%w: invalid %s JSON payload %q", ErrMalformedWire, code, stringValue.StringValue)
}
if _, err := strconv.ParseInt(trimmed, 10, 64); err != nil {
return "", fmt.Errorf("invalid %s JSON payload %q: %w", code, stringValue.StringValue, err)
return "", fmt.Errorf("%w: invalid %s JSON payload %q: %w", ErrMalformedWire, code, stringValue.StringValue, err)
}
case sppb.TypeCode_JSON:
if !json.Valid([]byte(stringValue.StringValue)) {
return "", fmt.Errorf("invalid %s JSON payload %q", code, stringValue.StringValue)
return "", fmt.Errorf("%w: invalid %s JSON payload %q", ErrMalformedWire, code, stringValue.StringValue)
}
}

Expand Down
62 changes: 62 additions & 0 deletions json_malformed_wire_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package spanvalue

import (
"errors"
"strconv"
"testing"

"cloud.google.com/go/spanner"
sppb "cloud.google.com/go/spanner/apiv1/spannerpb"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/types/known/structpb"
)

func TestJSONSpecialScalarMalformedWire(t *testing.T) {
t.Parallel()
for _, code := range []sppb.TypeCode{sppb.TypeCode_BOOL, sppb.TypeCode_INT64, sppb.TypeCode_ENUM, sppb.TypeCode_JSON} {
t.Run(code.String(), func(t *testing.T) {
t.Parallel()
_, err := JSONFormatConfig().FormatToplevelColumn(spanner.GenericColumnValue{Type: &sppb.Type{Code: code}, Value: structpb.NewListValue(&structpb.ListValue{})})
if !errors.Is(err, ErrMalformedWire) || errors.Is(err, ErrUnknownType) {
t.Errorf("malformed kind classification: %v", err)
}
})
}
for _, code := range []sppb.TypeCode{sppb.TypeCode_INT64, sppb.TypeCode_ENUM} {
for _, tt := range []struct {
wire string
cause error
}{{"12.5", strconv.ErrSyntax}, {"9223372036854775808", strconv.ErrRange}} {
t.Run(code.String()+"/"+tt.wire, func(t *testing.T) {
t.Parallel()
_, err := JSONFormatConfig().FormatToplevelColumn(spanner.GenericColumnValue{Type: &sppb.Type{Code: code}, Value: structpb.NewStringValue(tt.wire)})
var numErr *strconv.NumError
if !errors.Is(err, ErrMalformedWire) || !errors.Is(err, tt.cause) || !errors.As(err, &numErr) {
t.Fatalf("error lost classification or parse cause: %v", err)
}
if numErr.Num != tt.wire {
t.Errorf("Num=%q, want %q", numErr.Num, tt.wire)
}
})
}
}
}

func TestJSONSpecialScalarPreservesWire(t *testing.T) {
t.Parallel()
for _, tt := range []struct {
code sppb.TypeCode
wire string
}{{sppb.TypeCode_INT64, " 42 "}, {sppb.TypeCode_ENUM, "\n-1\t"}, {sppb.TypeCode_JSON, "{\n \"b\":1, \"a\":2\n}"}} {
t.Run(tt.code.String(), func(t *testing.T) {
t.Parallel()
got, err := JSONFormatConfig().FormatToplevelColumn(spanner.GenericColumnValue{Type: &sppb.Type{Code: tt.code}, Value: structpb.NewStringValue(tt.wire)})
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tt.wire, got); diff != "" {
t.Errorf("(-want +got):\n%s", diff)
}
})
}
}
3 changes: 3 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ func TestJSONFormatConfig_InvalidRawPayload(t *testing.T) {
if !strings.Contains(err.Error(), tt.wantErr) {
t.Fatalf("error = %q, want substring %q", err, tt.wantErr)
}
if !errors.Is(err, ErrMalformedWire) {
t.Fatalf("error = %v, want ErrMalformedWire", err)
}
})
}
}
Expand Down
Loading