From 9abcf9f1a72ee26f7fcd436c5e8db14e71038f41 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 21 Nov 2023 13:30:41 +0100 Subject: [PATCH 1/7] Implement array must occur check --- serializer/serix/decode.go | 6 + serializer/serix/encode.go | 7 ++ serializer/serix/map_decode.go | 6 + serializer/serix/map_encode.go | 6 + serializer/serix/serix.go | 41 +++++++ serializer/serix/serix_test.go | 210 +++++++++++++++++++++++++++++++++ 6 files changed, 276 insertions(+) diff --git a/serializer/serix/decode.go b/serializer/serix/decode.go index a4f52b2d..1968d657 100644 --- a/serializer/serix/decode.go +++ b/serializer/serix/decode.go @@ -435,6 +435,12 @@ func (api *API) decodeSlice(ctx context.Context, b []byte, value reflect.Value, value.Set(reflect.MakeSlice(valueType, 0, 0)) } + if opts.validation { + if err := api.checkArrayMustOccur(value, ts); err != nil { + return bytesRead, ierrors.Wrapf(err, "can't deserialize '%s' type", value.Kind()) + } + } + return bytesRead, nil } diff --git a/serializer/serix/encode.go b/serializer/serix/encode.go index 916a9397..41f4f2c2 100644 --- a/serializer/serix/encode.go +++ b/serializer/serix/encode.go @@ -319,6 +319,13 @@ func (api *API) encodeSlice(ctx context.Context, value reflect.Value, valueType return seri.Serialize() } + + if opts.validation { + if err := api.checkArrayMustOccur(value, ts); err != nil { + return nil, ierrors.Wrapf(err, "can't serialize '%s' type", value.Kind()) + } + } + sliceLen := value.Len() data := make([][]byte, sliceLen) for i := 0; i < sliceLen; i++ { diff --git a/serializer/serix/map_decode.go b/serializer/serix/map_decode.go index 4c5f7f9e..329edbad 100644 --- a/serializer/serix/map_decode.go +++ b/serializer/serix/map_decode.go @@ -455,6 +455,12 @@ func (api *API) mapDecodeSlice(ctx context.Context, mapVal any, value reflect.Va value.Set(reflect.MakeSlice(valueType, 0, 0)) } + if opts.validation { + if err := api.checkArrayMustOccur(value, ts); err != nil { + return ierrors.Wrapf(err, "can't deserialize '%s' type", value.Kind()) + } + } + return nil } diff --git a/serializer/serix/map_encode.go b/serializer/serix/map_encode.go index 36cee5ad..2f2b4cea 100644 --- a/serializer/serix/map_encode.go +++ b/serializer/serix/map_encode.go @@ -265,6 +265,12 @@ func (api *API) mapEncodeSlice(ctx context.Context, value reflect.Value, valueTy return nil, ierrors.Wrapf(err, "can't serialize '%s' type", value.Kind()) } + if opts.validation { + if err := api.checkArrayMustOccur(value, ts); err != nil { + return nil, ierrors.Wrapf(err, "can't serialize '%s' type", value.Kind()) + } + } + data := make([]any, sliceLen) for i := 0; i < sliceLen; i++ { elemValue := value.Index(i) diff --git a/serializer/serix/serix.go b/serializer/serix/serix.go index 2130f644..7e7d7b66 100644 --- a/serializer/serix/serix.go +++ b/serializer/serix/serix.go @@ -257,6 +257,47 @@ func (api *API) checkSerializedSize(ctx context.Context, value reflect.Value, ts return api.checkMaxByteSize(len(bytes), ts) } +func (api *API) checkArrayMustOccur(slice reflect.Value, ts TypeSettings) error { + if ts.arrayRules == nil || len(ts.arrayRules.MustOccur) == 0 { + return nil + } + + var mustOccurPrefixes *serializer.TypePrefixes + mustOccurPrefixesInner := make(serializer.TypePrefixes, len(ts.arrayRules.MustOccur)) + + for key, value := range ts.arrayRules.MustOccur { + mustOccurPrefixesInner[key] = value + } + mustOccurPrefixes = &mustOccurPrefixesInner + + sliceLen := slice.Len() + + for i := 0; i < sliceLen; i++ { + elemValue := slice.Index(i) + + // Get the type prefix of the element by retrieving the type settings. + if elemValue.Kind() == reflect.Ptr || elemValue.Kind() == reflect.Interface { + elemValue = reflect.Indirect(elemValue.Elem()) + } + + elemTypeSettings, exists := api.getTypeSettings(elemValue.Type()) + if !exists { + return ierrors.Errorf("missing type settings for %s; needed to check Must Occur rules", elemValue) + } + _, typePrefix, err := getTypeDenotationAndCode(elemTypeSettings.objectType) + if err != nil { + return ierrors.WithStack(err) + } + delete(*mustOccurPrefixes, typePrefix) + } + + if len(*mustOccurPrefixes) != 0 { + return ierrors.Wrapf(serializer.ErrArrayValidationTypesNotOccurred, "expected type prefixes that did not occur: %v", mustOccurPrefixes) + } + + return nil +} + // Encode serializes the provided object obj into bytes. // serix traverses the object recursively and serializes everything based on the type. // If a type implements the custom Serializable interface serix delegates the serialization to that type. diff --git a/serializer/serix/serix_test.go b/serializer/serix/serix_test.go index 67045eed..0996ee51 100644 --- a/serializer/serix/serix_test.go +++ b/serializer/serix/serix_test.go @@ -3,12 +3,14 @@ package serix_test import ( "context" + "fmt" "reflect" "testing" "github.com/iancoleman/orderedmap" "github.com/stretchr/testify/require" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/serializer/v2/serix" ) @@ -145,6 +147,7 @@ type serializeTest struct { func (test *serializeTest) run(t *testing.T) { // binary serialize serixData, err := testAPI.Encode(context.Background(), test.source, serix.WithValidation()) + fmt.Println("serialize ", serixData) if test.seriErr != nil { require.ErrorIs(t, err, test.seriErr, "binary serialization failed") @@ -614,3 +617,210 @@ func TestSerixFieldKeyString(t *testing.T) { }) } } + +type Shape interface { + constraints.Equalable[Shape] +} + +type Square struct { + Size uint8 `serix:""` +} + +func (s *Square) Equal(other Shape) bool { + otherSquare, is := other.(*Square) + if !is { + return false + } + return s.Size == otherSquare.Size +} + +type Triangle struct { + Size uint16 `serix:""` +} + +func (t *Triangle) Equal(other Shape) bool { + otherTriangle, is := other.(*Triangle) + if !is { + return false + } + return t.Size == otherTriangle.Size +} + +func TestSerixMustOccur(t *testing.T) { + const ( + ShapeSquare byte = 100 + ShapeTriangle byte = 101 + ) + + type ( + Shapes[T Shape] []T + ContainerShape = interface{ Shape } + ContainerShapes = Shapes[ContainerShape] + ) + + type Container struct { + MyShapes ContainerShapes `serix:",omitempty"` + } + + var containerShapesArrRules = &serix.ArrayRules{ + Min: 0, + Max: 10, + MustOccur: serializer.TypePrefixes{ + uint32(ShapeSquare): struct{}{}, + }, + ValidationMode: serializer.ArrayValidationModeNoDuplicates | + serializer.ArrayValidationModeLexicalOrdering | + serializer.ArrayValidationModeAtMostOneOfEachTypeByte, + } + + must(testAPI.RegisterTypeSettings(Triangle{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeTriangle)))) + must(testAPI.RegisterTypeSettings(Square{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeSquare)))) + must(testAPI.RegisterTypeSettings(Container{}, serix.TypeSettings{}.WithObjectType(uint8(5)))) + + must(testAPI.RegisterTypeSettings(ContainerShapes{}, + serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(containerShapesArrRules), + )) + + must(testAPI.RegisterInterfaceObjects((*ContainerShape)(nil), (*Triangle)(nil))) + must(testAPI.RegisterInterfaceObjects((*ContainerShape)(nil), (*Square)(nil))) + + tests := []encodingTest{ + { + name: "ok encoding", + source: &Container{ + MyShapes: ContainerShapes{ + &Square{Size: 10}, + &Triangle{Size: 3}, + }, + }, + target: &Container{}, + seriErr: nil, + }, + { + name: "fail encoding - square must occur", + source: &Container{ + MyShapes: ContainerShapes{ + &Triangle{Size: 3}, + }, + }, + target: &Container{}, + seriErr: serializer.ErrArrayValidationTypesNotOccurred, + }, + { + name: "fail encoding - square must occur - empty slice", + source: &Container{ + MyShapes: ContainerShapes{}, + }, + target: &Container{}, + seriErr: serializer.ErrArrayValidationTypesNotOccurred, + }, + } + + for _, tt := range tests { + t.Run(tt.name, tt.run) + } + + deSeriTests := []decodingTest{ + { + name: "ok decoding", + source: &Container{ + MyShapes: ContainerShapes{ + &Square{Size: 10}, + &Triangle{Size: 3}, + }, + }, + target: &Container{}, + deSeriErr: nil, + }, + { + name: "fail decoding - square must occur", + source: &Container{ + MyShapes: ContainerShapes{ + &Triangle{Size: 3}, + }, + }, + target: &Container{}, + deSeriErr: serializer.ErrArrayValidationTypesNotOccurred, + }, + { + name: "fail decoding - square must occur - empty slice", + source: &Container{ + MyShapes: ContainerShapes{}, + }, + target: &Container{}, + deSeriErr: serializer.ErrArrayValidationTypesNotOccurred, + }, + } + + for _, tt := range deSeriTests { + t.Run(tt.name, tt.run) + } +} + +type encodingTest struct { + name string + source any + target any + seriErr error +} + +func (test *encodingTest) run(t *testing.T) { + serixData, err := testAPI.Encode(context.Background(), test.source, serix.WithValidation()) + + jsonData, jsonErr := testAPI.JSONEncode(context.Background(), test.source, serix.WithValidation()) + + if test.seriErr != nil { + require.ErrorIs(t, err, test.seriErr) + require.ErrorIs(t, jsonErr, test.seriErr) + + return + } + require.NoError(t, err) + require.NoError(t, jsonErr) + + serixTarget := reflect.New(reflect.TypeOf(test.target).Elem()).Interface() + bytesRead, err := testAPI.Decode(context.Background(), serixData, serixTarget) + require.NoError(t, err) + require.Len(t, serixData, bytesRead) + require.EqualValues(t, test.source, serixTarget) + + jsonDest := reflect.New(reflect.TypeOf(test.target).Elem()).Interface() + require.NoError(t, testAPI.JSONDecode(context.Background(), jsonData, jsonDest)) + + require.EqualValues(t, test.source, jsonDest) +} + +type decodingTest struct { + name string + source any + target any + deSeriErr error +} + +func (test *decodingTest) run(t *testing.T) { + serixData, err := testAPI.Encode(context.Background(), test.source) + require.NoError(t, err) + + sourceJSON, err := testAPI.JSONEncode(context.Background(), test.source) + require.NoError(t, err) + + serixTarget := reflect.New(reflect.TypeOf(test.target).Elem()).Interface() + bytesRead, err := testAPI.Decode(context.Background(), serixData, serixTarget, serix.WithValidation()) + + jsonDest := reflect.New(reflect.TypeOf(test.target).Elem()).Interface() + jsonErr := testAPI.JSONDecode(context.Background(), sourceJSON, jsonDest, serix.WithValidation()) + + if test.deSeriErr != nil { + require.ErrorIs(t, err, test.deSeriErr) + require.ErrorIs(t, jsonErr, test.deSeriErr) + + return + } + require.NoError(t, err) + require.Len(t, serixData, bytesRead) + require.EqualValues(t, test.source, serixTarget) + + require.NoError(t, jsonErr) + + require.EqualValues(t, test.source, jsonDest) +} From f8048739b5f2d3299a8ee9d20ec709eb2f92a21b Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 21 Nov 2023 13:34:41 +0100 Subject: [PATCH 2/7] Simplify test setup --- serializer/serix/serix_test.go | 71 +++++++++++----------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/serializer/serix/serix_test.go b/serializer/serix/serix_test.go index 0996ee51..b706f533 100644 --- a/serializer/serix/serix_test.go +++ b/serializer/serix/serix_test.go @@ -10,7 +10,6 @@ import ( "github.com/iancoleman/orderedmap" "github.com/stretchr/testify/require" - "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/serializer/v2/serix" ) @@ -618,34 +617,6 @@ func TestSerixFieldKeyString(t *testing.T) { } } -type Shape interface { - constraints.Equalable[Shape] -} - -type Square struct { - Size uint8 `serix:""` -} - -func (s *Square) Equal(other Shape) bool { - otherSquare, is := other.(*Square) - if !is { - return false - } - return s.Size == otherSquare.Size -} - -type Triangle struct { - Size uint16 `serix:""` -} - -func (t *Triangle) Equal(other Shape) bool { - otherTriangle, is := other.(*Triangle) - if !is { - return false - } - return t.Size == otherTriangle.Size -} - func TestSerixMustOccur(t *testing.T) { const ( ShapeSquare byte = 100 @@ -653,16 +624,20 @@ func TestSerixMustOccur(t *testing.T) { ) type ( - Shapes[T Shape] []T - ContainerShape = interface{ Shape } - ContainerShapes = Shapes[ContainerShape] + Shape interface { + } + Square struct { + Size uint8 `serix:""` + } + Triangle struct { + Size uint16 `serix:""` + } + Container struct { + Shapes []Shape `serix:""` + } ) - type Container struct { - MyShapes ContainerShapes `serix:",omitempty"` - } - - var containerShapesArrRules = &serix.ArrayRules{ + var shapesArrRules = &serix.ArrayRules{ Min: 0, Max: 10, MustOccur: serializer.TypePrefixes{ @@ -677,18 +652,18 @@ func TestSerixMustOccur(t *testing.T) { must(testAPI.RegisterTypeSettings(Square{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeSquare)))) must(testAPI.RegisterTypeSettings(Container{}, serix.TypeSettings{}.WithObjectType(uint8(5)))) - must(testAPI.RegisterTypeSettings(ContainerShapes{}, - serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(containerShapesArrRules), + must(testAPI.RegisterTypeSettings([]Shape{}, + serix.TypeSettings{}.WithLengthPrefixType(serix.LengthPrefixTypeAsByte).WithArrayRules(shapesArrRules), )) - must(testAPI.RegisterInterfaceObjects((*ContainerShape)(nil), (*Triangle)(nil))) - must(testAPI.RegisterInterfaceObjects((*ContainerShape)(nil), (*Square)(nil))) + must(testAPI.RegisterInterfaceObjects((*Shape)(nil), (*Triangle)(nil))) + must(testAPI.RegisterInterfaceObjects((*Shape)(nil), (*Square)(nil))) tests := []encodingTest{ { name: "ok encoding", source: &Container{ - MyShapes: ContainerShapes{ + Shapes: []Shape{ &Square{Size: 10}, &Triangle{Size: 3}, }, @@ -699,7 +674,7 @@ func TestSerixMustOccur(t *testing.T) { { name: "fail encoding - square must occur", source: &Container{ - MyShapes: ContainerShapes{ + Shapes: []Shape{ &Triangle{Size: 3}, }, }, @@ -709,7 +684,7 @@ func TestSerixMustOccur(t *testing.T) { { name: "fail encoding - square must occur - empty slice", source: &Container{ - MyShapes: ContainerShapes{}, + Shapes: []Shape{}, }, target: &Container{}, seriErr: serializer.ErrArrayValidationTypesNotOccurred, @@ -724,7 +699,7 @@ func TestSerixMustOccur(t *testing.T) { { name: "ok decoding", source: &Container{ - MyShapes: ContainerShapes{ + Shapes: []Shape{ &Square{Size: 10}, &Triangle{Size: 3}, }, @@ -735,7 +710,7 @@ func TestSerixMustOccur(t *testing.T) { { name: "fail decoding - square must occur", source: &Container{ - MyShapes: ContainerShapes{ + Shapes: []Shape{ &Triangle{Size: 3}, }, }, @@ -745,7 +720,7 @@ func TestSerixMustOccur(t *testing.T) { { name: "fail decoding - square must occur - empty slice", source: &Container{ - MyShapes: ContainerShapes{}, + Shapes: []Shape{}, }, target: &Container{}, deSeriErr: serializer.ErrArrayValidationTypesNotOccurred, @@ -766,7 +741,6 @@ type encodingTest struct { func (test *encodingTest) run(t *testing.T) { serixData, err := testAPI.Encode(context.Background(), test.source, serix.WithValidation()) - jsonData, jsonErr := testAPI.JSONEncode(context.Background(), test.source, serix.WithValidation()) if test.seriErr != nil { @@ -780,6 +754,7 @@ func (test *encodingTest) run(t *testing.T) { serixTarget := reflect.New(reflect.TypeOf(test.target).Elem()).Interface() bytesRead, err := testAPI.Decode(context.Background(), serixData, serixTarget) + require.NoError(t, err) require.Len(t, serixData, bytesRead) require.EqualValues(t, test.source, serixTarget) From 0c6cdb33c0456c6c20d46092b673aa15c4703fef Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Tue, 21 Nov 2023 13:42:57 +0100 Subject: [PATCH 3/7] Remove print statement --- serializer/serix/serix_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/serializer/serix/serix_test.go b/serializer/serix/serix_test.go index b706f533..e25f983e 100644 --- a/serializer/serix/serix_test.go +++ b/serializer/serix/serix_test.go @@ -3,7 +3,6 @@ package serix_test import ( "context" - "fmt" "reflect" "testing" @@ -146,7 +145,6 @@ type serializeTest struct { func (test *serializeTest) run(t *testing.T) { // binary serialize serixData, err := testAPI.Encode(context.Background(), test.source, serix.WithValidation()) - fmt.Println("serialize ", serixData) if test.seriErr != nil { require.ErrorIs(t, err, test.seriErr, "binary serialization failed") From 9ceab8485f394c805c208e2fb427b97f41772a9a Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 22 Nov 2023 09:53:56 +0100 Subject: [PATCH 4/7] Add docs & catch error if called on non-slice --- serializer/serix/serix.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/serializer/serix/serix.go b/serializer/serix/serix.go index 7e7d7b66..bcd666ab 100644 --- a/serializer/serix/serix.go +++ b/serializer/serix/serix.go @@ -257,7 +257,12 @@ func (api *API) checkSerializedSize(ctx context.Context, value reflect.Value, ts return api.checkMaxByteSize(len(bytes), ts) } +// Checks the "Must Occur" array rules in the given slice. func (api *API) checkArrayMustOccur(slice reflect.Value, ts TypeSettings) error { + if slice.Kind() != reflect.Slice { + return ierrors.Errorf("must occur can only be checked for a slice, got value of kind %v", slice.Kind()) + } + if ts.arrayRules == nil || len(ts.arrayRules.MustOccur) == 0 { return nil } From 70d6e114b817a2597b5fa56007d9ef6355ed7f49 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 22 Nov 2023 12:16:27 +0100 Subject: [PATCH 5/7] Simplify prefix cloning --- serializer/serix/serix.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/serializer/serix/serix.go b/serializer/serix/serix.go index bcd666ab..82d0e9db 100644 --- a/serializer/serix/serix.go +++ b/serializer/serix/serix.go @@ -267,16 +267,12 @@ func (api *API) checkArrayMustOccur(slice reflect.Value, ts TypeSettings) error return nil } - var mustOccurPrefixes *serializer.TypePrefixes - mustOccurPrefixesInner := make(serializer.TypePrefixes, len(ts.arrayRules.MustOccur)) - + mustOccurPrefixes := make(serializer.TypePrefixes, len(ts.arrayRules.MustOccur)) for key, value := range ts.arrayRules.MustOccur { - mustOccurPrefixesInner[key] = value + mustOccurPrefixes[key] = value } - mustOccurPrefixes = &mustOccurPrefixesInner sliceLen := slice.Len() - for i := 0; i < sliceLen; i++ { elemValue := slice.Index(i) @@ -293,10 +289,10 @@ func (api *API) checkArrayMustOccur(slice reflect.Value, ts TypeSettings) error if err != nil { return ierrors.WithStack(err) } - delete(*mustOccurPrefixes, typePrefix) + delete(mustOccurPrefixes, typePrefix) } - if len(*mustOccurPrefixes) != 0 { + if len(mustOccurPrefixes) != 0 { return ierrors.Wrapf(serializer.ErrArrayValidationTypesNotOccurred, "expected type prefixes that did not occur: %v", mustOccurPrefixes) } From 3a05d05c59cc9090fe64c94c474ee312bfca60a5 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 22 Nov 2023 12:16:54 +0100 Subject: [PATCH 6/7] Add more documentation on MustOccur --- serializer/serializable.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/serializer/serializable.go b/serializer/serializable.go index bad021b6..69ef2518 100644 --- a/serializer/serializable.go +++ b/serializer/serializable.go @@ -122,7 +122,9 @@ type ArrayRules struct { Min uint // The max array bound. Max uint - // A map of types which must occur within the array. + // A map of object types which must occur within the array. + // This is only checked on slices of types with an object type set. + // In particular, this means this is not checked for byte slices. MustOccur TypePrefixes // The guards applied while de/serializing Serializables. Guards SerializableGuard From 65f84e04ad05771ec76186628ce41d22a87bc6a7 Mon Sep 17 00:00:00 2001 From: Philipp Gackstatter Date: Wed, 22 Nov 2023 12:22:58 +0100 Subject: [PATCH 7/7] Test with two must occurs --- serializer/serix/serix_test.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/serializer/serix/serix_test.go b/serializer/serix/serix_test.go index e25f983e..6129e73b 100644 --- a/serializer/serix/serix_test.go +++ b/serializer/serix/serix_test.go @@ -617,8 +617,9 @@ func TestSerixFieldKeyString(t *testing.T) { func TestSerixMustOccur(t *testing.T) { const ( - ShapeSquare byte = 100 - ShapeTriangle byte = 101 + ShapeSquare byte = 100 + ShapeRectangle byte = 101 + ShapeTriangle byte = 102 ) type ( @@ -627,6 +628,9 @@ func TestSerixMustOccur(t *testing.T) { Square struct { Size uint8 `serix:""` } + Rectangle struct { + Size uint8 `serix:""` + } Triangle struct { Size uint16 `serix:""` } @@ -639,7 +643,8 @@ func TestSerixMustOccur(t *testing.T) { Min: 0, Max: 10, MustOccur: serializer.TypePrefixes{ - uint32(ShapeSquare): struct{}{}, + uint32(ShapeSquare): struct{}{}, + uint32(ShapeRectangle): struct{}{}, }, ValidationMode: serializer.ArrayValidationModeNoDuplicates | serializer.ArrayValidationModeLexicalOrdering | @@ -648,6 +653,7 @@ func TestSerixMustOccur(t *testing.T) { must(testAPI.RegisterTypeSettings(Triangle{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeTriangle)))) must(testAPI.RegisterTypeSettings(Square{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeSquare)))) + must(testAPI.RegisterTypeSettings(Rectangle{}, serix.TypeSettings{}.WithObjectType(uint8(ShapeRectangle)))) must(testAPI.RegisterTypeSettings(Container{}, serix.TypeSettings{}.WithObjectType(uint8(5)))) must(testAPI.RegisterTypeSettings([]Shape{}, @@ -656,6 +662,7 @@ func TestSerixMustOccur(t *testing.T) { must(testAPI.RegisterInterfaceObjects((*Shape)(nil), (*Triangle)(nil))) must(testAPI.RegisterInterfaceObjects((*Shape)(nil), (*Square)(nil))) + must(testAPI.RegisterInterfaceObjects((*Shape)(nil), (*Rectangle)(nil))) tests := []encodingTest{ { @@ -663,6 +670,7 @@ func TestSerixMustOccur(t *testing.T) { source: &Container{ Shapes: []Shape{ &Square{Size: 10}, + &Rectangle{Size: 5}, &Triangle{Size: 3}, }, }, @@ -673,6 +681,7 @@ func TestSerixMustOccur(t *testing.T) { name: "fail encoding - square must occur", source: &Container{ Shapes: []Shape{ + &Rectangle{Size: 5}, &Triangle{Size: 3}, }, }, @@ -680,7 +689,7 @@ func TestSerixMustOccur(t *testing.T) { seriErr: serializer.ErrArrayValidationTypesNotOccurred, }, { - name: "fail encoding - square must occur - empty slice", + name: "fail encoding - square & rectangle must occur - empty slice", source: &Container{ Shapes: []Shape{}, }, @@ -699,6 +708,7 @@ func TestSerixMustOccur(t *testing.T) { source: &Container{ Shapes: []Shape{ &Square{Size: 10}, + &Rectangle{Size: 5}, &Triangle{Size: 3}, }, }, @@ -709,6 +719,7 @@ func TestSerixMustOccur(t *testing.T) { name: "fail decoding - square must occur", source: &Container{ Shapes: []Shape{ + &Rectangle{Size: 5}, &Triangle{Size: 3}, }, }, @@ -716,7 +727,7 @@ func TestSerixMustOccur(t *testing.T) { deSeriErr: serializer.ErrArrayValidationTypesNotOccurred, }, { - name: "fail decoding - square must occur - empty slice", + name: "fail decoding - square & rectangle must occur - empty slice", source: &Container{ Shapes: []Shape{}, },