diff --git a/examples/__snapshots__/matchJSON_test.snap b/examples/__snapshots__/matchJSON_test.snap index 271dfef..4d854a3 100755 --- a/examples/__snapshots__/matchJSON_test.snap +++ b/examples/__snapshots__/matchJSON_test.snap @@ -88,3 +88,15 @@ } } --- + +[TestMatchers/type_matcher - 1] +{ + "data": "" +} +--- + +[TestMatchers/type_matcher - 2] +{ + "metadata": "" +} +--- diff --git a/examples/matchJSON_test.go b/examples/matchJSON_test.go index 1ee579d..c66bb01 100644 --- a/examples/matchJSON_test.go +++ b/examples/matchJSON_test.go @@ -178,4 +178,13 @@ func TestMatchers(t *testing.T) { snaps.MatchJSON(t, body, match.Any("data.createdAt")) }) + + t.Run("type matcher", func(t *testing.T) { + snaps.MatchJSON(t, `{"data":10}`, match.Type[float64]("data")) + snaps.MatchJSON( + t, + `{"metadata":{"timestamp":"1687108093142"}}`, + match.Type[map[string]interface{}]("metadata"), + ) + }) } diff --git a/match/type.go b/match/type.go new file mode 100644 index 0000000..a7366b7 --- /dev/null +++ b/match/type.go @@ -0,0 +1,75 @@ +package match + +import ( + "errors" + "fmt" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +type typeMatcher[T any] struct { + paths []string + errOnMissingPath bool + name string + _type interface{} +} + +func Type[T any](paths ...string) *typeMatcher[T] { + return &typeMatcher[T]{ + paths: paths, + errOnMissingPath: true, + name: "Type", + _type: *new(T), + } +} + +// ErrOnMissingPath determines if matcher will fail in case of trying to access a json path +// that doesn't exist +func (t *typeMatcher[T]) ErrOnMissingPath(e bool) *typeMatcher[T] { + t.errOnMissingPath = e + return t +} + +func (t typeMatcher[T]) JSON(s []byte) ([]byte, []MatcherError) { + var errs []MatcherError + json := s + + for _, path := range t.paths { + r := gjson.GetBytes(json, path) + if !r.Exists() { + if t.errOnMissingPath { + errs = append(errs, MatcherError{ + Reason: errors.New("path does not exist"), + Matcher: t.name, + Path: path, + }) + } + continue + } + + _, ok := r.Value().(T) + value := fmt.Sprintf("", *new(T)) + if !ok { + value = fmt.Sprintf("", r.Value()) + } + + j, err := sjson.SetBytesOptions(json, path, value, &sjson.Options{ + Optimistic: true, + ReplaceInPlace: true, + }) + if err != nil { + errs = append(errs, MatcherError{ + Reason: err, + Matcher: t.name, + Path: path, + }) + + continue + } + + json = j + } + + return json, errs +}