-
Notifications
You must be signed in to change notification settings - Fork 100
/
circular_test.go
273 lines (204 loc) · 9.75 KB
/
circular_test.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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package spec
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestExpandCircular_Issue3(t *testing.T) {
jazon, root := expandThisOrDieTrying(t, "fixtures/expansion/overflow.json")
require.NotEmpty(t, jazon)
// circular $ref point to the expanded root document
assertRefInJSON(t, jazon, "#/definitions")
// verify that all $ref can resolved against the new root schema
assertRefResolve(t, jazon, "", root)
// verify that all $ref can be expanded in the new root schema
assertRefExpand(t, jazon, "", root)
}
func TestExpandCircular_RefExpansion(t *testing.T) {
basePath := filepath.Join("fixtures", "expansion", "circularRefs.json")
carsDoc, err := jsonDoc(basePath)
require.NoError(t, err)
spec := new(Swagger)
require.NoError(t, json.Unmarshal(carsDoc, spec))
resolver := defaultSchemaLoader(spec, &ExpandOptions{RelativeBase: basePath}, nil, nil)
schema := spec.Definitions["car"]
_, err = expandSchema(schema, []string{"#/definitions/car"}, resolver, normalizeBase(basePath))
require.NoError(t, err)
jazon := asJSON(t, schema)
// circular $ref point to the expanded root document
// there are only 2 types with circular definitions
assertRefInJSONRegexp(t, jazon, "#/definitions/(car|category)")
// verify that all $ref can resolved against the new root schema
assertRefResolve(t, jazon, "", spec)
// verify that all $ref can be expanded in the new root schema
assertRefExpand(t, jazon, "", spec)
}
func TestExpandCircular_Spec2Expansion(t *testing.T) {
// TODO: assert repeatable results (see commented section below)
fixturePath := filepath.Join("fixtures", "expansion", "circular-minimal.json")
jazon, root := expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
// circular $ref are not always the same, but they sure are one of the nodes
assertRefInJSONRegexp(t, jazon, `#/definitions/node\d+`)
// circular $ref always resolve against the root
assertRefResolve(t, jazon, "", root)
// assert stripped $ref in result
assert.NotContainsf(t, jazon, "circular-minimal.json#/",
"expected %s to be expanded with stripped circular $ref", fixturePath)
fixturePath = filepath.Join("fixtures", "expansion", "circularSpec2.json")
jazon, root = expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
// circular $ref resolved against the expanded root document
assertRefInJSON(t, jazon, `#/definitions/`)
// circular $ref always resolve against the root
assertRefResolve(t, jazon, "", root)
// circular $ref can always be further expanded against the root
assertRefExpand(t, jazon, "", root)
assert.NotContainsf(t, jazon, "circularSpec.json#/",
"expected %s to be expanded with stripped circular $ref", fixturePath)
/*
At the moment, the result of expanding circular references is not stable,
when several cycles have intersections:
the spec structure is randomly walked through and mutating as expansion is carried out.
detected cycles in $ref are not necessarily the shortest matches.
This may result in different, functionally correct expanded specs (e.g. with same validations)
for i := 0; i < 1; i++ {
bbb := expandThisOrDieTrying(t, fixturePath)
t.Log(bbb)
if !assert.JSONEqf(t, jazon, bbb, "on iteration %d, we should have stable expanded spec", i) {
t.FailNow()
return
}
}
*/
}
func TestExpandCircular_MoreCircular(t *testing.T) {
// Additional testcase for circular $ref (from go-openapi/validate):
// - $ref with file = current file
// - circular is located in remote file
//
// There are 4 variants to run:
// - with/without $ref with local file (so its not really remote)
// - with circular in a schema in #/responses
// - with circular in a schema in #/parameters
fixturePath := filepath.Join("fixtures", "more_circulars", "spec.json")
jazon, root := expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "item.json#/item")
assertRefResolve(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
fixturePath = filepath.Join("fixtures", "more_circulars", "spec2.json")
jazon, root = expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "item2.json#/item")
assertRefResolve(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
fixturePath = filepath.Join("fixtures", "more_circulars", "spec3.json")
jazon, root = expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "item.json#/item")
assertRefResolve(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
fixturePath = filepath.Join("fixtures", "more_circulars", "spec4.json")
jazon, root = expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "item4.json#/item")
assertRefResolve(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
}
func TestExpandCircular_Issue957(t *testing.T) {
fixturePath := filepath.Join("fixtures", "bugs", "957", "fixture-957.json")
jazon, root := expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
require.NotContainsf(t, jazon, "fixture-957.json#/",
"expected %s to be expanded with stripped circular $ref", fixturePath)
assertRefInJSON(t, jazon, "#/definitions/")
assertRefResolve(t, jazon, "", root)
assertRefExpand(t, jazon, "", root)
}
func TestExpandCircular_Bitbucket(t *testing.T) {
// Additional testcase for circular $ref (from bitbucket api)
fixturePath := filepath.Join("fixtures", "more_circulars", "bitbucket.json")
jazon, root := expandThisOrDieTrying(t, fixturePath)
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "#/definitions/")
assertRefResolve(t, jazon, "", root)
assertRefExpand(t, jazon, "", root)
}
func TestExpandCircular_ResponseWithRoot(t *testing.T) {
rootDoc := new(Swagger)
b, err := os.ReadFile(filepath.Join("fixtures", "more_circulars", "resp.json"))
require.NoError(t, err)
require.NoError(t, json.Unmarshal(b, rootDoc))
path := rootDoc.Paths.Paths["/api/v1/getx"]
resp := path.Post.Responses.StatusCodeResponses[200]
thisCache := cacheOrDefault(nil)
// during the first response expand, refs are getting expanded,
// so the following expands cannot properly resolve them w/o the document.
// this happens in validator.Validate() when different validators try to expand the same mutable response.
require.NoError(t, ExpandResponseWithRoot(&resp, rootDoc, thisCache))
jazon := asJSON(t, resp)
assertRefInJSON(t, jazon, "#/definitions/MyObj")
// do it again
require.NoError(t, ExpandResponseWithRoot(&resp, rootDoc, thisCache))
jazon = asJSON(t, resp)
assertRefInJSON(t, jazon, "#/definitions/MyObj")
}
func TestExpandCircular_Issue415(t *testing.T) {
jazon, root := expandThisOrDieTrying(t, filepath.Join("fixtures", "expansion", "clickmeter.json"))
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "#/definitions/")
assertRefResolve(t, jazon, "", root)
assertRefExpand(t, jazon, "", root)
}
func TestExpandCircular_SpecExpansion(t *testing.T) {
jazon, root := expandThisOrDieTrying(t, filepath.Join("fixtures", "expansion", "circularSpec.json"))
require.NotEmpty(t, jazon)
assertRefInJSON(t, jazon, "#/definitions/Book")
assertRefResolve(t, jazon, "", root)
assertRefExpand(t, jazon, "", root)
}
func TestExpandCircular_RemoteCircularID(t *testing.T) {
go func() {
err := http.ListenAndServe("localhost:1234", http.FileServer(http.Dir("fixtures/more_circulars/remote"))) //#nosec
if err != nil {
panic(err.Error())
}
}()
time.Sleep(100 * time.Millisecond)
// from json-schema test suite testcase for remote with circular ID
fixturePath := "http://localhost:1234/tree"
jazon, root := expandThisSchemaOrDieTrying(t, fixturePath)
assertRefResolve(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
assertRefExpand(t, jazon, "", root, &ExpandOptions{RelativeBase: fixturePath})
require.NoError(t, ExpandSchemaWithBasePath(root, nil, &ExpandOptions{}))
jazon = asJSON(t, root)
assertRefInJSONRegexp(t, jazon, "^http://localhost:1234/tree$") // $ref now point to the root doc
// a spec using the previous circular schema
fixtureSpecPath := filepath.Join("fixtures", "more_circulars", "with-id.json")
jazon, doc := expandThisOrDieTrying(t, fixtureSpecPath)
assertRefInJSON(t, jazon, fixturePath) // all remaining $ref's point to the circular ID (http://...)
// ResolveRef fails, because there are some remote $ref, but ResolveRefWithBasePath is successful
assertRefResolve(t, jazon, "", doc, &ExpandOptions{})
assertRefExpand(t, jazon, "", doc)
}
func TestCircular_RemoteExpandAzure(t *testing.T) {
// local copy of : https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/network/resource-manager/Microsoft.Network/stable/2020-04-01/publicIpAddress.json
server := httptest.NewServer(http.FileServer(http.Dir("fixtures/azure")))
defer server.Close()
basePath := server.URL + "/publicIpAddress.json"
jazon, sch := expandThisOrDieTrying(t, basePath)
// check a pointer with escaped path
pth1, err := ResolvePathItem(sch, MustCreateRef("#/paths/~1subscriptions~1%7BsubscriptionId%7D~1providers~1Microsoft.Network~1publicIPAddresses"), nil)
require.NoError(t, err)
require.NotNil(t, pth1)
// check expected remaining $ref
assertRefInJSONRegexp(t, jazon, `^(#/definitions/)|(networkInterface.json#/definitions/)|(networkSecurityGroup.json#/definitions/)|(network.json#/definitions)|(virtualNetworkTap.json#/definitions/)|(virtualNetwork.json#/definitions/)|(privateEndpoint.json#/definitions/)|(\./examples/)`)
// check all $ref resolve in the expanded root
// (filter out the remaining $ref in x-ms-example extensions, which are not expanded)
t.Run("resolve $ref azure", func(t *testing.T) {
assertRefResolve(t, jazon, `\./example`, sch, &ExpandOptions{RelativeBase: basePath})
})
}