123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- package httpexpect
- import (
- "encoding/json"
- "fmt"
- "reflect"
- "regexp"
- "github.com/xeipuuv/gojsonschema"
- "github.com/yalp/jsonpath"
- "github.com/yudai/gojsondiff"
- "github.com/yudai/gojsondiff/formatter"
- )
- func toString(str interface{}) (s string, ok bool) {
- ok = true
- defer func() {
- if err := recover(); err != nil {
- ok = false
- }
- }()
- s = reflect.ValueOf(str).Convert(reflect.TypeOf("")).String()
- return
- }
- func getPath(chain *chain, value interface{}, path string) *Value {
- if chain.failed() {
- return &Value{*chain, nil}
- }
- result, err := jsonpath.Read(value, path)
- if err != nil {
- chain.fail(err.Error())
- return &Value{*chain, nil}
- }
- return &Value{*chain, result}
- }
- func checkSchema(chain *chain, value, schema interface{}) {
- if chain.failed() {
- return
- }
- valueLoader := gojsonschema.NewGoLoader(value)
- var schemaLoader gojsonschema.JSONLoader
- if str, ok := toString(schema); ok {
- if ok, _ := regexp.MatchString(`^\w+://`, str); ok {
- schemaLoader = gojsonschema.NewReferenceLoader(str)
- } else {
- schemaLoader = gojsonschema.NewStringLoader(str)
- }
- } else {
- schemaLoader = gojsonschema.NewGoLoader(schema)
- }
- result, err := gojsonschema.Validate(schemaLoader, valueLoader)
- if err != nil {
- chain.fail("\n%s\n\nschema:\n%s\n\nvalue:\n%s",
- err.Error(),
- dumpSchema(schema),
- dumpValue(value))
- return
- }
- if !result.Valid() {
- errors := ""
- for _, err := range result.Errors() {
- errors += fmt.Sprintf(" %s\n", err)
- }
- chain.fail(
- "\njson schema validation failed, schema:\n%s\n\nvalue:%s\n\nerrors:\n%s",
- dumpSchema(schema),
- dumpValue(value),
- errors)
- return
- }
- }
- func dumpSchema(schema interface{}) string {
- if s, ok := toString(schema); ok {
- schema = s
- }
- return regexp.MustCompile(`(?m:^)`).
- ReplaceAllString(fmt.Sprintf("%v", schema), " ")
- }
- func canonNumber(chain *chain, number interface{}) (f float64, ok bool) {
- ok = true
- defer func() {
- if err := recover(); err != nil {
- chain.fail("%v", err)
- ok = false
- }
- }()
- f = reflect.ValueOf(number).Convert(reflect.TypeOf(float64(0))).Float()
- return
- }
- func canonArray(chain *chain, in interface{}) ([]interface{}, bool) {
- var out []interface{}
- data, ok := canonValue(chain, in)
- if ok {
- out, ok = data.([]interface{})
- if !ok {
- chain.fail("expected array, got %v", out)
- }
- }
- return out, ok
- }
- func canonMap(chain *chain, in interface{}) (map[string]interface{}, bool) {
- var out map[string]interface{}
- data, ok := canonValue(chain, in)
- if ok {
- out, ok = data.(map[string]interface{})
- if !ok {
- chain.fail("expected map, got %v", out)
- }
- }
- return out, ok
- }
- func canonValue(chain *chain, in interface{}) (interface{}, bool) {
- b, err := json.Marshal(in)
- if err != nil {
- chain.fail(err.Error())
- return nil, false
- }
- var out interface{}
- if err := json.Unmarshal(b, &out); err != nil {
- chain.fail(err.Error())
- return nil, false
- }
- return out, true
- }
- func dumpValue(value interface{}) string {
- b, err := json.MarshalIndent(value, " ", " ")
- if err != nil {
- return " " + fmt.Sprintf("%#v", value)
- }
- return " " + string(b)
- }
- func diffValues(expected, actual interface{}) string {
- differ := gojsondiff.New()
- var diff gojsondiff.Diff
- if ve, ok := expected.(map[string]interface{}); ok {
- if va, ok := actual.(map[string]interface{}); ok {
- diff = differ.CompareObjects(ve, va)
- } else {
- return " (unavailable)"
- }
- } else if ve, ok := expected.([]interface{}); ok {
- if va, ok := actual.([]interface{}); ok {
- diff = differ.CompareArrays(ve, va)
- } else {
- return " (unavailable)"
- }
- } else {
- return " (unavailable)"
- }
- config := formatter.AsciiFormatterConfig{
- ShowArrayIndex: true,
- }
- formatter := formatter.NewAsciiFormatter(expected, config)
- str, err := formatter.Format(diff)
- if err != nil {
- return " (unavailable)"
- }
- return "--- expected\n+++ actual\n" + str
- }
|