1
16
17 package builder
18
19 import (
20 "encoding/json"
21 "reflect"
22 "testing"
23
24 "github.com/stretchr/testify/require"
25
26 "k8s.io/kube-openapi/pkg/validation/spec"
27 )
28
29 func TestCollectSharedParameters(t *testing.T) {
30 tests := []struct {
31 name string
32 spec string
33 want map[string]string
34 }{
35 {
36 name: "empty",
37 spec: "",
38 want: nil,
39 },
40 {
41 name: "no shared",
42 spec: `{
43 "parameters": {"pre": {"in": "body", "name": "body", "required": true, "schema": {}}},
44 "paths": {
45 "/api/v1/a/{name}": {"get": {"parameters": [
46 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true},
47 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
48 ]}},
49 "/api/v1/a/{name}/foo": {"get": {"parameters": [
50 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true}
51 ]}},
52 "/api/v1/b/{name}": {"get": {"parameters": [
53 {"description": "x","in":"query","name": "x2","type":"boolean","uniqueItems":true},
54 {"description": "y","in":"query","name": "y2","type":"boolean","uniqueItems":true}
55 ]}},
56 "/api/v1/b/{name}/foo": {"get": {"parameters": [
57 {"description": "z","in":"query","name": "z2","type":"boolean","uniqueItems":true}
58 ]}}
59 }
60 }`,
61 want: map[string]string{
62 `{"uniqueItems":true,"type":"boolean","description":"x","name":"x","in":"query"}`: "x-yaDSHpi7",
63 `{"uniqueItems":true,"type":"boolean","description":"y","name":"y","in":"query"}`: "y-g6h7lEsz",
64 `{"uniqueItems":true,"type":"boolean","description":"z","name":"z","in":"query"}`: "z--SXYWoM_",
65 `{"uniqueItems":true,"type":"boolean","description":"x","name":"x2","in":"query"}`: "x2-nds6MpS1",
66 `{"uniqueItems":true,"type":"boolean","description":"y","name":"y2","in":"query"}`: "y2-exnalzYE",
67 `{"uniqueItems":true,"type":"boolean","description":"z","name":"z2","in":"query"}`: "z2-8oJfzBQF",
68 },
69 },
70 {
71 name: "shared per operation",
72 spec: `{
73 "parameters": {"pre": {"in": "body", "name": "body", "required": true, "schema": {}}},
74 "paths": {
75 "/api/v1/a/{name}": {"get": {"parameters": [
76 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true},
77 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
78 ]}},
79 "/api/v1/a/{name}/foo": {"get": {"parameters": [
80 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true},
81 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
82 ]}},
83 "/api/v1/b/{name}": {"get": {"parameters": [
84 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true}
85 ]}},
86 "/api/v1/b/{name}/foo": {"get": {"parameters": [
87 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true}
88 ]}}
89 }
90 }`,
91 want: map[string]string{
92 `{"uniqueItems":true,"type":"boolean","description":"x","name":"x","in":"query"}`: "x-yaDSHpi7",
93 `{"uniqueItems":true,"type":"boolean","description":"y","name":"y","in":"query"}`: "y-g6h7lEsz",
94 `{"uniqueItems":true,"type":"boolean","description":"z","name":"z","in":"query"}`: "z--SXYWoM_",
95 },
96 },
97 {
98 name: "shared per path",
99 spec: `{
100 "parameters": {"pre": {"in": "body", "name": "body", "required": true, "schema": {}}},
101 "paths": {
102 "/api/v1/a/{name}": {"get": {},
103 "parameters": [
104 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true},
105 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
106 ]
107 },
108 "/api/v1/a/{name}/foo": {"get": {"parameters": [
109 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true},
110 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
111 ]}},
112 "/api/v1/b/{name}": {"get": {},
113 "parameters": [
114 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true}
115 ]
116 },
117 "/api/v1/b/{name}/foo": {"get": {"parameters": [
118 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true}
119 ]}}
120 }
121 }`,
122 want: map[string]string{
123 `{"uniqueItems":true,"type":"boolean","description":"x","name":"x","in":"query"}`: "x-yaDSHpi7",
124 `{"uniqueItems":true,"type":"boolean","description":"y","name":"y","in":"query"}`: "y-g6h7lEsz",
125 `{"uniqueItems":true,"type":"boolean","description":"z","name":"z","in":"query"}`: "z--SXYWoM_",
126 },
127 },
128 }
129
130 for _, tt := range tests {
131 t.Run(tt.name, func(t *testing.T) {
132 var sp *spec.Swagger
133 if tt.spec != "" {
134 err := json.Unmarshal([]byte(tt.spec), &sp)
135 require.NoError(t, err)
136 }
137
138 gotNamesByJSON, _, err := collectSharedParameters(sp)
139 require.NoError(t, err)
140 require.Equalf(t, tt.want, gotNamesByJSON, "unexpected shared parameters")
141 })
142 }
143 }
144
145 func TestReplaceSharedParameters(t *testing.T) {
146 shared := map[string]string{
147 `{"uniqueItems":true,"type":"boolean","description":"x","name":"x","in":"query"}`: "x",
148 `{"uniqueItems":true,"type":"boolean","description":"y","name":"y","in":"query"}`: "y",
149 `{"uniqueItems":true,"type":"boolean","description":"z","name":"z","in":"query"}`: "z",
150 }
151
152 tests := []struct {
153 name string
154 spec string
155 want string
156 }{
157 {
158 name: "empty",
159 spec: "{}",
160 want: `{"paths":null}`,
161 },
162 {
163 name: "existing parameters",
164 spec: `{"parameters": {"a":{"type":"boolean"}}}`,
165 want: `{"parameters": {"a":{"type":"boolean"}},"paths":null}`,
166 },
167 {
168 name: "replace",
169 spec: `{
170 "parameters": {"pre": {"in": "body", "name": "body", "required": true, "schema": {}}},
171 "paths": {
172 "/api/v1/a/{name}": {"get": {"description":"foo"},
173 "parameters": [
174 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true},
175 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
176 ]
177 },
178 "/api/v1/a/{name}/foo": {"get": {"parameters": [
179 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true},
180 {"description": "y","in":"query","name": "y","type":"boolean","uniqueItems":true}
181 ]}},
182 "/api/v1/b/{name}": {"get": {"parameters": [
183 {"description": "z","in":"query","name": "z","type":"boolean","uniqueItems":true}
184 ]}},
185 "/api/v1/b/{name}/foo": {"get": {"parameters": [
186 {"description": "x","in":"query","name": "x","type":"boolean","uniqueItems":true},
187 {"description": "w","in":"query","name": "w","type":"boolean","uniqueItems":true}
188 ]}}
189 }
190 }`,
191 want: `{
192 "parameters": {"pre":{"in":"body","name":"body","required":true,"schema":{}}},
193 "paths": {
194 "/api/v1/a/{name}": {"get": {"description":"foo"},
195 "parameters": [
196 {"$ref": "#/parameters/x"},
197 {"$ref": "#/parameters/y"}
198 ]
199 },
200 "/api/v1/a/{name}/foo": {"get": {"parameters": [
201 {"$ref": "#/parameters/z"},
202 {"$ref": "#/parameters/y"}
203 ]}},
204 "/api/v1/b/{name}": {"get": {"parameters": [
205 {"$ref": "#/parameters/z"}
206 ]}},
207 "/api/v1/b/{name}/foo": {"get": {"parameters": [
208 {"$ref":"#/parameters/x"},
209 {"description": "w","in":"query","name": "w","type":"boolean","uniqueItems":true}
210 ]}}
211 }
212 }`,
213 },
214 }
215 for _, tt := range tests {
216 t.Run(tt.name, func(t *testing.T) {
217 var unmarshalled *spec.Swagger
218 err := json.Unmarshal([]byte(tt.spec), &unmarshalled)
219 require.NoError(t, err)
220
221 got, err := replaceSharedParameters(shared, unmarshalled)
222 require.NoError(t, err)
223
224 require.Equalf(t, normalizeJSON(t, tt.want), normalizeJSON(t, toJSON(t, got)), "unexpected result")
225 })
226 }
227 }
228
229 func toJSON(t *testing.T, x interface{}) string {
230 bs, err := json.Marshal(x)
231 require.NoError(t, err)
232
233 return string(bs)
234 }
235
236 func normalizeJSON(t *testing.T, j string) string {
237 var obj interface{}
238 err := json.Unmarshal([]byte(j), &obj)
239 require.NoError(t, err)
240 return toJSON(t, obj)
241 }
242
243 func TestOperations(t *testing.T) {
244 t.Log("Ensuring that operations() returns all operations in spec.PathItemProps")
245 path := spec.PathItem{}
246 v := reflect.ValueOf(path.PathItemProps)
247 var rOps []any
248 for i := 0; i < v.NumField(); i++ {
249 if v.Field(i).Kind() == reflect.Ptr {
250 rOps = append(rOps, v.Field(i).Interface())
251 }
252 }
253
254 ops := operations(&path)
255 require.Equal(t, len(rOps), len(ops), "operations() should return all operations in spec.PathItemProps")
256 }
257
View as plain text