Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR: Initial WRP Validation Framework #80

Merged
merged 26 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dfeee98
Add VSCode to .gitignore
denopink May 24, 2022
7f8bad0
Initial WRP Validation Framework
denopink May 25, 2022
9319c12
formatting
denopink May 25, 2022
209ffdb
formatting
denopink May 25, 2022
ddd0acf
convert alwaysInvalidMsg to literal func
denopink May 25, 2022
a2a4093
Formating and exported alwaysInvalidMsg
denopink May 25, 2022
f5a2e90
Decouple error from api
denopink May 25, 2022
eb5bf9b
Updates based on PR review
denopink May 26, 2022
afa08cb
removed duplicated test
denopink May 26, 2022
8cb0fa7
Fix type test name
denopink May 26, 2022
10afaa2
Fix type test name
denopink May 26, 2022
1923ec7
Add missing test/edge cases for Validators
denopink May 26, 2022
98c4b4a
Updated defaultValidator to work as a variadic for multi-default supp…
denopink May 26, 2022
3197568
rename test var `name` to `description`
denopink May 26, 2022
1ee6c2e
Merge branch 'denopink/FR-WRPValidationFramework' of https://github.c…
denopink May 26, 2022
793d6f4
Squash: examples, docs, multierr updates
denopink May 26, 2022
22ca097
formatting
denopink Jun 3, 2022
5ff49e3
Add missing test for `AlwaysValid` func
denopink Jun 3, 2022
1f1aa67
Updates based on PR feedback
denopink Jun 7, 2022
df7e2ff
Update based on PR review
denopink Jun 8, 2022
ffd531f
Move exported fields to the top
denopink Jun 8, 2022
13a9a57
update tests to include correct mac length
denopink Jun 8, 2022
3ac1d02
`testAlwaysValid` & `testAlwaysInvalid` ensure a non-existing message…
denopink Jun 8, 2022
e553e7e
`testTypeValidatorFactory` swap `assert.Zero/NotZero`
denopink Jun 8, 2022
8e6882b
Add missing scheme to source fields & add missing `assert.ErrorIs` to…
denopink Jun 8, 2022
fc099bf
Update based on PR feedback
denopink Jun 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ report.json

# Images
*.png

