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