-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathspec.go
168 lines (134 loc) · 3.78 KB
/
spec.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
package main
import (
"context"
"encoding/json"
"errors"
"net/http"
"github.com/getkin/kin-openapi/openapi3"
)
type XTestSuiteRequest struct {
PathParam map[string]string `json:"pathParam,omitempty"`
QueryParam map[string]string `json:"queryParam,omitempty"`
Header map[string]string `json:"header,omitempty"`
Body map[string]interface{} `json:"body,omitempty"` // TODO@adam: Possibly refactor this to be map[string]interface{}
}
func (xtsr XTestSuiteRequest) Validate() error {
return nil
}
type XTestSuiteResponse struct {
//TODO: Probably should refactor these fields as pointers
Body string `json:"body,omitempty"`
HTTPStatus int `json:"http-status,omitempty"`
}
func (xtsr XTestSuiteResponse) ShouldSkipBodyValidation() bool {
return xtsr.Body == ""
}
type XTestSuite struct {
Description string `json:"description"`
Request XTestSuiteRequest `json:"request"`
Response XTestSuiteResponse `json:"response"`
}
type Operation struct {
XTestSuites []XTestSuite `json:"x-test-suite,omitempty"`
}
func (o *Operation) HasTestSuites() bool {
return len(o.XTestSuites) != 0
}
type PathItem struct {
Post *Operation `json:"post"`
Get *Operation `json:"get"`
}
type Paths map[string]*PathItem
func (p Paths) Validate() error {
if len(p) == 0 {
return errors.New("There is no path to process")
}
return nil
}
type Server struct {
URL string `json:"url"`
Description string `json:"description"`
}
type Servers []*Server
func (s *Servers) Validate() error {
if s == nil {
return errors.New("Should specify server")
}
if len(*s) != 1 {
//TODO@adam: For MVP, this can only parse 1 server. If it has more than one servers, it will fail.
return errors.New("Server should only be 1, no more no less")
}
return nil
}
type Spec struct {
Servers Servers `json:"servers"`
Paths Paths `json:"paths"`
}
func (s Spec) Validate() error {
if err := s.Servers.Validate(); err != nil {
return err
}
if err := s.Paths.Validate(); err != nil {
return err
}
return nil
}
type testProcessor interface {
processTestSuites(httpMethod, pathName string, xTestSuites []XTestSuite) []testSuiteReport
}
func parseAndValidateSpec(fileBytes []byte) (s Spec, err error) {
openApi, err := openapi3.NewLoader().LoadFromData(fileBytes)
if err != nil {
return
}
ctx := context.Background()
if err = openApi.Validate(ctx); err != nil {
return
}
// Validate OpenApi spec format
openApiJSON, err := openApi.MarshalJSON()
if err != nil {
return
}
if err = json.Unmarshal(openApiJSON, &s); err != nil {
return
}
// Validate Oat's spec (X-Test-Suites)
if err = s.Validate(); err != nil {
return
}
return s, err
}
func exec(fileBytes []byte) (Report, error) {
s, err := parseAndValidateSpec(fileBytes)
if err != nil {
return Report{}, err
}
// Init http client once, so we can test this layer
httpClient := newHTTPClient(s.Servers[0].URL)
// Init test processor with httpClient as the dependency
testProcessor := newTestProcessor(httpClient)
// Inject dependencies and execute test
return execWithReporter(s, testProcessor)
}
func execWithReporter(s Spec, testProcessor testProcessor) (Report, error) {
report := Report{
TestSuites: []testSuiteReport{},
}
// In OpenApi
for pathName, path := range s.Paths {
if path.Get != nil {
if path.Get.HasTestSuites() {
testReports := testProcessor.processTestSuites(http.MethodGet, pathName, path.Get.XTestSuites)
report.TestSuites = append(report.TestSuites, testReports...)
}
}
if path.Post != nil && path.Post.HasTestSuites() {
if path.Post.HasTestSuites() {
testReports := testProcessor.processTestSuites(http.MethodPost, pathName, path.Post.XTestSuites)
report.TestSuites = append(report.TestSuites, testReports...)
}
}
}
return report, nil
}