# VSCode
*.code-workspace
.vscode/*
6 changes: 1 addition & 5 deletions header_wrp.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@

package wrp

import (
"errors"
)

// Constant HTTP header strings representing WRP fields
const (
MsgTypeHeader = "X-Midt-Msg-Type"
Expand All @@ -34,7 +30,7 @@ const (
SourceHeader = "X-Midt-Source"
)

var ErrInvalidMsgType = errors.New("Invalid Message Type")
// var ErrInvalidMsgType = errors.New("Invalid Message Type")
denopink marked this conversation as resolved.
Show resolved Hide resolved

// Map string to MessageType int
/*
Expand Down
91 changes: 91 additions & 0 deletions validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* 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"
)

var (
ErrInvalidMsgTypeValidator = errors.New("invalid WRP message type validator")
ErrInvalidMsgType = errors.New("invalid WRP message type")
)

// AlwaysInvalid doesn't validate anything about the message and always returns an error.
var AlwaysInvalidMsg ValidatorFunc = func(m Message) error {
denopink marked this conversation as resolved.
Show resolved Hide resolved
return ErrInvalidMsgType
}

// Validator is a WRP validator that allows access to the Validate function.
type Validator interface {
Validate(m Message) error
}

// Validators is a WRP validator that ensures messages are valid based on
// message type and each validator in the list.
type Validators []Validator
denopink marked this conversation as resolved.
Show resolved Hide resolved

// ValidatorFunc is a WRP validator that takes messages and validates them
// against functions.
type ValidatorFunc func(Message) error

// Validate executes its own ValidatorFunc receiver and returns the result.
func (vf ValidatorFunc) Validate(m Message) error {
return vf(m)
}

// MsgTypeValidator is a WRP validator that validates based on message type
// or using the defaultValidator if message type is unknown.
type MsgTypeValidator struct {
m map[MessageType]Validators
denopink marked this conversation as resolved.
Show resolved Hide resolved
defaultValidator Validator
}

// Validate validates messages based on message type or using the defaultValidator
// if message type is unknown.
func (m MsgTypeValidator) Validate(msg Message) error {
vs, ok := m.m[msg.MessageType()]
if !ok {
denopink marked this conversation as resolved.
Show resolved Hide resolved
return m.defaultValidator.Validate(msg)
}

for _, v := range vs {
err := v.Validate(msg)
if err != nil {
return err
}
}

return nil
}

// NewMsgTypeValidator is a MsgTypeValidator factory.
func NewMsgTypeValidator(m map[MessageType]Validators, defaultValidator Validator) (MsgTypeValidator, error) {
if m == nil {
return MsgTypeValidator{}, ErrInvalidMsgTypeValidator
}

if defaultValidator == nil {
defaultValidator = AlwaysInvalidMsg
}

return MsgTypeValidator{
m: m,
defaultValidator: defaultValidator,
}, nil
}
197 changes: 197 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/**
* 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"
)

denopink marked this conversation as resolved.
Show resolved Hide resolved
func testMsgTypeValidatorValidate(t *testing.T) {
type Test struct {
m map[MessageType]Validators
defaultValidator Validator
msg Message
}

var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil }
tests := []struct {
description string
value Test
denopink marked this conversation as resolved.
Show resolved Hide resolved
expectedErr error
}{
// Success case
{
description: "known message type with successful Validators",
denopink marked this conversation as resolved.
Show resolved Hide resolved
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {alwaysValidMsg},
},
msg: Message{Type: SimpleEventMessageType},
},
},
{
description: "unknown message type with provided default Validator",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {alwaysValidMsg},
denopink marked this conversation as resolved.
Show resolved Hide resolved
},
defaultValidator: alwaysValidMsg,
msg: Message{Type: CreateMessageType},
},
},
// Failure case
{
description: "known message type with failing Validators",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {AlwaysInvalidMsg},
denopink marked this conversation as resolved.
Show resolved Hide resolved
},
msg: Message{Type: SimpleEventMessageType},
},
expectedErr: ErrInvalidMsgType,
},
{
description: "unknown message type without provided default Validator",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {alwaysValidMsg},
},
msg: Message{Type: CreateMessageType},
},
expectedErr: ErrInvalidMsgType,
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)
msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator)
assert.NotNil(msgv)
assert.Nil(err)
denopink marked this conversation as resolved.
Show resolved Hide resolved
err = msgv.Validate(tc.value.msg)
if tc.expectedErr != nil {
assert.ErrorIs(err, tc.expectedErr)
return
}

assert.Nil(err)
denopink marked this conversation as resolved.
Show resolved Hide resolved
})
}
}

func testNewMsgTypeValidator(t *testing.T) {
type Test struct {
m map[MessageType]Validators
defaultValidator Validator
}

var alwaysValidMsg ValidatorFunc = func(msg Message) error { return nil }
tests := []struct {
description string
value Test
expectedErr error
}{
// Success case
{
description: "with provided default Validator",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {alwaysValidMsg},
},
defaultValidator: alwaysValidMsg,
},
expectedErr: nil,
},
{
description: "without provided default Validator",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {alwaysValidMsg},
},
},
expectedErr: nil,
},
{
description: "empty list of message type Validators",
value: Test{
m: map[MessageType]Validators{
SimpleEventMessageType: {},
},
defaultValidator: alwaysValidMsg,
},
expectedErr: nil,
},
// Failure case
{
description: "missing message type Validators",
value: Test{},
expectedErr: ErrInvalidMsgTypeValidator,
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)
msgv, err := NewMsgTypeValidator(tc.value.m, tc.value.defaultValidator)
assert.NotNil(msgv)
if tc.expectedErr != nil {
assert.ErrorIs(err, tc.expectedErr)
return
}

assert.Nil(err)
})
}
}

func testAlwaysInvalidMsg(t *testing.T) {
assert := assert.New(t)
msg := Message{}
err := AlwaysInvalidMsg(msg)

assert.NotNil(err)
assert.ErrorIs(err, ErrInvalidMsgType)

}

func TestHelperValidators(t *testing.T) {
kristinapathak marked this conversation as resolved.
Show resolved Hide resolved
tests := []struct {
name string
test func(*testing.T)
}{
{"AlwaysInvalidMsg", testAlwaysInvalidMsg},
}

for _, tc := range tests {
t.Run(tc.name, tc.test)
}
}

func TestMsgTypeValidator(t *testing.T) {
denopink marked this conversation as resolved.
Show resolved Hide resolved
tests := []struct {
name string
test func(*testing.T)
}{
{"MsgTypeValidator validate", testMsgTypeValidatorValidate},
{"MsgTypeValidator factory", testNewMsgTypeValidator},
}

for _, tc := range tests {
t.Run(tc.name, tc.test)
}
}