Add duplicate key and field case-sensitivity CBOR decode tests.

1. Decoding map with duplicate keys into struct or map produces error.
2. Decoding a map into a Go struct matches json field tag names case-sensitively.
3. When decoding a map into a Go struct, a case-insensitive match between a key and a json field tag
   name is treated the same as no match.

Signed-off-by: Vu Dinh <vudinh@outlook.com>
This commit is contained in:
Vu Dinh 2024-02-15 15:11:52 -05:00 committed by Ben Luddy
parent 57fc5d2401
commit 4fe78a17dd
No known key found for this signature in database
GPG Key ID: A6551E73A5974C30
2 changed files with 180 additions and 0 deletions

View File

@ -45,6 +45,177 @@ func TestDecode(t *testing.T) {
want interface{}
assertOnError func(t *testing.T, e error)
}{
{
name: "reject duplicate negative int keys into struct",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a220012002"), // {-1: 1, -1: 2}
into: struct{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(-1), Index: 1}),
},
{
name: "reject duplicate negative int keys into map",
in: hex("a220012002"), // {-1: 1, -1: 2}
into: map[int64]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(-1), Index: 1}),
},
{
name: "reject duplicate positive int keys into struct",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a201010102"), // {1: 1, 1: 2}
into: struct{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(1), Index: 1}),
},
{
name: "reject duplicate positive int keys into map",
in: hex("a201010102"), // {1: 1, 1: 2}
into: map[int64]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: int64(1), Index: 1}),
},
{
name: "reject duplicate text string keys into struct",
in: hex("a2614101614102"), // {"A": 1, "A": 2}
into: struct {
A int `json:"A"`
}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject duplicate text string keys into map",
in: hex("a2614101614102"), // {"A": 1, "A": 2}
into: map[string]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject duplicate byte string keys into map",
in: hex("a2414101414102"), // {'A': 1, 'A': 2}
into: map[string]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject duplicate byte string keys into struct",
in: hex("a2414101414102"), // {'A': 1, 'A': 2}
into: struct {
A int `json:"A"`
}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject duplicate byte string and text string keys into map",
in: hex("a2414101614102"), // {'A': 1, "A": 2}
into: map[string]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject duplicate byte string and text string keys into struct",
in: hex("a2414101614102"), // {'A': 1, "A": 2}
into: struct {
A int `json:"A"`
}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("A"), Index: 1}),
},
{
name: "reject two identical indefinite-length byte string keys split into chunks differently into struct",
in: hex("a25f426865436c6c6fff015f416844656c6c6fff02"), // {(_ 'he', 'llo'): 1, (_ 'h', 'ello'): 2}
into: struct {
Hello int `json:"hello"`
}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
},
{
name: "reject two identical indefinite-length byte string keys split into chunks differently into map",
in: hex("a25f426865436c6c6fff015f416844656c6c6fff02"), // {(_ 'he', 'llo'): 1, (_ 'h', 'ello'): 2}
into: map[string]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
},
{
name: "reject two identical indefinite-length text string keys split into chunks differently into struct",
in: hex("a27f626865636c6c6fff017f616864656c6c6fff02"), // {(_ "he", "llo"): 1, (_ "h", "ello"): 2}
into: struct {
Hello int `json:"hello"`
}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
},
{
name: "reject two identical indefinite-length text string keys split into chunks differently into map",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a27f626865636c6c6fff017f616864656c6c6fff02"), // {(_ "he", "llo"): 1, (_ "h", "ello"): 2}
into: map[string]interface{}{},
assertOnError: assertIdenticalError(&cbor.DupMapKeyError{Key: string("hello"), Index: 1}),
},
{
name: "case-insensitive match treated as unknown field",
modes: []cbor.DecMode{modes.Decode},
in: hex("a1614101"), // {"A": 1}
into: struct {
A int `json:"a"`
}{},
assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 0}),
},
{
name: "case-insensitive match ignored in lax mode",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a1614101"), // {"A": 1}
into: struct {
A int `json:"a"`
}{},
want: struct {
A int `json:"a"`
}{
A: 0,
},
assertOnError: assertNilError,
},
{
name: "case-insensitive match after exact match treated as unknown field",
modes: []cbor.DecMode{modes.Decode},
in: hex("a2616101614102"), // {"a": 1, "A": 2}
into: struct {
A int `json:"a"`
}{},
want: struct {
A int `json:"a"`
}{
A: 1,
},
assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 1}),
},
{
name: "case-insensitive match after exact match ignored in lax mode",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a2616101614102"), // {"a": 1, "A": 2}
into: struct {
A int `json:"a"`
}{},
want: struct {
A int `json:"a"`
}{
A: 1,
},
assertOnError: assertNilError,
},
{
name: "case-insensitive match before exact match treated as unknown field",
modes: []cbor.DecMode{modes.Decode},
in: hex("a2614101616102"), // {"A": 1, "a": 2}
into: struct {
A int `json:"a"`
}{},
assertOnError: assertIdenticalError(&cbor.UnknownFieldError{Index: 0}),
},
{
name: "case-insensitive match before exact match ignored in lax mode",
modes: []cbor.DecMode{modes.DecodeLax},
in: hex("a2614101616102"), // {"A": 1, "a": 2}
into: struct {
A int `json:"a"`
}{},
want: struct {
A int `json:"a"`
}{
A: 2,
},
assertOnError: assertNilError,
},
{
name: "reject text string containing invalid utf-8 sequence",
in: hex("6180"), // text string beginning with continuation byte 0x80

View File

@ -21,6 +21,7 @@ import (
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
)
@ -60,3 +61,11 @@ func assertOnConcreteError[E error](fn func(*testing.T, E)) func(t *testing.T, e
fn(t, ec)
}
}
func assertIdenticalError[E error](expected E) func(*testing.T, error) {
return assertOnConcreteError(func(t *testing.T, actual E) {
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("diff between actual error and expected error:\n%s", diff)
}
})
}