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  }