mirror of
https://github.com/adnanh/webhook.git
synced 2025-10-04 21:51:02 +00:00
Proposal: Remove side-effects from hook package
This commit removes logging from the hook package and relies on returning errors to convey...errors. Encountered errors are returned immediately. This commit may alter the behavior of hook. If errors were logged in the past but the given function did not return immediately, this commit would change that behavior. Uses named errors and custom error types. You may be able to consolidate ArgumentError and SourceError, but I need to think about it some more. These changes should be transparent to the caller if they're expecting standard "error" types. Tests have been updated to validate error return values and provide test coverage for a few new lines of code introduced by this commit.
This commit is contained in:
parent
2afc6e6a54
commit
ee64968d00
3 changed files with 185 additions and 86 deletions
|
@ -21,7 +21,8 @@ var checkPayloadSignatureTests = []struct {
|
|||
|
||||
func TestCheckPayloadSignature(t *testing.T) {
|
||||
for _, tt := range checkPayloadSignatureTests {
|
||||
mac, ok := CheckPayloadSignature(tt.payload, tt.secret, tt.signature)
|
||||
mac, err := CheckPayloadSignature(tt.payload, tt.secret, tt.signature)
|
||||
ok := err == nil
|
||||
if ok != tt.ok || mac != tt.mac {
|
||||
t.Errorf("failed to check payload signature {%q, %q, %q}:\nexpected {mac:%#v, ok:%#v},\ngot {mac:%#v, ok:%#v}", tt.payload, tt.secret, tt.signature, tt.mac, tt.ok, mac, ok)
|
||||
}
|
||||
|
@ -92,23 +93,25 @@ var hookParseJSONParametersTests = []struct {
|
|||
params []Argument
|
||||
headers, query, payload *map[string]interface{}
|
||||
rheaders, rquery, rpayload *map[string]interface{}
|
||||
ok bool
|
||||
}{
|
||||
{[]Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil},
|
||||
{[]Argument{Argument{"url", "a"}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil},
|
||||
{[]Argument{Argument{"payload", "a"}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}},
|
||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, nil, nil},
|
||||
{[]Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
||||
{[]Argument{Argument{"url", "a"}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
||||
{[]Argument{Argument{"payload", "a"}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": `{}`}, nil, nil, &map[string]interface{}{"z": map[string]interface{}{}}, nil, nil, true},
|
||||
// failures
|
||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil}, // empty string
|
||||
{[]Argument{Argument{"header", "y"}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil}, // missing parameter
|
||||
{[]Argument{Argument{"string", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil}, // invalid argument source
|
||||
{[]Argument{Argument{"header", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // empty string
|
||||
{[]Argument{Argument{"header", "y"}}, &map[string]interface{}{"X": `{}`}, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, false}, // missing parameter
|
||||
{[]Argument{Argument{"string", "z"}}, &map[string]interface{}{"z": ``}, nil, nil, &map[string]interface{}{"z": ``}, nil, nil, false}, // invalid argument source
|
||||
}
|
||||
|
||||
func TestHookParseJSONParameters(t *testing.T) {
|
||||
for _, tt := range hookParseJSONParametersTests {
|
||||
h := &Hook{JSONStringParameters: tt.params}
|
||||
h.ParseJSONParameters(tt.headers, tt.query, tt.payload)
|
||||
if !reflect.DeepEqual(tt.headers, tt.rheaders) {
|
||||
t.Errorf("failed to parse %v:\nexpected %#v,\ngot %#v", tt.params, *tt.rheaders, *tt.headers)
|
||||
err := h.ParseJSONParameters(tt.headers, tt.query, tt.payload)
|
||||
ok := err == nil
|
||||
if ok != tt.ok || !reflect.DeepEqual(tt.headers, tt.rheaders) {
|
||||
t.Errorf("failed to parse %v:\nexpected %#v, ok: %#v\ngot %#v, ok: %v", tt.params, *tt.rheaders, tt.ok, *tt.headers, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,18 +121,20 @@ var hookExtractCommandArgumentsTests = []struct {
|
|||
args []Argument
|
||||
headers, query, payload *map[string]interface{}
|
||||
value []string
|
||||
ok bool
|
||||
}{
|
||||
{"test", []Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"test", "z"}},
|
||||
{"test", []Argument{Argument{"header", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"test", "z"}, true},
|
||||
// failures
|
||||
{"fail", []Argument{Argument{"payload", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"fail", ""}},
|
||||
{"fail", []Argument{Argument{"payload", "a"}}, &map[string]interface{}{"a": "z"}, nil, nil, []string{"fail", ""}, false},
|
||||
}
|
||||
|
||||
func TestHookExtractCommandArguments(t *testing.T) {
|
||||
for _, tt := range hookExtractCommandArgumentsTests {
|
||||
h := &Hook{ExecuteCommand: tt.exec, PassArgumentsToCommand: tt.args}
|
||||
value := h.ExtractCommandArguments(tt.headers, tt.query, tt.payload)
|
||||
if !reflect.DeepEqual(value, tt.value) {
|
||||
t.Errorf("failed to extract args {cmd=%q, args=%v}:\nexpected %#v,\ngot %#v", tt.exec, tt.args, tt.value, value)
|
||||
value, err := h.ExtractCommandArguments(tt.headers, tt.query, tt.payload)
|
||||
ok := err == nil
|
||||
if ok != tt.ok || !reflect.DeepEqual(value, tt.value) {
|
||||
t.Errorf("failed to extract args {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,24 +183,26 @@ var matchRuleTests = []struct {
|
|||
headers, query, payload *map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{"value", "", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true},
|
||||
{"regex", "^z", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true},
|
||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true},
|
||||
{"value", "", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||
{"regex", "^z", "", "z", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
||||
// negatives
|
||||
{"regex", "^X", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||
{"value", "", "", "X", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||
// failures
|
||||
{"value", "", "", "X", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false},
|
||||
{"value", "", "", "X", Argument{"header", "a"}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false},
|
||||
{"regex", "^X", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false},
|
||||
{"regex", "*", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false},
|
||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": ""}, nil, nil, []byte{}, false},
|
||||
{"value", "", "2", "X", Argument{"header", "a"}, &map[string]interface{}{"y": "z"}, nil, nil, []byte{}, false, true}, // reference invalid header
|
||||
{"regex", "*", "", "", Argument{"header", "a"}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a"}, &map[string]interface{}{"a": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
||||
}
|
||||
|
||||
func TestMatchRule(t *testing.T) {
|
||||
for _, tt := range matchRuleTests {
|
||||
r := MatchRule{tt.typ, tt.regex, tt.secret, tt.value, tt.param}
|
||||
ok := r.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok {
|
||||
t.Errorf("failed to match %#v:\nexpected %#v,\ngot %#v", r, tt.ok, ok)
|
||||
ok, err := r.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok || (err == nil) == tt.err {
|
||||
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %#v,\ngot ok: %#v, err: %#v", r, tt.ok, tt.err, ok, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +213,7 @@ var andRuleTests = []struct {
|
|||
headers, query, payload *map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"(a=z, b=y): a=z && b=y",
|
||||
|
@ -214,7 +222,7 @@ var andRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
||||
},
|
||||
&map[string]interface{}{"a": "z", "b": "y"}, nil, nil, []byte{},
|
||||
true,
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
"(a=z, b=Y): a=z && b=y",
|
||||
|
@ -223,7 +231,7 @@ var andRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
||||
},
|
||||
&map[string]interface{}{"a": "z", "b": "Y"}, nil, nil, []byte{},
|
||||
false,
|
||||
false, false,
|
||||
},
|
||||
// Complex test to cover Rules.Evaluate
|
||||
{
|
||||
|
@ -249,16 +257,23 @@ var andRuleTests = []struct {
|
|||
},
|
||||
},
|
||||
&map[string]interface{}{"a": "z", "b": "y", "c": "x", "d": "w", "e": "X", "f": "X"}, nil, nil, []byte{},
|
||||
true,
|
||||
true, false,
|
||||
},
|
||||
{"empty rule", AndRule{{}}, nil, nil, nil, nil, false, false},
|
||||
// failures
|
||||
{
|
||||
"invalid rule",
|
||||
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a"}}}},
|
||||
&map[string]interface{}{"y": "z"}, nil, nil, nil,
|
||||
false, true,
|
||||
},
|
||||
{"empty rule", AndRule{{}}, nil, nil, nil, nil, false},
|
||||
}
|
||||
|
||||
func TestAndRule(t *testing.T) {
|
||||
for _, tt := range andRuleTests {
|
||||
ok := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok {
|
||||
t.Errorf("failed to match %#v:\nexpected %#v,\ngot %#v", tt.desc, tt.ok, ok)
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok || (err != nil) != tt.err {
|
||||
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %s,\ngot ok: %#v, err: %s", tt.desc, tt.ok, tt.err, ok, err != nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,6 +284,7 @@ var orRuleTests = []struct {
|
|||
headers, query, payload *map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
"(a=z, b=X): a=z || b=y",
|
||||
|
@ -277,7 +293,7 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
||||
},
|
||||
&map[string]interface{}{"a": "z", "b": "X"}, nil, nil, []byte{},
|
||||
true,
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
"(a=X, b=y): a=z || b=y",
|
||||
|
@ -286,7 +302,7 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
||||
},
|
||||
&map[string]interface{}{"a": "X", "b": "y"}, nil, nil, []byte{},
|
||||
true,
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
"(a=Z, b=Y): a=z || b=y",
|
||||
|
@ -295,15 +311,24 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b"}}},
|
||||
},
|
||||
&map[string]interface{}{"a": "Z", "b": "Y"}, nil, nil, []byte{},
|
||||
false,
|
||||
false, false,
|
||||
},
|
||||
// failures
|
||||
{
|
||||
"invalid rule",
|
||||
OrRule{
|
||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}},
|
||||
},
|
||||
&map[string]interface{}{"y": "Z"}, nil, nil, []byte{},
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestOrRule(t *testing.T) {
|
||||
for _, tt := range orRuleTests {
|
||||
ok := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok {
|
||||
t.Errorf("%#v:\nexpected %#v,\ngot %#v", tt.desc, tt.ok, ok)
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok || (err != nil) != tt.err {
|
||||
t.Errorf("%#v:\nexpected ok: %#v, err: %s,\ngot ok: %#v, err: %s", tt.desc, tt.ok, tt.err, ok, err != nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,16 +339,17 @@ var notRuleTests = []struct {
|
|||
headers, query, payload *map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true},
|
||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false},
|
||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, true, false},
|
||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a"}}}, &map[string]interface{}{"a": "z"}, nil, nil, []byte{}, false, false},
|
||||
}
|
||||
|
||||
func TestNotRule(t *testing.T) {
|
||||
for _, tt := range notRuleTests {
|
||||
ok := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok {
|
||||
t.Errorf("failed to match %#v:\nexpected %#v,\ngot %#v", tt.rule, tt.ok, ok)
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
||||
if ok != tt.ok || (err != nil) != tt.err {
|
||||
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %s,\ngot ok: %#v, err: %s", tt.rule, tt.ok, tt.err, ok, err != nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue