/
testfile.go
254 lines (199 loc) · 6.03 KB
/
testfile.go
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// Package manatest provides internal workings for go-mana-test.
package manatest
// Imports
import (
"errors"
"fmt"
yaml2 "github.com/ghodss/yaml"
"github.com/mattrmiller/go-mana-test/http"
"gopkg.in/yaml.v2"
"io/ioutil"
"net/url"
"path/filepath"
"strings"
)
// TestHeader is a structure to handle headers for a test.
type TestHeader struct {
// Key, hold the key of the header.
Key string `yaml:"key"`
// Value, hold the value of the header.
Value string `yaml:"value"`
}
// TestChecks is a structure to handle checks for a test.
type TestChecks struct {
// Name, hold the name of this check for the test.
Name string `yaml:"name"`
// Check, hold the check for the test.
Check string `yaml:"check"`
// Value, hold the value of the test.
Value string `yaml:"value"`
}
// TestCache is a structure to handle cache for a test.
type TestCache struct {
// Name, hold the name of this check for the test.
Name string `yaml:"name"`
// Value, hold the value of the test.
Value string `yaml:"value"`
}
// TestFile is a structure to handle an individual test file.
type TestFile struct {
// filePath, stores the path to the file.
filePath string
// Name, stores the name of the test.
Name string `yaml:"name"`
// Index, hold the index of this test.
Index int `yaml:"index"`
// URL, stores the url of the test.
URL string `yaml:"url"`
// Parms, key/val params to be attached to the URL
Params map[string]string `yaml:"params"`
// Method, stores the http method of the test.
RequestMethod string `yaml:"request.method"`
// Headers, stores the header variables.
RequestHeaders []TestHeader `yaml:"request.headers"`
// Body, stores the test http body.
ReqBody interface{} `yaml:"request.body"`
// TestChecks, stores the checks variables.
Checks []TestChecks `yaml:"checks"`
// Cache, stores the cache variables.
Cache []TestCache `yaml:"cache"`
}
// ReadTestFile Reads a test file.
func ReadTestFile(pathFile string) (*TestFile, error) {
// Check if yml
if filepath.Ext(strings.TrimSpace(pathFile)) != ".yml" {
return nil, fmt.Errorf("test file is not a 'yml' file: %s", pathFile)
}
// Read file
source, err := ioutil.ReadFile(pathFile) // nolint: gosec
if err != nil {
return nil, fmt.Errorf("unable to read test file at: %s", pathFile)
}
// Unmarshal yaml
var testFile TestFile
err = yaml.Unmarshal(source, &testFile)
if err != nil {
return nil, fmt.Errorf("invalid test file format at: %s", pathFile)
}
// Set path
testFile.filePath = pathFile
return &testFile, nil
}
// Validate Validates a test file is in proper format.
func (testFile *TestFile) Validate() error {
// Must have a name
if len(testFile.Name) == 0 {
return errors.New("test file must have 'name' field")
}
// Must have a url
if len(testFile.URL) == 0 {
return errors.New("test file must have 'url' field")
}
// Must have a method
if len(testFile.RequestMethod) == 0 {
return errors.New("test file must have 'method' field")
}
if !http.ValidateMethod(&testFile.RequestMethod) {
return errors.New("test file has invalid 'method' field")
}
// Correct index
if testFile.Index < 0 {
testFile.Index = 0
}
// Convert request body to json
if testFile.ReqBody != nil {
yamlBytes, err := yaml.Marshal(testFile.ReqBody)
if err != nil {
return fmt.Errorf("unable to unmarshal YAML request body: %s", err)
}
body, err := yaml2.YAMLToJSON(yamlBytes)
if err != nil {
return fmt.Errorf("unable to unmarshal JSON request body: %s", err)
}
testFile.ReqBody = string(body)
}
// Validate headers
for _, header := range testFile.RequestHeaders {
// Key
if len(header.Key) == 0 {
return errors.New("test file header must have 'key' field")
}
// Value
if len(header.Value) == 0 {
return errors.New("test file header must have 'value' field")
}
}
// Validate params
for key := range testFile.Params {
if len(key) == 0 {
return errors.New("test file param must have 'key' field")
}
}
// Validate checks
for _, check := range testFile.Checks {
// Name
if len(check.Name) == 0 {
return errors.New("test file check must have 'name' fieldt")
}
// Check
if len(check.Check) == 0 {
return errors.New("test file check must have 'check' field")
}
check.Check = strings.ToLower(check.Check)
if !ValidateCheck(&check.Check) {
return fmt.Errorf("test file check has an invalid 'check' field: `%s'", check.Check)
}
// Check
if len(check.Value) == 0 {
return errors.New("test file check must have 'value' field")
}
}
// Validate cache
for _, cache := range testFile.Cache {
// Name
if len(cache.Name) == 0 {
return errors.New("test file cache must have 'name' field")
}
// Value
if len(cache.Value) == 0 {
return errors.New("test file cache must have 'value' field")
}
cache.Value = strings.ToLower(cache.Value)
if !ValidateCacheValue(&cache.Value) {
return fmt.Errorf("test file cache has an invalid 'value' field: '%s'", cache.Value)
}
}
return nil
}
// MakeTestHeaders Prepares HTTP headers for the test but replacing necessary variables.
func (testFile *TestFile) MakeTestHeaders(projFile *ProjectFile) []TestHeader {
// Replace headers global values
headers := make([]TestHeader, 0)
for _, header := range testFile.RequestHeaders {
header.Value = ReplaceVarsInHeader(header.Value, &projFile.Globals)
headers = append(headers, header)
}
return headers
}
// MakeTestURL Prepares HTTP URL for the test but replacing necessary variables.
func (testFile *TestFile) MakeTestURL(projFile *ProjectFile) string {
v := url.Values{}
for key, value := range testFile.Params {
key = ReplaceVarsInHeader(key, &projFile.Globals)
value = ReplaceVarsInHeader(value, &projFile.Globals)
v.Set(key, value)
}
url := ReplaceVarsInTestURL(testFile.URL, &projFile.Globals)
if len(v) != 0 {
url = url + "?" + v.Encode()
}
return url
}
// GetPath Gets the path of the test file.
func (testFile *TestFile) GetPath() string {
return filepath.Dir(testFile.filePath)
}
// GetFilePath Gets the path to the test file.
func (testFile *TestFile) GetFilePath() string {
return testFile.filePath
}