-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GraphQL Matchers for tests using httpexpect (#13)
- Loading branch information
Showing
10 changed files
with
1,029 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package gqlerrors | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/mitchellh/mapstructure" | ||
"gopkg.in/gavv/httpexpect.v1" | ||
) | ||
|
||
type FormattedError struct { | ||
Message string `json:"message"` | ||
Extensions map[string]interface{} `json:"extensions,omitempty"` | ||
} | ||
|
||
type graphQLError struct { | ||
Data map[string]interface{} `json:"data"` | ||
Errors []*FormattedError `json:"errors"` | ||
} | ||
|
||
// decode an objects | ||
func (g *graphQLError) decode(input interface{}) error { | ||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | ||
TagName: "json", | ||
Result: g, | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
return decoder.Decode(input) | ||
} | ||
|
||
func prepareFromJSON(mutateOrQuery string, actual interface{}) (*graphQLError, error) { | ||
data, ok := actual.(*httpexpect.Object) | ||
if !ok { | ||
return nil, fmt.Errorf("`actual` is not an json object") | ||
} | ||
|
||
// Decoding GraphQL error | ||
var graphQLError graphQLError | ||
err := graphQLError.decode(data.Raw()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(graphQLError.Errors) == 0 { | ||
return nil, fmt.Errorf("expected an error is not `%s`", actual) | ||
} | ||
|
||
for key := range graphQLError.Data { | ||
if key != mutateOrQuery { | ||
return nil, fmt.Errorf("expected mutate or query name [%s] not is equal [%s]", key, mutateOrQuery) | ||
} | ||
} | ||
|
||
return &graphQLError, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package gqlerrors | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/lab259/errors/v2" | ||
"github.com/onsi/gomega/format" | ||
) | ||
|
||
type errWithGraphQLCodeMatcher struct { | ||
Code interface{} | ||
MutateOrQuery string | ||
} | ||
|
||
func (matcher *errWithGraphQLCodeMatcher) Match(actual interface{}) (bool, error) { | ||
graphQLError, err := prepareFromJSON(matcher.MutateOrQuery, actual) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
for _, v := range graphQLError.Errors { | ||
code, ok := v.Extensions["code"].(string) | ||
if !ok { | ||
return false, fmt.Errorf("couldn't have key `code` %q", v.Extensions) | ||
} | ||
|
||
switch matcher.Code.(type) { | ||
case errors.Option: | ||
c := matcher.Code.(errors.Option) | ||
cError := c(nil).Error() | ||
if code != cError { | ||
return false, fmt.Errorf("code [%s] not equal [%s]", cError, code) | ||
|
||
} | ||
case string: | ||
if code != matcher.Code { | ||
return false, fmt.Errorf("code [%s] not equal [%s]", matcher.Code, code) | ||
} | ||
case nil: | ||
return false, fmt.Errorf("the code cannot be null") | ||
} | ||
|
||
} | ||
|
||
return true, nil | ||
} | ||
|
||
func (matcher *errWithGraphQLCodeMatcher) FailureMessage(actual interface{}) string { | ||
return format.Message(actual, "to have any code equal field", matcher.Code) | ||
} | ||
|
||
func (matcher *errWithGraphQLCodeMatcher) NegatedFailureMessage(actual interface{}) string { | ||
return format.Message(actual, "to have any code equal field", matcher.Code) | ||
} | ||
|
||
func ErrWithGraphQLCode(mutateOrQueryName string, code interface{}) *errWithGraphQLCodeMatcher { | ||
return &errWithGraphQLCodeMatcher{ | ||
Code: code, | ||
MutateOrQuery: mutateOrQueryName, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package gqlerrors_test | ||
|
||
import ( | ||
"github.com/lab259/errors/v2" | ||
"github.com/lab259/errors/v2/gqlerrors" | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
"github.com/onsi/gomega/format" | ||
"gopkg.in/gavv/httpexpect.v1" | ||
) | ||
|
||
var _ = Describe("GraphQL Extensions Code", func() { | ||
It("should check the return code", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"code": "validation", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
a := gqlerrors.ErrWithGraphQLCode("mutate", "validation") | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeTrue()) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
It("should fail check the return code when input not object", func() { | ||
a := gqlerrors.ErrWithGraphQLCode("mutate", "validate") | ||
ok, err := a.Match("it is not an object of the type `*httpexpect.Object`") | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("`actual` is not an json object")) | ||
}) | ||
|
||
It("should fail when not matcher extension error", func() { | ||
m := gqlerrors.ErrWithGraphQLCode("mutate", "validate") | ||
|
||
ok, err := m.Match(&httpexpect.Object{}) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("expected an error is not `&{{<nil> %!s(bool=false)} map[]}`")) | ||
}) | ||
|
||
It("should fail checking the return code when mutate not matcher", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"code": "validation", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
code := errors.Code("invalid") | ||
a := gqlerrors.ErrWithGraphQLCode("mutateInvalid", code) | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("expected mutate or query name [mutate] not is equal [mutateInvalid]")) | ||
}) | ||
|
||
It("should fail checking the return when error not contains code", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"module": "accounts", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
a := gqlerrors.ErrWithGraphQLCode("mutate", "users") | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("couldn't have key `code` map[\"module\":\"accounts\"]")) | ||
}) | ||
|
||
It("should fail to decode when error not matcher", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": map[string]interface{}{ | ||
"extensions": map[string]interface{}{ | ||
"code": "invalid-account-id", | ||
}, | ||
}, | ||
}) | ||
|
||
code := errors.Code("invalid-account-id") | ||
a := gqlerrors.ErrWithGraphQLCode("mutate", code) | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
}) | ||
|
||
It("should fail checking the return when error not matcher code option", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"code": "validation", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
op := errors.Code("new-code") | ||
a := gqlerrors.ErrWithGraphQLCode("mutate", op) | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("code [new-code] not equal [validation]")) | ||
}) | ||
|
||
It("should fail checking the return when error not matcher code option nil", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"code": "validation", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
a := gqlerrors.ErrWithGraphQLCode("mutate", nil) | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("the code cannot be null")) | ||
}) | ||
|
||
It("should fail checking the return when error not matcher code text", func() { | ||
jsonData := httpexpect.NewObject(&httpGomegaFail{}, map[string]interface{}{ | ||
"data": map[string]interface{}{"mutate": nil}, | ||
"errors": []map[string]interface{}{ | ||
{ | ||
"extensions": map[string]interface{}{ | ||
"code": "validation", | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
a := gqlerrors.ErrWithGraphQLCode("mutate", "graphql") | ||
ok, err := a.Match(jsonData) | ||
Expect(ok).To(BeFalse()) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("code [graphql] not equal [validation]")) | ||
}) | ||
|
||
It("should checking failure message", func() { | ||
mutate := "mutate" | ||
code := "graphql" | ||
a := gqlerrors.ErrWithGraphQLCode(mutate, code) | ||
failureMessage := a.FailureMessage("mutate") | ||
fMessage := format.Message(mutate, "to have any code equal field", code) | ||
Expect(failureMessage).To(Equal(fMessage)) | ||
}) | ||
|
||
It("should checking negative failure message", func() { | ||
mutate := "mutate" | ||
code := "graphql" | ||
a := gqlerrors.ErrWithGraphQLCode(mutate, code) | ||
failureMessage := a.NegatedFailureMessage("mutate") | ||
fMessage := format.Message(mutate, "to have any code equal field", code) | ||
Expect(failureMessage).To(Equal(fMessage)) | ||
}) | ||
|
||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package gqlerrors | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/lab259/errors/v2" | ||
"github.com/onsi/gomega/format" | ||
) | ||
|
||
type errWithGraphQLMessageMatcher struct { | ||
Message interface{} | ||
MutateOrQuery string | ||
} | ||
|
||
func (matcher *errWithGraphQLMessageMatcher) Match(actual interface{}) (bool, error) { | ||
graphQLError, err := prepareFromJSON(matcher.MutateOrQuery, actual) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
for _, v := range graphQLError.Errors { | ||
if extMessage, ok := v.Extensions["message"].(string); ok { | ||
switch matcher.Message.(type) { | ||
case errors.Option: | ||
op := matcher.Message.(errors.Option) | ||
message := op(nil).Error() | ||
return matcher.messageOption(extMessage, message) | ||
case string: | ||
message := matcher.Message.(string) | ||
return matcher.messageOption(extMessage, message) | ||
} | ||
} | ||
|
||
if v.Message != "" { | ||
switch matcher.Message.(type) { | ||
case errors.Option: | ||
op := matcher.Message.(errors.Option) | ||
message := op(nil).Error() | ||
return matcher.messageOption(v.Message, message) | ||
case string: | ||
message := matcher.Message.(string) | ||
return matcher.messageOption(v.Message, message) | ||
case error: | ||
message := matcher.Message.(error) | ||
return matcher.messageOption(v.Message, message.Error()) | ||
} | ||
} | ||
} | ||
|
||
return false, fmt.Errorf("the field message not found") | ||
} | ||
|
||
func (matcher *errWithGraphQLMessageMatcher) FailureMessage(actual interface{}) string { | ||
return format.Message(actual, "to have any message equal field", matcher.Message) | ||
} | ||
|
||
func (matcher *errWithGraphQLMessageMatcher) NegatedFailureMessage(actual interface{}) string { | ||
return format.Message(actual, "to have any message equal field", matcher.Message) | ||
} | ||
|
||
func ErrWithGraphQLMessage(mutateOrQueryName string, message interface{}) *errWithGraphQLMessageMatcher { | ||
return &errWithGraphQLMessageMatcher{ | ||
Message: message, | ||
MutateOrQuery: mutateOrQueryName, | ||
} | ||
} | ||
|
||
func (matcher *errWithGraphQLMessageMatcher) messageOption(source, expected string) (bool, error) { | ||
ok := strings.Contains(source, expected) | ||
if !ok { | ||
message := matcher.Message | ||
switch matcher.Message.(type) { | ||
case errors.Option: | ||
op := matcher.Message.(errors.Option) | ||
message = op(nil).Error() | ||
} | ||
return false, fmt.Errorf("message [%s] not equal [%s]", message, source) | ||
} | ||
return true, nil | ||
} |
Oops, something went wrong.