github.com/w3security/vervet/v5@v5.3.1-0.20230618081846-5bd9b5d799dc/merge_test.go (about) 1 package vervet_test 2 3 import ( 4 "testing" 5 6 qt "github.com/frankban/quicktest" 7 "github.com/getkin/kin-openapi/openapi3" 8 "github.com/google/go-cmp/cmp/cmpopts" 9 10 "github.com/w3security/vervet/v5" 11 "github.com/w3security/vervet/v5/testdata" 12 ) 13 14 var openapiCmp = qt.CmpEquals(cmpopts.IgnoreUnexported( 15 openapi3.ExampleRef{}, 16 openapi3.HeaderRef{}, 17 openapi3.ParameterRef{}, 18 openapi3.RequestBodyRef{}, 19 openapi3.ResponseRef{}, 20 openapi3.Schema{}, 21 openapi3.SchemaRef{}, 22 openapi3.SecuritySchemeRef{}, 23 )) 24 25 func TestMergeComponents(t *testing.T) { 26 c := qt.New(t) 27 c.Run("component without replace", func(c *qt.C) { 28 src := mustLoadFile(c, "merge_test_src.yaml") 29 dstOrig := mustLoadFile(c, "merge_test_dst.yaml") 30 dst := mustLoadFile(c, "merge_test_dst.yaml") 31 vervet.Merge(dst, src, false) 32 33 c.Assert(dst.Components.Schemas["Foo"], openapiCmp, dstOrig.Components.Schemas["Foo"]) 34 c.Assert(dst.Components.Schemas["Bar"], openapiCmp, src.Components.Schemas["Bar"]) 35 c.Assert(dst.Components.Schemas["Baz"], openapiCmp, dstOrig.Components.Schemas["Baz"]) 36 37 c.Assert(dst.Components.Parameters["Foo"], openapiCmp, dstOrig.Components.Parameters["Foo"]) 38 c.Assert(dst.Components.Parameters["Bar"], openapiCmp, src.Components.Parameters["Bar"]) 39 c.Assert(dst.Components.Parameters["Baz"], openapiCmp, dstOrig.Components.Parameters["Baz"]) 40 41 c.Assert(dst.Components.Headers["Foo"], openapiCmp, dstOrig.Components.Headers["Foo"]) 42 c.Assert(dst.Components.Headers["Bar"], openapiCmp, src.Components.Headers["Bar"]) 43 c.Assert(dst.Components.Headers["Baz"], openapiCmp, dstOrig.Components.Headers["Baz"]) 44 45 c.Assert(dst.Components.RequestBodies["Foo"], openapiCmp, dstOrig.Components.RequestBodies["Foo"]) 46 c.Assert(dst.Components.RequestBodies["Bar"], openapiCmp, src.Components.RequestBodies["Bar"]) 47 c.Assert(dst.Components.RequestBodies["Baz"], openapiCmp, dstOrig.Components.RequestBodies["Baz"]) 48 49 c.Assert(dst.Components.Responses["200"], openapiCmp, dstOrig.Components.Responses["200"]) 50 c.Assert(dst.Components.Responses["201"], openapiCmp, src.Components.Responses["201"]) 51 c.Assert(dst.Components.Responses["202"], openapiCmp, dstOrig.Components.Responses["202"]) 52 53 c.Assert(dst.Components.SecuritySchemes["Foo"], openapiCmp, dstOrig.Components.SecuritySchemes["Foo"]) 54 c.Assert(dst.Components.SecuritySchemes["Bar"], openapiCmp, src.Components.SecuritySchemes["Bar"]) 55 c.Assert(dst.Components.SecuritySchemes["Baz"], openapiCmp, dstOrig.Components.SecuritySchemes["Baz"]) 56 57 c.Assert(dst.Components.Examples["Foo"], openapiCmp, dstOrig.Components.Examples["Foo"]) 58 c.Assert(dst.Components.Examples["Bar"], openapiCmp, src.Components.Examples["Bar"]) 59 c.Assert(dst.Components.Examples["Baz"], openapiCmp, dstOrig.Components.Examples["Baz"]) 60 }) 61 c.Run("component with replace", func(c *qt.C) { 62 src := mustLoadFile(c, "merge_test_src.yaml") 63 dstOrig := mustLoadFile(c, "merge_test_dst.yaml") 64 dst := mustLoadFile(c, "merge_test_dst.yaml") 65 vervet.Merge(dst, src, true) 66 67 c.Assert(dst.Components.Schemas["Foo"], openapiCmp, src.Components.Schemas["Foo"]) 68 c.Assert(dst.Components.Schemas["Bar"], openapiCmp, src.Components.Schemas["Bar"]) 69 c.Assert(dst.Components.Schemas["Baz"], openapiCmp, dstOrig.Components.Schemas["Baz"]) 70 71 c.Assert(dst.Components.Parameters["Foo"], openapiCmp, src.Components.Parameters["Foo"]) 72 c.Assert(dst.Components.Parameters["Bar"], openapiCmp, src.Components.Parameters["Bar"]) 73 c.Assert(dst.Components.Parameters["Baz"], openapiCmp, dstOrig.Components.Parameters["Baz"]) 74 75 c.Assert(dst.Components.Headers["Foo"], openapiCmp, src.Components.Headers["Foo"]) 76 c.Assert(dst.Components.Headers["Bar"], openapiCmp, src.Components.Headers["Bar"]) 77 c.Assert(dst.Components.Headers["Baz"], openapiCmp, dstOrig.Components.Headers["Baz"]) 78 79 c.Assert(dst.Components.RequestBodies["Foo"], openapiCmp, src.Components.RequestBodies["Foo"]) 80 c.Assert(dst.Components.RequestBodies["Bar"], openapiCmp, src.Components.RequestBodies["Bar"]) 81 c.Assert(dst.Components.RequestBodies["Baz"], openapiCmp, dstOrig.Components.RequestBodies["Baz"]) 82 83 c.Assert(dst.Components.RequestBodies["200"], openapiCmp, src.Components.RequestBodies["200"]) 84 c.Assert(dst.Components.RequestBodies["201"], openapiCmp, src.Components.RequestBodies["201"]) 85 c.Assert(dst.Components.RequestBodies["202"], openapiCmp, dstOrig.Components.RequestBodies["202"]) 86 87 c.Assert(dst.Components.SecuritySchemes["Foo"], openapiCmp, src.Components.SecuritySchemes["Foo"]) 88 c.Assert(dst.Components.SecuritySchemes["Bar"], openapiCmp, src.Components.SecuritySchemes["Bar"]) 89 c.Assert(dst.Components.SecuritySchemes["Baz"], openapiCmp, dstOrig.Components.SecuritySchemes["Baz"]) 90 91 c.Assert(dst.Components.Examples["Foo"], openapiCmp, src.Components.Examples["Foo"]) 92 c.Assert(dst.Components.Examples["Bar"], openapiCmp, src.Components.Examples["Bar"]) 93 c.Assert(dst.Components.Examples["Baz"], openapiCmp, dstOrig.Components.Examples["Baz"]) 94 }) 95 c.Run("component with missing sections", func(c *qt.C) { 96 src := mustLoadFile(c, "merge_test_src.yaml") 97 dstOrig := mustLoadFile(c, "merge_test_dst_missing_components.yaml") 98 dst := mustLoadFile(c, "merge_test_dst_missing_components.yaml") 99 vervet.Merge(dst, src, true) 100 101 c.Assert(dst.Components.Schemas["Foo"], openapiCmp, src.Components.Schemas["Foo"]) 102 c.Assert(dst.Components.Schemas["Bar"], openapiCmp, src.Components.Schemas["Bar"]) 103 c.Assert(dst.Components.Schemas["Baz"], openapiCmp, dstOrig.Components.Schemas["Baz"]) 104 105 c.Assert(dst.Components.Parameters["Foo"], openapiCmp, src.Components.Parameters["Foo"]) 106 c.Assert(dst.Components.Parameters["Bar"], openapiCmp, src.Components.Parameters["Bar"]) 107 c.Assert(dst.Components.Parameters["Baz"], openapiCmp, dstOrig.Components.Parameters["Baz"]) 108 109 c.Assert(dst.Components.Headers["Foo"], openapiCmp, src.Components.Headers["Foo"]) 110 c.Assert(dst.Components.Headers["Bar"], openapiCmp, src.Components.Headers["Bar"]) 111 c.Assert(dst.Components.Headers["Baz"], openapiCmp, dstOrig.Components.Headers["Baz"]) 112 113 c.Assert(dst.Components.RequestBodies["Foo"], openapiCmp, src.Components.RequestBodies["Foo"]) 114 c.Assert(dst.Components.RequestBodies["Bar"], openapiCmp, src.Components.RequestBodies["Bar"]) 115 c.Assert(dst.Components.RequestBodies["Baz"], openapiCmp, dstOrig.Components.RequestBodies["Baz"]) 116 117 c.Assert(dst.Components.RequestBodies["200"], openapiCmp, src.Components.RequestBodies["200"]) 118 c.Assert(dst.Components.RequestBodies["201"], openapiCmp, src.Components.RequestBodies["201"]) 119 c.Assert(dst.Components.RequestBodies["202"], openapiCmp, dstOrig.Components.RequestBodies["202"]) 120 121 c.Assert(dst.Components.SecuritySchemes["Foo"], openapiCmp, src.Components.SecuritySchemes["Foo"]) 122 c.Assert(dst.Components.SecuritySchemes["Bar"], openapiCmp, src.Components.SecuritySchemes["Bar"]) 123 c.Assert(dst.Components.SecuritySchemes["Baz"], openapiCmp, dstOrig.Components.SecuritySchemes["Baz"]) 124 125 c.Assert(dst.Components.Examples["Foo"], openapiCmp, src.Components.Examples["Foo"]) 126 c.Assert(dst.Components.Examples["Bar"], openapiCmp, src.Components.Examples["Bar"]) 127 c.Assert(dst.Components.Examples["Baz"], openapiCmp, dstOrig.Components.Examples["Baz"]) 128 }) 129 } 130 131 func TestMergeTags(t *testing.T) { 132 srcYaml := ` 133 tags: 134 - name: foo 135 description: foo resource (src) 136 - name: bar 137 description: bar resource (src) 138 ` 139 dstYaml := ` 140 tags: 141 - name: foo 142 description: foo resource (dst) 143 - name: baz 144 description: baz resource (dst) 145 ` 146 c := qt.New(t) 147 c.Run("tags without replace", func(c *qt.C) { 148 src := mustLoad(c, srcYaml) 149 dst := mustLoad(c, dstYaml) 150 vervet.Merge(dst, src, false) 151 c.Assert(dst.Tags, qt.DeepEquals, openapi3.Tags{{ 152 Extensions: map[string]interface{}{}, 153 Name: "bar", 154 Description: "bar resource (src)", 155 }, { 156 Extensions: map[string]interface{}{}, 157 Name: "baz", 158 Description: "baz resource (dst)", 159 }, { 160 Extensions: map[string]interface{}{}, 161 Name: "foo", 162 Description: "foo resource (dst)", 163 }}) 164 }) 165 c.Run("tags with replace", func(c *qt.C) { 166 src := mustLoad(c, srcYaml) 167 dst := mustLoad(c, dstYaml) 168 vervet.Merge(dst, src, true) 169 c.Assert(dst.Tags, qt.DeepEquals, openapi3.Tags{{ 170 Extensions: map[string]interface{}{}, 171 Name: "bar", 172 Description: "bar resource (src)", 173 }, { 174 Extensions: map[string]interface{}{}, 175 Name: "baz", 176 Description: "baz resource (dst)", 177 }, { 178 Extensions: map[string]interface{}{}, 179 Name: "foo", 180 Description: "foo resource (src)", 181 }}) 182 }) 183 } 184 185 func TestMergeTopLevel(t *testing.T) { 186 srcYaml := ` 187 info: 188 title: Src 189 version: src 190 security: 191 - Foo: [] 192 - Bar: 193 - read 194 - write 195 servers: 196 - url: https://example.com/foo 197 description: Foo (src) 198 - url: https://example.com/bar 199 description: Bar (src) 200 x-extension: 201 key0: value0 202 key1: value1 203 ` 204 dstYaml := ` 205 info: 206 title: Dst 207 version: dst 208 security: 209 - Foo: 210 - up 211 - down 212 - Baz: 213 - strange 214 - crunchy 215 servers: 216 - url: https://example.com/foo 217 description: Foo (dst) 218 - url: https://example.com/baz 219 description: Baz (dst) 220 x-extension: 221 key1: value11 222 key2: value2 223 ` 224 c := qt.New(t) 225 c.Run("without replace", func(c *qt.C) { 226 src := mustLoad(c, srcYaml) 227 dst := mustLoad(c, dstYaml) 228 vervet.Merge(dst, src, false) 229 c.Assert(dst.Info, qt.DeepEquals, &openapi3.Info{ 230 Extensions: map[string]interface{}{}, 231 Title: "Dst", 232 Version: "dst", 233 }) 234 c.Assert(dst.Security, qt.DeepEquals, openapi3.SecurityRequirements{{ 235 "Foo": []string{"up", "down"}, 236 }, { 237 "Baz": []string{"strange", "crunchy"}, 238 }}) 239 c.Assert(dst.Servers, qt.DeepEquals, openapi3.Servers{{ 240 Extensions: map[string]interface{}{}, 241 URL: "https://example.com/foo", 242 Description: "Foo (dst)", 243 }, { 244 Extensions: map[string]interface{}{}, 245 URL: "https://example.com/baz", 246 Description: "Baz (dst)", 247 }}) 248 c.Assert(dst.Extensions, qt.DeepEquals, map[string]interface{}{ 249 "x-extension": map[string]interface{}{ 250 "key1": "value11", 251 "key2": "value2", 252 }, 253 }) 254 }) 255 c.Run("with replace", func(c *qt.C) { 256 src := mustLoad(c, srcYaml) 257 dst := mustLoad(c, dstYaml) 258 vervet.Merge(dst, src, true) 259 c.Assert(dst.Info, qt.DeepEquals, &openapi3.Info{ 260 Extensions: map[string]interface{}{}, 261 Title: "Src", 262 Version: "src", 263 }) 264 c.Assert(dst.Security, qt.DeepEquals, openapi3.SecurityRequirements{{ 265 "Foo": []string{}, 266 }, { 267 "Bar": []string{"read", "write"}, 268 }}) 269 c.Assert(dst.Servers, qt.DeepEquals, openapi3.Servers{{ 270 Extensions: map[string]interface{}{}, 271 URL: "https://example.com/foo", 272 Description: "Foo (src)", 273 }, { 274 Extensions: map[string]interface{}{}, 275 URL: "https://example.com/bar", 276 Description: "Bar (src)", 277 }}) 278 c.Assert(dst.Extensions, qt.DeepEquals, map[string]interface{}{ 279 "x-extension": map[string]interface{}{ 280 "key0": "value0", 281 "key1": "value1", 282 }, 283 }) 284 }) 285 } 286 287 func TestMergeIntoEmpty(t *testing.T) { 288 c := qt.New(t) 289 srcYaml := ` 290 info: 291 title: Src 292 version: src 293 paths: 294 /foo: 295 get: 296 description: get a foo 297 responses: 298 200: 299 contents: 300 application/json: 301 schema: 302 type: object 303 ` 304 src := mustLoad(c, srcYaml) 305 dst := &openapi3.T{} 306 vervet.Merge(dst, src, false) 307 c.Assert(dst.Paths, qt.HasLen, 1) 308 } 309 310 func mustLoadFile(c *qt.C, path string) *openapi3.T { 311 doc, err := vervet.NewDocumentFile(testdata.Path(path)) 312 c.Assert(err, qt.IsNil) 313 return doc.T 314 } 315 316 func mustLoad(c *qt.C, s string) *openapi3.T { 317 doc, err := openapi3.NewLoader().LoadFromData([]byte(s)) 318 c.Assert(err, qt.IsNil) 319 return doc 320 }