From eefa39ab712c0edb5710c76119f598d7f2f041d9 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 16:52:33 -0400 Subject: [PATCH 01/15] 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 b75115c272e4d06e5dd98640e2e878cf240492fb Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:28 -0400 Subject: [PATCH 02/15] 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 95d2a262b01ade829c9a2fba20966145fbcb741b Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:33:57 -0400 Subject: [PATCH 03/15] 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 58de8fadb8ec5d9380c1f8f32e00332f2d3d5e5f Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 17:45:24 -0400 Subject: [PATCH 04/15] 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 8c34e3f1628bbf9850aaa824429bd9f68921b5cb Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 3 Jun 2022 18:34:58 -0400 Subject: [PATCH 05/15] 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 9d8b5615fdce5a993e406e925735185fac6e3a69 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Mon, 6 Jun 2022 13:14:40 -0400 Subject: [PATCH 06/15] 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 6391d76a47537a36ea1273d60d3ce0cb50108b18 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Tue, 7 Jun 2022 16:39:51 -0400 Subject: [PATCH 07/15] 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 df209e4a35c1abde0bcc45aa81cc757951bcab87 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:19:34 -0400 Subject: [PATCH 08/15] 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 979cab54ac4eddcbebf60689990e16f5073eff60 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 08:29:22 -0400 Subject: [PATCH 09/15] 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 00f943a00a7be9e1c747ba6852f5726319cf792f Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 17:06:03 -0400 Subject: [PATCH 10/15] unexport `LocatorPattern` & update `validateLocator` func error --- spec_validator.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index 7cb8de9..60c3381 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -42,12 +42,6 @@ var ( 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. Only validates the opinionated portions of the spec. func SpecValidators() Validators { @@ -100,11 +94,17 @@ var DestinationValidator ValidatorFunc = func(m Message) error { return nil } +// 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[^/]+)?`, +) + // 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 { - match := LocatorPattern.FindStringSubmatch(l) + match := locatorPattern.FindStringSubmatch(l) if match == nil { return fmt.Errorf("spec scheme not found") } @@ -139,7 +139,7 @@ func validateLocator(l string) error { } case serialPrefix, eventPrefix, dnsPrefix: if len(idPart) == 0 { - return fmt.Errorf("invalid %v empty authority (ID)", serialPrefix) + return fmt.Errorf("invalid empty authority (ID)") } } From 9bfdca20ed98b3d4dc0ab1f7150258d766f35ead Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 19:00:01 -0400 Subject: [PATCH 11/15] fix doc typo --- validator_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validator_test.go b/validator_test.go index 70c3aab..edc78d8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -307,7 +307,6 @@ func testAlwaysValid(t *testing.T) { SessionID: "sessionID123", }, }, - // Failure case { description: "Filled message success", msg: Message{ From 7c049c3d392c3754e102744702336a60eacb531d Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 19:25:27 -0400 Subject: [PATCH 12/15] Update `TestSpecValidators`'s bad `playload` value --- spec_validator_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec_validator_test.go b/spec_validator_test.go index 5f11970..235b21e 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -96,9 +96,8 @@ func TestSpecValidators(t *testing.T) { 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", + Payload: []byte{1, 2, 3, 4, 0xff, 0xce}, + ServiceName: "ServiceName", // Not UFT8 URL string URL: "someURL\xed\xbf\xbf.com", PartnerIDs: []string{"foo"}, From c776b253f5fd47d0f9bdd62d5e705daf2c487844 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 19:50:49 -0400 Subject: [PATCH 13/15] update docs --- spec_validator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/spec_validator.go b/spec_validator.go index 60c3381..f2c85c5 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -44,6 +44,7 @@ var ( // 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. +// SpecValidators validates the following fields: UTF8 (all string fields), MessageType, Source, Destination func SpecValidators() Validators { return Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} } From 02b9969a2efb249156cc6bf6299167cdb46ec6d4 Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Wed, 8 Jun 2022 20:26:42 -0400 Subject: [PATCH 14/15] Update `MessageTypeValidator` func to use MessageType directly --- spec_validator.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index f2c85c5..fb710be 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -60,12 +60,11 @@ var UTF8Validator ValidatorFunc = func(m Message) error { // 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 { + if m.Type < Invalid0MessageType || m.Type > lastMessageType { return ErrorInvalidMessageType } - switch t { + switch m.Type { case Invalid0MessageType, Invalid1MessageType, lastMessageType: return ErrorInvalidMessageType } From edeb68ed093e009cd9a1e0e3b934241df0db413e Mon Sep 17 00:00:00 2001 From: Owen Cabalceta Date: Fri, 10 Jun 2022 16:03:44 -0400 Subject: [PATCH 15/15] Update based on PR Feedback * add unexported errors for `validateLocator` func to allow better/easier testing * improve error message for `validateLocator` func when values to match locator pattern * move `locatorPattern` var to the top of the file * convert vars `DestinationValidator SourceValidator MessageTypeValidator UTF8Validator AlwaysInvalid AlwaysValid` to funcs that return their validator & update their tests and code position (if needed) * Improve error message for `DestinationValidator SourceValidator` funcs --- spec_validator.go | 101 +++++++++++++++++++++++------------------ spec_validator_test.go | 72 ++++++++++++++--------------- validator.go | 18 +++++--- validator_test.go | 40 ++++++++-------- 4 files changed, 124 insertions(+), 107 deletions(-) diff --git a/spec_validator.go b/spec_validator.go index fb710be..c9bae3f 100644 --- a/spec_validator.go +++ b/spec_validator.go @@ -40,76 +40,93 @@ var ( ErrorInvalidMessageType = errors.New("invalid message type") ErrorInvalidSource = errors.New("invalid Source name") ErrorInvalidDestination = errors.New("invalid Destination name") + errInvalidUUID = errors.New("invalid UUID") + errEmptyAuthority = errors.New("invalid empty authority (ID)") + errInvalidMacLength = errors.New("invalid mac length") + errInvalidCharacter = errors.New("invalid character") + errInvalidLocatorPattern = errors.New("value given doesn't match expected locator pattern") ) -// SpecValidators is a WRP validator that ensures messages are valid based on +// 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 returns a WRP validator that ensures messages are valid based on // each spec validator in the list. Only validates the opinionated portions of the spec. // SpecValidators validates the following fields: UTF8 (all string fields), MessageType, Source, Destination func SpecValidators() Validators { - return Validators{UTF8Validator, MessageTypeValidator, SourceValidator, DestinationValidator} + 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 { - if err := UTF8(m); err != nil { - return fmt.Errorf("%w: %v", ErrorInvalidMessageEncoding, err) - } +// UTF8Validator returns a WRP validator that takes messages and validates that it contains UTF-8 strings. +func UTF8Validator() ValidatorFunc { + return func(m Message) error { + if err := UTF8(m); err != nil { + return fmt.Errorf("%w: %v", ErrorInvalidMessageEncoding, err) + } - return nil + return nil + } } -// MessageTypeValidator is a WRP validator that takes messages and validates their Type. -var MessageTypeValidator ValidatorFunc = func(m Message) error { - if m.Type < Invalid0MessageType || m.Type > lastMessageType { - return ErrorInvalidMessageType - } +// MessageTypeValidator returns a WRP validator that takes messages and validates their Type. +func MessageTypeValidator() ValidatorFunc { + return func(m Message) error { + if m.Type < Invalid0MessageType || m.Type > lastMessageType { + return ErrorInvalidMessageType + } - switch m.Type { - case Invalid0MessageType, Invalid1MessageType, lastMessageType: - return ErrorInvalidMessageType - } + switch m.Type { + case Invalid0MessageType, Invalid1MessageType, lastMessageType: + return ErrorInvalidMessageType + } - return nil + return nil + } } -// SourceValidator is a WRP validator that takes messages and validates their Source. +// SourceValidator returns 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 { - if err := validateLocator(m.Source); err != nil { - return fmt.Errorf("%w: %v", ErrorInvalidSource, err) - } +func SourceValidator() ValidatorFunc { + return func(m Message) error { + if err := validateLocator(m.Source); err != nil { + return fmt.Errorf("%w '%s': %v", ErrorInvalidSource, m.Source, err) + } - return nil + return nil + } } -// DestinationValidator is a WRP validator that takes messages and validates their Destination. +// DestinationValidator returns 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 { - if err := validateLocator(m.Destination); err != nil { - return fmt.Errorf("%w: %v", ErrorInvalidDestination, err) - } +func DestinationValidator() ValidatorFunc { + return func(m Message) error { + if err := validateLocator(m.Destination); err != nil { + return fmt.Errorf("%w '%s': %v", ErrorInvalidDestination, m.Destination, err) + } - return nil + return nil + } } -// 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[^/]+)?`, -) - // 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 { match := locatorPattern.FindStringSubmatch(l) if match == nil { - return fmt.Errorf("spec scheme not found") + return errInvalidLocatorPattern } idPart := match[2] + if len(idPart) == 0 { + return errEmptyAuthority + } + switch strings.ToLower(match[1]) { case macPrefix: var invalidCharacter rune = -1 @@ -129,17 +146,13 @@ func validateLocator(l string) error { ) if invalidCharacter != -1 { - return fmt.Errorf("invalid character %v", strconv.QuoteRune(invalidCharacter)) + return fmt.Errorf("%w: %v", errInvalidCharacter, strconv.QuoteRune(invalidCharacter)) } else if len(idPart) != macLength { - return errors.New("invalid mac length") + return errInvalidMacLength } case uuidPrefix: if _, err := uuid.Parse(idPart); err != nil { - return err - } - case serialPrefix, eventPrefix, dnsPrefix: - if len(idPart) == 0 { - return fmt.Errorf("invalid empty authority (ID)") + return fmt.Errorf("%w: %v", errInvalidUUID, err) } } diff --git a/spec_validator_test.go b/spec_validator_test.go index 235b21e..2d64cbc 100644 --- a/spec_validator_test.go +++ b/spec_validator_test.go @@ -144,10 +144,10 @@ func ExampleTypeValidator_Validate_specValidators() { // Validates opinionated portions of the spec SimpleEventMessageType: SpecValidators(), // Only validates Source and nothing else - SimpleRequestResponseMessageType: SourceValidator, + SimpleRequestResponseMessageType: SourceValidator(), }, // Validates unfound msg types - AlwaysInvalid) + AlwaysInvalid()) if err != nil { return } @@ -262,7 +262,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.msg) + err := UTF8Validator()(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -356,7 +356,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.msg) + err := MessageTypeValidator()(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -394,7 +394,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.msg) + err := SourceValidator()(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -432,7 +432,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.msg) + err := DestinationValidator()(tc.msg) if tc.expectedErr != nil { assert.ErrorIs(err, tc.expectedErr) return @@ -447,157 +447,157 @@ func testValidateLocator(t *testing.T) { tests := []struct { description string value string - shouldErr bool + expectedErr error }{ // mac success case { description: "Mac ID success, ':' delimiter", value: "MAC:11:22:33:44:55:66", - shouldErr: false, + expectedErr: nil, }, { description: "Mac ID success, no delimiter", value: "MAC:11aaBB445566", - shouldErr: false, + expectedErr: nil, }, { description: "Mac ID success, '-' delimiter", value: "mac:11-aa-BB-44-55-66", - shouldErr: false, + expectedErr: nil, }, { description: "Mac ID success, ',' delimiter", value: "mac:11,aa,BB,44,55,66", - shouldErr: false, + expectedErr: nil, }, { description: "Mac service success", value: "mac:11,aa,BB,44,55,66/parodus/tag/test0", - shouldErr: false, + expectedErr: nil, }, // Mac failure case { description: "Mac ID error, invalid mac ID character", value: "MAC:invalid45566", - shouldErr: true, + expectedErr: errInvalidCharacter, }, { description: "Mac ID error, invalid mac ID length", value: "mac:11-aa-BB-44-55", - shouldErr: true, + expectedErr: errInvalidMacLength, }, { description: "Mac ID error, no ID", value: "mac:", - shouldErr: true, + expectedErr: errEmptyAuthority, }, // Serial success case { description: "Serial ID success", value: "serial:anything Goes!", - shouldErr: false, + expectedErr: nil, }, // Serial failure case { description: "Invalid serial ID error, no ID", value: "serial:", - shouldErr: true, + expectedErr: errEmptyAuthority, }, // UUID success case // The variant specified in RFC4122 { description: "UUID RFC4122 variant ID success", value: "uuid:f47ac10b-58cc-0372-8567-0e02b2c3d479", - shouldErr: false, + expectedErr: nil, }, { description: "UUID RFC4122 variant ID success, with Microsoft encoding", value: "uuid:{f47ac10b-58cc-0372-8567-0e02b2c3d479}", - shouldErr: false, + expectedErr: nil, }, // 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, + expectedErr: nil, }, { description: "UUID Reserved variant ID success, with URN upper case", value: "UUID:URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - shouldErr: false, + expectedErr: nil, }, { description: "UUID Reserved variant ID success, without URN", value: "UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", - shouldErr: false, + expectedErr: nil, }, // Reserved, Microsoft Corporation backward compatibility. { description: "UUID Microsoft variant ID success", value: "uuid:f47ac10b-58cc-4372-c567-0e02b2c3d479", - shouldErr: false, + expectedErr: nil, }, // Reserved for future definition. { description: "UUID Future variant ID success", value: "uuid:f47ac10b-58cc-4372-e567-0e02b2c3d479", - shouldErr: false, + expectedErr: nil, }, // UUID failure case { description: "Invalid UUID ID error", value: "uuid:invalid45566", - shouldErr: true, + expectedErr: errInvalidUUID, }, { description: "Invalid UUID ID error, with URN", value: "uuid:URN:UUID:invalid45566", - shouldErr: true, + expectedErr: errInvalidUUID, }, { description: "Invalid UUID ID error, with Microsoft encoding", value: "uuid:{invalid45566}", - shouldErr: true, + expectedErr: errInvalidUUID, }, { description: "Invalid UUID ID error, no ID", value: "uuid:", - shouldErr: true, + expectedErr: errEmptyAuthority, }, // Event success case { description: "Event ID success", value: "event:anything Goes!", - shouldErr: false, + expectedErr: nil, }, // Event failure case { description: "Invalid event ID error, no ID", value: "event:", - shouldErr: true, + expectedErr: errEmptyAuthority, }, // DNS success case { description: "DNS ID success", value: "dns:anything Goes!", - shouldErr: false, + expectedErr: nil, }, // DNS failure case { description: "Invalid DNS ID error, no ID", value: "dns:", - shouldErr: true, + expectedErr: errEmptyAuthority, }, // Scheme failure case { description: "Invalid scheme error", value: "invalid:a-BB-44-55", - shouldErr: true, + expectedErr: errInvalidLocatorPattern, }, { description: "Invalid scheme error, empty string", value: "", - shouldErr: true, + expectedErr: errInvalidLocatorPattern, }, } @@ -605,8 +605,8 @@ func testValidateLocator(t *testing.T) { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) err := validateLocator(tc.value) - if tc.shouldErr { - assert.Error(err) + if tc.expectedErr != nil { + assert.ErrorIs(err, tc.expectedErr) return } diff --git a/validator.go b/validator.go index 1b3c07e..61d6ac1 100644 --- a/validator.go +++ b/validator.go @@ -29,12 +29,6 @@ var ( ErrInvalidMsgType = errors.New("invalid WRP message type") ) -// AlwaysInvalid doesn't validate anything about the message and always returns an error. -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 { Validate(m Message) error @@ -91,7 +85,7 @@ func NewTypeValidator(m map[MessageType]Validator, defaultValidator Validator) ( } if defaultValidator == nil { - defaultValidator = AlwaysInvalid + defaultValidator = AlwaysInvalid() } return TypeValidator{ @@ -99,3 +93,13 @@ func NewTypeValidator(m map[MessageType]Validator, defaultValidator Validator) ( defaultValidator: defaultValidator, }, nil } + +// AlwaysInvalid returns a WRP validator that doesn't validate anything about the message and always returns an error. +func AlwaysInvalid() ValidatorFunc { + return func(_ Message) error { return ErrInvalidMsgType } +} + +// AlwaysValid returns a WRP validator that doesn't validate anything about the message and always returns nil. +func AlwaysValid() ValidatorFunc { + return func(_ Message) error { return nil } +} diff --git a/validator_test.go b/validator_test.go index edc78d8..1bafb36 100644 --- a/validator_test.go +++ b/validator_test.go @@ -42,7 +42,7 @@ func TestValidators(t *testing.T) { // Failure case { description: "Mix Validators error", - vs: Validators{AlwaysValid, nil, AlwaysInvalid, Validators{AlwaysValid, nil, AlwaysInvalid}}, + vs: Validators{AlwaysValid(), nil, AlwaysInvalid(), Validators{AlwaysValid(), nil, AlwaysInvalid()}}, msg: Message{Type: SimpleEventMessageType}, expectedErr: []error{ErrInvalidMsgType, ErrInvalidMsgType}, }, @@ -96,9 +96,9 @@ func TestTypeValidator(t *testing.T) { func ExampleNewTypeValidator() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid()}, // Validates unfound msg types - AlwaysInvalid) + AlwaysInvalid()) fmt.Printf("%v %T", err == nil, msgv) // Output: true wrp.TypeValidator } @@ -106,9 +106,9 @@ func ExampleNewTypeValidator() { func ExampleTypeValidator_Validate() { msgv, err := NewTypeValidator( // Validates found msg types - map[MessageType]Validator{SimpleEventMessageType: AlwaysValid}, + map[MessageType]Validator{SimpleEventMessageType: AlwaysValid()}, // Validates unfound msg types - AlwaysInvalid) + AlwaysInvalid()) if err != nil { return } @@ -131,22 +131,22 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found success", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, + SimpleEventMessageType: AlwaysValid(), }, msg: Message{Type: SimpleEventMessageType}, }, { description: "Unfound success", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysInvalid, + SimpleEventMessageType: AlwaysInvalid(), }, - defaultValidator: AlwaysValid, + defaultValidator: AlwaysValid(), msg: Message{Type: CreateMessageType}, }, { description: "Unfound success, nil list of default Validators", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysInvalid, + SimpleEventMessageType: AlwaysInvalid(), }, defaultValidator: Validators{nil}, msg: Message{Type: CreateMessageType}, @@ -154,7 +154,7 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound success, empty map of default Validators", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysInvalid, + SimpleEventMessageType: AlwaysInvalid(), }, defaultValidator: Validators{}, msg: Message{Type: CreateMessageType}, @@ -163,9 +163,9 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Found error", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysInvalid, + SimpleEventMessageType: AlwaysInvalid(), }, - defaultValidator: AlwaysValid, + defaultValidator: AlwaysValid(), msg: Message{Type: SimpleEventMessageType}, expectedErr: ErrInvalidMsgType, }, @@ -180,7 +180,7 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound error", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, + SimpleEventMessageType: AlwaysValid(), }, msg: Message{Type: CreateMessageType}, expectedErr: ErrInvalidMsgType, @@ -188,7 +188,7 @@ func testTypeValidatorValidate(t *testing.T) { { description: "Unfound error, nil default Validators", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysInvalid, + SimpleEventMessageType: AlwaysInvalid(), }, defaultValidator: nil, msg: Message{Type: CreateMessageType}, @@ -232,15 +232,15 @@ func testTypeValidatorFactory(t *testing.T) { { description: "Default Validators success", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, + SimpleEventMessageType: AlwaysValid(), }, - defaultValidator: AlwaysValid, + defaultValidator: AlwaysValid(), expectedErr: nil, }, { description: "Omit default Validators success", m: map[MessageType]Validator{ - SimpleEventMessageType: AlwaysValid, + SimpleEventMessageType: AlwaysValid(), }, expectedErr: nil, }, @@ -248,7 +248,7 @@ func testTypeValidatorFactory(t *testing.T) { { description: "Nil map of Validators error", m: nil, - defaultValidator: AlwaysValid, + defaultValidator: AlwaysValid(), expectedErr: ErrInvalidValidator, }, } @@ -347,7 +347,7 @@ func testAlwaysValid(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := AlwaysValid.Validate(tc.msg) + err := AlwaysValid().Validate(tc.msg) assert.NoError(err) }) } @@ -429,7 +429,7 @@ func testAlwaysInvalid(t *testing.T) { for _, tc := range tests { t.Run(tc.description, func(t *testing.T) { assert := assert.New(t) - err := AlwaysInvalid.Validate(tc.msg) + err := AlwaysInvalid().Validate(tc.msg) assert.ErrorIs(err, ErrInvalidMsgType) }) }