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  }