match_yaml_matcher.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. package matchers
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/onsi/gomega/format"
  6. "gopkg.in/yaml.v2"
  7. )
  8. type MatchYAMLMatcher struct {
  9. YAMLToMatch interface{}
  10. firstFailurePath []interface{}
  11. }
  12. func (matcher *MatchYAMLMatcher) Match(actual interface{}) (success bool, err error) {
  13. actualString, expectedString, err := matcher.toStrings(actual)
  14. if err != nil {
  15. return false, err
  16. }
  17. var aval interface{}
  18. var eval interface{}
  19. if err := yaml.Unmarshal([]byte(actualString), &aval); err != nil {
  20. return false, fmt.Errorf("Actual '%s' should be valid YAML, but it is not.\nUnderlying error:%s", actualString, err)
  21. }
  22. if err := yaml.Unmarshal([]byte(expectedString), &eval); err != nil {
  23. return false, fmt.Errorf("Expected '%s' should be valid YAML, but it is not.\nUnderlying error:%s", expectedString, err)
  24. }
  25. var equal bool
  26. equal, matcher.firstFailurePath = deepEqual(aval, eval)
  27. return equal, nil
  28. }
  29. func (matcher *MatchYAMLMatcher) FailureMessage(actual interface{}) (message string) {
  30. actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
  31. return formattedMessage(format.Message(actualString, "to match YAML of", expectedString), matcher.firstFailurePath)
  32. }
  33. func (matcher *MatchYAMLMatcher) NegatedFailureMessage(actual interface{}) (message string) {
  34. actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
  35. return formattedMessage(format.Message(actualString, "not to match YAML of", expectedString), matcher.firstFailurePath)
  36. }
  37. func (matcher *MatchYAMLMatcher) toNormalisedStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
  38. actualString, expectedString, err := matcher.toStrings(actual)
  39. return normalise(actualString), normalise(expectedString), err
  40. }
  41. func normalise(input string) string {
  42. var val interface{}
  43. err := yaml.Unmarshal([]byte(input), &val)
  44. if err != nil {
  45. panic(err) // unreachable since Match already calls Unmarshal
  46. }
  47. output, err := yaml.Marshal(val)
  48. if err != nil {
  49. panic(err) // untested section, unreachable since we Unmarshal above
  50. }
  51. return strings.TrimSpace(string(output))
  52. }
  53. func (matcher *MatchYAMLMatcher) toStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
  54. actualString, ok := toString(actual)
  55. if !ok {
  56. return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
  57. }
  58. expectedString, ok := toString(matcher.YAMLToMatch)
  59. if !ok {
  60. return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.YAMLToMatch, 1))
  61. }
  62. return actualString, expectedString, nil
  63. }