-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassertions.go
More file actions
154 lines (143 loc) · 4.32 KB
/
assertions.go
File metadata and controls
154 lines (143 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package testpilot
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"github.com/google/go-cmp/cmp"
)
type AssertionFunc func(body []byte) error
// AssertEqual asserts that the response body is equal to the given value
// It uses reflect.DeepEqual to compare the response body with the given value
func AssertEqual(response any) AssertionFunc {
return func(body []byte) error {
t := reflect.TypeOf(response)
if t.Kind() == reflect.Ptr {
t = t.Elem() // If it's a pointer, get the type it points to
}
newVar := reflect.New(t).Interface()
var got any
if t.Kind() == reflect.Struct || t.Kind() == reflect.Map || t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
if err := json.Unmarshal(body, newVar); err != nil {
return err
}
got = reflect.ValueOf(newVar).Elem().Interface()
} else {
got = strings.TrimSpace(string(body))
}
if !reflect.DeepEqual(response, got) {
diff := cmp.Diff(response, got)
coloredDiff := formatDiff(diff)
return errors.New(fmt.Sprintf("response body does not match. \n "+
"expected: %v \n "+
"got : %v \n\n Diff: \n %v", truncate(response), truncate(got), coloredDiff))
}
return nil
}
}
// AssertExists asserts that the value at the given path exists in the response body
func AssertExists(path string) AssertionFunc {
return func(body []byte) error {
var data any
if err := json.Unmarshal(body, &data); err != nil {
return err
}
if path[0] == '.' {
path = strings.Replace(path, ".", "", 1)
}
_, err := navigateJSON(data, path)
if err != nil {
return fmt.Errorf("could not find key %s with error %v", path, err)
}
return nil
}
}
// AssertPath asserts that the value at the given path in the response body satisfies the given assertion
// the path is a dot separated string representing the path to the value in the response body
// e.g. "data.user.0.name" will navigate to the first user in the data array and check if the name field satisfies the given assertion
func AssertPath[T comparable](path string, assert func(val T) error) AssertionFunc {
return func(body []byte) error {
var data any
if err := json.Unmarshal(body, &data); err != nil {
return err
}
if path[0] == '.' {
path = strings.Replace(path, ".", "", 1)
}
value, err := navigateJSON(data, path)
if err != nil {
return err
}
v, err := convertToType[T](value)
if err != nil {
return err
}
if err := assert(v); err != nil {
return err
}
return nil
}
}
// Equal returns an assertion function that checks if the given value is equal to the expected value
func Equal[T comparable](expected T) func(val T) error {
return func(val T) error {
if val != expected {
return fmt.Errorf("expected %v got %v", expected, val)
}
return nil
}
}
func Exists() func(val string) error {
return func(val string) error {
if val == "" {
return fmt.Errorf("expected value to exist")
}
return nil
}
}
func truncate(data interface{}) string {
value := fmt.Sprintf("%#v", data)
maxSize := bufio.MaxScanTokenSize - 100
if len(value) > maxSize {
value = value[0:maxSize] + "..."
}
return value
}
func formatDiff(diff string) string {
lines := strings.Split(diff, "\n")
var formattedDiff string
for _, line := range lines {
if strings.HasPrefix(line, "-") {
formattedDiff += fmt.Sprintf("\033[31m%s\033[0m\n", line)
} else if strings.HasPrefix(line, "+") {
formattedDiff += fmt.Sprintf("\033[32m%s\033[0m\n", line)
} else {
formattedDiff += fmt.Sprintf("%s\n", line)
}
}
return formattedDiff
}
func convertToType[T comparable](value interface{}) (T, error) {
var result T
targetType := reflect.TypeOf(result)
valueReflect := reflect.ValueOf(value)
if valueReflect.Type().ConvertibleTo(targetType) {
convertedValue := valueReflect.Convert(targetType)
result = convertedValue.Interface().(T)
return result, nil
}
if targetType.Kind() == reflect.Struct && valueReflect.Kind() == reflect.Map {
valueMap := valueReflect.Interface().(map[string]interface{})
structValue := reflect.ValueOf(&result).Elem()
for key, val := range valueMap {
field := structValue.FieldByName(key)
if field.IsValid() && field.CanSet() {
field.Set(reflect.ValueOf(val).Convert(field.Type()))
}
}
return result, nil
}
return result, fmt.Errorf("cannot convert value of type %s to type %s", valueReflect.Type(), targetType)
}