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