From dfeee984caeb9dc2d2f3ef32c05458841b1b512b Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 24 May 2022 09:22:23 -0400 Subject: [PATCH 01/68] Add VSCode to .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 846a894..237b4c0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,7 @@ report.json # Images *.png + +# VSCode +*.code-workspace +.vscode/* From 7f8bad05fae5a3a80a0fb0af1178ee841179dbd4 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 12:31:57 -0400 Subject: [PATCH 02/68] Initial WRP Validation Framework ## Overview related to #25, #78, xmidt-org/scytale#88, xmidt-org/talaria#153 s.t. we want a validation mechanism that is configurable by the application & verifies the spec. ### tl;rd This pr introduces the initial validation framework, where applications supply validators (satisfying the `Validator interface`) to `NewMsgTypeValidator` and then used to verify the spec.
Explanation Apps supply validators satisfying: ```go // Validator is a WRP validator that allows access to the Validate function. type Validator interface { Validate(m Message) error } ``` and listing which validators are used on known and unknown msg types (where unknown msg types are handled by `defaultValidator`): ```go var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewMsgTypeValidator( // Validates known msg types m: map[MessageType]Validators{SimpleEventMessageType: {alwaysValidMsg}}, // Validates unknown msg types defaultValidator: alwaysValidMsg) err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Known msg type err == nil // True err = msgv.Validate(Message{Type: CreateMessageType}) // Unknown msg type, uses defaultValidator err == nil // True ``` if a default validator is not provided, all unknown msg type will **fail** by default ```go var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewMsgTypeValidator( // Omitted defaultValidator m: map[MessageType]Validators{SimpleEventMessageType: {alwaysInvalidMsg()}}) err = msgv.Validate(Message{Type: CreateMessageType}) err != nil // True ```
Type of Change(s) - Non-breaking Enhancement - All new and existing tests passed.
Module Unit Testing: [PASSING]
PR Affecting Unit Testing: validator_test.go [PASSING] ```console Running tool: /usr/local/bin/go test -timeout 30s -run ^(TestHelperValidators|TestMsgTypeValidator)$ github.com/xmidt-org/wrp-go/v3 === RUN TestHelperValidators === RUN TestHelperValidators/alwaysInvalidMsg --- PASS: TestHelperValidators (0.00s) --- PASS: TestHelperValidators/alwaysInvalidMsg (0.00s) === RUN TestMsgTypeValidator === RUN TestMsgTypeValidator/MsgTypeValidator_validate === RUN TestMsgTypeValidator/MsgTypeValidator_validate/known_message_type === RUN TestMsgTypeValidator/MsgTypeValidator_validate/unknown_message_type_with_provided_default_Validator === RUN TestMsgTypeValidator/MsgTypeValidator_validate/known_message_type_with_a_failing_Validator === RUN TestMsgTypeValidator/MsgTypeValidator_validate/unknown_message_type_without_provided_default_Validator === RUN TestMsgTypeValidator/MsgTypeValidator_factory === RUN TestMsgTypeValidator/MsgTypeValidator_factory/with_provided_default_Validator === RUN TestMsgTypeValidator/MsgTypeValidator_factory/without_provided_default_Validator === RUN TestMsgTypeValidator/MsgTypeValidator_factory/empty_list_of_message_type_Validators === RUN TestMsgTypeValidator/MsgTypeValidator_factory/empty_value_'m'_map[MessageType]Validators --- PASS: TestMsgTypeValidator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_validate (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_validate/known_message_type (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_validate/unknown_message_type_with_provided_default_Validator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_validate/known_message_type_with_a_failing_Validator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_validate/unknown_message_type_without_provided_default_Validator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_factory (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_factory/with_provided_default_Validator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_factory/without_provided_default_Validator (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_factory/empty_list_of_message_type_Validators (0.00s) --- PASS: TestMsgTypeValidator/MsgTypeValidator_factory/empty_value_'m'_map[MessageType]Validators (0.00s) PASS ok github.com/xmidt-org/wrp-go/v3 0.303s > Test run finished at 5/25/2022, 11:39:22 AM < ```
--- header_wrp.go | 6 +- validator.go | 93 ++++++++++++++++++++++ validator_test.go | 199 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 validator.go create mode 100644 validator_test.go diff --git a/header_wrp.go b/header_wrp.go index 26feeaf..4123652 100644 --- a/header_wrp.go +++ b/header_wrp.go @@ -17,10 +17,6 @@ package wrp -import ( - "errors" -) - // Constant HTTP header strings representing WRP fields const ( MsgTypeHeader = "X-Midt-Msg-Type" @@ -34,7 +30,7 @@ const ( SourceHeader = "X-Midt-Source" ) -var ErrInvalidMsgType = errors.New("Invalid Message Type") +// var ErrInvalidMsgType = errors.New("Invalid Message Type") // Map string to MessageType int /* diff --git a/validator.go b/validator.go new file mode 100644 index 0000000..dcdf91d --- /dev/null +++ b/validator.go @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wrp + +import ( + "errors" + "fmt" +) + +var ( + ErrInvalidMsgTypeValidator = errors.New("invalid WRP message type validator") + ErrInvalidMsgType = errors.New("invalid WRP message type") +) + +// Validator is a WRP validator that allows access to the Validate function. +type Validator interface { + Validate(m Message) error +} + +// Validators is a WRP validator that ensures messages are valid based on +// message type and each validator in the list. +type Validators []Validator + +// ValidatorFunc is a WRP validator that takes messages and validates them +// against functions. +type ValidatorFunc func(Message) error + +// Validate executes its own ValidatorFunc receiver and returns the result. +func (vf ValidatorFunc) Validate(m Message) error { + return vf(m) +} + +// MsgTypeValidator is a WRP validator that validates based on message type +// or using the defaultValidator if message type is unknown +type MsgTypeValidator struct { + m map[MessageType]Validators + defaultValidator Validator +} + +// Validate validates messages based on message type or using the defaultValidator +// if message type is unknown +func (m MsgTypeValidator) Validate(msg Message) error { + vs, ok := m.m[msg.MessageType()] + if !ok { + return m.defaultValidator.Validate(msg) + } + + for _, v := range vs { + err := v.Validate(msg) + if err != nil { + return err + } + } + + return nil +} + +// NewMsgTypeValidator returns a MsgTypeValidator +func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (MsgTypeValidator, error) { + if m == nil { + return MsgTypeValidator{}, fmt.Errorf("%w: %v", ErrInvalidMsgTypeValidator, m) + } + if defaultValidator == nil { + defaultValidator = alwaysInvalidMsg() + } + + return MsgTypeValidator{ + m: m, + defaultValidator: defaultValidator, + }, nil +} + +// AlwaysInvalid doesn't validate anything about the message and always returns an error. +func alwaysInvalidMsg() ValidatorFunc { + return func(m Message) error { + return fmt.Errorf("%w: %v", ErrInvalidMsgType, m.MessageType().String()) + } +} diff --git a/validator_test.go b/validator_test.go new file mode 100644 index 0000000..8be4dde --- /dev/null +++ b/validator_test.go @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package wrp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func testMsgTypeValidatorValidate(t *testing.T) { + type Test struct { + m map[MessageType]Validators + defaultValidator Validator + msg Message + } + + var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } + tests := []struct { + description string + value Test + expectedErr error + }{ + // Success case + { + description: "known message type with successful Validators", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValidMsg}, + }, + msg: Message{Type: SimpleEventMessageType}, + }, + }, + { + description: "unknown message type with provided default Validator", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValidMsg}, + }, + defaultValidator: alwaysValidMsg, + msg: Message{Type: CreateMessageType}, + }, + }, + // Failure case + { + description: "known message type with failing Validators", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysInvalidMsg()}, + }, + msg: Message{Type: SimpleEventMessageType}, + }, + expectedErr: ErrInvalidMsgType, + }, + { + description: "unknown message type without provided default Validator", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValidMsg}, + }, + msg: Message{Type: CreateMessageType}, + }, + expectedErr: ErrInvalidMsgType, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator) + assert.NotNil(msgv) + assert.Nil(err) + err = msgv.Validate(tc.value.msg) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.Nil(err) + }) + } +} + +func testNewMsgTypeValidator(t *testing.T) { + type Test struct { + m map[MessageType]Validators + defaultValidator Validator + } + + var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } + tests := []struct { + description string + value Test + expectedErr error + }{ + // Success case + { + description: "with provided default Validator", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValidMsg}, + }, + defaultValidator: alwaysValidMsg, + }, + expectedErr: nil, + }, + { + description: "without provided default Validator", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValidMsg}, + }, + }, + expectedErr: nil, + }, + { + description: "empty list of message type Validators", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {}, + }, + defaultValidator: alwaysValidMsg, + }, + expectedErr: nil, + }, + // Failure case + { + description: "missing message type Validators", + value: Test{}, + expectedErr: ErrInvalidMsgTypeValidator, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator) + assert.NotNil(msgv) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.Nil(err) + }) + } +} + +func testAlwaysInvalidMsg(t *testing.T) { + assert := assert.New(t) + msg := Message{} + v := alwaysInvalidMsg() + + assert.NotNil(v) + err := v(msg) + + assert.NotNil(err) + assert.ErrorIs(err, ErrInvalidMsgType) + +} + +func TestHelperValidators(t *testing.T) { + tests := []struct { + name string + test func(*testing.T) + }{ + {"alwaysInvalidMsg", testAlwaysInvalidMsg}, + } + + for _, tc := range tests { + t.Run(tc.name, tc.test) + } +} + +func TestMsgTypeValidator(t *testing.T) { + tests := []struct { + name string + test func(*testing.T) + }{ + {"MsgTypeValidator validate", testMsgTypeValidatorValidate}, + {"MsgTypeValidator factory", testNewMsgTypeValidator}, + } + + for _, tc := range tests { + t.Run(tc.name, tc.test) + } +} From 9319c121345d7acfdc5877f1363be8f0e33088da Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 12:34:52 -0400 Subject: [PATCH 03/68] formatting --- validator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validator.go b/validator.go index dcdf91d..a246349 100644 --- a/validator.go +++ b/validator.go @@ -75,6 +75,7 @@ func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validato if m == nil { return MsgTypeValidator{}, fmt.Errorf("%w: %v", ErrInvalidMsgTypeValidator, m) } + if defaultValidator == nil { defaultValidator = alwaysInvalidMsg() } From 209ffdb017b8dd7c2a32bcad6dca902f5154551a Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 12:35:43 -0400 Subject: [PATCH 04/68] formatting --- validator_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validator_test.go b/validator_test.go index 8be4dde..5dfa55c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -14,6 +14,7 @@ * limitations under the License. * */ + package wrp import ( From ddd0acfa21f22e88f80df56b92e6f545d3bde1f3 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 12:42:17 -0400 Subject: [PATCH 05/68] convert alwaysInvalidMsg to literal func simplifies the usage of alwaysInvalidMsg --- validator.go | 14 ++++++-------- validator_test.go | 7 ++----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/validator.go b/validator.go index a246349..ee422a4 100644 --- a/validator.go +++ b/validator.go @@ -27,6 +27,11 @@ var ( ErrInvalidMsgType = errors.New("invalid WRP message type") ) +// AlwaysInvalid doesn't validate anything about the message and always returns an error. +var alwaysInvalidMsg ValidatorFunc = func(m Message) error { + return fmt.Errorf("%w: %v", ErrInvalidMsgType, m.MessageType().String()) +} + // Validator is a WRP validator that allows access to the Validate function. type Validator interface { Validate(m Message) error @@ -77,7 +82,7 @@ func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validato } if defaultValidator == nil { - defaultValidator = alwaysInvalidMsg() + defaultValidator = alwaysInvalidMsg } return MsgTypeValidator{ @@ -85,10 +90,3 @@ func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validato defaultValidator: defaultValidator, }, nil } - -// AlwaysInvalid doesn't validate anything about the message and always returns an error. -func alwaysInvalidMsg() ValidatorFunc { - return func(m Message) error { - return fmt.Errorf("%w: %v", ErrInvalidMsgType, m.MessageType().String()) - } -} diff --git a/validator_test.go b/validator_test.go index 5dfa55c..5de9d3c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -61,7 +61,7 @@ func testMsgTypeValidatorValidate(t *testing.T) { description: "known message type with failing Validators", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysInvalidMsg()}, + SimpleEventMessageType: {alwaysInvalidMsg}, }, msg: Message{Type: SimpleEventMessageType}, }, @@ -162,10 +162,7 @@ func testNewMsgTypeValidator(t *testing.T) { func testAlwaysInvalidMsg(t *testing.T) { assert := assert.New(t) msg := Message{} - v := alwaysInvalidMsg() - - assert.NotNil(v) - err := v(msg) + err := alwaysInvalidMsg(msg) assert.NotNil(err) assert.ErrorIs(err, ErrInvalidMsgType) From a2a4093ee52b9a90d65e1b5645ff006ca08c1000 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 12:46:29 -0400 Subject: [PATCH 06/68] Formating and exported alwaysInvalidMsg --- validator.go | 10 +++++----- validator_test.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/validator.go b/validator.go index ee422a4..d40dca4 100644 --- a/validator.go +++ b/validator.go @@ -28,7 +28,7 @@ var ( ) // AlwaysInvalid doesn't validate anything about the message and always returns an error. -var alwaysInvalidMsg ValidatorFunc = func(m Message) error { +var AlwaysInvalidMsg ValidatorFunc = func(m Message) error { return fmt.Errorf("%w: %v", ErrInvalidMsgType, m.MessageType().String()) } @@ -51,14 +51,14 @@ func (vf ValidatorFunc) Validate(m Message) error { } // MsgTypeValidator is a WRP validator that validates based on message type -// or using the defaultValidator if message type is unknown +// or using the defaultValidator if message type is unknown. type MsgTypeValidator struct { m map[MessageType]Validators defaultValidator Validator } // Validate validates messages based on message type or using the defaultValidator -// if message type is unknown +// if message type is unknown. func (m MsgTypeValidator) Validate(msg Message) error { vs, ok := m.m[msg.MessageType()] if !ok { @@ -75,14 +75,14 @@ func (m MsgTypeValidator) Validate(msg Message) error { return nil } -// NewMsgTypeValidator returns a MsgTypeValidator +// NewMsgTypeValidator is a MsgTypeValidator factory. func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (MsgTypeValidator, error) { if m == nil { return MsgTypeValidator{}, fmt.Errorf("%w: %v", ErrInvalidMsgTypeValidator, m) } if defaultValidator == nil { - defaultValidator = alwaysInvalidMsg + defaultValidator = AlwaysInvalidMsg } return MsgTypeValidator{ diff --git a/validator_test.go b/validator_test.go index 5de9d3c..5fb5bc0 100644 --- a/validator_test.go +++ b/validator_test.go @@ -61,7 +61,7 @@ func testMsgTypeValidatorValidate(t *testing.T) { description: "known message type with failing Validators", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysInvalidMsg}, + SimpleEventMessageType: {AlwaysInvalidMsg}, }, msg: Message{Type: SimpleEventMessageType}, }, @@ -162,7 +162,7 @@ func testNewMsgTypeValidator(t *testing.T) { func testAlwaysInvalidMsg(t *testing.T) { assert := assert.New(t) msg := Message{} - err := alwaysInvalidMsg(msg) + err := AlwaysInvalidMsg(msg) assert.NotNil(err) assert.ErrorIs(err, ErrInvalidMsgType) @@ -174,7 +174,7 @@ func TestHelperValidators(t *testing.T) { name string test func(*testing.T) }{ - {"alwaysInvalidMsg", testAlwaysInvalidMsg}, + {"AlwaysInvalidMsg", testAlwaysInvalidMsg}, } for _, tc := range tests { From f5a2e90f47dcee1c789b90e3108705bb38988231 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 13:15:22 -0400 Subject: [PATCH 07/68] Decouple error from api Return `ErrInvalidMsgTypeValidator` and `ErrInvalidMsgType` without additional details. We can add those error details downstream later --- validator.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/validator.go b/validator.go index d40dca4..997e575 100644 --- a/validator.go +++ b/validator.go @@ -19,7 +19,6 @@ package wrp import ( "errors" - "fmt" ) var ( @@ -29,7 +28,7 @@ var ( // AlwaysInvalid doesn't validate anything about the message and always returns an error. var AlwaysInvalidMsg ValidatorFunc = func(m Message) error { - return fmt.Errorf("%w: %v", ErrInvalidMsgType, m.MessageType().String()) + return ErrInvalidMsgType } // Validator is a WRP validator that allows access to the Validate function. @@ -78,7 +77,7 @@ func (m MsgTypeValidator) Validate(msg Message) error { // NewMsgTypeValidator is a MsgTypeValidator factory. func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (MsgTypeValidator, error) { if m == nil { - return MsgTypeValidator{}, fmt.Errorf("%w: %v", ErrInvalidMsgTypeValidator, m) + return MsgTypeValidator{}, ErrInvalidMsgTypeValidator } if defaultValidator == nil { From eb5bf9b47b938e5662ac073c46c422de2900e784 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 20:42:12 -0400 Subject: [PATCH 08/68] Updates based on PR review Thx @kristinapathak for the feedback! :beers: --- header_wrp.go | 2 - validator.go | 54 ++++++++++++++++----------- validator_test.go | 95 ++++++++++++++++++++++++++++------------------- 3 files changed, 90 insertions(+), 61 deletions(-) diff --git a/header_wrp.go b/header_wrp.go index 4123652..eada008 100644 --- a/header_wrp.go +++ b/header_wrp.go @@ -30,8 +30,6 @@ const ( SourceHeader = "X-Midt-Source" ) -// var ErrInvalidMsgType = errors.New("Invalid Message Type") - // Map string to MessageType int /* func StringToMessageType(str string) MessageType { diff --git a/validator.go b/validator.go index 997e575..f770ab2 100644 --- a/validator.go +++ b/validator.go @@ -22,12 +22,12 @@ import ( ) var ( - ErrInvalidMsgTypeValidator = errors.New("invalid WRP message type validator") - ErrInvalidMsgType = errors.New("invalid WRP message type") + ErrInvalidTypeValidator = errors.New("invalid WRP message type validator") + ErrInvalidMsgType = errors.New("invalid WRP message type") ) // AlwaysInvalid doesn't validate anything about the message and always returns an error. -var AlwaysInvalidMsg ValidatorFunc = func(m Message) error { +var AlwaysInvalid ValidatorFunc = func(m Message) error { return ErrInvalidMsgType } @@ -40,6 +40,19 @@ type Validator interface { // message type and each validator in the list. type Validators []Validator +// Validate runs messages through each validator in the validators list. +// It returns as soon as the message is considered invalid, otherwise returns nil if valid. +func (vs Validators) Validate(m Message) error { + for _, v := range vs { + err := v.Validate(m) + if err != nil { + return err + } + } + + return nil +} + // ValidatorFunc is a WRP validator that takes messages and validates them // against functions. type ValidatorFunc func(Message) error @@ -49,42 +62,41 @@ func (vf ValidatorFunc) Validate(m Message) error { return vf(m) } -// MsgTypeValidator is a WRP validator that validates based on message type +// TypeValidator is a WRP validator that validates based on message type // or using the defaultValidator if message type is unknown. -type MsgTypeValidator struct { +type TypeValidator struct { m map[MessageType]Validators defaultValidator Validator } // Validate validates messages based on message type or using the defaultValidator // if message type is unknown. -func (m MsgTypeValidator) Validate(msg Message) error { - vs, ok := m.m[msg.MessageType()] - if !ok { +func (m TypeValidator) Validate(msg Message) error { + vs := m.m[msg.MessageType()] + if vs == nil { return m.defaultValidator.Validate(msg) } - for _, v := range vs { - err := v.Validate(msg) - if err != nil { - return err - } - } - - return nil + return vs.Validate(msg) } -// NewMsgTypeValidator is a MsgTypeValidator factory. -func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (MsgTypeValidator, error) { +// NewTypeValidator is a TypeValidator factory. +func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (TypeValidator, error) { if m == nil { - return MsgTypeValidator{}, ErrInvalidMsgTypeValidator + return TypeValidator{}, ErrInvalidTypeValidator + } + + for _, v := range m { + if v == nil || len(v) == 0 { + return TypeValidator{}, ErrInvalidTypeValidator + } } if defaultValidator == nil { - defaultValidator = AlwaysInvalidMsg + defaultValidator = AlwaysInvalid } - return MsgTypeValidator{ + return TypeValidator{ m: m, defaultValidator: defaultValidator, }, nil diff --git a/validator_test.go b/validator_test.go index 5fb5bc0..8e88061 100644 --- a/validator_test.go +++ b/validator_test.go @@ -21,16 +21,17 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func testMsgTypeValidatorValidate(t *testing.T) { +func testTypeValidatorValidate(t *testing.T) { type Test struct { m map[MessageType]Validators defaultValidator Validator msg Message } - var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -38,70 +39,81 @@ func testMsgTypeValidatorValidate(t *testing.T) { }{ // Success case { - description: "known message type with successful Validators", + description: "Found success", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValidMsg}, + SimpleEventMessageType: {alwaysValid}, }, msg: Message{Type: SimpleEventMessageType}, }, }, { - description: "unknown message type with provided default Validator", + description: "Not found success", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValidMsg}, + SimpleEventMessageType: {AlwaysInvalid}, }, - defaultValidator: alwaysValidMsg, + defaultValidator: alwaysValid, + msg: Message{Type: CreateMessageType}, + }, + }, + { + description: "Never found success", + value: Test{ + m: map[MessageType]Validators{}, + defaultValidator: alwaysValid, msg: Message{Type: CreateMessageType}, }, }, // Failure case { - description: "known message type with failing Validators", + description: "Found error", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalidMsg}, + SimpleEventMessageType: {AlwaysInvalid}, }, - msg: Message{Type: SimpleEventMessageType}, + defaultValidator: alwaysValid, + msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, }, { - description: "unknown message type without provided default Validator", + description: "Not Found error", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValidMsg}, + SimpleEventMessageType: {alwaysValid}, }, msg: Message{Type: CreateMessageType}, }, expectedErr: ErrInvalidMsgType, }, } + for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator) - assert.NotNil(msgv) - assert.Nil(err) + require := require.New(t) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) + require.NotNil(msgv) + require.NoError(err) err = msgv.Validate(tc.value.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return } - assert.Nil(err) + assert.NoError(err) }) } } -func testNewMsgTypeValidator(t *testing.T) { +func testNewTypeValidator(t *testing.T) { type Test struct { m map[MessageType]Validators defaultValidator Validator } - var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil } + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -109,62 +121,69 @@ func testNewMsgTypeValidator(t *testing.T) { }{ // Success case { - description: "with provided default Validator", + description: "Default Validator success", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValidMsg}, + SimpleEventMessageType: {alwaysValid}, }, - defaultValidator: alwaysValidMsg, + defaultValidator: alwaysValid, }, expectedErr: nil, }, { - description: "without provided default Validator", + description: "Omit map of Validator success", + value: Test{ + m: map[MessageType]Validators{}, + defaultValidator: alwaysValid, + }, + expectedErr: nil, + }, + { + description: "Omit default Validator success", value: Test{ m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValidMsg}, + SimpleEventMessageType: {alwaysValid}, }, }, expectedErr: nil, }, + // Failure case { - description: "empty list of message type Validators", + description: "Empty list of Validators error", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {}, }, - defaultValidator: alwaysValidMsg, + defaultValidator: alwaysValid, }, - expectedErr: nil, + expectedErr: ErrInvalidTypeValidator, }, - // Failure case { - description: "missing message type Validators", + description: "Empty map of Validators error", value: Test{}, - expectedErr: ErrInvalidMsgTypeValidator, + expectedErr: ErrInvalidTypeValidator, }, } for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) assert.NotNil(msgv) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return } - assert.Nil(err) + assert.NoError(err) }) } } -func testAlwaysInvalidMsg(t *testing.T) { +func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} - err := AlwaysInvalidMsg(msg) + err := AlwaysInvalid(msg) - assert.NotNil(err) assert.ErrorIs(err, ErrInvalidMsgType) } @@ -174,7 +193,7 @@ func TestHelperValidators(t *testing.T) { name string test func(*testing.T) }{ - {"AlwaysInvalidMsg", testAlwaysInvalidMsg}, + {"AlwaysInvalid", testAlwaysInvalid}, } for _, tc := range tests { @@ -182,13 +201,13 @@ func TestHelperValidators(t *testing.T) { } } -func TestMsgTypeValidator(t *testing.T) { +func TestTypeValidator(t *testing.T) { tests := []struct { name string test func(*testing.T) }{ - {"MsgTypeValidator validate", testMsgTypeValidatorValidate}, - {"MsgTypeValidator factory", testNewMsgTypeValidator}, + {"TypeValidator validate", testTypeValidatorValidate}, + {"TypeValidator factory", testNewTypeValidator}, } for _, tc := range tests { From afa08cb071f84a544801cede147dcbb51267e176 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 21:09:25 -0400 Subject: [PATCH 09/68] removed duplicated test already covered by "Not found success" --- validator_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/validator_test.go b/validator_test.go index 8e88061..3f5108d 100644 --- a/validator_test.go +++ b/validator_test.go @@ -57,14 +57,6 @@ func testTypeValidatorValidate(t *testing.T) { msg: Message{Type: CreateMessageType}, }, }, - { - description: "Never found success", - value: Test{ - m: map[MessageType]Validators{}, - defaultValidator: alwaysValid, - msg: Message{Type: CreateMessageType}, - }, - }, // Failure case { description: "Found error", From 8cb0fa700dfab2460c6272fb7ad1e583f8b18385 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 21:11:22 -0400 Subject: [PATCH 10/68] Fix type test name --- validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index 3f5108d..ecc9521 100644 --- a/validator_test.go +++ b/validator_test.go @@ -123,7 +123,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, { - description: "Omit map of Validator success", + description: "Empty map of Validator success", value: Test{ m: map[MessageType]Validators{}, defaultValidator: alwaysValid, From 10afaa2cdf3e41462e347fffd2b3f840e4861524 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 21:11:48 -0400 Subject: [PATCH 11/68] Fix type test name --- validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index ecc9521..31091dd 100644 --- a/validator_test.go +++ b/validator_test.go @@ -123,7 +123,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, { - description: "Empty map of Validator success", + description: "Empty map of Validators success", value: Test{ m: map[MessageType]Validators{}, defaultValidator: alwaysValid, From 1923ec78abb83effc79a936f97022d1e85c08e42 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 25 May 2022 21:20:56 -0400 Subject: [PATCH 12/68] Add missing test/edge cases for Validators --- validator.go | 10 ++++++++-- validator_test.go | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/validator.go b/validator.go index f770ab2..618b09f 100644 --- a/validator.go +++ b/validator.go @@ -86,10 +86,16 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) return TypeValidator{}, ErrInvalidTypeValidator } - for _, v := range m { - if v == nil || len(v) == 0 { + for _, vs := range m { + if vs == nil || len(vs) == 0 { return TypeValidator{}, ErrInvalidTypeValidator } + + for _, v := range vs { + if v == nil { + return TypeValidator{}, ErrInvalidTypeValidator + } + } } if defaultValidator == nil { diff --git a/validator_test.go b/validator_test.go index 31091dd..68c7215 100644 --- a/validator_test.go +++ b/validator_test.go @@ -150,6 +150,26 @@ func testNewTypeValidator(t *testing.T) { }, expectedErr: ErrInvalidTypeValidator, }, + { + description: "Nil Validators error", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: nil, + }, + defaultValidator: alwaysValid, + }, + expectedErr: ErrInvalidTypeValidator, + }, + { + description: "Nil list of Validators error", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {nil}, + }, + defaultValidator: alwaysValid, + }, + expectedErr: ErrInvalidTypeValidator, + }, { description: "Empty map of Validators error", value: Test{}, From 98c4b4a1c050534d9120891cd2db428692615c20 Mon Sep 17 00:00:00 2001 From: ocabal200 Date: Thu, 26 May 2022 12:50:42 -0400 Subject: [PATCH 13/68] Updated defaultValidator to work as a variadic for multi-default support & added examples --- validator.go | 26 ++++++++++------- validator_test.go | 74 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/validator.go b/validator.go index 618b09f..6736faf 100644 --- a/validator.go +++ b/validator.go @@ -63,25 +63,25 @@ func (vf ValidatorFunc) Validate(m Message) error { } // TypeValidator is a WRP validator that validates based on message type -// or using the defaultValidator if message type is unknown. +// or using the defaultValidators if message type is unknown. type TypeValidator struct { - m map[MessageType]Validators - defaultValidator Validator + m map[MessageType]Validators + defaultValidators Validators } -// Validate validates messages based on message type or using the defaultValidator +// Validate validates messages based on message type or using the defaultValidators // if message type is unknown. func (m TypeValidator) Validate(msg Message) error { vs := m.m[msg.MessageType()] if vs == nil { - return m.defaultValidator.Validate(msg) + return m.defaultValidators.Validate(msg) } return vs.Validate(msg) } // NewTypeValidator is a TypeValidator factory. -func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (TypeValidator, error) { +func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { return TypeValidator{}, ErrInvalidTypeValidator } @@ -98,12 +98,18 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) } } - if defaultValidator == nil { - defaultValidator = AlwaysInvalid + if len(defaultValidators) == 0 { + defaultValidators = Validators{AlwaysInvalid} + } + + for _, v := range defaultValidators { + if v == nil { + return TypeValidator{}, ErrInvalidTypeValidator + } } return TypeValidator{ - m: m, - defaultValidator: defaultValidator, + m: m, + defaultValidators: defaultValidators, }, nil } diff --git a/validator_test.go b/validator_test.go index 68c7215..66e9e67 100644 --- a/validator_test.go +++ b/validator_test.go @@ -18,6 +18,7 @@ package wrp import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -26,9 +27,9 @@ import ( func testTypeValidatorValidate(t *testing.T) { type Test struct { - m map[MessageType]Validators - defaultValidator Validator - msg Message + m map[MessageType]Validators + defaultValidators Validators + msg Message } var alwaysValid ValidatorFunc = func(msg Message) error { return nil } @@ -53,8 +54,8 @@ func testTypeValidatorValidate(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, }, - defaultValidator: alwaysValid, - msg: Message{Type: CreateMessageType}, + defaultValidators: Validators{alwaysValid}, + msg: Message{Type: CreateMessageType}, }, }, // Failure case @@ -64,8 +65,8 @@ func testTypeValidatorValidate(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, }, - defaultValidator: alwaysValid, - msg: Message{Type: SimpleEventMessageType}, + defaultValidators: Validators{alwaysValid}, + msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, }, @@ -85,7 +86,7 @@ func testTypeValidatorValidate(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) require := require.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) require.NotNil(msgv) require.NoError(err) err = msgv.Validate(tc.value.msg) @@ -101,8 +102,8 @@ func testTypeValidatorValidate(t *testing.T) { func testNewTypeValidator(t *testing.T) { type Test struct { - m map[MessageType]Validators - defaultValidator Validator + m map[MessageType]Validators + defaultValidators Validators } var alwaysValid ValidatorFunc = func(msg Message) error { return nil } @@ -118,15 +119,15 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: nil, }, { description: "Empty map of Validators success", value: Test{ - m: map[MessageType]Validators{}, - defaultValidator: alwaysValid, + m: map[MessageType]Validators{}, + defaultValidators: Validators{alwaysValid}, }, expectedErr: nil, }, @@ -146,7 +147,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -156,7 +157,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: nil, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -166,7 +167,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {nil}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -179,7 +180,7 @@ func testNewTypeValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) assert.NotNil(msgv) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) @@ -226,3 +227,42 @@ func TestTypeValidator(t *testing.T) { t.Run(tc.name, tc.test) } } + +func ExampleNewTypeValidator() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + if err != nil { + return + } + err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + fmt.Println(err == nil) + // Output: true + +} + +func ExampleTypeValidator_Validate_found() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + fmt.Println(err == nil) + // Output: true +} +func ExampleTypeValidator_Validate_notFound() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + err = msgv.Validate(Message{Type: CreateMessageType}) // Not Found error + fmt.Println(err == nil) + // Output: false +} From 319756825bf1961050dd1808406caac90fb291ee Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:22:34 -0400 Subject: [PATCH 14/68] rename test var `name` to `description` --- validator_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/validator_test.go b/validator_test.go index 68c7215..fe376fd 100644 --- a/validator_test.go +++ b/validator_test.go @@ -202,27 +202,27 @@ func testAlwaysInvalid(t *testing.T) { func TestHelperValidators(t *testing.T) { tests := []struct { - name string - test func(*testing.T) + description string + test func(*testing.T) }{ {"AlwaysInvalid", testAlwaysInvalid}, } for _, tc := range tests { - t.Run(tc.name, tc.test) + t.Run(tc.description, tc.test) } } func TestTypeValidator(t *testing.T) { tests := []struct { - name string - test func(*testing.T) + description string + test func(*testing.T) }{ {"TypeValidator validate", testTypeValidatorValidate}, {"TypeValidator factory", testNewTypeValidator}, } for _, tc := range tests { - t.Run(tc.name, tc.test) + t.Run(tc.description, tc.test) } } From 9208a8f82b9cc3c528107679976a96e02a4e1025 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:32:47 -0400 Subject: [PATCH 15/68] update examples --- validator_test.go | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/validator_test.go b/validator_test.go index 11a41a9..8688c6c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -235,34 +235,24 @@ func ExampleNewTypeValidator() { map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unknown msg types AlwaysInvalid) - if err != nil { - return - } - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true -} + fmt.Printf("%v, %T", err == nil, msgv) + // Output: true, wrp.TypeValidator -func ExampleTypeValidator_Validate_found() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } - msgv, err := NewTypeValidator( - // Validates known msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types - AlwaysInvalid) - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true } -func ExampleTypeValidator_Validate_notFound() { + +func ExampleTypeValidator_Validate() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates known msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unknown msg types AlwaysInvalid) - err = msgv.Validate(Message{Type: CreateMessageType}) // Not Found error - fmt.Println(err == nil) - // Output: false + if err != nil { + return + } + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + notFoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Not Found error + fmt.Printf("foundErr is nil: %v, notFoundErr is nil: %v", foundErr == nil, notFoundErr == nil) + // Output: foundErr is nil: true, notFoundErr is nil: false } From f80ac927139925019c1b5e5c397c09dbf7af2e50 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:38:15 -0400 Subject: [PATCH 16/68] Doc/var update --- validator_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/validator_test.go b/validator_test.go index 8688c6c..09d9d3f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -49,7 +49,7 @@ func testTypeValidatorValidate(t *testing.T) { }, }, { - description: "Not found success", + description: "Unfound success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, @@ -71,7 +71,7 @@ func testTypeValidatorValidate(t *testing.T) { expectedErr: ErrInvalidMsgType, }, { - description: "Not Found error", + description: "Unfound error", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -244,15 +244,15 @@ func ExampleNewTypeValidator() { func ExampleTypeValidator_Validate() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( - // Validates known msg types + // Validates found msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates unfound msg types AlwaysInvalid) if err != nil { return } foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - notFoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Not Found error - fmt.Printf("foundErr is nil: %v, notFoundErr is nil: %v", foundErr == nil, notFoundErr == nil) - // Output: foundErr is nil: true, notFoundErr is nil: false + unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Printf("foundErr is nil: %v, unfoundErr is nil: %v", foundErr == nil, unfoundErr == nil) + // Output: foundErr is nil: true, unfoundErr is nil: false } From c0cceaa4f9d060486f9b82dbbaee2f82cc132bd8 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:45:04 -0400 Subject: [PATCH 17/68] Add test for `Nil default Validators` edge case --- validator_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/validator_test.go b/validator_test.go index 09d9d3f..604faaa 100644 --- a/validator_test.go +++ b/validator_test.go @@ -114,7 +114,7 @@ func testNewTypeValidator(t *testing.T) { }{ // Success case { - description: "Default Validator success", + description: "Default Validators success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -132,7 +132,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, { - description: "Omit default Validator success", + description: "Omit default Validators success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -141,6 +141,16 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, // Failure case + { + description: "Nil default Validators", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValid}, + }, + defaultValidators: Validators{nil}, + }, + expectedErr: ErrInvalidTypeValidator, + }, { description: "Empty list of Validators error", value: Test{ From 7e454c4fddee43d8368e0a5eae99a5f8419634bb Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:51:08 -0400 Subject: [PATCH 18/68] update examples output --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index 604faaa..906ea4b 100644 --- a/validator_test.go +++ b/validator_test.go @@ -246,8 +246,8 @@ func ExampleNewTypeValidator() { // Validates unknown msg types AlwaysInvalid) - fmt.Printf("%v, %T", err == nil, msgv) - // Output: true, wrp.TypeValidator + fmt.Printf("%v %T", err == nil, msgv) + // Output: true wrp.TypeValidator } @@ -263,6 +263,6 @@ func ExampleTypeValidator_Validate() { } foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Printf("foundErr is nil: %v, unfoundErr is nil: %v", foundErr == nil, unfoundErr == nil) - // Output: foundErr is nil: true, unfoundErr is nil: false + fmt.Println(foundErr == nil, unfoundErr == nil) + // Output: true false } From 1909c2a926febb2f7dba78f905545ad669208cd0 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:54:35 -0400 Subject: [PATCH 19/68] doc update --- validator.go | 4 ++-- validator_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/validator.go b/validator.go index 6736faf..6eb4846 100644 --- a/validator.go +++ b/validator.go @@ -63,14 +63,14 @@ func (vf ValidatorFunc) Validate(m Message) error { } // TypeValidator is a WRP validator that validates based on message type -// or using the defaultValidators if message type is unknown. +// or using the defaultValidators if message type is unfound. type TypeValidator struct { m map[MessageType]Validators defaultValidators Validators } // Validate validates messages based on message type or using the defaultValidators -// if message type is unknown. +// if message type is unfound. func (m TypeValidator) Validate(msg Message) error { vs := m.m[msg.MessageType()] if vs == nil { diff --git a/validator_test.go b/validator_test.go index 906ea4b..33ebd8c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -241,9 +241,9 @@ func TestTypeValidator(t *testing.T) { func ExampleNewTypeValidator() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( - // Validates known msg types + // Validates found msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) From ee29bf3183268cb238b0c90e5d011c4caf5b1c40 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:59:13 -0400 Subject: [PATCH 20/68] formatting --- validator_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index 33ebd8c..66c2f49 100644 --- a/validator_test.go +++ b/validator_test.go @@ -187,6 +187,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: ErrInvalidTypeValidator, }, } + for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) @@ -206,9 +207,7 @@ func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} err := AlwaysInvalid(msg) - assert.ErrorIs(err, ErrInvalidMsgType) - } func TestHelperValidators(t *testing.T) { @@ -245,10 +244,8 @@ func ExampleNewTypeValidator() { map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unfound msg types AlwaysInvalid) - fmt.Printf("%v %T", err == nil, msgv) // Output: true wrp.TypeValidator - } func ExampleTypeValidator_Validate() { @@ -261,6 +258,7 @@ func ExampleTypeValidator_Validate() { if err != nil { return } + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error fmt.Println(foundErr == nil, unfoundErr == nil) From 607066ef6a95c449f797524741de0b4949973596 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 31 May 2022 14:47:58 -0400 Subject: [PATCH 21/68] Updates based on PR review * exported alwaysValid * decoupled data structures leveraging `Validators` to `Validator` * added validateValidator --- validator.go | 49 ++++++++++++++++++++---------- validator_test.go | 77 +++++++++++++++++++++++------------------------ 2 files changed, 71 insertions(+), 55 deletions(-) diff --git a/validator.go b/validator.go index 6eb4846..33d9ba3 100644 --- a/validator.go +++ b/validator.go @@ -27,9 +27,10 @@ var ( ) // AlwaysInvalid doesn't validate anything about the message and always returns an error. -var AlwaysInvalid ValidatorFunc = func(m Message) error { - return ErrInvalidMsgType -} +var AlwaysInvalid ValidatorFunc = func(m Message) error { return ErrInvalidMsgType } + +// AlwaysValid doesn't validate anything about the message and always returns nil. +var AlwaysValid ValidatorFunc = func(msg Message) error { return nil } // Validator is a WRP validator that allows access to the Validate function. type Validator interface { @@ -65,7 +66,7 @@ func (vf ValidatorFunc) Validate(m Message) error { // TypeValidator is a WRP validator that validates based on message type // or using the defaultValidators if message type is unfound. type TypeValidator struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } @@ -81,20 +82,14 @@ func (m TypeValidator) Validate(msg Message) error { } // NewTypeValidator is a TypeValidator factory. -func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validator) (TypeValidator, error) { +func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { return TypeValidator{}, ErrInvalidTypeValidator } - for _, vs := range m { - if vs == nil || len(vs) == 0 { - return TypeValidator{}, ErrInvalidTypeValidator - } - - for _, v := range vs { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator - } + for _, v := range m { + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -103,8 +98,8 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat } for _, v := range defaultValidators { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -113,3 +108,25 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat defaultValidators: defaultValidators, }, nil } + +// validateValidator validates a given Validator. +func validateValidator(v Validator) error { + switch vs := v.(type) { + case Validators: + if vs == nil || len(vs) == 0 { + return ErrInvalidTypeValidator + } + + for _, v := range vs { + if v == nil { + return ErrInvalidTypeValidator + } + } + case Validator, ValidatorFunc: + // catch nil Validator + default: + return ErrInvalidTypeValidator + } + + return nil +} diff --git a/validator_test.go b/validator_test.go index 66c2f49..64cf8c7 100644 --- a/validator_test.go +++ b/validator_test.go @@ -27,12 +27,11 @@ import ( func testTypeValidatorValidate(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators msg Message } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -42,8 +41,8 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: SimpleEventMessageType}, }, @@ -51,10 +50,10 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: CreateMessageType}, }, }, @@ -62,10 +61,10 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, @@ -73,8 +72,8 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: CreateMessageType}, }, @@ -102,11 +101,10 @@ func testTypeValidatorValidate(t *testing.T) { func testNewTypeValidator(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -116,36 +114,36 @@ func testNewTypeValidator(t *testing.T) { { description: "Default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { description: "Empty map of Validators success", value: Test{ - m: map[MessageType]Validators{}, - defaultValidators: Validators{alwaysValid}, + m: map[MessageType]Validator{}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { description: "Omit default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, }, expectedErr: nil, }, // Failure case { - description: "Nil default Validators", + description: "Nil list of default Validators error ", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, defaultValidators: Validators{nil}, }, @@ -154,36 +152,39 @@ func testNewTypeValidator(t *testing.T) { { description: "Empty list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Nil Validators error", + description: "Nil Validator error", value: Test{ - m: map[MessageType]Validators{ + m: map[MessageType]Validator{ SimpleEventMessageType: nil, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { description: "Nil list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {nil}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{nil}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Empty map of Validators error", - value: Test{}, + description: "Nil map of Validators error", + value: Test{ + m: nil, + defaultValidators: Validators{AlwaysValid}, + }, expectedErr: ErrInvalidTypeValidator, }, } @@ -238,10 +239,9 @@ func TestTypeValidator(t *testing.T) { } func ExampleNewTypeValidator() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) @@ -249,10 +249,9 @@ func ExampleNewTypeValidator() { } func ExampleTypeValidator_Validate() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, // Validates unfound msg types AlwaysInvalid) if err != nil { From d943b603ef5540bf7b994376e4b2be5870538ec9 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 1 Jun 2022 12:43:08 -0400 Subject: [PATCH 22/68] Add multierr to Validator --- go.mod | 1 + go.sum | 4 ++++ validator.go | 10 +++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 1fd3bdd..393c95c 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/ugorji/go/codec v1.2.6 github.com/xmidt-org/httpaux v0.3.0 github.com/xmidt-org/webpa-common v1.3.2 + go.uber.org/multierr v1.8.0 ) diff --git a/go.sum b/go.sum index a1d9119..8370579 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/xmidt-org/httpaux v0.3.0 h1:JdV4QceiE8EMA1Qf5rnJzHdkIPzQV12ddARHLU8hr github.com/xmidt-org/httpaux v0.3.0/go.mod h1:mviIlg5fHGb3lAv3l0sbiwVG/q9rqvXaudEYxVrzXdE= github.com/xmidt-org/webpa-common v1.3.2 h1:dE1Fi+XVnkt3tMGMjH7/hN/UGcaQ/ukKriXuMDyCWnM= github.com/xmidt-org/webpa-common v1.3.2/go.mod h1:oCpKzOC+9h2vYHVzAU/06tDTQuBN4RZz+rhgIXptpOI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/validator.go b/validator.go index 33d9ba3..f909660 100644 --- a/validator.go +++ b/validator.go @@ -19,6 +19,8 @@ package wrp import ( "errors" + + "go.uber.org/multierr" ) var ( @@ -44,14 +46,12 @@ type Validators []Validator // Validate runs messages through each validator in the validators list. // It returns as soon as the message is considered invalid, otherwise returns nil if valid. func (vs Validators) Validate(m Message) error { + var err error for _, v := range vs { - err := v.Validate(m) - if err != nil { - return err - } + err = multierr.Append(err, v.Validate(m)) } - return nil + return err } // ValidatorFunc is a WRP validator that takes messages and validates them From 793d6f40c94aed96b06b382e46dd83f69145e566 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:32:47 -0400 Subject: [PATCH 23/68] Squash: examples, docs, multierr updates update examples Doc/var update Add test for `Nil default Validators` edge case update examples output doc update Updates based on PR review * exported alwaysValid * decoupled data structures leveraging `Validators` to `Validator` * added validateValidator Add multierr to Validator --- go.mod | 1 + go.sum | 4 ++ validator.go | 63 ++++++++++++++-------- validator_test.go | 133 ++++++++++++++++++++++------------------------ 4 files changed, 110 insertions(+), 91 deletions(-) diff --git a/go.mod b/go.mod index 1fd3bdd..393c95c 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/ugorji/go/codec v1.2.6 github.com/xmidt-org/httpaux v0.3.0 github.com/xmidt-org/webpa-common v1.3.2 + go.uber.org/multierr v1.8.0 ) diff --git a/go.sum b/go.sum index a1d9119..8370579 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/xmidt-org/httpaux v0.3.0 h1:JdV4QceiE8EMA1Qf5rnJzHdkIPzQV12ddARHLU8hr github.com/xmidt-org/httpaux v0.3.0/go.mod h1:mviIlg5fHGb3lAv3l0sbiwVG/q9rqvXaudEYxVrzXdE= github.com/xmidt-org/webpa-common v1.3.2 h1:dE1Fi+XVnkt3tMGMjH7/hN/UGcaQ/ukKriXuMDyCWnM= github.com/xmidt-org/webpa-common v1.3.2/go.mod h1:oCpKzOC+9h2vYHVzAU/06tDTQuBN4RZz+rhgIXptpOI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/validator.go b/validator.go index 6736faf..f909660 100644 --- a/validator.go +++ b/validator.go @@ -19,6 +19,8 @@ package wrp import ( "errors" + + "go.uber.org/multierr" ) var ( @@ -27,9 +29,10 @@ var ( ) // AlwaysInvalid doesn't validate anything about the message and always returns an error. -var AlwaysInvalid ValidatorFunc = func(m Message) error { - return ErrInvalidMsgType -} +var AlwaysInvalid ValidatorFunc = func(m Message) error { return ErrInvalidMsgType } + +// AlwaysValid doesn't validate anything about the message and always returns nil. +var AlwaysValid ValidatorFunc = func(msg Message) error { return nil } // Validator is a WRP validator that allows access to the Validate function. type Validator interface { @@ -43,14 +46,12 @@ type Validators []Validator // Validate runs messages through each validator in the validators list. // It returns as soon as the message is considered invalid, otherwise returns nil if valid. func (vs Validators) Validate(m Message) error { + var err error for _, v := range vs { - err := v.Validate(m) - if err != nil { - return err - } + err = multierr.Append(err, v.Validate(m)) } - return nil + return err } // ValidatorFunc is a WRP validator that takes messages and validates them @@ -63,14 +64,14 @@ func (vf ValidatorFunc) Validate(m Message) error { } // TypeValidator is a WRP validator that validates based on message type -// or using the defaultValidators if message type is unknown. +// or using the defaultValidators if message type is unfound. type TypeValidator struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } // Validate validates messages based on message type or using the defaultValidators -// if message type is unknown. +// if message type is unfound. func (m TypeValidator) Validate(msg Message) error { vs := m.m[msg.MessageType()] if vs == nil { @@ -81,20 +82,14 @@ func (m TypeValidator) Validate(msg Message) error { } // NewTypeValidator is a TypeValidator factory. -func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validator) (TypeValidator, error) { +func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { return TypeValidator{}, ErrInvalidTypeValidator } - for _, vs := range m { - if vs == nil || len(vs) == 0 { - return TypeValidator{}, ErrInvalidTypeValidator - } - - for _, v := range vs { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator - } + for _, v := range m { + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -103,8 +98,8 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat } for _, v := range defaultValidators { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -113,3 +108,25 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat defaultValidators: defaultValidators, }, nil } + +// validateValidator validates a given Validator. +func validateValidator(v Validator) error { + switch vs := v.(type) { + case Validators: + if vs == nil || len(vs) == 0 { + return ErrInvalidTypeValidator + } + + for _, v := range vs { + if v == nil { + return ErrInvalidTypeValidator + } + } + case Validator, ValidatorFunc: + // catch nil Validator + default: + return ErrInvalidTypeValidator + } + + return nil +} diff --git a/validator_test.go b/validator_test.go index 11a41a9..64cf8c7 100644 --- a/validator_test.go +++ b/validator_test.go @@ -27,12 +27,11 @@ import ( func testTypeValidatorValidate(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators msg Message } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -42,19 +41,19 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: SimpleEventMessageType}, }, }, { - description: "Not found success", + description: "Unfound success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: CreateMessageType}, }, }, @@ -62,19 +61,19 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, }, { - description: "Not Found error", + description: "Unfound error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: CreateMessageType}, }, @@ -102,11 +101,10 @@ func testTypeValidatorValidate(t *testing.T) { func testNewTypeValidator(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -114,69 +112,83 @@ func testNewTypeValidator(t *testing.T) { }{ // Success case { - description: "Default Validator success", + description: "Default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { description: "Empty map of Validators success", value: Test{ - m: map[MessageType]Validators{}, - defaultValidators: Validators{alwaysValid}, + m: map[MessageType]Validator{}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { - description: "Omit default Validator success", + description: "Omit default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, }, expectedErr: nil, }, // Failure case + { + description: "Nil list of default Validators error ", + value: Test{ + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + defaultValidators: Validators{nil}, + }, + expectedErr: ErrInvalidTypeValidator, + }, { description: "Empty list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Nil Validators error", + description: "Nil Validator error", value: Test{ - m: map[MessageType]Validators{ + m: map[MessageType]Validator{ SimpleEventMessageType: nil, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { description: "Nil list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {nil}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{nil}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Empty map of Validators error", - value: Test{}, + description: "Nil map of Validators error", + value: Test{ + m: nil, + defaultValidators: Validators{AlwaysValid}, + }, expectedErr: ErrInvalidTypeValidator, }, } + for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) @@ -196,9 +208,7 @@ func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} err := AlwaysInvalid(msg) - assert.ErrorIs(err, ErrInvalidMsgType) - } func TestHelperValidators(t *testing.T) { @@ -229,40 +239,27 @@ func TestTypeValidator(t *testing.T) { } func ExampleNewTypeValidator() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( - // Validates known msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + // Validates unfound msg types AlwaysInvalid) - if err != nil { - return - } - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true - + fmt.Printf("%v %T", err == nil, msgv) + // Output: true wrp.TypeValidator } -func ExampleTypeValidator_Validate_found() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } +func ExampleTypeValidator_Validate() { msgv, err := NewTypeValidator( - // Validates known msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + // Validates unfound msg types AlwaysInvalid) - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true -} -func ExampleTypeValidator_Validate_notFound() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } - msgv, err := NewTypeValidator( - // Validates known msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types - AlwaysInvalid) - err = msgv.Validate(Message{Type: CreateMessageType}) // Not Found error - fmt.Println(err == nil) - // Output: false + if err != nil { + return + } + + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErr == nil, unfoundErr == nil) + // Output: true false } From 22ca097194233f192a8dad0c268601277c0fad71 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 08:10:28 -0400 Subject: [PATCH 24/68] formatting --- validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index 64cf8c7..afe50c5 100644 --- a/validator_test.go +++ b/validator_test.go @@ -140,7 +140,7 @@ func testNewTypeValidator(t *testing.T) { }, // Failure case { - description: "Nil list of default Validators error ", + description: "Nil list of default Validators error", value: Test{ m: map[MessageType]Validator{ SimpleEventMessageType: AlwaysValid, From 5ff49e320c248034d8432c160d16b0db30736342 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 16:15:54 -0400 Subject: [PATCH 25/68] Add missing test for `AlwaysValid` func --- validator_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/validator_test.go b/validator_test.go index afe50c5..14bc9b8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -204,6 +204,13 @@ func testNewTypeValidator(t *testing.T) { } } +func testAlwaysValid(t *testing.T) { + assert := assert.New(t) + msg := Message{} + err := AlwaysValid(msg) + assert.NoError(err) +} + func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} @@ -217,6 +224,7 @@ func TestHelperValidators(t *testing.T) { test func(*testing.T) }{ {"AlwaysInvalid", testAlwaysInvalid}, + {"AlwaysValid", testAlwaysValid}, } for _, tc := range tests { From 25d4468dcfa8b15dac3631ebdbe6335f50ce097d Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 16:52:33 -0400 Subject: [PATCH 26/68] First pass of spec validators * Validates requirements described at https://xmidt.io/docs/wrp/basics/#overarching-guidelines * Validates requirements described at https://xmidt.io/docs/wrp/basics/#locators * validates message type values * Does not cover validators for a specific message type (future prs) --- go.mod | 1 + go.sum | 2 + spec_validator.go | 144 ++++++++++++++ spec_validator_test.go | 443 +++++++++++++++++++++++++++++++++++++++++ utf8.go | 2 +- 5 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 spec_validator.go create mode 100644 spec_validator_test.go diff --git a/go.mod b/go.mod index 393c95c..5cb024a 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-kit/kit v0.8.0 github.com/go-logfmt/logfmt v0.3.0 // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/google/uuid v1.3.0 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.6 diff --git a/go.sum b/go.sum index 8370579..6684580 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2i github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/spec_validator.go b/spec_validator.go new file mode 100644 index 0000000..b1cce5e --- /dev/null +++ b/spec_validator.go @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wrp + +import ( + "errors" + "regexp" + "strings" + "unicode" + + "github.com/google/uuid" + "go.uber.org/multierr" +) + +const ( + serialPrefix = "serial" + uuidPrefix = "uuid" + eventPrefix = "event" + dnsPrefix = "dns" +) + +var ( + ErrorInvalidMessageEncoding = errors.New("invalid message encoding") + ErrorInvalidMessageType = errors.New("invalid message type") + ErrorInvalidLocator = errors.New("invalid locator") + ErrorInvalidSource = errors.New("invalid Source name") + ErrorInvalidDestination = errors.New("invalid Destination name") +) + +// locatorPattern is the precompiled regular expression that all source and dest locators must match. +// Matching is partial, as everything after the authority (ID) is ignored. https://xmidt.io/docs/wrp/basics/#locators +var LocatorPattern = regexp.MustCompile( + `^(?P(?i)` + macPrefix + `|` + uuidPrefix + `|` + eventPrefix + `|` + dnsPrefix + `|` + serialPrefix + `):(?P[^/]+)?`, +) + +// SpecValidators is a WRP validator that ensures messages are valid based on +// each spec validator in the list. +var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} + +// UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. +var UTF8Validator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, UTF8(m)) { + err = multierr.Append(err, ErrorInvalidMessageEncoding) + } + + return err +} + +// MessageTypeValidator is a WRP validator that takes messages and validates their Type. +var MessageTypeValidator ValidatorFunc = func(m Message) error { + t := m.MessageType() + if t < Invalid0MessageType || t > lastMessageType { + return ErrorInvalidMessageType + } + + switch t { + case Invalid0MessageType, Invalid1MessageType, lastMessageType: + return ErrorInvalidMessageType + } + + return nil +} + +// SourceValidator is a WRP validator that takes messages and validates their Source. +// Only mac and uuid sources are validated. Serial, event and dns sources are +// not validated. +var SourceValidator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, validateLocator(m.Source)) { + err = multierr.Append(err, ErrorInvalidSource) + } + + return err +} + +// DestinationValidator is a WRP validator that takes messages and validates their Destination. +// Only mac and uuid destinations are validated. Serial, event and dns destinations are +// not validated. +var DestinationValidator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, validateLocator(m.Destination)) { + err = multierr.Append(err, ErrorInvalidDestination) + } + + return err +} + +// validateLocator validates a given locator's scheme and authority (ID). +// Only mac and uuid schemes' IDs are validated. IDs from serial, event and dns schemes are +// not validated. +func validateLocator(l string) error { + var err error + + match := LocatorPattern.FindStringSubmatch(l) + if match == nil { + return multierr.Append(err, ErrorInvalidLocator) + } + + idPart := match[2] + switch strings.ToLower(match[1]) { + case macPrefix: + var invalidCharacter rune = -1 + idPart = strings.Map( + func(r rune) rune { + switch { + case strings.ContainsRune(hexDigits, r): + return unicode.ToLower(r) + case strings.ContainsRune(macDelimiters, r): + return -1 + default: + invalidCharacter = r + return -1 + } + }, + idPart, + ) + + if invalidCharacter != -1 || len(idPart) != macLength { + return multierr.Append(err, ErrorInvalidLocator) + } + case uuidPrefix: + if _, uuidErr := uuid.Parse(idPart); multierr.AppendInto(&err, uuidErr) { + return multierr.Append(err, ErrorInvalidLocator) + } + } + + return err +} diff --git a/spec_validator_test.go b/spec_validator_test.go new file mode 100644 index 0000000..18c43a2 --- /dev/null +++ b/spec_validator_test.go @@ -0,0 +1,443 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wrp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testUTF8Validator(t *testing.T) { + /* + "\x85" - 5 name value pairs + "\xa8""msg_type" : "\x03" // 3 + "\xa4""dest" : "\xac""\xed\xbf\xbft-address" + "\xa7""payload" : "\xc4""\x03" - len 3 + "123" + "\xa6""source" : "\xae""source-address" + "\xb0""transaction_uuid" : "\xd9\x24""c07ee5e1-70be-444c-a156-097c767ad8aa" + */ + invalid := []byte{ + 0x85, + 0xa8, 'm', 's', 'g', '_', 't', 'y', 'p', 'e', 0x03, + 0xa4, 'd', 'e', 's', 't', 0xac /* \xed\xbf\xbf is invalid */, 0xed, 0xbf, 0xbf, 't', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', + 0xa7, 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0xc4, 0x03, '1', '2', '3', + 0xa6, 's', 'o', 'u', 'r', 'c', 'e', 0xae, 's', 'o', 'u', 'r', 'c', 'e', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', + 0xb0, 't', 'r', 'a', 'n', 's', 'a', 'c', 't', 'i', 'o', 'n', '_', 'u', 'u', 'i', 'd', 0xd9, 0x24, 'c', '0', '7', 'e', 'e', '5', 'e', '1', '-', '7', '0', 'b', 'e', '-', '4', '4', '4', 'c', '-', 'a', '1', '5', '6', '-', '0', '9', '7', 'c', '7', '6', '7', 'a', 'd', '8', 'a', 'a', + } + decoder := NewDecoderBytes(invalid, Msgpack) + msg := new(Message) + err := decoder.Decode(msg) + require.NoError(t, err) + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "UTF8 success", + value: Message{Source: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + { + description: "Not UTF8 error", + value: *msg, + expectedErr: []error{ErrorInvalidMessageEncoding, ErrNotUTF8}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := UTF8Validator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testMessageTypeValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr error + }{ + // Success case + { + description: "AuthorizationMessageType success", + value: Message{Type: AuthorizationMessageType}, + expectedErr: nil, + }, + { + description: "SimpleRequestResponseMessageType success", + value: Message{Type: SimpleRequestResponseMessageType}, + expectedErr: nil, + }, + { + description: "SimpleEventMessageType success", + value: Message{Type: SimpleEventMessageType}, + expectedErr: nil, + }, + { + description: "CreateMessageType success", + value: Message{Type: CreateMessageType}, + expectedErr: nil, + }, + { + description: "RetrieveMessageType success", + value: Message{Type: RetrieveMessageType}, + expectedErr: nil, + }, + { + description: "UpdateMessageType success", + value: Message{Type: UpdateMessageType}, + expectedErr: nil, + }, + { + description: "DeleteMessageType success", + value: Message{Type: DeleteMessageType}, + expectedErr: nil, + }, + { + description: "ServiceRegistrationMessageType success", + value: Message{Type: ServiceRegistrationMessageType}, + expectedErr: nil, + }, + { + description: "ServiceAliveMessageType success", + value: Message{Type: ServiceAliveMessageType}, + expectedErr: nil, + }, + { + description: "UnknownMessageType success", + value: Message{Type: UnknownMessageType}, + expectedErr: nil, + }, + // Failure case + { + description: "Invalid0MessageType error", + value: Message{Type: Invalid0MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Invalid0MessageType error", + value: Message{Type: Invalid0MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Invalid1MessageType error", + value: Message{Type: Invalid1MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "lastMessageType error", + value: Message{Type: lastMessageType}, + expectedErr: ErrorInvalidMessageType, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := MessageTypeValidator(tc.value) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.NoError(err) + }) + } +} + +func testSourceValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Source success", + value: Message{Source: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + // Failures + { + description: "Source error", + value: Message{Source: "invalid:a-BB-44-55"}, + expectedErr: []error{ErrorInvalidSource, ErrorInvalidLocator}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SourceValidator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testDestinationValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Destination success", + value: Message{Destination: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + // Failures + { + description: "Destination error", + value: Message{Destination: "invalid:a-BB-44-55"}, + expectedErr: []error{ErrorInvalidDestination, ErrorInvalidLocator}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := DestinationValidator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testValidateLocator(t *testing.T) { + tests := []struct { + description string + value string + expectedErr error + }{ + // mac success case + { + description: "Mac ID ':' delimiter success", + value: "MAC:11:22:33:44:55:66", + expectedErr: nil, + }, + { + description: "Mac ID no delimiter success", + value: "MAC:11aaBB445566", + expectedErr: nil, + }, + { + description: "Mac ID '-' delimiter success", + value: "mac:11-aa-BB-44-55-66", + expectedErr: nil, + }, + { + description: "Mac ID ',' delimiter success", + value: "mac:11,aa,BB,44,55,66", + expectedErr: nil, + }, + { + description: "Mac with service success", + value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", + expectedErr: nil, + }, + // Mac failure case + { + description: "Invalid mac ID character error", + value: "MAC:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid mac ID length error", + value: "mac:11-aa-BB-44-55", + expectedErr: ErrorInvalidLocator, + }, + // Serial success case + { + description: "Serial ID success", + value: "serial:anything Goes!", + expectedErr: nil, + }, + // UUID success case + { + description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 + value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 + value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. + value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. + value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. + value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. + value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Future variant ID success", // Reserved for future definition. + value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", + expectedErr: nil, + }, + // UUID failure case + { + description: "Invalid UUID ID #1 error", + value: "uuid:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid UUID ID #2 error", + value: "uuid:URN:UUID:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid UUID ID #3 error", + value: "uuid:{invalid45566}", + expectedErr: ErrorInvalidLocator, + }, + // Event success case + { + description: "Event ID success", + value: "event:anything Goes!", + expectedErr: nil, + }, + // DNS success case + { + description: "DNS ID success", + value: "dns:anything Goes!", + expectedErr: nil, + }, + // Scheme failure case + { + description: "Invalid scheme error", + value: "invalid:a-BB-44-55", + expectedErr: ErrorInvalidLocator, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := validateLocator(tc.value) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.NoError(err) + }) + } +} + +func TestSpecHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"UTF8Validator", testUTF8Validator}, + {"MessageTypeValidator", testMessageTypeValidator}, + {"SourceValidator", testSourceValidator}, + {"DestinationValidator", testDestinationValidator}, + {"validateLocator", testValidateLocator}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestSpecValidators(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Valid specs success", + value: Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: nil, + }, + // Failure cases + { + description: "Invaild specs error", + value: Message{ + Type: Invalid0MessageType, + Source: "invalid:a-BB-44-55", + Destination: "invalid:a-BB-44-55", + }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidLocator, ErrorInvalidDestination}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SpecValidators.Validate(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} diff --git a/utf8.go b/utf8.go index 5aa0417..9a5d4fc 100644 --- a/utf8.go +++ b/utf8.go @@ -26,7 +26,7 @@ import ( var ( ErrNotUTF8 = errors.New("field contains non-utf-8 characters") - ErrUnexpectedKind = errors.New("A struct or non-nil pointer to struct is required") + ErrUnexpectedKind = errors.New("a struct or non-nil pointer to struct is required") ) // UTF8 takes any struct verifies that it contains UTF-8 strings. From b2c6baa2d59bfdf2f20ed107b61ed48f5c73f501 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:28 -0400 Subject: [PATCH 27/68] Add an example for SpecValidators --- spec_validator_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec_validator_test.go b/spec_validator_test.go index 18c43a2..e3f5eb6 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -18,6 +18,7 @@ package wrp import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -441,3 +442,37 @@ func TestSpecValidators(t *testing.T) { }) } } + +func ExampleTypeValidator_Validate_specValidators() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{ + SimpleEventMessageType: SpecValidators, + // Only validates Source and nothing else + SimpleRequestResponseMessageType: SourceValidator, + }, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + foundErrSuccess1 := msgv.Validate(Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:61", + }) // Found success + foundErrSuccess2 := msgv.Validate(Message{ + Type: SimpleRequestResponseMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "invalid:a-BB-44-55", + }) // Found success + foundErrFailure := msgv.Validate(Message{ + Type: Invalid0MessageType, + Source: "invalid:a-BB-44-55", + Destination: "invalid:a-BB-44-55", + }) // Found error + unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErrSuccess1 == nil, foundErrSuccess2 == nil, foundErrFailure == nil, unfoundErrFailure == nil) + // Output: true true false false +} From 8ec49b14761dcb467e896aa8c052a4bfd2881340 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:57 -0400 Subject: [PATCH 28/68] Remove println from UTF8 validate func --- utf8.go | 1 - 1 file changed, 1 deletion(-) diff --git a/utf8.go b/utf8.go index 9a5d4fc..d1e26a2 100644 --- a/utf8.go +++ b/utf8.go @@ -55,7 +55,6 @@ func UTF8(v interface{}) error { if !utf8.ValidString(s) { return fmt.Errorf("%w: '%s:%v'", ErrNotUTF8, ft.Name, s) } - fmt.Println(s) } } From 2e2cd23a027a32b5db8bf0bbb3997af28bb45a7a Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:45:24 -0400 Subject: [PATCH 29/68] Add missing tests for MessageType --- spec_validator_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec_validator_test.go b/spec_validator_test.go index e3f5eb6..861d216 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -159,6 +159,16 @@ func testMessageTypeValidator(t *testing.T) { value: Message{Type: lastMessageType}, expectedErr: ErrorInvalidMessageType, }, + { + description: "Non-existing negative MessageType error", + value: Message{Type: -10}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Non-existing positive MessageType error", + value: Message{Type: lastMessageType + 1}, + expectedErr: ErrorInvalidMessageType, + }, } for _, tc := range tests { From 8e83772de006d6365286e3c874215ed686dcaf36 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 18:34:58 -0400 Subject: [PATCH 30/68] Update docs --- spec_validator.go | 2 +- spec_validator_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec_validator.go b/spec_validator.go index b1cce5e..78cff67 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -49,7 +49,7 @@ var LocatorPattern = regexp.MustCompile( ) // SpecValidators is a WRP validator that ensures messages are valid based on -// each spec validator in the list. +// each spec validator in the list. Only validates the opinionated portions of the spec. var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} // UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. diff --git a/spec_validator_test.go b/spec_validator_test.go index 861d216..d0c1c6f 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -457,6 +457,7 @@ func ExampleTypeValidator_Validate_specValidators() { msgv, err := NewTypeValidator( // Validates found msg types map[MessageType]Validator{ + // Validates opinionated portions of the spec SimpleEventMessageType: SpecValidators, // Only validates Source and nothing else SimpleRequestResponseMessageType: SourceValidator, From 87a84814febffbccccc26fb17e5122f6ddc8c779 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Mon, 6 Jun 2022 13:14:40 -0400 Subject: [PATCH 31/68] Updates based on PR Feedback * Clean up errors * Remove multierr with some wrapping and regular errors * Wrap SpecValidators within a func --- spec_validator.go | 45 +++++++++++++------------- spec_validator_test.go | 72 ++++++++++++++++++++---------------------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index 78cff67..3033a2f 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -19,12 +19,13 @@ package wrp import ( "errors" + "fmt" "regexp" + "strconv" "strings" "unicode" "github.com/google/uuid" - "go.uber.org/multierr" ) const ( @@ -37,7 +38,6 @@ const ( var ( ErrorInvalidMessageEncoding = errors.New("invalid message encoding") ErrorInvalidMessageType = errors.New("invalid message type") - ErrorInvalidLocator = errors.New("invalid locator") ErrorInvalidSource = errors.New("invalid Source name") ErrorInvalidDestination = errors.New("invalid Destination name") ) @@ -50,16 +50,17 @@ var LocatorPattern = regexp.MustCompile( // SpecValidators is a WRP validator that ensures messages are valid based on // each spec validator in the list. Only validates the opinionated portions of the spec. -var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} +func SpecValidators() Validators { + return Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} +} // UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. var UTF8Validator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, UTF8(m)) { - err = multierr.Append(err, ErrorInvalidMessageEncoding) + if err := UTF8(m); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidMessageEncoding, err) } - return err + return nil } // MessageTypeValidator is a WRP validator that takes messages and validates their Type. @@ -81,35 +82,31 @@ var MessageTypeValidator ValidatorFunc = func(m Message) error { // Only mac and uuid sources are validated. Serial, event and dns sources are // not validated. var SourceValidator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, validateLocator(m.Source)) { - err = multierr.Append(err, ErrorInvalidSource) + if err := validateLocator(m.Source); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidSource, err) } - return err + return nil } // DestinationValidator is a WRP validator that takes messages and validates their Destination. // Only mac and uuid destinations are validated. Serial, event and dns destinations are // not validated. var DestinationValidator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, validateLocator(m.Destination)) { - err = multierr.Append(err, ErrorInvalidDestination) + if err := validateLocator(m.Destination); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidDestination, err) } - return err + return nil } // validateLocator validates a given locator's scheme and authority (ID). // Only mac and uuid schemes' IDs are validated. IDs from serial, event and dns schemes are // not validated. func validateLocator(l string) error { - var err error - match := LocatorPattern.FindStringSubmatch(l) if match == nil { - return multierr.Append(err, ErrorInvalidLocator) + return fmt.Errorf("spec scheme not found") } idPart := match[2] @@ -131,14 +128,16 @@ func validateLocator(l string) error { idPart, ) - if invalidCharacter != -1 || len(idPart) != macLength { - return multierr.Append(err, ErrorInvalidLocator) + if invalidCharacter != -1 { + return fmt.Errorf("invalid character %v", strconv.QuoteRune(invalidCharacter)) + } else if len(idPart) != macLength { + return errors.New("invalid mac length") } case uuidPrefix: - if _, uuidErr := uuid.Parse(idPart); multierr.AppendInto(&err, uuidErr) { - return multierr.Append(err, ErrorInvalidLocator) + if _, err := uuid.Parse(idPart); err != nil { + return err } } - return err + return nil } diff --git a/spec_validator_test.go b/spec_validator_test.go index d0c1c6f..f8c543f 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -61,7 +61,7 @@ func testUTF8Validator(t *testing.T) { { description: "Not UTF8 error", value: *msg, - expectedErr: []error{ErrorInvalidMessageEncoding, ErrNotUTF8}, + expectedErr: []error{ErrorInvalidMessageEncoding}, }, } @@ -189,7 +189,7 @@ func testSourceValidator(t *testing.T) { tests := []struct { description string value Message - expectedErr []error + expectedErr error }{ // Success case { @@ -201,7 +201,7 @@ func testSourceValidator(t *testing.T) { { description: "Source error", value: Message{Source: "invalid:a-BB-44-55"}, - expectedErr: []error{ErrorInvalidSource, ErrorInvalidLocator}, + expectedErr: ErrorInvalidSource, }, } @@ -210,9 +210,7 @@ func testSourceValidator(t *testing.T) { assert := assert.New(t) err := SourceValidator(tc.value) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -225,7 +223,7 @@ func testDestinationValidator(t *testing.T) { tests := []struct { description string value Message - expectedErr []error + expectedErr error }{ // Success case { @@ -237,7 +235,7 @@ func testDestinationValidator(t *testing.T) { { description: "Destination error", value: Message{Destination: "invalid:a-BB-44-55"}, - expectedErr: []error{ErrorInvalidDestination, ErrorInvalidLocator}, + expectedErr: ErrorInvalidDestination, }, } @@ -246,9 +244,7 @@ func testDestinationValidator(t *testing.T) { assert := assert.New(t) err := DestinationValidator(tc.value) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -261,120 +257,120 @@ func testValidateLocator(t *testing.T) { tests := []struct { description string value string - expectedErr error + shouldErr bool }{ // mac success case { description: "Mac ID ':' delimiter success", value: "MAC:11:22:33:44:55:66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID no delimiter success", value: "MAC:11aaBB445566", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID '-' delimiter success", value: "mac:11-aa-BB-44-55-66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID ',' delimiter success", value: "mac:11,aa,BB,44,55,66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac with service success", value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", - expectedErr: nil, + shouldErr: false, }, // Mac failure case { description: "Invalid mac ID character error", value: "MAC:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid mac ID length error", value: "mac:11-aa-BB-44-55", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, // Serial success case { description: "Serial ID success", value: "serial:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // UUID success case { description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Future variant ID success", // Reserved for future definition. value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, // UUID failure case { description: "Invalid UUID ID #1 error", value: "uuid:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid UUID ID #2 error", value: "uuid:URN:UUID:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid UUID ID #3 error", value: "uuid:{invalid45566}", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, // Event success case { description: "Event ID success", value: "event:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // DNS success case { description: "DNS ID success", value: "dns:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // Scheme failure case { description: "Invalid scheme error", value: "invalid:a-BB-44-55", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, } @@ -382,8 +378,8 @@ func testValidateLocator(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) err := validateLocator(tc.value) - if tc.expectedErr != nil { - assert.ErrorIs(err, tc.expectedErr) + if tc.shouldErr { + assert.Error(err) return } @@ -433,14 +429,14 @@ func TestSpecValidators(t *testing.T) { Source: "invalid:a-BB-44-55", Destination: "invalid:a-BB-44-55", }, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidLocator, ErrorInvalidDestination}, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, }, } for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SpecValidators.Validate(tc.value) + err := SpecValidators().Validate(tc.value) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) @@ -458,7 +454,7 @@ func ExampleTypeValidator_Validate_specValidators() { // Validates found msg types map[MessageType]Validator{ // Validates opinionated portions of the spec - SimpleEventMessageType: SpecValidators, + SimpleEventMessageType: SpecValidators(), // Only validates Source and nothing else SimpleRequestResponseMessageType: SourceValidator, }, From 1f1aa67f5e7c1da59f826631249af115eb820c08 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 7 Jun 2022 16:32:43 -0400 Subject: [PATCH 32/68] Updates based on PR feedback * converted remaining `Validators` to `Validator` * remove func `validateValidator` * move nil checks to `Validate` func * remove Test struct * add new unexported field to check if `TypeValidato` is valid * add func `IsBad` to TypeValidato to say whether `TypeValidato` is valid * update tests for testAlwaysInvalid and testAlwaysValid * update tests names for `TestTypeValidator` * add tests for `Validators` --- validator.go | 59 +++---- validator_test.go | 387 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 294 insertions(+), 152 deletions(-) diff --git a/validator.go b/validator.go index f909660..79c6a13 100644 --- a/validator.go +++ b/validator.go @@ -24,7 +24,8 @@ import ( ) var ( - ErrInvalidTypeValidator = errors.New("invalid WRP message type validator") + ErrInvalidTypeValidator = errors.New("invalid TypeValidator") + ErrInvalidValidator = errors.New("invalid WRP message type validator") ErrInvalidMsgType = errors.New("invalid WRP message type") ) @@ -48,7 +49,9 @@ type Validators []Validator func (vs Validators) Validate(m Message) error { var err error for _, v := range vs { - err = multierr.Append(err, v.Validate(m)) + if v != nil { + err = multierr.Append(err, v.Validate(m)) + } } return err @@ -67,12 +70,17 @@ func (vf ValidatorFunc) Validate(m Message) error { // or using the defaultValidators if message type is unfound. type TypeValidator struct { m map[MessageType]Validator - defaultValidators Validators + defaultValidators Validator + isbad bool } // Validate validates messages based on message type or using the defaultValidators // if message type is unfound. func (m TypeValidator) Validate(msg Message) error { + if m.isbad { + return ErrInvalidTypeValidator + } + vs := m.m[msg.MessageType()] if vs == nil { return m.defaultValidators.Validate(msg) @@ -81,52 +89,23 @@ func (m TypeValidator) Validate(msg Message) error { return vs.Validate(msg) } +// IsBad returns a boolean indicating whether the TypeValidator receiver is valid +func (m TypeValidator) IsBad() bool { + return m.isbad +} + // NewTypeValidator is a TypeValidator factory. func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { - return TypeValidator{}, ErrInvalidTypeValidator + return TypeValidator{isbad: true}, ErrInvalidValidator } - for _, v := range m { - if err := validateValidator(v); err != nil { - return TypeValidator{}, err - } - } - - if len(defaultValidators) == 0 { + if defaultValidators == nil { defaultValidators = Validators{AlwaysInvalid} } - for _, v := range defaultValidators { - if err := validateValidator(v); err != nil { - return TypeValidator{}, err - } - } - return TypeValidator{ m: m, - defaultValidators: defaultValidators, + defaultValidators: Validators(defaultValidators), }, nil } - -// validateValidator validates a given Validator. -func validateValidator(v Validator) error { - switch vs := v.(type) { - case Validators: - if vs == nil || len(vs) == 0 { - return ErrInvalidTypeValidator - } - - for _, v := range vs { - if v == nil { - return ErrInvalidTypeValidator - } - } - case Validator, ValidatorFunc: - // catch nil Validator - default: - return ErrInvalidTypeValidator - } - - return nil -} diff --git a/validator_test.go b/validator_test.go index 14bc9b8..70701d3 100644 --- a/validator_test.go +++ b/validator_test.go @@ -23,60 +23,88 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/multierr" ) func testTypeValidatorValidate(t *testing.T) { - type Test struct { + tests := []struct { + description string m map[MessageType]Validator - defaultValidators Validators + defaultValidators []Validator msg Message - } - - tests := []struct { - description string - value Test - expectedErr error + expectedErr error }{ // Success case { description: "Found success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, - msg: Message{Type: SimpleEventMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, + msg: Message{Type: SimpleEventMessageType}, }, { description: "Unfound success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysInvalid}, - }, - defaultValidators: Validators{AlwaysValid}, - msg: Message{Type: CreateMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: []Validator{AlwaysValid}, + msg: Message{Type: CreateMessageType}, + }, + { + description: "Unfound success, nil list of default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, + }, + defaultValidators: []Validator{nil}, + msg: Message{Type: CreateMessageType}, + }, + { + description: "Unfound success, empty map of default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, + }, + defaultValidators: []Validator{}, + msg: Message{Type: CreateMessageType}, }, // Failure case { description: "Found error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysInvalid}, - }, - defaultValidators: Validators{AlwaysValid}, - msg: Message{Type: SimpleEventMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: []Validator{AlwaysValid}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Found error, nil Validator", + m: map[MessageType]Validator{ + SimpleEventMessageType: nil, + }, + msg: Message{Type: SimpleEventMessageType}, expectedErr: ErrInvalidMsgType, }, { description: "Unfound error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, - msg: Message{Type: CreateMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + msg: Message{Type: CreateMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Unfound error, nil default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: nil, + msg: Message{Type: CreateMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Unfound error, empty map of Validators", + m: map[MessageType]Validator{}, + msg: Message{Type: CreateMessageType}, expectedErr: ErrInvalidMsgType, }, } @@ -85,10 +113,11 @@ func testTypeValidatorValidate(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) require := require.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) - require.NotNil(msgv) + msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) require.NoError(err) - err = msgv.Validate(tc.value.msg) + require.NotNil(msgv) + require.False(msgv.IsBad()) + err = msgv.Validate(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -99,103 +128,251 @@ func testTypeValidatorValidate(t *testing.T) { } } -func testNewTypeValidator(t *testing.T) { - type Test struct { +func testTypeValidatorFactory(t *testing.T) { + tests := []struct { + description string m map[MessageType]Validator - defaultValidators Validators + defaultValidators []Validator + expectedErr error + }{ + // Success case + { + description: "Default Validators success", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + defaultValidators: []Validator{AlwaysValid}, + expectedErr: nil, + }, + { + description: "Omit default Validators success", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + expectedErr: nil, + }, + // Failure case + { + description: "Nil map of Validators error", + m: nil, + defaultValidators: []Validator{AlwaysValid}, + expectedErr: ErrInvalidValidator, + }, } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) + assert.NotNil(msgv) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + assert.ErrorIs(msgv.Validate(Message{}), ErrInvalidTypeValidator) + assert.True(msgv.IsBad()) + return + } + + assert.NoError(err) + assert.False(msgv.IsBad()) + }) + } +} + +func testAlwaysValid(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) tests := []struct { description string - value Test - expectedErr error + msg Message + expectedErr []error }{ // Success case { - description: "Default Validators success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Not UTF8 success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "mac:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, { - description: "Empty map of Validators success", - value: Test{ - m: map[MessageType]Validator{}, - defaultValidators: Validators{AlwaysValid}, + description: "Filled message success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, { - description: "Omit default Validators success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, + description: "Empty message success", + msg: Message{}, + }, + { + description: "Bad message type success", + msg: Message{ + Type: lastMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", }, - expectedErr: nil, }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := AlwaysValid.Validate(tc.msg) + assert.NoError(err) + }) + } +} + +func testAlwaysInvalid(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { + description string + msg Message + expectedErr []error + }{ // Failure case { - description: "Nil list of default Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, - }, - defaultValidators: Validators{nil}, + description: "Not UTF8 error", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "mac:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: ErrInvalidTypeValidator, }, { - description: "Empty list of Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{}, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Filled message error", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: ErrInvalidTypeValidator, }, { - description: "Nil Validator error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: nil, - }, - defaultValidators: Validators{AlwaysValid}, - }, - expectedErr: ErrInvalidTypeValidator, + description: "Empty message error", + msg: Message{}, }, { - description: "Nil list of Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{nil}, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Bad message type error", + msg: Message{ + Type: lastMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", }, - expectedErr: ErrInvalidTypeValidator, }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := AlwaysInvalid.Validate(tc.msg) + assert.Error(err) + }) + } +} + +func TestValidators(t *testing.T) { + tests := []struct { + description string + vs Validators + msg Message + expectedErr []error + }{ + // Success case { - description: "Nil map of Validators error", - value: Test{ - m: nil, - defaultValidators: Validators{AlwaysValid}, - }, - expectedErr: ErrInvalidTypeValidator, + description: "Empty Validators success", + vs: Validators{}, + msg: Message{Type: SimpleEventMessageType}, + }, + // Failure case + { + description: "Mix Validators error", + vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, }, } for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) - assert.NotNil(msgv) + err := tc.vs.Validate(tc.msg) if tc.expectedErr != nil { - assert.ErrorIs(err, tc.expectedErr) + assert.Equal(multierr.Errors(err), tc.expectedErr) + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } return } @@ -204,20 +381,6 @@ func testNewTypeValidator(t *testing.T) { } } -func testAlwaysValid(t *testing.T) { - assert := assert.New(t) - msg := Message{} - err := AlwaysValid(msg) - assert.NoError(err) -} - -func testAlwaysInvalid(t *testing.T) { - assert := assert.New(t) - msg := Message{} - err := AlwaysInvalid(msg) - assert.ErrorIs(err, ErrInvalidMsgType) -} - func TestHelperValidators(t *testing.T) { tests := []struct { description string @@ -237,8 +400,8 @@ func TestTypeValidator(t *testing.T) { description string test func(*testing.T) }{ - {"TypeValidator validate", testTypeValidatorValidate}, - {"TypeValidator factory", testNewTypeValidator}, + {"Validate", testTypeValidatorValidate}, + {"Factory", testTypeValidatorFactory}, } for _, tc := range tests { @@ -249,7 +412,7 @@ func TestTypeValidator(t *testing.T) { func ExampleNewTypeValidator() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) @@ -259,7 +422,7 @@ func ExampleNewTypeValidator() { func ExampleTypeValidator_Validate() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, // Validates unfound msg types AlwaysInvalid) if err != nil { From 1645a22bf49fd1f24aaedc95eb9cb5b9ae206ef8 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 7 Jun 2022 16:39:51 -0400 Subject: [PATCH 33/68] Update testUTF8Validator test and rename var `value` to `msg` for Message test vars --- spec_validator_test.go | 136 ++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 56 deletions(-) diff --git a/spec_validator_test.go b/spec_validator_test.go index f8c543f..c64a9fd 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -22,45 +22,69 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func testUTF8Validator(t *testing.T) { - /* - "\x85" - 5 name value pairs - "\xa8""msg_type" : "\x03" // 3 - "\xa4""dest" : "\xac""\xed\xbf\xbft-address" - "\xa7""payload" : "\xc4""\x03" - len 3 - "123" - "\xa6""source" : "\xae""source-address" - "\xb0""transaction_uuid" : "\xd9\x24""c07ee5e1-70be-444c-a156-097c767ad8aa" - */ - invalid := []byte{ - 0x85, - 0xa8, 'm', 's', 'g', '_', 't', 'y', 'p', 'e', 0x03, - 0xa4, 'd', 'e', 's', 't', 0xac /* \xed\xbf\xbf is invalid */, 0xed, 0xbf, 0xbf, 't', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', - 0xa7, 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0xc4, 0x03, '1', '2', '3', - 0xa6, 's', 'o', 'u', 'r', 'c', 'e', 0xae, 's', 'o', 'u', 'r', 'c', 'e', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', - 0xb0, 't', 'r', 'a', 'n', 's', 'a', 'c', 't', 'i', 'o', 'n', '_', 'u', 'u', 'i', 'd', 0xd9, 0x24, 'c', '0', '7', 'e', 'e', '5', 'e', '1', '-', '7', '0', 'b', 'e', '-', '4', '4', '4', 'c', '-', 'a', '1', '5', '6', '-', '0', '9', '7', 'c', '7', '6', '7', 'a', 'd', '8', 'a', 'a', - } - decoder := NewDecoderBytes(invalid, Msgpack) - msg := new(Message) - err := decoder.Decode(msg) - require.NoError(t, err) + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { description string - value Message + msg Message expectedErr []error }{ // Success case { description: "UTF8 success", - value: Message{Source: "MAC:11:22:33:44:55:66"}, + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, expectedErr: nil, }, { description: "Not UTF8 error", - value: *msg, + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "MAC:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, expectedErr: []error{ErrorInvalidMessageEncoding}, }, } @@ -68,7 +92,7 @@ func testUTF8Validator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := UTF8Validator(tc.value) + err := UTF8Validator(tc.msg) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) @@ -84,89 +108,89 @@ func testUTF8Validator(t *testing.T) { func testMessageTypeValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "AuthorizationMessageType success", - value: Message{Type: AuthorizationMessageType}, + msg: Message{Type: AuthorizationMessageType}, expectedErr: nil, }, { description: "SimpleRequestResponseMessageType success", - value: Message{Type: SimpleRequestResponseMessageType}, + msg: Message{Type: SimpleRequestResponseMessageType}, expectedErr: nil, }, { description: "SimpleEventMessageType success", - value: Message{Type: SimpleEventMessageType}, + msg: Message{Type: SimpleEventMessageType}, expectedErr: nil, }, { description: "CreateMessageType success", - value: Message{Type: CreateMessageType}, + msg: Message{Type: CreateMessageType}, expectedErr: nil, }, { description: "RetrieveMessageType success", - value: Message{Type: RetrieveMessageType}, + msg: Message{Type: RetrieveMessageType}, expectedErr: nil, }, { description: "UpdateMessageType success", - value: Message{Type: UpdateMessageType}, + msg: Message{Type: UpdateMessageType}, expectedErr: nil, }, { description: "DeleteMessageType success", - value: Message{Type: DeleteMessageType}, + msg: Message{Type: DeleteMessageType}, expectedErr: nil, }, { description: "ServiceRegistrationMessageType success", - value: Message{Type: ServiceRegistrationMessageType}, + msg: Message{Type: ServiceRegistrationMessageType}, expectedErr: nil, }, { description: "ServiceAliveMessageType success", - value: Message{Type: ServiceAliveMessageType}, + msg: Message{Type: ServiceAliveMessageType}, expectedErr: nil, }, { description: "UnknownMessageType success", - value: Message{Type: UnknownMessageType}, + msg: Message{Type: UnknownMessageType}, expectedErr: nil, }, // Failure case { description: "Invalid0MessageType error", - value: Message{Type: Invalid0MessageType}, + msg: Message{Type: Invalid0MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Invalid0MessageType error", - value: Message{Type: Invalid0MessageType}, + msg: Message{Type: Invalid0MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Invalid1MessageType error", - value: Message{Type: Invalid1MessageType}, + msg: Message{Type: Invalid1MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "lastMessageType error", - value: Message{Type: lastMessageType}, + msg: Message{Type: lastMessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Non-existing negative MessageType error", - value: Message{Type: -10}, + msg: Message{Type: -10}, expectedErr: ErrorInvalidMessageType, }, { description: "Non-existing positive MessageType error", - value: Message{Type: lastMessageType + 1}, + msg: Message{Type: lastMessageType + 1}, expectedErr: ErrorInvalidMessageType, }, } @@ -174,7 +198,7 @@ func testMessageTypeValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := MessageTypeValidator(tc.value) + err := MessageTypeValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -188,19 +212,19 @@ func testMessageTypeValidator(t *testing.T) { func testSourceValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "Source success", - value: Message{Source: "MAC:11:22:33:44:55:66"}, + msg: Message{Source: "MAC:11:22:33:44:55:66"}, expectedErr: nil, }, // Failures { description: "Source error", - value: Message{Source: "invalid:a-BB-44-55"}, + msg: Message{Source: "invalid:a-BB-44-55"}, expectedErr: ErrorInvalidSource, }, } @@ -208,7 +232,7 @@ func testSourceValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SourceValidator(tc.value) + err := SourceValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -222,19 +246,19 @@ func testSourceValidator(t *testing.T) { func testDestinationValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "Destination success", - value: Message{Destination: "MAC:11:22:33:44:55:66"}, + msg: Message{Destination: "MAC:11:22:33:44:55:66"}, expectedErr: nil, }, // Failures { description: "Destination error", - value: Message{Destination: "invalid:a-BB-44-55"}, + msg: Message{Destination: "invalid:a-BB-44-55"}, expectedErr: ErrorInvalidDestination, }, } @@ -242,7 +266,7 @@ func testDestinationValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := DestinationValidator(tc.value) + err := DestinationValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -408,13 +432,13 @@ func TestSpecHelperValidators(t *testing.T) { func TestSpecValidators(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr []error }{ // Success case { description: "Valid specs success", - value: Message{ + msg: Message{ Type: SimpleEventMessageType, Source: "MAC:11:22:33:44:55:66", Destination: "MAC:11:22:33:44:55:66", @@ -424,7 +448,7 @@ func TestSpecValidators(t *testing.T) { // Failure cases { description: "Invaild specs error", - value: Message{ + msg: Message{ Type: Invalid0MessageType, Source: "invalid:a-BB-44-55", Destination: "invalid:a-BB-44-55", @@ -436,7 +460,7 @@ func TestSpecValidators(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SpecValidators().Validate(tc.value) + err := SpecValidators().Validate(tc.msg) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) From df7e2ff8508c4938c4912fb5eee7e13b616098dd Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 05:54:33 -0400 Subject: [PATCH 34/68] Update based on PR review * [misunderstanding] Remove unexported field `isbad` from `TypeValidator` and its references * [misunderstanding] Remove `IsBad` func from `TypeValidator` * Use `assert.Zero/NotZero` funcs to test `TypeValidator`'s state --- validator.go | 12 +----------- validator_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/validator.go b/validator.go index 79c6a13..f3ed27e 100644 --- a/validator.go +++ b/validator.go @@ -71,16 +71,11 @@ func (vf ValidatorFunc) Validate(m Message) error { type TypeValidator struct { m map[MessageType]Validator defaultValidators Validator - isbad bool } // Validate validates messages based on message type or using the defaultValidators // if message type is unfound. func (m TypeValidator) Validate(msg Message) error { - if m.isbad { - return ErrInvalidTypeValidator - } - vs := m.m[msg.MessageType()] if vs == nil { return m.defaultValidators.Validate(msg) @@ -89,15 +84,10 @@ func (m TypeValidator) Validate(msg Message) error { return vs.Validate(msg) } -// IsBad returns a boolean indicating whether the TypeValidator receiver is valid -func (m TypeValidator) IsBad() bool { - return m.isbad -} - // NewTypeValidator is a TypeValidator factory. func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { - return TypeValidator{isbad: true}, ErrInvalidValidator + return TypeValidator{}, ErrInvalidValidator } if defaultValidators == nil { diff --git a/validator_test.go b/validator_test.go index 70701d3..9c2bdcb 100644 --- a/validator_test.go +++ b/validator_test.go @@ -116,7 +116,7 @@ func testTypeValidatorValidate(t *testing.T) { msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) require.NoError(err) require.NotNil(msgv) - require.False(msgv.IsBad()) + assert.NotZero(msgv) err = msgv.Validate(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) @@ -164,16 +164,16 @@ func testTypeValidatorFactory(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) - assert.NotNil(msgv) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) - assert.ErrorIs(msgv.Validate(Message{}), ErrInvalidTypeValidator) - assert.True(msgv.IsBad()) + assert.NotNil(msgv) + assert.NotZero(msgv) return } assert.NoError(err) - assert.False(msgv.IsBad()) + // Zero asserts that msgv is the zero value for its type and not nil. + assert.Zero(msgv) }) } } From ffd531f457e777c93920a8c592816a57e35b9cac Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 05:55:49 -0400 Subject: [PATCH 35/68] Move exported fields to the top --- validator_test.go | 186 +++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/validator_test.go b/validator_test.go index 9c2bdcb..e618b25 100644 --- a/validator_test.go +++ b/validator_test.go @@ -26,6 +26,99 @@ import ( "go.uber.org/multierr" ) +func TestValidators(t *testing.T) { + tests := []struct { + description string + vs Validators + msg Message + expectedErr []error + }{ + // Success case + { + description: "Empty Validators success", + vs: Validators{}, + msg: Message{Type: SimpleEventMessageType}, + }, + // Failure case + { + description: "Mix Validators error", + vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := tc.vs.Validate(tc.msg) + if tc.expectedErr != nil { + assert.Equal(multierr.Errors(err), tc.expectedErr) + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func TestHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"AlwaysInvalid", testAlwaysInvalid}, + {"AlwaysValid", testAlwaysValid}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestTypeValidator(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"Validate", testTypeValidatorValidate}, + {"Factory", testTypeValidatorFactory}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func ExampleNewTypeValidator() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + // Validates unfound msg types + AlwaysInvalid) + fmt.Printf("%v %T", err == nil, msgv) + // Output: true wrp.TypeValidator +} + +func ExampleTypeValidator_Validate() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErr == nil, unfoundErr == nil) + // Output: true false +} + func testTypeValidatorValidate(t *testing.T) { tests := []struct { description string @@ -341,96 +434,3 @@ func testAlwaysInvalid(t *testing.T) { }) } } - -func TestValidators(t *testing.T) { - tests := []struct { - description string - vs Validators - msg Message - expectedErr []error - }{ - // Success case - { - description: "Empty Validators success", - vs: Validators{}, - msg: Message{Type: SimpleEventMessageType}, - }, - // Failure case - { - description: "Mix Validators error", - vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, - msg: Message{Type: SimpleEventMessageType}, - expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, - }, - } - - for _, tc := range tests { - t.Run(tc.description, func(t *testing.T) { - assert := assert.New(t) - err := tc.vs.Validate(tc.msg) - if tc.expectedErr != nil { - assert.Equal(multierr.Errors(err), tc.expectedErr) - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } - return - } - - assert.NoError(err) - }) - } -} - -func TestHelperValidators(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"AlwaysInvalid", testAlwaysInvalid}, - {"AlwaysValid", testAlwaysValid}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func TestTypeValidator(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"Validate", testTypeValidatorValidate}, - {"Factory", testTypeValidatorFactory}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func ExampleNewTypeValidator() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, - // Validates unfound msg types - AlwaysInvalid) - fmt.Printf("%v %T", err == nil, msgv) - // Output: true wrp.TypeValidator -} - -func ExampleTypeValidator_Validate() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, - // Validates unfound msg types - AlwaysInvalid) - if err != nil { - return - } - - foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErr == nil, unfoundErr == nil) - // Output: true false -} From 13a9a576c1ba8d6ee2aea8197eaed9335d62420f Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:34:20 -0400 Subject: [PATCH 36/68] update tests to include correct mac length --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index e618b25..8c9d440 100644 --- a/validator_test.go +++ b/validator_test.go @@ -312,7 +312,7 @@ func testAlwaysValid(t *testing.T) { msg: Message{ Type: SimpleRequestResponseMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", Accept: "Accept", @@ -339,7 +339,7 @@ func testAlwaysValid(t *testing.T) { msg: Message{ Type: lastMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", }, }, } @@ -394,7 +394,7 @@ func testAlwaysInvalid(t *testing.T) { msg: Message{ Type: SimpleRequestResponseMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", Accept: "Accept", @@ -421,7 +421,7 @@ func testAlwaysInvalid(t *testing.T) { msg: Message{ Type: lastMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", }, }, } From 3ac1d027bec3bfbbb8c469d7c2372ed5dc69c74f Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:45:19 -0400 Subject: [PATCH 37/68] `testAlwaysValid` & `testAlwaysInvalid` ensure a non-existing message type is used --- validator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator_test.go b/validator_test.go index 8c9d440..de5c421 100644 --- a/validator_test.go +++ b/validator_test.go @@ -337,7 +337,7 @@ func testAlwaysValid(t *testing.T) { { description: "Bad message type success", msg: Message{ - Type: lastMessageType, + Type: lastMessageType + 1, Source: "external.com", Destination: "MAC:11:22:33:44:55:66", }, @@ -419,7 +419,7 @@ func testAlwaysInvalid(t *testing.T) { { description: "Bad message type error", msg: Message{ - Type: lastMessageType, + Type: lastMessageType + 1, Source: "external.com", Destination: "MAC:11:22:33:44:55:66", }, From e553e7e250f15da86a4c34df4abdfb5bcc992d51 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:48:04 -0400 Subject: [PATCH 38/68] `testTypeValidatorFactory` swap `assert.Zero/NotZero` --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index de5c421..2ff1457 100644 --- a/validator_test.go +++ b/validator_test.go @@ -259,14 +259,14 @@ func testTypeValidatorFactory(t *testing.T) { msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) - assert.NotNil(msgv) - assert.NotZero(msgv) + // Zero asserts that msgv is the zero value for its type and not nil. + assert.Zero(msgv) return } assert.NoError(err) - // Zero asserts that msgv is the zero value for its type and not nil. - assert.Zero(msgv) + assert.NotNil(msgv) + assert.NotZero(msgv) }) } } From dff0053040a5cc3fc22e1b7587772f1df28a5d2d Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:19:34 -0400 Subject: [PATCH 39/68] Improve Test descriptions, improved & added missing tests, improved example * update testUTF8Validator success case to contain a correct source * removed unused error list * removed nil struct field inits * added missing test cases for testValidateLocator * improved test descriptions for testValidateLocator * added missing test cases for TestSpecValidators * improved example --- spec_validator.go | 4 + spec_validator_test.go | 224 ++++++++++++++++++++++++++++++----------- 2 files changed, 170 insertions(+), 58 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index 3033a2f..7cb8de9 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -137,6 +137,10 @@ func validateLocator(l string) error { if _, err := uuid.Parse(idPart); err != nil { return err } + case serialPrefix, eventPrefix, dnsPrefix: + if len(idPart) == 0 { + return fmt.Errorf("invalid %v empty authority (ID)", serialPrefix) + } } return nil diff --git a/spec_validator_test.go b/spec_validator_test.go index c64a9fd..6b8874b 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -34,15 +34,14 @@ func testUTF8Validator(t *testing.T) { tests := []struct { description string msg Message - expectedErr []error + expectedErr error }{ // Success case { description: "UTF8 success", msg: Message{ - Type: SimpleRequestResponseMessageType, - Source: "external.com", - // Not UFT8 Destination string + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -60,13 +59,12 @@ func testUTF8Validator(t *testing.T) { PartnerIDs: []string{"foo"}, SessionID: "sessionID123", }, - expectedErr: nil, }, { description: "Not UTF8 error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "MAC:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -85,7 +83,7 @@ func testUTF8Validator(t *testing.T) { PartnerIDs: []string{"foo"}, SessionID: "sessionID123", }, - expectedErr: []error{ErrorInvalidMessageEncoding}, + expectedErr: ErrorInvalidMessageEncoding, }, } @@ -94,9 +92,7 @@ func testUTF8Validator(t *testing.T) { assert := assert.New(t) err := UTF8Validator(tc.msg) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -115,52 +111,42 @@ func testMessageTypeValidator(t *testing.T) { { description: "AuthorizationMessageType success", msg: Message{Type: AuthorizationMessageType}, - expectedErr: nil, }, { description: "SimpleRequestResponseMessageType success", msg: Message{Type: SimpleRequestResponseMessageType}, - expectedErr: nil, }, { description: "SimpleEventMessageType success", msg: Message{Type: SimpleEventMessageType}, - expectedErr: nil, }, { description: "CreateMessageType success", msg: Message{Type: CreateMessageType}, - expectedErr: nil, }, { description: "RetrieveMessageType success", msg: Message{Type: RetrieveMessageType}, - expectedErr: nil, }, { description: "UpdateMessageType success", msg: Message{Type: UpdateMessageType}, - expectedErr: nil, }, { description: "DeleteMessageType success", msg: Message{Type: DeleteMessageType}, - expectedErr: nil, }, { description: "ServiceRegistrationMessageType success", msg: Message{Type: ServiceRegistrationMessageType}, - expectedErr: nil, }, { description: "ServiceAliveMessageType success", msg: Message{Type: ServiceAliveMessageType}, - expectedErr: nil, }, { description: "UnknownMessageType success", msg: Message{Type: UnknownMessageType}, - expectedErr: nil, }, // Failure case { @@ -184,12 +170,12 @@ func testMessageTypeValidator(t *testing.T) { expectedErr: ErrorInvalidMessageType, }, { - description: "Non-existing negative MessageType error", + description: "Nonexistent negative MessageType error", msg: Message{Type: -10}, expectedErr: ErrorInvalidMessageType, }, { - description: "Non-existing positive MessageType error", + description: "Nonexistent positive MessageType error", msg: Message{Type: lastMessageType + 1}, expectedErr: ErrorInvalidMessageType, }, @@ -210,6 +196,11 @@ func testMessageTypeValidator(t *testing.T) { } func testSourceValidator(t *testing.T) { + // SourceValidator is mainly a wrapper for validateLocator. + // This test mainly ensures that SourceValidator returns nil for non errors + // and wraps errors with ErrorInvalidSource. + // testValidateLocator covers the actual spectrum of test cases. + tests := []struct { description string msg Message @@ -219,7 +210,6 @@ func testSourceValidator(t *testing.T) { { description: "Source success", msg: Message{Source: "MAC:11:22:33:44:55:66"}, - expectedErr: nil, }, // Failures { @@ -244,6 +234,11 @@ func testSourceValidator(t *testing.T) { } func testDestinationValidator(t *testing.T) { + // DestinationValidator is mainly a wrapper for validateLocator. + // This test mainly ensures that DestinationValidator returns nil for non errors + // and wraps errors with ErrorInvalidDestination. + // testValidateLocator covers the actual spectrum of test cases. + tests := []struct { description string msg Message @@ -253,7 +248,6 @@ func testDestinationValidator(t *testing.T) { { description: "Destination success", msg: Message{Destination: "MAC:11:22:33:44:55:66"}, - expectedErr: nil, }, // Failures { @@ -285,117 +279,154 @@ func testValidateLocator(t *testing.T) { }{ // mac success case { - description: "Mac ID ':' delimiter success", + description: "Mac ID success, ':' delimiter", value: "MAC:11:22:33:44:55:66", shouldErr: false, }, { - description: "Mac ID no delimiter success", + description: "Mac ID success, no delimiter", value: "MAC:11aaBB445566", shouldErr: false, }, { - description: "Mac ID '-' delimiter success", + description: "Mac ID success, '-' delimiter", value: "mac:11-aa-BB-44-55-66", shouldErr: false, }, { - description: "Mac ID ',' delimiter success", + description: "Mac ID success, ',' delimiter", value: "mac:11,aa,BB,44,55,66", shouldErr: false, }, { - description: "Mac with service success", + description: "Mac service success", value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", shouldErr: false, }, // Mac failure case { - description: "Invalid mac ID character error", + description: "Mac ID error, invalid mac ID character", value: "MAC:invalid45566", shouldErr: true, }, { - description: "Invalid mac ID length error", + description: "Mac ID error, invalid mac ID length", value: "mac:11-aa-BB-44-55", shouldErr: true, }, + { + description: "Mac ID error, no ID", + value: "mac:", + shouldErr: true, + }, // Serial success case { description: "Serial ID success", value: "serial:anything Goes!", shouldErr: false, }, + // Serial failure case + { + description: "Invalid serial ID error, no ID", + value: "serial:", + shouldErr: true, + }, // UUID success case + // The variant specified in RFC4122 { - description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 + description: "UUID RFC4122 variant ID success", value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 + description: "UUID RFC4122 variant ID success, with Microsoft encoding", value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", shouldErr: false, }, + // Reserved, NCS backward compatibility. { - description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, with URN lower case ", value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, with URN upper case", value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, without URN", value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, + // Reserved, Microsoft Corporation backward compatibility. { - description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. + description: "UUID Microsoft variant ID success", value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", shouldErr: false, }, + // Reserved for future definition. { - description: "UUID Future variant ID success", // Reserved for future definition. + description: "UUID Future variant ID success", value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", shouldErr: false, }, // UUID failure case { - description: "Invalid UUID ID #1 error", + description: "Invalid UUID ID error", value: "uuid:invalid45566", shouldErr: true, }, { - description: "Invalid UUID ID #2 error", + description: "Invalid UUID ID error, with URN", value: "uuid:URN:UUID:invalid45566", shouldErr: true, }, { - description: "Invalid UUID ID #3 error", + description: "Invalid UUID ID error, with Microsoft encoding", value: "uuid:{invalid45566}", shouldErr: true, }, + { + description: "Invalid UUID ID error, no ID", + value: "uuid:", + shouldErr: true, + }, // Event success case { description: "Event ID success", value: "event:anything Goes!", shouldErr: false, }, + // Event failure case + { + description: "Invalid event ID error, no ID", + value: "event:", + shouldErr: true, + }, // DNS success case { description: "DNS ID success", value: "dns:anything Goes!", shouldErr: false, }, + // DNS failure case + { + description: "Invalid DNS ID error, no ID", + value: "dns:", + shouldErr: true, + }, // Scheme failure case { description: "Invalid scheme error", value: "invalid:a-BB-44-55", shouldErr: true, }, + { + description: "Invalid scheme error, empty string", + value: "", + shouldErr: true, + }, } for _, tc := range tests { @@ -430,6 +461,12 @@ func TestSpecHelperValidators(t *testing.T) { } func TestSpecValidators(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { description string msg Message @@ -437,24 +474,71 @@ func TestSpecValidators(t *testing.T) { }{ // Success case { - description: "Valid specs success", + description: "Valid spec success", msg: Message{ - Type: SimpleEventMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "MAC:11:22:33:44:55:66", + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, - // Failure cases + // Failure case { - description: "Invaild specs error", + description: "Invaild spec error", msg: Message{ - Type: Invalid0MessageType, - Source: "invalid:a-BB-44-55", - Destination: "invalid:a-BB-44-55", + Type: Invalid0MessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, + }, + { + description: "Invaild spec error, empty message", + msg: Message{}, expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, }, + { + description: "Invaild spec error, nonexistent MessageType", + msg: Message{ + Type: lastMessageType + 1, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: []error{ErrorInvalidMessageType}, + }, } for _, tc := range tests { @@ -488,6 +572,35 @@ func ExampleTypeValidator_Validate_specValidators() { return } + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + foundErrFailure := msgv.Validate(Message{ + Type: SimpleEventMessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }) // Found error foundErrSuccess1 := msgv.Validate(Message{ Type: SimpleEventMessageType, Source: "MAC:11:22:33:44:55:66", @@ -498,12 +611,7 @@ func ExampleTypeValidator_Validate_specValidators() { Source: "MAC:11:22:33:44:55:66", Destination: "invalid:a-BB-44-55", }) // Found success - foundErrFailure := msgv.Validate(Message{ - Type: Invalid0MessageType, - Source: "invalid:a-BB-44-55", - Destination: "invalid:a-BB-44-55", - }) // Found error unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErrSuccess1 == nil, foundErrSuccess2 == nil, foundErrFailure == nil, unfoundErrFailure == nil) - // Output: true true false false + fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) + // Output: false true true false } From 8e6882ba1e63fb6bf61b2a973a1e698e0fdb7aed Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:24:12 -0400 Subject: [PATCH 40/68] Add missing scheme to source fields & add missing `assert.ErrorIs` to `testAlwaysInvalid` --- validator_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/validator_test.go b/validator_test.go index 2ff1457..8f9278f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -287,7 +287,7 @@ func testAlwaysValid(t *testing.T) { description: "Not UTF8 success", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "mac:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -307,11 +307,12 @@ func testAlwaysValid(t *testing.T) { SessionID: "sessionID123", }, }, + // Failure case { description: "Filled message success", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -338,7 +339,7 @@ func testAlwaysValid(t *testing.T) { description: "Bad message type success", msg: Message{ Type: lastMessageType + 1, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", }, }, @@ -369,7 +370,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Not UTF8 error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "mac:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -393,7 +394,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Filled message error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -420,7 +421,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Bad message type error", msg: Message{ Type: lastMessageType + 1, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", }, }, @@ -430,7 +431,7 @@ func testAlwaysInvalid(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) err := AlwaysInvalid.Validate(tc.msg) - assert.Error(err) + assert.ErrorIs(err, ErrInvalidMsgType) }) } } From cadaeeade8c6db46bf30158940a57049160e7574 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:29:22 -0400 Subject: [PATCH 41/68] Move exported tests to the top --- spec_validator_test.go | 346 ++++++++++++++++++++--------------------- 1 file changed, 173 insertions(+), 173 deletions(-) diff --git a/spec_validator_test.go b/spec_validator_test.go index 6b8874b..5f11970 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -24,6 +24,179 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSpecHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"UTF8Validator", testUTF8Validator}, + {"MessageTypeValidator", testMessageTypeValidator}, + {"SourceValidator", testSourceValidator}, + {"DestinationValidator", testDestinationValidator}, + {"validateLocator", testValidateLocator}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestSpecValidators(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + + tests := []struct { + description string + msg Message + expectedErr []error + }{ + // Success case + { + description: "Valid spec success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, + }, + // Failure case + { + description: "Invaild spec error", + msg: Message{ + Type: Invalid0MessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, + }, + { + description: "Invaild spec error, empty message", + msg: Message{}, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, + }, + { + description: "Invaild spec error, nonexistent MessageType", + msg: Message{ + Type: lastMessageType + 1, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: []error{ErrorInvalidMessageType}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SpecValidators().Validate(tc.msg) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func ExampleTypeValidator_Validate_specValidators() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{ + // Validates opinionated portions of the spec + SimpleEventMessageType: SpecValidators(), + // Only validates Source and nothing else + SimpleRequestResponseMessageType: SourceValidator, + }, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + foundErrFailure := msgv.Validate(Message{ + Type: SimpleEventMessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }) // Found error + foundErrSuccess1 := msgv.Validate(Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:61", + }) // Found success + foundErrSuccess2 := msgv.Validate(Message{ + Type: SimpleRequestResponseMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "invalid:a-BB-44-55", + }) // Found success + unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) + // Output: false true true false +} + func testUTF8Validator(t *testing.T) { var ( expectedStatus int64 = 3471 @@ -442,176 +615,3 @@ func testValidateLocator(t *testing.T) { }) } } - -func TestSpecHelperValidators(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"UTF8Validator", testUTF8Validator}, - {"MessageTypeValidator", testMessageTypeValidator}, - {"SourceValidator", testSourceValidator}, - {"DestinationValidator", testDestinationValidator}, - {"validateLocator", testValidateLocator}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func TestSpecValidators(t *testing.T) { - var ( - expectedStatus int64 = 3471 - expectedRequestDeliveryResponse int64 = 34 - expectedIncludeSpans bool = true - ) - - tests := []struct { - description string - msg Message - expectedErr []error - }{ - // Success case - { - description: "Valid spec success", - msg: Message{ - Type: SimpleRequestResponseMessageType, - Source: "dns:external.com", - Destination: "MAC:11:22:33:44:55:66", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, - ServiceName: "ServiceName", - URL: "someURL.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }, - }, - // Failure case - { - description: "Invaild spec error", - msg: Message{ - Type: Invalid0MessageType, - // Missing scheme - Source: "external.com", - // Invalid Mac - Destination: "MAC:+++BB-44-55", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - // Not UFT8 Payload - Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, - ServiceName: "ServiceName", - // Not UFT8 URL string - URL: "someURL\xed\xbf\xbf.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, - }, - { - description: "Invaild spec error, empty message", - msg: Message{}, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, - }, - { - description: "Invaild spec error, nonexistent MessageType", - msg: Message{ - Type: lastMessageType + 1, - Source: "dns:external.com", - Destination: "MAC:11:22:33:44:55:66", - }, - expectedErr: []error{ErrorInvalidMessageType}, - }, - } - - for _, tc := range tests { - t.Run(tc.description, func(t *testing.T) { - assert := assert.New(t) - err := SpecValidators().Validate(tc.msg) - if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } - return - } - - assert.NoError(err) - }) - } -} - -func ExampleTypeValidator_Validate_specValidators() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{ - // Validates opinionated portions of the spec - SimpleEventMessageType: SpecValidators(), - // Only validates Source and nothing else - SimpleRequestResponseMessageType: SourceValidator, - }, - // Validates unfound msg types - AlwaysInvalid) - if err != nil { - return - } - - var ( - expectedStatus int64 = 3471 - expectedRequestDeliveryResponse int64 = 34 - expectedIncludeSpans bool = true - ) - foundErrFailure := msgv.Validate(Message{ - Type: SimpleEventMessageType, - // Missing scheme - Source: "external.com", - // Invalid Mac - Destination: "MAC:+++BB-44-55", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - // Not UFT8 Payload - Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, - ServiceName: "ServiceName", - // Not UFT8 URL string - URL: "someURL\xed\xbf\xbf.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }) // Found error - foundErrSuccess1 := msgv.Validate(Message{ - Type: SimpleEventMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "MAC:11:22:33:44:55:61", - }) // Found success - foundErrSuccess2 := msgv.Validate(Message{ - Type: SimpleRequestResponseMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "invalid:a-BB-44-55", - }) // Found success - unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) - // Output: false true true false -} From 8e68b95a10751198846d3411a650396f400a6ef8 Mon Sep 17 00:00:00 2001 From: ocabal200 Date: Thu, 26 May 2022 12:50:42 -0400 Subject: [PATCH 42/68] Updated defaultValidator to work as a variadic for multi-default support & added examples --- validator.go | 26 ++++++++++------- validator_test.go | 74 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/validator.go b/validator.go index 618b09f..6736faf 100644 --- a/validator.go +++ b/validator.go @@ -63,25 +63,25 @@ func (vf ValidatorFunc) Validate(m Message) error { } // TypeValidator is a WRP validator that validates based on message type -// or using the defaultValidator if message type is unknown. +// or using the defaultValidators if message type is unknown. type TypeValidator struct { - m map[MessageType]Validators - defaultValidator Validator + m map[MessageType]Validators + defaultValidators Validators } -// Validate validates messages based on message type or using the defaultValidator +// Validate validates messages based on message type or using the defaultValidators // if message type is unknown. func (m TypeValidator) Validate(msg Message) error { vs := m.m[msg.MessageType()] if vs == nil { - return m.defaultValidator.Validate(msg) + return m.defaultValidators.Validate(msg) } return vs.Validate(msg) } // NewTypeValidator is a TypeValidator factory. -func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (TypeValidator, error) { +func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { return TypeValidator{}, ErrInvalidTypeValidator } @@ -98,12 +98,18 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidator Validator) } } - if defaultValidator == nil { - defaultValidator = AlwaysInvalid + if len(defaultValidators) == 0 { + defaultValidators = Validators{AlwaysInvalid} + } + + for _, v := range defaultValidators { + if v == nil { + return TypeValidator{}, ErrInvalidTypeValidator + } } return TypeValidator{ - m: m, - defaultValidator: defaultValidator, + m: m, + defaultValidators: defaultValidators, }, nil } diff --git a/validator_test.go b/validator_test.go index fe376fd..11a41a9 100644 --- a/validator_test.go +++ b/validator_test.go @@ -18,6 +18,7 @@ package wrp import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -26,9 +27,9 @@ import ( func testTypeValidatorValidate(t *testing.T) { type Test struct { - m map[MessageType]Validators - defaultValidator Validator - msg Message + m map[MessageType]Validators + defaultValidators Validators + msg Message } var alwaysValid ValidatorFunc = func(msg Message) error { return nil } @@ -53,8 +54,8 @@ func testTypeValidatorValidate(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, }, - defaultValidator: alwaysValid, - msg: Message{Type: CreateMessageType}, + defaultValidators: Validators{alwaysValid}, + msg: Message{Type: CreateMessageType}, }, }, // Failure case @@ -64,8 +65,8 @@ func testTypeValidatorValidate(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, }, - defaultValidator: alwaysValid, - msg: Message{Type: SimpleEventMessageType}, + defaultValidators: Validators{alwaysValid}, + msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, }, @@ -85,7 +86,7 @@ func testTypeValidatorValidate(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) require := require.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) require.NotNil(msgv) require.NoError(err) err = msgv.Validate(tc.value.msg) @@ -101,8 +102,8 @@ func testTypeValidatorValidate(t *testing.T) { func testNewTypeValidator(t *testing.T) { type Test struct { - m map[MessageType]Validators - defaultValidator Validator + m map[MessageType]Validators + defaultValidators Validators } var alwaysValid ValidatorFunc = func(msg Message) error { return nil } @@ -118,15 +119,15 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: nil, }, { description: "Empty map of Validators success", value: Test{ - m: map[MessageType]Validators{}, - defaultValidator: alwaysValid, + m: map[MessageType]Validators{}, + defaultValidators: Validators{alwaysValid}, }, expectedErr: nil, }, @@ -146,7 +147,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -156,7 +157,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: nil, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -166,7 +167,7 @@ func testNewTypeValidator(t *testing.T) { m: map[MessageType]Validators{ SimpleEventMessageType: {nil}, }, - defaultValidator: alwaysValid, + defaultValidators: Validators{alwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, @@ -179,7 +180,7 @@ func testNewTypeValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidator) + msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) assert.NotNil(msgv) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) @@ -226,3 +227,42 @@ func TestTypeValidator(t *testing.T) { t.Run(tc.description, tc.test) } } + +func ExampleNewTypeValidator() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + if err != nil { + return + } + err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + fmt.Println(err == nil) + // Output: true + +} + +func ExampleTypeValidator_Validate_found() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + fmt.Println(err == nil) + // Output: true +} +func ExampleTypeValidator_Validate_notFound() { + var alwaysValid ValidatorFunc = func(msg Message) error { return nil } + msgv, err := NewTypeValidator( + // Validates known msg types + map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + // Validates unknown msg types + AlwaysInvalid) + err = msgv.Validate(Message{Type: CreateMessageType}) // Not Found error + fmt.Println(err == nil) + // Output: false +} From bbee089218dcf36da854ae18a6b8a7a3fc073a9c Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:32:47 -0400 Subject: [PATCH 43/68] update examples --- validator_test.go | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/validator_test.go b/validator_test.go index 11a41a9..8688c6c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -235,34 +235,24 @@ func ExampleNewTypeValidator() { map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unknown msg types AlwaysInvalid) - if err != nil { - return - } - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true -} + fmt.Printf("%v, %T", err == nil, msgv) + // Output: true, wrp.TypeValidator -func ExampleTypeValidator_Validate_found() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } - msgv, err := NewTypeValidator( - // Validates known msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types - AlwaysInvalid) - err = msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - fmt.Println(err == nil) - // Output: true } -func ExampleTypeValidator_Validate_notFound() { + +func ExampleTypeValidator_Validate() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates known msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unknown msg types AlwaysInvalid) - err = msgv.Validate(Message{Type: CreateMessageType}) // Not Found error - fmt.Println(err == nil) - // Output: false + if err != nil { + return + } + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + notFoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Not Found error + fmt.Printf("foundErr is nil: %v, notFoundErr is nil: %v", foundErr == nil, notFoundErr == nil) + // Output: foundErr is nil: true, notFoundErr is nil: false } From 803ff98d3276d59ee92331b39fabb09f365f1056 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:38:15 -0400 Subject: [PATCH 44/68] Doc/var update --- validator_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/validator_test.go b/validator_test.go index 8688c6c..09d9d3f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -49,7 +49,7 @@ func testTypeValidatorValidate(t *testing.T) { }, }, { - description: "Not found success", + description: "Unfound success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {AlwaysInvalid}, @@ -71,7 +71,7 @@ func testTypeValidatorValidate(t *testing.T) { expectedErr: ErrInvalidMsgType, }, { - description: "Not Found error", + description: "Unfound error", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -244,15 +244,15 @@ func ExampleNewTypeValidator() { func ExampleTypeValidator_Validate() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( - // Validates known msg types + // Validates found msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates unfound msg types AlwaysInvalid) if err != nil { return } foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - notFoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Not Found error - fmt.Printf("foundErr is nil: %v, notFoundErr is nil: %v", foundErr == nil, notFoundErr == nil) - // Output: foundErr is nil: true, notFoundErr is nil: false + unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Printf("foundErr is nil: %v, unfoundErr is nil: %v", foundErr == nil, unfoundErr == nil) + // Output: foundErr is nil: true, unfoundErr is nil: false } From 31f9479b3cfab004c6a2caa72538b7be306d3d94 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:45:04 -0400 Subject: [PATCH 45/68] Add test for `Nil default Validators` edge case --- validator_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/validator_test.go b/validator_test.go index 09d9d3f..604faaa 100644 --- a/validator_test.go +++ b/validator_test.go @@ -114,7 +114,7 @@ func testNewTypeValidator(t *testing.T) { }{ // Success case { - description: "Default Validator success", + description: "Default Validators success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -132,7 +132,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, { - description: "Omit default Validator success", + description: "Omit default Validators success", value: Test{ m: map[MessageType]Validators{ SimpleEventMessageType: {alwaysValid}, @@ -141,6 +141,16 @@ func testNewTypeValidator(t *testing.T) { expectedErr: nil, }, // Failure case + { + description: "Nil default Validators", + value: Test{ + m: map[MessageType]Validators{ + SimpleEventMessageType: {alwaysValid}, + }, + defaultValidators: Validators{nil}, + }, + expectedErr: ErrInvalidTypeValidator, + }, { description: "Empty list of Validators error", value: Test{ From 30373c0a123f4bba08881d00af634bcc49220226 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:51:08 -0400 Subject: [PATCH 46/68] update examples output --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index 604faaa..906ea4b 100644 --- a/validator_test.go +++ b/validator_test.go @@ -246,8 +246,8 @@ func ExampleNewTypeValidator() { // Validates unknown msg types AlwaysInvalid) - fmt.Printf("%v, %T", err == nil, msgv) - // Output: true, wrp.TypeValidator + fmt.Printf("%v %T", err == nil, msgv) + // Output: true wrp.TypeValidator } @@ -263,6 +263,6 @@ func ExampleTypeValidator_Validate() { } foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Printf("foundErr is nil: %v, unfoundErr is nil: %v", foundErr == nil, unfoundErr == nil) - // Output: foundErr is nil: true, unfoundErr is nil: false + fmt.Println(foundErr == nil, unfoundErr == nil) + // Output: true false } From 72518e4acb2d0b4004538b68568123aaa8b7ca90 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:54:35 -0400 Subject: [PATCH 47/68] doc update --- validator.go | 4 ++-- validator_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/validator.go b/validator.go index 6736faf..6eb4846 100644 --- a/validator.go +++ b/validator.go @@ -63,14 +63,14 @@ func (vf ValidatorFunc) Validate(m Message) error { } // TypeValidator is a WRP validator that validates based on message type -// or using the defaultValidators if message type is unknown. +// or using the defaultValidators if message type is unfound. type TypeValidator struct { m map[MessageType]Validators defaultValidators Validators } // Validate validates messages based on message type or using the defaultValidators -// if message type is unknown. +// if message type is unfound. func (m TypeValidator) Validate(msg Message) error { vs := m.m[msg.MessageType()] if vs == nil { diff --git a/validator_test.go b/validator_test.go index 906ea4b..33ebd8c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -241,9 +241,9 @@ func TestTypeValidator(t *testing.T) { func ExampleNewTypeValidator() { var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( - // Validates known msg types + // Validates found msg types map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, - // Validates unknown msg types + // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) From 0e4b691be5a7f8cc986c007798c6adab597827dc Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Thu, 26 May 2022 13:59:13 -0400 Subject: [PATCH 48/68] formatting --- validator_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index 33ebd8c..66c2f49 100644 --- a/validator_test.go +++ b/validator_test.go @@ -187,6 +187,7 @@ func testNewTypeValidator(t *testing.T) { expectedErr: ErrInvalidTypeValidator, }, } + for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) @@ -206,9 +207,7 @@ func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} err := AlwaysInvalid(msg) - assert.ErrorIs(err, ErrInvalidMsgType) - } func TestHelperValidators(t *testing.T) { @@ -245,10 +244,8 @@ func ExampleNewTypeValidator() { map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, // Validates unfound msg types AlwaysInvalid) - fmt.Printf("%v %T", err == nil, msgv) // Output: true wrp.TypeValidator - } func ExampleTypeValidator_Validate() { @@ -261,6 +258,7 @@ func ExampleTypeValidator_Validate() { if err != nil { return } + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error fmt.Println(foundErr == nil, unfoundErr == nil) From 505d92f5658943e43e10288964568331357abadf Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 31 May 2022 14:47:58 -0400 Subject: [PATCH 49/68] Updates based on PR review * exported alwaysValid * decoupled data structures leveraging `Validators` to `Validator` * added validateValidator --- validator.go | 49 ++++++++++++++++++++---------- validator_test.go | 77 +++++++++++++++++++++++------------------------ 2 files changed, 71 insertions(+), 55 deletions(-) diff --git a/validator.go b/validator.go index 6eb4846..33d9ba3 100644 --- a/validator.go +++ b/validator.go @@ -27,9 +27,10 @@ var ( ) // AlwaysInvalid doesn't validate anything about the message and always returns an error. -var AlwaysInvalid ValidatorFunc = func(m Message) error { - return ErrInvalidMsgType -} +var AlwaysInvalid ValidatorFunc = func(m Message) error { return ErrInvalidMsgType } + +// AlwaysValid doesn't validate anything about the message and always returns nil. +var AlwaysValid ValidatorFunc = func(msg Message) error { return nil } // Validator is a WRP validator that allows access to the Validate function. type Validator interface { @@ -65,7 +66,7 @@ func (vf ValidatorFunc) Validate(m Message) error { // TypeValidator is a WRP validator that validates based on message type // or using the defaultValidators if message type is unfound. type TypeValidator struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } @@ -81,20 +82,14 @@ func (m TypeValidator) Validate(msg Message) error { } // NewTypeValidator is a TypeValidator factory. -func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validator) (TypeValidator, error) { +func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { return TypeValidator{}, ErrInvalidTypeValidator } - for _, vs := range m { - if vs == nil || len(vs) == 0 { - return TypeValidator{}, ErrInvalidTypeValidator - } - - for _, v := range vs { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator - } + for _, v := range m { + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -103,8 +98,8 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat } for _, v := range defaultValidators { - if v == nil { - return TypeValidator{}, ErrInvalidTypeValidator + if err := validateValidator(v); err != nil { + return TypeValidator{}, err } } @@ -113,3 +108,25 @@ func NewTypeValidator(m map[MessageType]Validators, defaultValidators ...Validat defaultValidators: defaultValidators, }, nil } + +// validateValidator validates a given Validator. +func validateValidator(v Validator) error { + switch vs := v.(type) { + case Validators: + if vs == nil || len(vs) == 0 { + return ErrInvalidTypeValidator + } + + for _, v := range vs { + if v == nil { + return ErrInvalidTypeValidator + } + } + case Validator, ValidatorFunc: + // catch nil Validator + default: + return ErrInvalidTypeValidator + } + + return nil +} diff --git a/validator_test.go b/validator_test.go index 66c2f49..64cf8c7 100644 --- a/validator_test.go +++ b/validator_test.go @@ -27,12 +27,11 @@ import ( func testTypeValidatorValidate(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators msg Message } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -42,8 +41,8 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: SimpleEventMessageType}, }, @@ -51,10 +50,10 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: CreateMessageType}, }, }, @@ -62,10 +61,10 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {AlwaysInvalid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysInvalid}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, msg: Message{Type: SimpleEventMessageType}, }, expectedErr: ErrInvalidMsgType, @@ -73,8 +72,8 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, msg: Message{Type: CreateMessageType}, }, @@ -102,11 +101,10 @@ func testTypeValidatorValidate(t *testing.T) { func testNewTypeValidator(t *testing.T) { type Test struct { - m map[MessageType]Validators + m map[MessageType]Validator defaultValidators Validators } - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } tests := []struct { description string value Test @@ -116,36 +114,36 @@ func testNewTypeValidator(t *testing.T) { { description: "Default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { description: "Empty map of Validators success", value: Test{ - m: map[MessageType]Validators{}, - defaultValidators: Validators{alwaysValid}, + m: map[MessageType]Validator{}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: nil, }, { description: "Omit default Validators success", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{AlwaysValid}, }, }, expectedErr: nil, }, // Failure case { - description: "Nil default Validators", + description: "Nil list of default Validators error ", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {alwaysValid}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, defaultValidators: Validators{nil}, }, @@ -154,36 +152,39 @@ func testNewTypeValidator(t *testing.T) { { description: "Empty list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Nil Validators error", + description: "Nil Validator error", value: Test{ - m: map[MessageType]Validators{ + m: map[MessageType]Validator{ SimpleEventMessageType: nil, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { description: "Nil list of Validators error", value: Test{ - m: map[MessageType]Validators{ - SimpleEventMessageType: {nil}, + m: map[MessageType]Validator{ + SimpleEventMessageType: Validators{nil}, }, - defaultValidators: Validators{alwaysValid}, + defaultValidators: Validators{AlwaysValid}, }, expectedErr: ErrInvalidTypeValidator, }, { - description: "Empty map of Validators error", - value: Test{}, + description: "Nil map of Validators error", + value: Test{ + m: nil, + defaultValidators: Validators{AlwaysValid}, + }, expectedErr: ErrInvalidTypeValidator, }, } @@ -238,10 +239,9 @@ func TestTypeValidator(t *testing.T) { } func ExampleNewTypeValidator() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) @@ -249,10 +249,9 @@ func ExampleNewTypeValidator() { } func ExampleTypeValidator_Validate() { - var alwaysValid ValidatorFunc = func(msg Message) error { return nil } msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validators{SimpleEventMessageType: {alwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, // Validates unfound msg types AlwaysInvalid) if err != nil { From 998528baf772d73888b0c5012652b3ef73e861d1 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 1 Jun 2022 12:43:08 -0400 Subject: [PATCH 50/68] Add multierr to Validator --- go.mod | 1 + go.sum | 4 ++++ validator.go | 10 +++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 1fd3bdd..393c95c 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/ugorji/go/codec v1.2.6 github.com/xmidt-org/httpaux v0.3.0 github.com/xmidt-org/webpa-common v1.3.2 + go.uber.org/multierr v1.8.0 ) diff --git a/go.sum b/go.sum index a1d9119..8370579 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/xmidt-org/httpaux v0.3.0 h1:JdV4QceiE8EMA1Qf5rnJzHdkIPzQV12ddARHLU8hr github.com/xmidt-org/httpaux v0.3.0/go.mod h1:mviIlg5fHGb3lAv3l0sbiwVG/q9rqvXaudEYxVrzXdE= github.com/xmidt-org/webpa-common v1.3.2 h1:dE1Fi+XVnkt3tMGMjH7/hN/UGcaQ/ukKriXuMDyCWnM= github.com/xmidt-org/webpa-common v1.3.2/go.mod h1:oCpKzOC+9h2vYHVzAU/06tDTQuBN4RZz+rhgIXptpOI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/validator.go b/validator.go index 33d9ba3..f909660 100644 --- a/validator.go +++ b/validator.go @@ -19,6 +19,8 @@ package wrp import ( "errors" + + "go.uber.org/multierr" ) var ( @@ -44,14 +46,12 @@ type Validators []Validator // Validate runs messages through each validator in the validators list. // It returns as soon as the message is considered invalid, otherwise returns nil if valid. func (vs Validators) Validate(m Message) error { + var err error for _, v := range vs { - err := v.Validate(m) - if err != nil { - return err - } + err = multierr.Append(err, v.Validate(m)) } - return nil + return err } // ValidatorFunc is a WRP validator that takes messages and validates them From f2cd00e1bd3436d41433a9df42362e5893ddf333 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 08:10:28 -0400 Subject: [PATCH 51/68] formatting --- validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index 64cf8c7..afe50c5 100644 --- a/validator_test.go +++ b/validator_test.go @@ -140,7 +140,7 @@ func testNewTypeValidator(t *testing.T) { }, // Failure case { - description: "Nil list of default Validators error ", + description: "Nil list of default Validators error", value: Test{ m: map[MessageType]Validator{ SimpleEventMessageType: AlwaysValid, From 17a1c8bd0d6272f8c97ed6d700b96f7c5564f87f Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 16:15:54 -0400 Subject: [PATCH 52/68] Add missing test for `AlwaysValid` func --- validator_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/validator_test.go b/validator_test.go index afe50c5..14bc9b8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -204,6 +204,13 @@ func testNewTypeValidator(t *testing.T) { } } +func testAlwaysValid(t *testing.T) { + assert := assert.New(t) + msg := Message{} + err := AlwaysValid(msg) + assert.NoError(err) +} + func testAlwaysInvalid(t *testing.T) { assert := assert.New(t) msg := Message{} @@ -217,6 +224,7 @@ func TestHelperValidators(t *testing.T) { test func(*testing.T) }{ {"AlwaysInvalid", testAlwaysInvalid}, + {"AlwaysValid", testAlwaysValid}, } for _, tc := range tests { From 390e74d3a445b57917c37e6f091d3552a1672b94 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 16:52:33 -0400 Subject: [PATCH 53/68] First pass of spec validators * Validates requirements described at https://xmidt.io/docs/wrp/basics/#overarching-guidelines * Validates requirements described at https://xmidt.io/docs/wrp/basics/#locators * validates message type values * Does not cover validators for a specific message type (future prs) --- go.mod | 1 + go.sum | 2 + spec_validator.go | 144 ++++++++++++++ spec_validator_test.go | 443 +++++++++++++++++++++++++++++++++++++++++ utf8.go | 2 +- 5 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 spec_validator.go create mode 100644 spec_validator_test.go diff --git a/go.mod b/go.mod index 393c95c..5cb024a 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-kit/kit v0.8.0 github.com/go-logfmt/logfmt v0.3.0 // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/google/uuid v1.3.0 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/stretchr/testify v1.7.0 github.com/ugorji/go/codec v1.2.6 diff --git a/go.sum b/go.sum index 8370579..6684580 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2i github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/spec_validator.go b/spec_validator.go new file mode 100644 index 0000000..b1cce5e --- /dev/null +++ b/spec_validator.go @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wrp + +import ( + "errors" + "regexp" + "strings" + "unicode" + + "github.com/google/uuid" + "go.uber.org/multierr" +) + +const ( + serialPrefix = "serial" + uuidPrefix = "uuid" + eventPrefix = "event" + dnsPrefix = "dns" +) + +var ( + ErrorInvalidMessageEncoding = errors.New("invalid message encoding") + ErrorInvalidMessageType = errors.New("invalid message type") + ErrorInvalidLocator = errors.New("invalid locator") + ErrorInvalidSource = errors.New("invalid Source name") + ErrorInvalidDestination = errors.New("invalid Destination name") +) + +// locatorPattern is the precompiled regular expression that all source and dest locators must match. +// Matching is partial, as everything after the authority (ID) is ignored. https://xmidt.io/docs/wrp/basics/#locators +var LocatorPattern = regexp.MustCompile( + `^(?P(?i)` + macPrefix + `|` + uuidPrefix + `|` + eventPrefix + `|` + dnsPrefix + `|` + serialPrefix + `):(?P[^/]+)?`, +) + +// SpecValidators is a WRP validator that ensures messages are valid based on +// each spec validator in the list. +var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} + +// UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. +var UTF8Validator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, UTF8(m)) { + err = multierr.Append(err, ErrorInvalidMessageEncoding) + } + + return err +} + +// MessageTypeValidator is a WRP validator that takes messages and validates their Type. +var MessageTypeValidator ValidatorFunc = func(m Message) error { + t := m.MessageType() + if t < Invalid0MessageType || t > lastMessageType { + return ErrorInvalidMessageType + } + + switch t { + case Invalid0MessageType, Invalid1MessageType, lastMessageType: + return ErrorInvalidMessageType + } + + return nil +} + +// SourceValidator is a WRP validator that takes messages and validates their Source. +// Only mac and uuid sources are validated. Serial, event and dns sources are +// not validated. +var SourceValidator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, validateLocator(m.Source)) { + err = multierr.Append(err, ErrorInvalidSource) + } + + return err +} + +// DestinationValidator is a WRP validator that takes messages and validates their Destination. +// Only mac and uuid destinations are validated. Serial, event and dns destinations are +// not validated. +var DestinationValidator ValidatorFunc = func(m Message) error { + var err error + if multierr.AppendInto(&err, validateLocator(m.Destination)) { + err = multierr.Append(err, ErrorInvalidDestination) + } + + return err +} + +// validateLocator validates a given locator's scheme and authority (ID). +// Only mac and uuid schemes' IDs are validated. IDs from serial, event and dns schemes are +// not validated. +func validateLocator(l string) error { + var err error + + match := LocatorPattern.FindStringSubmatch(l) + if match == nil { + return multierr.Append(err, ErrorInvalidLocator) + } + + idPart := match[2] + switch strings.ToLower(match[1]) { + case macPrefix: + var invalidCharacter rune = -1 + idPart = strings.Map( + func(r rune) rune { + switch { + case strings.ContainsRune(hexDigits, r): + return unicode.ToLower(r) + case strings.ContainsRune(macDelimiters, r): + return -1 + default: + invalidCharacter = r + return -1 + } + }, + idPart, + ) + + if invalidCharacter != -1 || len(idPart) != macLength { + return multierr.Append(err, ErrorInvalidLocator) + } + case uuidPrefix: + if _, uuidErr := uuid.Parse(idPart); multierr.AppendInto(&err, uuidErr) { + return multierr.Append(err, ErrorInvalidLocator) + } + } + + return err +} diff --git a/spec_validator_test.go b/spec_validator_test.go new file mode 100644 index 0000000..18c43a2 --- /dev/null +++ b/spec_validator_test.go @@ -0,0 +1,443 @@ +/** + * Copyright (c) 2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wrp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testUTF8Validator(t *testing.T) { + /* + "\x85" - 5 name value pairs + "\xa8""msg_type" : "\x03" // 3 + "\xa4""dest" : "\xac""\xed\xbf\xbft-address" + "\xa7""payload" : "\xc4""\x03" - len 3 + "123" + "\xa6""source" : "\xae""source-address" + "\xb0""transaction_uuid" : "\xd9\x24""c07ee5e1-70be-444c-a156-097c767ad8aa" + */ + invalid := []byte{ + 0x85, + 0xa8, 'm', 's', 'g', '_', 't', 'y', 'p', 'e', 0x03, + 0xa4, 'd', 'e', 's', 't', 0xac /* \xed\xbf\xbf is invalid */, 0xed, 0xbf, 0xbf, 't', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', + 0xa7, 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0xc4, 0x03, '1', '2', '3', + 0xa6, 's', 'o', 'u', 'r', 'c', 'e', 0xae, 's', 'o', 'u', 'r', 'c', 'e', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', + 0xb0, 't', 'r', 'a', 'n', 's', 'a', 'c', 't', 'i', 'o', 'n', '_', 'u', 'u', 'i', 'd', 0xd9, 0x24, 'c', '0', '7', 'e', 'e', '5', 'e', '1', '-', '7', '0', 'b', 'e', '-', '4', '4', '4', 'c', '-', 'a', '1', '5', '6', '-', '0', '9', '7', 'c', '7', '6', '7', 'a', 'd', '8', 'a', 'a', + } + decoder := NewDecoderBytes(invalid, Msgpack) + msg := new(Message) + err := decoder.Decode(msg) + require.NoError(t, err) + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "UTF8 success", + value: Message{Source: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + { + description: "Not UTF8 error", + value: *msg, + expectedErr: []error{ErrorInvalidMessageEncoding, ErrNotUTF8}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := UTF8Validator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testMessageTypeValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr error + }{ + // Success case + { + description: "AuthorizationMessageType success", + value: Message{Type: AuthorizationMessageType}, + expectedErr: nil, + }, + { + description: "SimpleRequestResponseMessageType success", + value: Message{Type: SimpleRequestResponseMessageType}, + expectedErr: nil, + }, + { + description: "SimpleEventMessageType success", + value: Message{Type: SimpleEventMessageType}, + expectedErr: nil, + }, + { + description: "CreateMessageType success", + value: Message{Type: CreateMessageType}, + expectedErr: nil, + }, + { + description: "RetrieveMessageType success", + value: Message{Type: RetrieveMessageType}, + expectedErr: nil, + }, + { + description: "UpdateMessageType success", + value: Message{Type: UpdateMessageType}, + expectedErr: nil, + }, + { + description: "DeleteMessageType success", + value: Message{Type: DeleteMessageType}, + expectedErr: nil, + }, + { + description: "ServiceRegistrationMessageType success", + value: Message{Type: ServiceRegistrationMessageType}, + expectedErr: nil, + }, + { + description: "ServiceAliveMessageType success", + value: Message{Type: ServiceAliveMessageType}, + expectedErr: nil, + }, + { + description: "UnknownMessageType success", + value: Message{Type: UnknownMessageType}, + expectedErr: nil, + }, + // Failure case + { + description: "Invalid0MessageType error", + value: Message{Type: Invalid0MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Invalid0MessageType error", + value: Message{Type: Invalid0MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Invalid1MessageType error", + value: Message{Type: Invalid1MessageType}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "lastMessageType error", + value: Message{Type: lastMessageType}, + expectedErr: ErrorInvalidMessageType, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := MessageTypeValidator(tc.value) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.NoError(err) + }) + } +} + +func testSourceValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Source success", + value: Message{Source: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + // Failures + { + description: "Source error", + value: Message{Source: "invalid:a-BB-44-55"}, + expectedErr: []error{ErrorInvalidSource, ErrorInvalidLocator}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SourceValidator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testDestinationValidator(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Destination success", + value: Message{Destination: "MAC:11:22:33:44:55:66"}, + expectedErr: nil, + }, + // Failures + { + description: "Destination error", + value: Message{Destination: "invalid:a-BB-44-55"}, + expectedErr: []error{ErrorInvalidDestination, ErrorInvalidLocator}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := DestinationValidator(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func testValidateLocator(t *testing.T) { + tests := []struct { + description string + value string + expectedErr error + }{ + // mac success case + { + description: "Mac ID ':' delimiter success", + value: "MAC:11:22:33:44:55:66", + expectedErr: nil, + }, + { + description: "Mac ID no delimiter success", + value: "MAC:11aaBB445566", + expectedErr: nil, + }, + { + description: "Mac ID '-' delimiter success", + value: "mac:11-aa-BB-44-55-66", + expectedErr: nil, + }, + { + description: "Mac ID ',' delimiter success", + value: "mac:11,aa,BB,44,55,66", + expectedErr: nil, + }, + { + description: "Mac with service success", + value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", + expectedErr: nil, + }, + // Mac failure case + { + description: "Invalid mac ID character error", + value: "MAC:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid mac ID length error", + value: "mac:11-aa-BB-44-55", + expectedErr: ErrorInvalidLocator, + }, + // Serial success case + { + description: "Serial ID success", + value: "serial:anything Goes!", + expectedErr: nil, + }, + // UUID success case + { + description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 + value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 + value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. + value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. + value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. + value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. + value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", + expectedErr: nil, + }, + { + description: "UUID Future variant ID success", // Reserved for future definition. + value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", + expectedErr: nil, + }, + // UUID failure case + { + description: "Invalid UUID ID #1 error", + value: "uuid:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid UUID ID #2 error", + value: "uuid:URN:UUID:invalid45566", + expectedErr: ErrorInvalidLocator, + }, + { + description: "Invalid UUID ID #3 error", + value: "uuid:{invalid45566}", + expectedErr: ErrorInvalidLocator, + }, + // Event success case + { + description: "Event ID success", + value: "event:anything Goes!", + expectedErr: nil, + }, + // DNS success case + { + description: "DNS ID success", + value: "dns:anything Goes!", + expectedErr: nil, + }, + // Scheme failure case + { + description: "Invalid scheme error", + value: "invalid:a-BB-44-55", + expectedErr: ErrorInvalidLocator, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := validateLocator(tc.value) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + return + } + + assert.NoError(err) + }) + } +} + +func TestSpecHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"UTF8Validator", testUTF8Validator}, + {"MessageTypeValidator", testMessageTypeValidator}, + {"SourceValidator", testSourceValidator}, + {"DestinationValidator", testDestinationValidator}, + {"validateLocator", testValidateLocator}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestSpecValidators(t *testing.T) { + tests := []struct { + description string + value Message + expectedErr []error + }{ + // Success case + { + description: "Valid specs success", + value: Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: nil, + }, + // Failure cases + { + description: "Invaild specs error", + value: Message{ + Type: Invalid0MessageType, + Source: "invalid:a-BB-44-55", + Destination: "invalid:a-BB-44-55", + }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidLocator, ErrorInvalidDestination}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SpecValidators.Validate(tc.value) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} diff --git a/utf8.go b/utf8.go index 5aa0417..9a5d4fc 100644 --- a/utf8.go +++ b/utf8.go @@ -26,7 +26,7 @@ import ( var ( ErrNotUTF8 = errors.New("field contains non-utf-8 characters") - ErrUnexpectedKind = errors.New("A struct or non-nil pointer to struct is required") + ErrUnexpectedKind = errors.New("a struct or non-nil pointer to struct is required") ) // UTF8 takes any struct verifies that it contains UTF-8 strings. From e3bd2ce381971678295e9e23aec4a16b984713e4 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:28 -0400 Subject: [PATCH 54/68] Add an example for SpecValidators --- spec_validator_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec_validator_test.go b/spec_validator_test.go index 18c43a2..e3f5eb6 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -18,6 +18,7 @@ package wrp import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -441,3 +442,37 @@ func TestSpecValidators(t *testing.T) { }) } } + +func ExampleTypeValidator_Validate_specValidators() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{ + SimpleEventMessageType: SpecValidators, + // Only validates Source and nothing else + SimpleRequestResponseMessageType: SourceValidator, + }, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + foundErrSuccess1 := msgv.Validate(Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:61", + }) // Found success + foundErrSuccess2 := msgv.Validate(Message{ + Type: SimpleRequestResponseMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "invalid:a-BB-44-55", + }) // Found success + foundErrFailure := msgv.Validate(Message{ + Type: Invalid0MessageType, + Source: "invalid:a-BB-44-55", + Destination: "invalid:a-BB-44-55", + }) // Found error + unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErrSuccess1 == nil, foundErrSuccess2 == nil, foundErrFailure == nil, unfoundErrFailure == nil) + // Output: true true false false +} From 63ec769d5b57a34fb79f5ada335a5f6ad49e9609 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:57 -0400 Subject: [PATCH 55/68] Remove println from UTF8 validate func --- utf8.go | 1 - 1 file changed, 1 deletion(-) diff --git a/utf8.go b/utf8.go index 9a5d4fc..d1e26a2 100644 --- a/utf8.go +++ b/utf8.go @@ -55,7 +55,6 @@ func UTF8(v interface{}) error { if !utf8.ValidString(s) { return fmt.Errorf("%w: '%s:%v'", ErrNotUTF8, ft.Name, s) } - fmt.Println(s) } } From 27b59bdb8e7e908a973a756678c7b64b53970db9 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:45:24 -0400 Subject: [PATCH 56/68] Add missing tests for MessageType --- spec_validator_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec_validator_test.go b/spec_validator_test.go index e3f5eb6..861d216 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -159,6 +159,16 @@ func testMessageTypeValidator(t *testing.T) { value: Message{Type: lastMessageType}, expectedErr: ErrorInvalidMessageType, }, + { + description: "Non-existing negative MessageType error", + value: Message{Type: -10}, + expectedErr: ErrorInvalidMessageType, + }, + { + description: "Non-existing positive MessageType error", + value: Message{Type: lastMessageType + 1}, + expectedErr: ErrorInvalidMessageType, + }, } for _, tc := range tests { From 76593e02b291873de6dfb74b5ae9756b9adb66b2 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 18:34:58 -0400 Subject: [PATCH 57/68] Update docs --- spec_validator.go | 2 +- spec_validator_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec_validator.go b/spec_validator.go index b1cce5e..78cff67 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -49,7 +49,7 @@ var LocatorPattern = regexp.MustCompile( ) // SpecValidators is a WRP validator that ensures messages are valid based on -// each spec validator in the list. +// each spec validator in the list. Only validates the opinionated portions of the spec. var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} // UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. diff --git a/spec_validator_test.go b/spec_validator_test.go index 861d216..d0c1c6f 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -457,6 +457,7 @@ func ExampleTypeValidator_Validate_specValidators() { msgv, err := NewTypeValidator( // Validates found msg types map[MessageType]Validator{ + // Validates opinionated portions of the spec SimpleEventMessageType: SpecValidators, // Only validates Source and nothing else SimpleRequestResponseMessageType: SourceValidator, From fd7f29c702c573a7fcf81ffd2bee140ebfbbd574 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Mon, 6 Jun 2022 13:14:40 -0400 Subject: [PATCH 58/68] Updates based on PR Feedback * Clean up errors * Remove multierr with some wrapping and regular errors * Wrap SpecValidators within a func --- spec_validator.go | 45 +++++++++++++------------- spec_validator_test.go | 72 ++++++++++++++++++++---------------------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index 78cff67..3033a2f 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -19,12 +19,13 @@ package wrp import ( "errors" + "fmt" "regexp" + "strconv" "strings" "unicode" "github.com/google/uuid" - "go.uber.org/multierr" ) const ( @@ -37,7 +38,6 @@ const ( var ( ErrorInvalidMessageEncoding = errors.New("invalid message encoding") ErrorInvalidMessageType = errors.New("invalid message type") - ErrorInvalidLocator = errors.New("invalid locator") ErrorInvalidSource = errors.New("invalid Source name") ErrorInvalidDestination = errors.New("invalid Destination name") ) @@ -50,16 +50,17 @@ var LocatorPattern = regexp.MustCompile( // SpecValidators is a WRP validator that ensures messages are valid based on // each spec validator in the list. Only validates the opinionated portions of the spec. -var SpecValidators = Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} +func SpecValidators() Validators { + return Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} +} // UTF8Validator is a WRP validator that takes messages and validates that it contains UTF-8 strings. var UTF8Validator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, UTF8(m)) { - err = multierr.Append(err, ErrorInvalidMessageEncoding) + if err := UTF8(m); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidMessageEncoding, err) } - return err + return nil } // MessageTypeValidator is a WRP validator that takes messages and validates their Type. @@ -81,35 +82,31 @@ var MessageTypeValidator ValidatorFunc = func(m Message) error { // Only mac and uuid sources are validated. Serial, event and dns sources are // not validated. var SourceValidator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, validateLocator(m.Source)) { - err = multierr.Append(err, ErrorInvalidSource) + if err := validateLocator(m.Source); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidSource, err) } - return err + return nil } // DestinationValidator is a WRP validator that takes messages and validates their Destination. // Only mac and uuid destinations are validated. Serial, event and dns destinations are // not validated. var DestinationValidator ValidatorFunc = func(m Message) error { - var err error - if multierr.AppendInto(&err, validateLocator(m.Destination)) { - err = multierr.Append(err, ErrorInvalidDestination) + if err := validateLocator(m.Destination); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidDestination, err) } - return err + return nil } // validateLocator validates a given locator's scheme and authority (ID). // Only mac and uuid schemes' IDs are validated. IDs from serial, event and dns schemes are // not validated. func validateLocator(l string) error { - var err error - match := LocatorPattern.FindStringSubmatch(l) if match == nil { - return multierr.Append(err, ErrorInvalidLocator) + return fmt.Errorf("spec scheme not found") } idPart := match[2] @@ -131,14 +128,16 @@ func validateLocator(l string) error { idPart, ) - if invalidCharacter != -1 || len(idPart) != macLength { - return multierr.Append(err, ErrorInvalidLocator) + if invalidCharacter != -1 { + return fmt.Errorf("invalid character %v", strconv.QuoteRune(invalidCharacter)) + } else if len(idPart) != macLength { + return errors.New("invalid mac length") } case uuidPrefix: - if _, uuidErr := uuid.Parse(idPart); multierr.AppendInto(&err, uuidErr) { - return multierr.Append(err, ErrorInvalidLocator) + if _, err := uuid.Parse(idPart); err != nil { + return err } } - return err + return nil } diff --git a/spec_validator_test.go b/spec_validator_test.go index d0c1c6f..f8c543f 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -61,7 +61,7 @@ func testUTF8Validator(t *testing.T) { { description: "Not UTF8 error", value: *msg, - expectedErr: []error{ErrorInvalidMessageEncoding, ErrNotUTF8}, + expectedErr: []error{ErrorInvalidMessageEncoding}, }, } @@ -189,7 +189,7 @@ func testSourceValidator(t *testing.T) { tests := []struct { description string value Message - expectedErr []error + expectedErr error }{ // Success case { @@ -201,7 +201,7 @@ func testSourceValidator(t *testing.T) { { description: "Source error", value: Message{Source: "invalid:a-BB-44-55"}, - expectedErr: []error{ErrorInvalidSource, ErrorInvalidLocator}, + expectedErr: ErrorInvalidSource, }, } @@ -210,9 +210,7 @@ func testSourceValidator(t *testing.T) { assert := assert.New(t) err := SourceValidator(tc.value) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -225,7 +223,7 @@ func testDestinationValidator(t *testing.T) { tests := []struct { description string value Message - expectedErr []error + expectedErr error }{ // Success case { @@ -237,7 +235,7 @@ func testDestinationValidator(t *testing.T) { { description: "Destination error", value: Message{Destination: "invalid:a-BB-44-55"}, - expectedErr: []error{ErrorInvalidDestination, ErrorInvalidLocator}, + expectedErr: ErrorInvalidDestination, }, } @@ -246,9 +244,7 @@ func testDestinationValidator(t *testing.T) { assert := assert.New(t) err := DestinationValidator(tc.value) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -261,120 +257,120 @@ func testValidateLocator(t *testing.T) { tests := []struct { description string value string - expectedErr error + shouldErr bool }{ // mac success case { description: "Mac ID ':' delimiter success", value: "MAC:11:22:33:44:55:66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID no delimiter success", value: "MAC:11aaBB445566", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID '-' delimiter success", value: "mac:11-aa-BB-44-55-66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac ID ',' delimiter success", value: "mac:11,aa,BB,44,55,66", - expectedErr: nil, + shouldErr: false, }, { description: "Mac with service success", value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", - expectedErr: nil, + shouldErr: false, }, // Mac failure case { description: "Invalid mac ID character error", value: "MAC:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid mac ID length error", value: "mac:11-aa-BB-44-55", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, // Serial success case { description: "Serial ID success", value: "serial:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // UUID success case { description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, { description: "UUID Future variant ID success", // Reserved for future definition. value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", - expectedErr: nil, + shouldErr: false, }, // UUID failure case { description: "Invalid UUID ID #1 error", value: "uuid:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid UUID ID #2 error", value: "uuid:URN:UUID:invalid45566", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, { description: "Invalid UUID ID #3 error", value: "uuid:{invalid45566}", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, // Event success case { description: "Event ID success", value: "event:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // DNS success case { description: "DNS ID success", value: "dns:anything Goes!", - expectedErr: nil, + shouldErr: false, }, // Scheme failure case { description: "Invalid scheme error", value: "invalid:a-BB-44-55", - expectedErr: ErrorInvalidLocator, + shouldErr: true, }, } @@ -382,8 +378,8 @@ func testValidateLocator(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) err := validateLocator(tc.value) - if tc.expectedErr != nil { - assert.ErrorIs(err, tc.expectedErr) + if tc.shouldErr { + assert.Error(err) return } @@ -433,14 +429,14 @@ func TestSpecValidators(t *testing.T) { Source: "invalid:a-BB-44-55", Destination: "invalid:a-BB-44-55", }, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidLocator, ErrorInvalidDestination}, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, }, } for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SpecValidators.Validate(tc.value) + err := SpecValidators().Validate(tc.value) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) @@ -458,7 +454,7 @@ func ExampleTypeValidator_Validate_specValidators() { // Validates found msg types map[MessageType]Validator{ // Validates opinionated portions of the spec - SimpleEventMessageType: SpecValidators, + SimpleEventMessageType: SpecValidators(), // Only validates Source and nothing else SimpleRequestResponseMessageType: SourceValidator, }, From 665db7555fc16cb824fb38f824ecf341fdd4a7ad Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 7 Jun 2022 16:39:51 -0400 Subject: [PATCH 59/68] Update testUTF8Validator test and rename var `value` to `msg` for Message test vars --- spec_validator_test.go | 136 ++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 56 deletions(-) diff --git a/spec_validator_test.go b/spec_validator_test.go index f8c543f..c64a9fd 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -22,45 +22,69 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func testUTF8Validator(t *testing.T) { - /* - "\x85" - 5 name value pairs - "\xa8""msg_type" : "\x03" // 3 - "\xa4""dest" : "\xac""\xed\xbf\xbft-address" - "\xa7""payload" : "\xc4""\x03" - len 3 - "123" - "\xa6""source" : "\xae""source-address" - "\xb0""transaction_uuid" : "\xd9\x24""c07ee5e1-70be-444c-a156-097c767ad8aa" - */ - invalid := []byte{ - 0x85, - 0xa8, 'm', 's', 'g', '_', 't', 'y', 'p', 'e', 0x03, - 0xa4, 'd', 'e', 's', 't', 0xac /* \xed\xbf\xbf is invalid */, 0xed, 0xbf, 0xbf, 't', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', - 0xa7, 'p', 'a', 'y', 'l', 'o', 'a', 'd', 0xc4, 0x03, '1', '2', '3', - 0xa6, 's', 'o', 'u', 'r', 'c', 'e', 0xae, 's', 'o', 'u', 'r', 'c', 'e', '-', 'a', 'd', 'd', 'r', 'e', 's', 's', - 0xb0, 't', 'r', 'a', 'n', 's', 'a', 'c', 't', 'i', 'o', 'n', '_', 'u', 'u', 'i', 'd', 0xd9, 0x24, 'c', '0', '7', 'e', 'e', '5', 'e', '1', '-', '7', '0', 'b', 'e', '-', '4', '4', '4', 'c', '-', 'a', '1', '5', '6', '-', '0', '9', '7', 'c', '7', '6', '7', 'a', 'd', '8', 'a', 'a', - } - decoder := NewDecoderBytes(invalid, Msgpack) - msg := new(Message) - err := decoder.Decode(msg) - require.NoError(t, err) + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { description string - value Message + msg Message expectedErr []error }{ // Success case { description: "UTF8 success", - value: Message{Source: "MAC:11:22:33:44:55:66"}, + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, expectedErr: nil, }, { description: "Not UTF8 error", - value: *msg, + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "MAC:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, expectedErr: []error{ErrorInvalidMessageEncoding}, }, } @@ -68,7 +92,7 @@ func testUTF8Validator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := UTF8Validator(tc.value) + err := UTF8Validator(tc.msg) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) @@ -84,89 +108,89 @@ func testUTF8Validator(t *testing.T) { func testMessageTypeValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "AuthorizationMessageType success", - value: Message{Type: AuthorizationMessageType}, + msg: Message{Type: AuthorizationMessageType}, expectedErr: nil, }, { description: "SimpleRequestResponseMessageType success", - value: Message{Type: SimpleRequestResponseMessageType}, + msg: Message{Type: SimpleRequestResponseMessageType}, expectedErr: nil, }, { description: "SimpleEventMessageType success", - value: Message{Type: SimpleEventMessageType}, + msg: Message{Type: SimpleEventMessageType}, expectedErr: nil, }, { description: "CreateMessageType success", - value: Message{Type: CreateMessageType}, + msg: Message{Type: CreateMessageType}, expectedErr: nil, }, { description: "RetrieveMessageType success", - value: Message{Type: RetrieveMessageType}, + msg: Message{Type: RetrieveMessageType}, expectedErr: nil, }, { description: "UpdateMessageType success", - value: Message{Type: UpdateMessageType}, + msg: Message{Type: UpdateMessageType}, expectedErr: nil, }, { description: "DeleteMessageType success", - value: Message{Type: DeleteMessageType}, + msg: Message{Type: DeleteMessageType}, expectedErr: nil, }, { description: "ServiceRegistrationMessageType success", - value: Message{Type: ServiceRegistrationMessageType}, + msg: Message{Type: ServiceRegistrationMessageType}, expectedErr: nil, }, { description: "ServiceAliveMessageType success", - value: Message{Type: ServiceAliveMessageType}, + msg: Message{Type: ServiceAliveMessageType}, expectedErr: nil, }, { description: "UnknownMessageType success", - value: Message{Type: UnknownMessageType}, + msg: Message{Type: UnknownMessageType}, expectedErr: nil, }, // Failure case { description: "Invalid0MessageType error", - value: Message{Type: Invalid0MessageType}, + msg: Message{Type: Invalid0MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Invalid0MessageType error", - value: Message{Type: Invalid0MessageType}, + msg: Message{Type: Invalid0MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Invalid1MessageType error", - value: Message{Type: Invalid1MessageType}, + msg: Message{Type: Invalid1MessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "lastMessageType error", - value: Message{Type: lastMessageType}, + msg: Message{Type: lastMessageType}, expectedErr: ErrorInvalidMessageType, }, { description: "Non-existing negative MessageType error", - value: Message{Type: -10}, + msg: Message{Type: -10}, expectedErr: ErrorInvalidMessageType, }, { description: "Non-existing positive MessageType error", - value: Message{Type: lastMessageType + 1}, + msg: Message{Type: lastMessageType + 1}, expectedErr: ErrorInvalidMessageType, }, } @@ -174,7 +198,7 @@ func testMessageTypeValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := MessageTypeValidator(tc.value) + err := MessageTypeValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -188,19 +212,19 @@ func testMessageTypeValidator(t *testing.T) { func testSourceValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "Source success", - value: Message{Source: "MAC:11:22:33:44:55:66"}, + msg: Message{Source: "MAC:11:22:33:44:55:66"}, expectedErr: nil, }, // Failures { description: "Source error", - value: Message{Source: "invalid:a-BB-44-55"}, + msg: Message{Source: "invalid:a-BB-44-55"}, expectedErr: ErrorInvalidSource, }, } @@ -208,7 +232,7 @@ func testSourceValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SourceValidator(tc.value) + err := SourceValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -222,19 +246,19 @@ func testSourceValidator(t *testing.T) { func testDestinationValidator(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr error }{ // Success case { description: "Destination success", - value: Message{Destination: "MAC:11:22:33:44:55:66"}, + msg: Message{Destination: "MAC:11:22:33:44:55:66"}, expectedErr: nil, }, // Failures { description: "Destination error", - value: Message{Destination: "invalid:a-BB-44-55"}, + msg: Message{Destination: "invalid:a-BB-44-55"}, expectedErr: ErrorInvalidDestination, }, } @@ -242,7 +266,7 @@ func testDestinationValidator(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := DestinationValidator(tc.value) + err := DestinationValidator(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -408,13 +432,13 @@ func TestSpecHelperValidators(t *testing.T) { func TestSpecValidators(t *testing.T) { tests := []struct { description string - value Message + msg Message expectedErr []error }{ // Success case { description: "Valid specs success", - value: Message{ + msg: Message{ Type: SimpleEventMessageType, Source: "MAC:11:22:33:44:55:66", Destination: "MAC:11:22:33:44:55:66", @@ -424,7 +448,7 @@ func TestSpecValidators(t *testing.T) { // Failure cases { description: "Invaild specs error", - value: Message{ + msg: Message{ Type: Invalid0MessageType, Source: "invalid:a-BB-44-55", Destination: "invalid:a-BB-44-55", @@ -436,7 +460,7 @@ func TestSpecValidators(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := SpecValidators().Validate(tc.value) + err := SpecValidators().Validate(tc.msg) if tc.expectedErr != nil { for _, e := range tc.expectedErr { assert.ErrorIs(err, e) From 5b65251c42b17f9a09b489c8c357f2c76771b0ad Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:19:34 -0400 Subject: [PATCH 60/68] Improve Test descriptions, improved & added missing tests, improved example * update testUTF8Validator success case to contain a correct source * removed unused error list * removed nil struct field inits * added missing test cases for testValidateLocator * improved test descriptions for testValidateLocator * added missing test cases for TestSpecValidators * improved example --- spec_validator.go | 4 + spec_validator_test.go | 224 ++++++++++++++++++++++++++++++----------- 2 files changed, 170 insertions(+), 58 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index 3033a2f..7cb8de9 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -137,6 +137,10 @@ func validateLocator(l string) error { if _, err := uuid.Parse(idPart); err != nil { return err } + case serialPrefix, eventPrefix, dnsPrefix: + if len(idPart) == 0 { + return fmt.Errorf("invalid %v empty authority (ID)", serialPrefix) + } } return nil diff --git a/spec_validator_test.go b/spec_validator_test.go index c64a9fd..6b8874b 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -34,15 +34,14 @@ func testUTF8Validator(t *testing.T) { tests := []struct { description string msg Message - expectedErr []error + expectedErr error }{ // Success case { description: "UTF8 success", msg: Message{ - Type: SimpleRequestResponseMessageType, - Source: "external.com", - // Not UFT8 Destination string + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -60,13 +59,12 @@ func testUTF8Validator(t *testing.T) { PartnerIDs: []string{"foo"}, SessionID: "sessionID123", }, - expectedErr: nil, }, { description: "Not UTF8 error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "MAC:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -85,7 +83,7 @@ func testUTF8Validator(t *testing.T) { PartnerIDs: []string{"foo"}, SessionID: "sessionID123", }, - expectedErr: []error{ErrorInvalidMessageEncoding}, + expectedErr: ErrorInvalidMessageEncoding, }, } @@ -94,9 +92,7 @@ func testUTF8Validator(t *testing.T) { assert := assert.New(t) err := UTF8Validator(tc.msg) if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } + assert.ErrorIs(err, tc.expectedErr) return } @@ -115,52 +111,42 @@ func testMessageTypeValidator(t *testing.T) { { description: "AuthorizationMessageType success", msg: Message{Type: AuthorizationMessageType}, - expectedErr: nil, }, { description: "SimpleRequestResponseMessageType success", msg: Message{Type: SimpleRequestResponseMessageType}, - expectedErr: nil, }, { description: "SimpleEventMessageType success", msg: Message{Type: SimpleEventMessageType}, - expectedErr: nil, }, { description: "CreateMessageType success", msg: Message{Type: CreateMessageType}, - expectedErr: nil, }, { description: "RetrieveMessageType success", msg: Message{Type: RetrieveMessageType}, - expectedErr: nil, }, { description: "UpdateMessageType success", msg: Message{Type: UpdateMessageType}, - expectedErr: nil, }, { description: "DeleteMessageType success", msg: Message{Type: DeleteMessageType}, - expectedErr: nil, }, { description: "ServiceRegistrationMessageType success", msg: Message{Type: ServiceRegistrationMessageType}, - expectedErr: nil, }, { description: "ServiceAliveMessageType success", msg: Message{Type: ServiceAliveMessageType}, - expectedErr: nil, }, { description: "UnknownMessageType success", msg: Message{Type: UnknownMessageType}, - expectedErr: nil, }, // Failure case { @@ -184,12 +170,12 @@ func testMessageTypeValidator(t *testing.T) { expectedErr: ErrorInvalidMessageType, }, { - description: "Non-existing negative MessageType error", + description: "Nonexistent negative MessageType error", msg: Message{Type: -10}, expectedErr: ErrorInvalidMessageType, }, { - description: "Non-existing positive MessageType error", + description: "Nonexistent positive MessageType error", msg: Message{Type: lastMessageType + 1}, expectedErr: ErrorInvalidMessageType, }, @@ -210,6 +196,11 @@ func testMessageTypeValidator(t *testing.T) { } func testSourceValidator(t *testing.T) { + // SourceValidator is mainly a wrapper for validateLocator. + // This test mainly ensures that SourceValidator returns nil for non errors + // and wraps errors with ErrorInvalidSource. + // testValidateLocator covers the actual spectrum of test cases. + tests := []struct { description string msg Message @@ -219,7 +210,6 @@ func testSourceValidator(t *testing.T) { { description: "Source success", msg: Message{Source: "MAC:11:22:33:44:55:66"}, - expectedErr: nil, }, // Failures { @@ -244,6 +234,11 @@ func testSourceValidator(t *testing.T) { } func testDestinationValidator(t *testing.T) { + // DestinationValidator is mainly a wrapper for validateLocator. + // This test mainly ensures that DestinationValidator returns nil for non errors + // and wraps errors with ErrorInvalidDestination. + // testValidateLocator covers the actual spectrum of test cases. + tests := []struct { description string msg Message @@ -253,7 +248,6 @@ func testDestinationValidator(t *testing.T) { { description: "Destination success", msg: Message{Destination: "MAC:11:22:33:44:55:66"}, - expectedErr: nil, }, // Failures { @@ -285,117 +279,154 @@ func testValidateLocator(t *testing.T) { }{ // mac success case { - description: "Mac ID ':' delimiter success", + description: "Mac ID success, ':' delimiter", value: "MAC:11:22:33:44:55:66", shouldErr: false, }, { - description: "Mac ID no delimiter success", + description: "Mac ID success, no delimiter", value: "MAC:11aaBB445566", shouldErr: false, }, { - description: "Mac ID '-' delimiter success", + description: "Mac ID success, '-' delimiter", value: "mac:11-aa-BB-44-55-66", shouldErr: false, }, { - description: "Mac ID ',' delimiter success", + description: "Mac ID success, ',' delimiter", value: "mac:11,aa,BB,44,55,66", shouldErr: false, }, { - description: "Mac with service success", + description: "Mac service success", value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", shouldErr: false, }, // Mac failure case { - description: "Invalid mac ID character error", + description: "Mac ID error, invalid mac ID character", value: "MAC:invalid45566", shouldErr: true, }, { - description: "Invalid mac ID length error", + description: "Mac ID error, invalid mac ID length", value: "mac:11-aa-BB-44-55", shouldErr: true, }, + { + description: "Mac ID error, no ID", + value: "mac:", + shouldErr: true, + }, // Serial success case { description: "Serial ID success", value: "serial:anything Goes!", shouldErr: false, }, + // Serial failure case + { + description: "Invalid serial ID error, no ID", + value: "serial:", + shouldErr: true, + }, // UUID success case + // The variant specified in RFC4122 { - description: "UUID RFC4122 variant ID success", // The variant specified in RFC4122 + description: "UUID RFC4122 variant ID success", value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID RFC4122 variant with Microsoft encoding ID success", // The variant specified in RFC4122 + description: "UUID RFC4122 variant ID success, with Microsoft encoding", value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", shouldErr: false, }, + // Reserved, NCS backward compatibility. { - description: "UUID Reserved variant ID #1 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, with URN lower case ", value: "UUID:urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID Reserved variant ID #2 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, with URN upper case", value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, { - description: "UUID Reserved variant ID #3 success", // Reserved, NCS backward compatibility. + description: "UUID Reserved variant ID success, without URN", value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", shouldErr: false, }, + // Reserved, Microsoft Corporation backward compatibility. { - description: "UUID Microsoft variant ID success", // Reserved, Microsoft Corporation backward compatibility. + description: "UUID Microsoft variant ID success", value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", shouldErr: false, }, + // Reserved for future definition. { - description: "UUID Future variant ID success", // Reserved for future definition. + description: "UUID Future variant ID success", value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", shouldErr: false, }, // UUID failure case { - description: "Invalid UUID ID #1 error", + description: "Invalid UUID ID error", value: "uuid:invalid45566", shouldErr: true, }, { - description: "Invalid UUID ID #2 error", + description: "Invalid UUID ID error, with URN", value: "uuid:URN:UUID:invalid45566", shouldErr: true, }, { - description: "Invalid UUID ID #3 error", + description: "Invalid UUID ID error, with Microsoft encoding", value: "uuid:{invalid45566}", shouldErr: true, }, + { + description: "Invalid UUID ID error, no ID", + value: "uuid:", + shouldErr: true, + }, // Event success case { description: "Event ID success", value: "event:anything Goes!", shouldErr: false, }, + // Event failure case + { + description: "Invalid event ID error, no ID", + value: "event:", + shouldErr: true, + }, // DNS success case { description: "DNS ID success", value: "dns:anything Goes!", shouldErr: false, }, + // DNS failure case + { + description: "Invalid DNS ID error, no ID", + value: "dns:", + shouldErr: true, + }, // Scheme failure case { description: "Invalid scheme error", value: "invalid:a-BB-44-55", shouldErr: true, }, + { + description: "Invalid scheme error, empty string", + value: "", + shouldErr: true, + }, } for _, tc := range tests { @@ -430,6 +461,12 @@ func TestSpecHelperValidators(t *testing.T) { } func TestSpecValidators(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { description string msg Message @@ -437,24 +474,71 @@ func TestSpecValidators(t *testing.T) { }{ // Success case { - description: "Valid specs success", + description: "Valid spec success", msg: Message{ - Type: SimpleEventMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "MAC:11:22:33:44:55:66", + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, - // Failure cases + // Failure case { - description: "Invaild specs error", + description: "Invaild spec error", msg: Message{ - Type: Invalid0MessageType, - Source: "invalid:a-BB-44-55", - Destination: "invalid:a-BB-44-55", + Type: Invalid0MessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, + }, + { + description: "Invaild spec error, empty message", + msg: Message{}, expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, }, + { + description: "Invaild spec error, nonexistent MessageType", + msg: Message{ + Type: lastMessageType + 1, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: []error{ErrorInvalidMessageType}, + }, } for _, tc := range tests { @@ -488,6 +572,35 @@ func ExampleTypeValidator_Validate_specValidators() { return } + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + foundErrFailure := msgv.Validate(Message{ + Type: SimpleEventMessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }) // Found error foundErrSuccess1 := msgv.Validate(Message{ Type: SimpleEventMessageType, Source: "MAC:11:22:33:44:55:66", @@ -498,12 +611,7 @@ func ExampleTypeValidator_Validate_specValidators() { Source: "MAC:11:22:33:44:55:66", Destination: "invalid:a-BB-44-55", }) // Found success - foundErrFailure := msgv.Validate(Message{ - Type: Invalid0MessageType, - Source: "invalid:a-BB-44-55", - Destination: "invalid:a-BB-44-55", - }) // Found error unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErrSuccess1 == nil, foundErrSuccess2 == nil, foundErrFailure == nil, unfoundErrFailure == nil) - // Output: true true false false + fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) + // Output: false true true false } From d7f98bb8159b2cf1106dda9d6d61e927a1e91016 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 7 Jun 2022 16:32:43 -0400 Subject: [PATCH 61/68] Updates based on PR feedback * converted remaining `Validators` to `Validator` * remove func `validateValidator` * move nil checks to `Validate` func * remove Test struct * add new unexported field to check if `TypeValidato` is valid * add func `IsBad` to TypeValidato to say whether `TypeValidato` is valid * update tests for testAlwaysInvalid and testAlwaysValid * update tests names for `TestTypeValidator` * add tests for `Validators` --- validator.go | 59 +++---- validator_test.go | 387 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 294 insertions(+), 152 deletions(-) diff --git a/validator.go b/validator.go index f909660..79c6a13 100644 --- a/validator.go +++ b/validator.go @@ -24,7 +24,8 @@ import ( ) var ( - ErrInvalidTypeValidator = errors.New("invalid WRP message type validator") + ErrInvalidTypeValidator = errors.New("invalid TypeValidator") + ErrInvalidValidator = errors.New("invalid WRP message type validator") ErrInvalidMsgType = errors.New("invalid WRP message type") ) @@ -48,7 +49,9 @@ type Validators []Validator func (vs Validators) Validate(m Message) error { var err error for _, v := range vs { - err = multierr.Append(err, v.Validate(m)) + if v != nil { + err = multierr.Append(err, v.Validate(m)) + } } return err @@ -67,12 +70,17 @@ func (vf ValidatorFunc) Validate(m Message) error { // or using the defaultValidators if message type is unfound. type TypeValidator struct { m map[MessageType]Validator - defaultValidators Validators + defaultValidators Validator + isbad bool } // Validate validates messages based on message type or using the defaultValidators // if message type is unfound. func (m TypeValidator) Validate(msg Message) error { + if m.isbad { + return ErrInvalidTypeValidator + } + vs := m.m[msg.MessageType()] if vs == nil { return m.defaultValidators.Validate(msg) @@ -81,52 +89,23 @@ func (m TypeValidator) Validate(msg Message) error { return vs.Validate(msg) } +// IsBad returns a boolean indicating whether the TypeValidator receiver is valid +func (m TypeValidator) IsBad() bool { + return m.isbad +} + // NewTypeValidator is a TypeValidator factory. func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { - return TypeValidator{}, ErrInvalidTypeValidator + return TypeValidator{isbad: true}, ErrInvalidValidator } - for _, v := range m { - if err := validateValidator(v); err != nil { - return TypeValidator{}, err - } - } - - if len(defaultValidators) == 0 { + if defaultValidators == nil { defaultValidators = Validators{AlwaysInvalid} } - for _, v := range defaultValidators { - if err := validateValidator(v); err != nil { - return TypeValidator{}, err - } - } - return TypeValidator{ m: m, - defaultValidators: defaultValidators, + defaultValidators: Validators(defaultValidators), }, nil } - -// validateValidator validates a given Validator. -func validateValidator(v Validator) error { - switch vs := v.(type) { - case Validators: - if vs == nil || len(vs) == 0 { - return ErrInvalidTypeValidator - } - - for _, v := range vs { - if v == nil { - return ErrInvalidTypeValidator - } - } - case Validator, ValidatorFunc: - // catch nil Validator - default: - return ErrInvalidTypeValidator - } - - return nil -} diff --git a/validator_test.go b/validator_test.go index 14bc9b8..70701d3 100644 --- a/validator_test.go +++ b/validator_test.go @@ -23,60 +23,88 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/multierr" ) func testTypeValidatorValidate(t *testing.T) { - type Test struct { + tests := []struct { + description string m map[MessageType]Validator - defaultValidators Validators + defaultValidators []Validator msg Message - } - - tests := []struct { - description string - value Test - expectedErr error + expectedErr error }{ // Success case { description: "Found success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, - msg: Message{Type: SimpleEventMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, }, + msg: Message{Type: SimpleEventMessageType}, }, { description: "Unfound success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysInvalid}, - }, - defaultValidators: Validators{AlwaysValid}, - msg: Message{Type: CreateMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: []Validator{AlwaysValid}, + msg: Message{Type: CreateMessageType}, + }, + { + description: "Unfound success, nil list of default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, + }, + defaultValidators: []Validator{nil}, + msg: Message{Type: CreateMessageType}, + }, + { + description: "Unfound success, empty map of default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, + }, + defaultValidators: []Validator{}, + msg: Message{Type: CreateMessageType}, }, // Failure case { description: "Found error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysInvalid}, - }, - defaultValidators: Validators{AlwaysValid}, - msg: Message{Type: SimpleEventMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: []Validator{AlwaysValid}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Found error, nil Validator", + m: map[MessageType]Validator{ + SimpleEventMessageType: nil, + }, + msg: Message{Type: SimpleEventMessageType}, expectedErr: ErrInvalidMsgType, }, { description: "Unfound error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, - msg: Message{Type: CreateMessageType}, + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + msg: Message{Type: CreateMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Unfound error, nil default Validators", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysInvalid, }, + defaultValidators: nil, + msg: Message{Type: CreateMessageType}, + expectedErr: ErrInvalidMsgType, + }, + { + description: "Unfound error, empty map of Validators", + m: map[MessageType]Validator{}, + msg: Message{Type: CreateMessageType}, expectedErr: ErrInvalidMsgType, }, } @@ -85,10 +113,11 @@ func testTypeValidatorValidate(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) require := require.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) - require.NotNil(msgv) + msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) require.NoError(err) - err = msgv.Validate(tc.value.msg) + require.NotNil(msgv) + require.False(msgv.IsBad()) + err = msgv.Validate(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -99,103 +128,251 @@ func testTypeValidatorValidate(t *testing.T) { } } -func testNewTypeValidator(t *testing.T) { - type Test struct { +func testTypeValidatorFactory(t *testing.T) { + tests := []struct { + description string m map[MessageType]Validator - defaultValidators Validators + defaultValidators []Validator + expectedErr error + }{ + // Success case + { + description: "Default Validators success", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + defaultValidators: []Validator{AlwaysValid}, + expectedErr: nil, + }, + { + description: "Omit default Validators success", + m: map[MessageType]Validator{ + SimpleEventMessageType: AlwaysValid, + }, + expectedErr: nil, + }, + // Failure case + { + description: "Nil map of Validators error", + m: nil, + defaultValidators: []Validator{AlwaysValid}, + expectedErr: ErrInvalidValidator, + }, } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) + assert.NotNil(msgv) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) + assert.ErrorIs(msgv.Validate(Message{}), ErrInvalidTypeValidator) + assert.True(msgv.IsBad()) + return + } + + assert.NoError(err) + assert.False(msgv.IsBad()) + }) + } +} + +func testAlwaysValid(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) tests := []struct { description string - value Test - expectedErr error + msg Message + expectedErr []error }{ // Success case { - description: "Default Validators success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Not UTF8 success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "mac:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, { - description: "Empty map of Validators success", - value: Test{ - m: map[MessageType]Validator{}, - defaultValidators: Validators{AlwaysValid}, + description: "Filled message success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: nil, }, { - description: "Omit default Validators success", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{AlwaysValid}, - }, + description: "Empty message success", + msg: Message{}, + }, + { + description: "Bad message type success", + msg: Message{ + Type: lastMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", }, - expectedErr: nil, }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := AlwaysValid.Validate(tc.msg) + assert.NoError(err) + }) + } +} + +func testAlwaysInvalid(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + tests := []struct { + description string + msg Message + expectedErr []error + }{ // Failure case { - description: "Nil list of default Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, - }, - defaultValidators: Validators{nil}, + description: "Not UTF8 error", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + // Not UFT8 Destination string + Destination: "mac:\xed\xbf\xbf", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: ErrInvalidTypeValidator, }, { - description: "Empty list of Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{}, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Filled message error", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", }, - expectedErr: ErrInvalidTypeValidator, }, { - description: "Nil Validator error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: nil, - }, - defaultValidators: Validators{AlwaysValid}, - }, - expectedErr: ErrInvalidTypeValidator, + description: "Empty message error", + msg: Message{}, }, { - description: "Nil list of Validators error", - value: Test{ - m: map[MessageType]Validator{ - SimpleEventMessageType: Validators{nil}, - }, - defaultValidators: Validators{AlwaysValid}, + description: "Bad message type error", + msg: Message{ + Type: lastMessageType, + Source: "external.com", + Destination: "mac:FFEEAADD44443333", }, - expectedErr: ErrInvalidTypeValidator, }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := AlwaysInvalid.Validate(tc.msg) + assert.Error(err) + }) + } +} + +func TestValidators(t *testing.T) { + tests := []struct { + description string + vs Validators + msg Message + expectedErr []error + }{ + // Success case { - description: "Nil map of Validators error", - value: Test{ - m: nil, - defaultValidators: Validators{AlwaysValid}, - }, - expectedErr: ErrInvalidTypeValidator, + description: "Empty Validators success", + vs: Validators{}, + msg: Message{Type: SimpleEventMessageType}, + }, + // Failure case + { + description: "Mix Validators error", + vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, }, } for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - msgv, err := NewTypeValidator(tc.value.m, tc.value.defaultValidators...) - assert.NotNil(msgv) + err := tc.vs.Validate(tc.msg) if tc.expectedErr != nil { - assert.ErrorIs(err, tc.expectedErr) + assert.Equal(multierr.Errors(err), tc.expectedErr) + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } return } @@ -204,20 +381,6 @@ func testNewTypeValidator(t *testing.T) { } } -func testAlwaysValid(t *testing.T) { - assert := assert.New(t) - msg := Message{} - err := AlwaysValid(msg) - assert.NoError(err) -} - -func testAlwaysInvalid(t *testing.T) { - assert := assert.New(t) - msg := Message{} - err := AlwaysInvalid(msg) - assert.ErrorIs(err, ErrInvalidMsgType) -} - func TestHelperValidators(t *testing.T) { tests := []struct { description string @@ -237,8 +400,8 @@ func TestTypeValidator(t *testing.T) { description string test func(*testing.T) }{ - {"TypeValidator validate", testTypeValidatorValidate}, - {"TypeValidator factory", testNewTypeValidator}, + {"Validate", testTypeValidatorValidate}, + {"Factory", testTypeValidatorFactory}, } for _, tc := range tests { @@ -249,7 +412,7 @@ func TestTypeValidator(t *testing.T) { func ExampleNewTypeValidator() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, // Validates unfound msg types AlwaysInvalid) fmt.Printf("%v %T", err == nil, msgv) @@ -259,7 +422,7 @@ func ExampleNewTypeValidator() { func ExampleTypeValidator_Validate() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: Validators{AlwaysValid}}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, // Validates unfound msg types AlwaysInvalid) if err != nil { From bc26b1e7886316a62da1aefbfe60723630ece599 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 05:54:33 -0400 Subject: [PATCH 62/68] Update based on PR review * [misunderstanding] Remove unexported field `isbad` from `TypeValidator` and its references * [misunderstanding] Remove `IsBad` func from `TypeValidator` * Use `assert.Zero/NotZero` funcs to test `TypeValidator`'s state --- validator.go | 12 +----------- validator_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/validator.go b/validator.go index 79c6a13..f3ed27e 100644 --- a/validator.go +++ b/validator.go @@ -71,16 +71,11 @@ func (vf ValidatorFunc) Validate(m Message) error { type TypeValidator struct { m map[MessageType]Validator defaultValidators Validator - isbad bool } // Validate validates messages based on message type or using the defaultValidators // if message type is unfound. func (m TypeValidator) Validate(msg Message) error { - if m.isbad { - return ErrInvalidTypeValidator - } - vs := m.m[msg.MessageType()] if vs == nil { return m.defaultValidators.Validate(msg) @@ -89,15 +84,10 @@ func (m TypeValidator) Validate(msg Message) error { return vs.Validate(msg) } -// IsBad returns a boolean indicating whether the TypeValidator receiver is valid -func (m TypeValidator) IsBad() bool { - return m.isbad -} - // NewTypeValidator is a TypeValidator factory. func NewTypeValidator(m map[MessageType]Validator, defaultValidators ...Validator) (TypeValidator, error) { if m == nil { - return TypeValidator{isbad: true}, ErrInvalidValidator + return TypeValidator{}, ErrInvalidValidator } if defaultValidators == nil { diff --git a/validator_test.go b/validator_test.go index 70701d3..9c2bdcb 100644 --- a/validator_test.go +++ b/validator_test.go @@ -116,7 +116,7 @@ func testTypeValidatorValidate(t *testing.T) { msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) require.NoError(err) require.NotNil(msgv) - require.False(msgv.IsBad()) + assert.NotZero(msgv) err = msgv.Validate(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) @@ -164,16 +164,16 @@ func testTypeValidatorFactory(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) - assert.NotNil(msgv) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) - assert.ErrorIs(msgv.Validate(Message{}), ErrInvalidTypeValidator) - assert.True(msgv.IsBad()) + assert.NotNil(msgv) + assert.NotZero(msgv) return } assert.NoError(err) - assert.False(msgv.IsBad()) + // Zero asserts that msgv is the zero value for its type and not nil. + assert.Zero(msgv) }) } } From db62e6f361b4e7e4b2f45f632fed4c08c38ce188 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 05:55:49 -0400 Subject: [PATCH 63/68] Move exported fields to the top --- validator_test.go | 186 +++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/validator_test.go b/validator_test.go index 9c2bdcb..e618b25 100644 --- a/validator_test.go +++ b/validator_test.go @@ -26,6 +26,99 @@ import ( "go.uber.org/multierr" ) +func TestValidators(t *testing.T) { + tests := []struct { + description string + vs Validators + msg Message + expectedErr []error + }{ + // Success case + { + description: "Empty Validators success", + vs: Validators{}, + msg: Message{Type: SimpleEventMessageType}, + }, + // Failure case + { + description: "Mix Validators error", + vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, + msg: Message{Type: SimpleEventMessageType}, + expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := tc.vs.Validate(tc.msg) + if tc.expectedErr != nil { + assert.Equal(multierr.Errors(err), tc.expectedErr) + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func TestHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"AlwaysInvalid", testAlwaysInvalid}, + {"AlwaysValid", testAlwaysValid}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestTypeValidator(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"Validate", testTypeValidatorValidate}, + {"Factory", testTypeValidatorFactory}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func ExampleNewTypeValidator() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + // Validates unfound msg types + AlwaysInvalid) + fmt.Printf("%v %T", err == nil, msgv) + // Output: true wrp.TypeValidator +} + +func ExampleTypeValidator_Validate() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success + unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErr == nil, unfoundErr == nil) + // Output: true false +} + func testTypeValidatorValidate(t *testing.T) { tests := []struct { description string @@ -341,96 +434,3 @@ func testAlwaysInvalid(t *testing.T) { }) } } - -func TestValidators(t *testing.T) { - tests := []struct { - description string - vs Validators - msg Message - expectedErr []error - }{ - // Success case - { - description: "Empty Validators success", - vs: Validators{}, - msg: Message{Type: SimpleEventMessageType}, - }, - // Failure case - { - description: "Mix Validators error", - vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, - msg: Message{Type: SimpleEventMessageType}, - expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, - }, - } - - for _, tc := range tests { - t.Run(tc.description, func(t *testing.T) { - assert := assert.New(t) - err := tc.vs.Validate(tc.msg) - if tc.expectedErr != nil { - assert.Equal(multierr.Errors(err), tc.expectedErr) - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } - return - } - - assert.NoError(err) - }) - } -} - -func TestHelperValidators(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"AlwaysInvalid", testAlwaysInvalid}, - {"AlwaysValid", testAlwaysValid}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func TestTypeValidator(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"Validate", testTypeValidatorValidate}, - {"Factory", testTypeValidatorFactory}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func ExampleNewTypeValidator() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, - // Validates unfound msg types - AlwaysInvalid) - fmt.Printf("%v %T", err == nil, msgv) - // Output: true wrp.TypeValidator -} - -func ExampleTypeValidator_Validate() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, - // Validates unfound msg types - AlwaysInvalid) - if err != nil { - return - } - - foundErr := msgv.Validate(Message{Type: SimpleEventMessageType}) // Found success - unfoundErr := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErr == nil, unfoundErr == nil) - // Output: true false -} From 4252c2d155b5bf9182732cd06c6a9b178cbd329a Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:34:20 -0400 Subject: [PATCH 64/68] update tests to include correct mac length --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index e618b25..8c9d440 100644 --- a/validator_test.go +++ b/validator_test.go @@ -312,7 +312,7 @@ func testAlwaysValid(t *testing.T) { msg: Message{ Type: SimpleRequestResponseMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", Accept: "Accept", @@ -339,7 +339,7 @@ func testAlwaysValid(t *testing.T) { msg: Message{ Type: lastMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", }, }, } @@ -394,7 +394,7 @@ func testAlwaysInvalid(t *testing.T) { msg: Message{ Type: SimpleRequestResponseMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", Accept: "Accept", @@ -421,7 +421,7 @@ func testAlwaysInvalid(t *testing.T) { msg: Message{ Type: lastMessageType, Source: "external.com", - Destination: "mac:FFEEAADD44443333", + Destination: "MAC:11:22:33:44:55:66", }, }, } From 77033c6198365616462d00ddf13ae51206c73409 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:45:19 -0400 Subject: [PATCH 65/68] `testAlwaysValid` & `testAlwaysInvalid` ensure a non-existing message type is used --- validator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator_test.go b/validator_test.go index 8c9d440..de5c421 100644 --- a/validator_test.go +++ b/validator_test.go @@ -337,7 +337,7 @@ func testAlwaysValid(t *testing.T) { { description: "Bad message type success", msg: Message{ - Type: lastMessageType, + Type: lastMessageType + 1, Source: "external.com", Destination: "MAC:11:22:33:44:55:66", }, @@ -419,7 +419,7 @@ func testAlwaysInvalid(t *testing.T) { { description: "Bad message type error", msg: Message{ - Type: lastMessageType, + Type: lastMessageType + 1, Source: "external.com", Destination: "MAC:11:22:33:44:55:66", }, From f2dc8a4aab8a0abbefd1ff8cac822b9d3ae98b76 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 06:48:04 -0400 Subject: [PATCH 66/68] `testTypeValidatorFactory` swap `assert.Zero/NotZero` --- validator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/validator_test.go b/validator_test.go index de5c421..2ff1457 100644 --- a/validator_test.go +++ b/validator_test.go @@ -259,14 +259,14 @@ func testTypeValidatorFactory(t *testing.T) { msgv, err := NewTypeValidator(tc.m, tc.defaultValidators...) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) - assert.NotNil(msgv) - assert.NotZero(msgv) + // Zero asserts that msgv is the zero value for its type and not nil. + assert.Zero(msgv) return } assert.NoError(err) - // Zero asserts that msgv is the zero value for its type and not nil. - assert.Zero(msgv) + assert.NotNil(msgv) + assert.NotZero(msgv) }) } } From 52949476ed59b165662c8b928ee96f32eb47bb53 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:24:12 -0400 Subject: [PATCH 67/68] Add missing scheme to source fields & add missing `assert.ErrorIs` to `testAlwaysInvalid` --- validator_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/validator_test.go b/validator_test.go index 2ff1457..8f9278f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -287,7 +287,7 @@ func testAlwaysValid(t *testing.T) { description: "Not UTF8 success", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "mac:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -307,11 +307,12 @@ func testAlwaysValid(t *testing.T) { SessionID: "sessionID123", }, }, + // Failure case { description: "Filled message success", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -338,7 +339,7 @@ func testAlwaysValid(t *testing.T) { description: "Bad message type success", msg: Message{ Type: lastMessageType + 1, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", }, }, @@ -369,7 +370,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Not UTF8 error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", // Not UFT8 Destination string Destination: "mac:\xed\xbf\xbf", TransactionUUID: "DEADBEEF", @@ -393,7 +394,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Filled message error", msg: Message{ Type: SimpleRequestResponseMessageType, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", TransactionUUID: "DEADBEEF", ContentType: "ContentType", @@ -420,7 +421,7 @@ func testAlwaysInvalid(t *testing.T) { description: "Bad message type error", msg: Message{ Type: lastMessageType + 1, - Source: "external.com", + Source: "dns:external.com", Destination: "MAC:11:22:33:44:55:66", }, }, @@ -430,7 +431,7 @@ func testAlwaysInvalid(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) err := AlwaysInvalid.Validate(tc.msg) - assert.Error(err) + assert.ErrorIs(err, ErrInvalidMsgType) }) } } From 2428b8ac13e2f2203851cb125cdf9d5dfe012421 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:29:22 -0400 Subject: [PATCH 68/68] Move exported tests to the top --- spec_validator_test.go | 346 ++++++++++++++++++++--------------------- 1 file changed, 173 insertions(+), 173 deletions(-) diff --git a/spec_validator_test.go b/spec_validator_test.go index 6b8874b..5f11970 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -24,6 +24,179 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSpecHelperValidators(t *testing.T) { + tests := []struct { + description string + test func(*testing.T) + }{ + {"UTF8Validator", testUTF8Validator}, + {"MessageTypeValidator", testMessageTypeValidator}, + {"SourceValidator", testSourceValidator}, + {"DestinationValidator", testDestinationValidator}, + {"validateLocator", testValidateLocator}, + } + + for _, tc := range tests { + t.Run(tc.description, tc.test) + } +} + +func TestSpecValidators(t *testing.T) { + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + + tests := []struct { + description string + msg Message + expectedErr []error + }{ + // Success case + { + description: "Valid spec success", + msg: Message{ + Type: SimpleRequestResponseMessageType, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", + URL: "someURL.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, + }, + // Failure case + { + description: "Invaild spec error", + msg: Message{ + Type: Invalid0MessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, + }, + { + description: "Invaild spec error, empty message", + msg: Message{}, + expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, + }, + { + description: "Invaild spec error, nonexistent MessageType", + msg: Message{ + Type: lastMessageType + 1, + Source: "dns:external.com", + Destination: "MAC:11:22:33:44:55:66", + }, + expectedErr: []error{ErrorInvalidMessageType}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + assert := assert.New(t) + err := SpecValidators().Validate(tc.msg) + if tc.expectedErr != nil { + for _, e := range tc.expectedErr { + assert.ErrorIs(err, e) + } + return + } + + assert.NoError(err) + }) + } +} + +func ExampleTypeValidator_Validate_specValidators() { + msgv, err := NewTypeValidator( + // Validates found msg types + map[MessageType]Validator{ + // Validates opinionated portions of the spec + SimpleEventMessageType: SpecValidators(), + // Only validates Source and nothing else + SimpleRequestResponseMessageType: SourceValidator, + }, + // Validates unfound msg types + AlwaysInvalid) + if err != nil { + return + } + + var ( + expectedStatus int64 = 3471 + expectedRequestDeliveryResponse int64 = 34 + expectedIncludeSpans bool = true + ) + foundErrFailure := msgv.Validate(Message{ + Type: SimpleEventMessageType, + // Missing scheme + Source: "external.com", + // Invalid Mac + Destination: "MAC:+++BB-44-55", + TransactionUUID: "DEADBEEF", + ContentType: "ContentType", + Accept: "Accept", + Status: &expectedStatus, + RequestDeliveryResponse: &expectedRequestDeliveryResponse, + Headers: []string{"Header1", "Header2"}, + Metadata: map[string]string{"name": "value"}, + Spans: [][]string{{"1", "2"}, {"3"}}, + IncludeSpans: &expectedIncludeSpans, + Path: "/some/where/over/the/rainbow", + // Not UFT8 Payload + Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, + ServiceName: "ServiceName", + // Not UFT8 URL string + URL: "someURL\xed\xbf\xbf.com", + PartnerIDs: []string{"foo"}, + SessionID: "sessionID123", + }) // Found error + foundErrSuccess1 := msgv.Validate(Message{ + Type: SimpleEventMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "MAC:11:22:33:44:55:61", + }) // Found success + foundErrSuccess2 := msgv.Validate(Message{ + Type: SimpleRequestResponseMessageType, + Source: "MAC:11:22:33:44:55:66", + Destination: "invalid:a-BB-44-55", + }) // Found success + unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error + fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) + // Output: false true true false +} + func testUTF8Validator(t *testing.T) { var ( expectedStatus int64 = 3471 @@ -442,176 +615,3 @@ func testValidateLocator(t *testing.T) { }) } } - -func TestSpecHelperValidators(t *testing.T) { - tests := []struct { - description string - test func(*testing.T) - }{ - {"UTF8Validator", testUTF8Validator}, - {"MessageTypeValidator", testMessageTypeValidator}, - {"SourceValidator", testSourceValidator}, - {"DestinationValidator", testDestinationValidator}, - {"validateLocator", testValidateLocator}, - } - - for _, tc := range tests { - t.Run(tc.description, tc.test) - } -} - -func TestSpecValidators(t *testing.T) { - var ( - expectedStatus int64 = 3471 - expectedRequestDeliveryResponse int64 = 34 - expectedIncludeSpans bool = true - ) - - tests := []struct { - description string - msg Message - expectedErr []error - }{ - // Success case - { - description: "Valid spec success", - msg: Message{ - Type: SimpleRequestResponseMessageType, - Source: "dns:external.com", - Destination: "MAC:11:22:33:44:55:66", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, - ServiceName: "ServiceName", - URL: "someURL.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }, - }, - // Failure case - { - description: "Invaild spec error", - msg: Message{ - Type: Invalid0MessageType, - // Missing scheme - Source: "external.com", - // Invalid Mac - Destination: "MAC:+++BB-44-55", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - // Not UFT8 Payload - Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, - ServiceName: "ServiceName", - // Not UFT8 URL string - URL: "someURL\xed\xbf\xbf.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination, ErrorInvalidMessageEncoding}, - }, - { - description: "Invaild spec error, empty message", - msg: Message{}, - expectedErr: []error{ErrorInvalidMessageType, ErrorInvalidSource, ErrorInvalidDestination}, - }, - { - description: "Invaild spec error, nonexistent MessageType", - msg: Message{ - Type: lastMessageType + 1, - Source: "dns:external.com", - Destination: "MAC:11:22:33:44:55:66", - }, - expectedErr: []error{ErrorInvalidMessageType}, - }, - } - - for _, tc := range tests { - t.Run(tc.description, func(t *testing.T) { - assert := assert.New(t) - err := SpecValidators().Validate(tc.msg) - if tc.expectedErr != nil { - for _, e := range tc.expectedErr { - assert.ErrorIs(err, e) - } - return - } - - assert.NoError(err) - }) - } -} - -func ExampleTypeValidator_Validate_specValidators() { - msgv, err := NewTypeValidator( - // Validates found msg types - map[MessageType]Validator{ - // Validates opinionated portions of the spec - SimpleEventMessageType: SpecValidators(), - // Only validates Source and nothing else - SimpleRequestResponseMessageType: SourceValidator, - }, - // Validates unfound msg types - AlwaysInvalid) - if err != nil { - return - } - - var ( - expectedStatus int64 = 3471 - expectedRequestDeliveryResponse int64 = 34 - expectedIncludeSpans bool = true - ) - foundErrFailure := msgv.Validate(Message{ - Type: SimpleEventMessageType, - // Missing scheme - Source: "external.com", - // Invalid Mac - Destination: "MAC:+++BB-44-55", - TransactionUUID: "DEADBEEF", - ContentType: "ContentType", - Accept: "Accept", - Status: &expectedStatus, - RequestDeliveryResponse: &expectedRequestDeliveryResponse, - Headers: []string{"Header1", "Header2"}, - Metadata: map[string]string{"name": "value"}, - Spans: [][]string{{"1", "2"}, {"3"}}, - IncludeSpans: &expectedIncludeSpans, - Path: "/some/where/over/the/rainbow", - // Not UFT8 Payload - Payload: []byte{1, 2, 3, 4, 0xff /* \xed\xbf\xbf is invalid */, 0xce}, - ServiceName: "ServiceName", - // Not UFT8 URL string - URL: "someURL\xed\xbf\xbf.com", - PartnerIDs: []string{"foo"}, - SessionID: "sessionID123", - }) // Found error - foundErrSuccess1 := msgv.Validate(Message{ - Type: SimpleEventMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "MAC:11:22:33:44:55:61", - }) // Found success - foundErrSuccess2 := msgv.Validate(Message{ - Type: SimpleRequestResponseMessageType, - Source: "MAC:11:22:33:44:55:66", - Destination: "invalid:a-BB-44-55", - }) // Found success - unfoundErrFailure := msgv.Validate(Message{Type: CreateMessageType}) // Unfound error - fmt.Println(foundErrFailure == nil, foundErrSuccess1 == nil, foundErrSuccess2 == nil, unfoundErrFailure == nil) - // Output: false true true false -}