...

Source file src/sigs.k8s.io/structured-merge-diff/v4/typed/reconcile_schema_test.go

Documentation: sigs.k8s.io/structured-merge-diff/v4/typed

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package typed_test
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    24  	"sigs.k8s.io/structured-merge-diff/v4/typed"
    25  )
    26  
    27  type reconcileTestCase struct {
    28  	name         string
    29  	rootTypeName string
    30  	oldSchema    typed.YAMLObject
    31  	newSchema    typed.YAMLObject
    32  	liveObject   typed.YAMLObject
    33  	oldFields    *fieldpath.Set
    34  	fixedFields  *fieldpath.Set
    35  }
    36  
    37  func granularSchema(version string) typed.YAMLObject {
    38  	return typed.YAMLObject(fmt.Sprintf(`types:
    39  - name: %s
    40    map:
    41      fields:
    42        - name: struct
    43          type:
    44            namedType: struct
    45        - name: list
    46          type:
    47            namedType: list
    48        - name: objectList
    49          type:
    50            namedType: objectList
    51        - name: stringMap
    52          type:
    53            namedType: stringMap
    54        - name: unchanged
    55          type:
    56            namedType: unchanged
    57  - name: struct
    58    map:
    59      fields:
    60      - name: numeric
    61        type:
    62          scalar: numeric
    63      - name: string
    64        type:
    65          scalar: string
    66  - name: list
    67    list:
    68      elementType:
    69        scalar: string
    70      elementRelationship: associative
    71  - name: objectList
    72    list:
    73      elementType:
    74        namedType: listItem
    75      elementRelationship: associative
    76      keys:
    77        - keyA
    78        - keyB
    79  - name: listItem
    80    map:
    81      fields:
    82      - name: keyA
    83        type:
    84          scalar: string
    85      - name: keyB
    86        type:
    87          scalar: string
    88      - name: value
    89        type:
    90          scalar: string
    91  - name: stringMap
    92    map:
    93      elementType:
    94        scalar: string
    95  - name: unchanged
    96    map:
    97      fields:
    98      - name: numeric
    99        type:
   100          scalar: numeric
   101  - name: empty
   102    map:
   103      elementType:
   104        scalar: untyped
   105        list:
   106          elementType:
   107            namedType: __untyped_atomic_
   108          elementRelationship: atomic
   109        map:
   110          elementType:
   111            namedType: __untyped_deduced_
   112          elementRelationship: separable
   113  - name: emptyWithPreserveUnknown
   114    map:
   115      fields:
   116      - name: preserveField
   117        type:
   118          map:
   119            elementType:
   120              scalar: untyped
   121              list:
   122                elementType:
   123                  namedType: __untyped_atomic_
   124                elementRelationship: atomic
   125              map:
   126                elementType:
   127                  namedType: __untyped_deduced_
   128                elementRelationship: separable
   129  - name: populatedWithPreserveUnknown
   130    map:
   131      fields:
   132      - name: preserveField
   133        type:
   134          map:
   135            fields:
   136            - name: list
   137              type:
   138                namedType: list
   139            elementType:
   140              namedType: __untyped_deduced_
   141  - name: __untyped_atomic_
   142    scalar: untyped
   143    list:
   144      elementType:
   145        namedType: __untyped_atomic_
   146      elementRelationship: atomic
   147    map:
   148      elementType:
   149        namedType: __untyped_atomic_
   150      elementRelationship: atomic
   151  - name: __untyped_deduced_
   152    scalar: untyped
   153    list:
   154      elementType:
   155        namedType: __untyped_atomic_
   156      elementRelationship: atomic
   157    map:
   158      elementType:
   159        namedType: __untyped_deduced_
   160      elementRelationship: separable
   161  `, version))
   162  }
   163  
   164  func atomicSchema(version string) typed.YAMLObject {
   165  	return typed.YAMLObject(fmt.Sprintf(`types:
   166  - name: %s
   167    map:
   168      fields:
   169        - name: struct
   170          type:
   171            namedType: struct
   172        - name: list
   173          type:
   174            namedType: list
   175        - name: objectList
   176          type:
   177            namedType: objectList
   178        - name: stringMap
   179          type:
   180            namedType: stringMap
   181        - name: unchanged
   182          type:
   183            namedType: unchanged
   184  - name: struct
   185    map:
   186      fields:
   187      - name: numeric
   188        type:
   189          scalar: numeric
   190      - name: string
   191        type:
   192          scalar: string
   193      elementRelationship: atomic
   194  - name: list
   195    list:
   196      elementType:
   197        scalar: string
   198      elementRelationship: atomic
   199  - name: objectList
   200    list:
   201      elementType:
   202        namedType: listItem
   203      elementRelationship: atomic
   204  - name: listItem
   205    map:
   206      fields:
   207      - name: keyA
   208        type:
   209          scalar: string
   210      - name: keyB
   211        type:
   212          scalar: string
   213      - name: value
   214        type:
   215          scalar: string
   216  - name: stringMap
   217    map:
   218      elementType:
   219        scalar: string
   220      elementRelationship: atomic
   221  - name: unchanged
   222    map:
   223      fields:
   224      - name: numeric
   225        type:
   226          scalar: numeric
   227  - name: empty
   228    map:
   229      elementType:
   230        scalar: untyped
   231        list:
   232          elementType:
   233            namedType: __untyped_atomic_
   234          elementRelationship: atomic
   235        map:
   236          elementType:
   237            namedType: __untyped_deduced_
   238          elementRelationship: separable
   239  - name: emptyWithPreserveUnknown
   240    map:
   241      fields:
   242      - name: preserveField
   243        type:
   244          map:
   245            elementType:
   246              scalar: untyped
   247              list:
   248                elementType:
   249                  namedType: __untyped_atomic_
   250                elementRelationship: atomic
   251              map:
   252                elementType:
   253                  namedType: __untyped_deduced_
   254                elementRelationship: separable
   255  - name: populatedWithPreserveUnknown
   256    map:
   257      fields:
   258      - name: preserveField
   259        type:
   260          map:
   261            fields:
   262            - name: list
   263              type:
   264                namedType: list
   265            elementType:
   266              namedType: __untyped_deduced_
   267  - name: __untyped_atomic_
   268    scalar: untyped
   269    list:
   270      elementType:
   271        namedType: __untyped_atomic_
   272      elementRelationship: atomic
   273    map:
   274      elementType:
   275        namedType: __untyped_atomic_
   276      elementRelationship: atomic
   277  - name: __untyped_deduced_
   278    scalar: untyped
   279    list:
   280      elementType:
   281        namedType: __untyped_atomic_
   282      elementRelationship: atomic
   283    map:
   284      elementType:
   285        namedType: __untyped_deduced_
   286      elementRelationship: separable
   287  `, version))
   288  }
   289  
   290  const basicLiveObject = typed.YAMLObject(`
   291  struct:
   292    numeric: 1
   293    string: "two"
   294  list:
   295    - one
   296    - two
   297  objectList:
   298    - keyA: a1
   299      keyB: b1
   300      value: v1
   301    - keyA: a2
   302      keyB: b2
   303      value: v2
   304  stringMap:
   305    key1: value1
   306  unchanged:
   307    numeric: 10
   308  `)
   309  
   310  var reconcileCases = []reconcileTestCase{{
   311  	name:         "granular-to-atomic",
   312  	rootTypeName: "v1",
   313  	oldSchema:    granularSchema("v1"),
   314  	newSchema:    atomicSchema("v1"),
   315  	liveObject:   basicLiveObject,
   316  	oldFields: _NS(
   317  		_P("struct", "numeric"),
   318  		_P("list", _V("one")),
   319  		_P("stringMap", "key1"),
   320  		_P("objectList", _KBF("keyA", "a1", "keyB", "b1"), "value"),
   321  		_P("unchanged", "numeric"),
   322  	),
   323  	fixedFields: _NS(
   324  		_P("struct"),
   325  		_P("list"),
   326  		_P("objectList"),
   327  		_P("stringMap"),
   328  		_P("unchanged", "numeric"),
   329  	),
   330  }, {
   331  	name:         "no-change-granular",
   332  	rootTypeName: "v1",
   333  	oldSchema:    granularSchema("v1"),
   334  	newSchema:    granularSchema("v1"),
   335  	liveObject:   basicLiveObject,
   336  	oldFields: _NS(
   337  		_P("struct", "numeric"),
   338  		_P("list", _V("one")),
   339  		_P("objectList", _KBF("keyA", "a1", "keyB", "b1"), "value"),
   340  		_P("stringMap", "key1"),
   341  		_P("unchanged", "numeric"),
   342  	),
   343  	fixedFields: nil, // indicates no change
   344  }, {
   345  	name:         "no-change-atomic",
   346  	rootTypeName: "v1",
   347  	oldSchema:    atomicSchema("v1"),
   348  	newSchema:    atomicSchema("v1"),
   349  	liveObject:   basicLiveObject,
   350  	oldFields: _NS(
   351  		_P("struct"),
   352  		_P("list"),
   353  		_P("objectList"),
   354  		_P("unchanged", "numeric"),
   355  	),
   356  	fixedFields: nil, // indicates no change
   357  }, {
   358  	name:         "no-change-empty-granular",
   359  	rootTypeName: "v1",
   360  	oldSchema:    granularSchema("v1"),
   361  	newSchema:    granularSchema("v1"),
   362  	liveObject: typed.YAMLObject(`
   363  struct: {}
   364  list: []
   365  objectList:
   366    - keyA: a1
   367      keyB: b1
   368  stringMap: {}
   369  unchanged: {}
   370  `),
   371  	oldFields: _NS(
   372  		_P("struct"),
   373  		_P("list"),
   374  		_P("objectList"),
   375  		_P("objectList", _KBF("keyA", "a1", "keyB", "b1")),
   376  		_P("objectList", _KBF("keyA", "a1", "keyB", "b1"), "value"),
   377  		_P("unchanged"),
   378  	),
   379  	fixedFields: nil, // indicates no change
   380  }, {
   381  	name:         "deduced",
   382  	rootTypeName: "empty",
   383  	oldSchema:    atomicSchema("v1"),
   384  	newSchema:    granularSchema("v1"),
   385  	liveObject:   basicLiveObject,
   386  	oldFields: _NS(
   387  		_P("struct"),
   388  		_P("list"),
   389  		_P("objectList"),
   390  		_P("unchanged", "numeric"),
   391  	),
   392  	fixedFields: nil, // indicates no change
   393  }, {
   394  	name:         "empty-preserve-unknown",
   395  	rootTypeName: "emptyWithPreserveUnknown",
   396  	oldSchema:    atomicSchema("v1"),
   397  	newSchema:    granularSchema("v1"),
   398  	liveObject: typed.YAMLObject(`
   399  preserveField:
   400    arbitrary: abc
   401  `),
   402  	oldFields: _NS(
   403  		_P("preserveField"),
   404  		_P("preserveField", "arbitrary"),
   405  	),
   406  	fixedFields: nil, // indicates no change
   407  }, {
   408  	name:         "populated-preserve-unknown",
   409  	rootTypeName: "populatedWithPreserveUnknown",
   410  	oldSchema:    granularSchema("v1"),
   411  	newSchema:    atomicSchema("v1"),
   412  	liveObject: typed.YAMLObject(`
   413  preserveField:
   414    arbitrary: abc
   415    list:
   416    - one
   417  `),
   418  	oldFields: _NS(
   419  		_P("preserveField"),
   420  		_P("preserveField", "arbitrary"),
   421  		_P("preserveField", "list"),
   422  		_P("preserveField", "list", _V("one")),
   423  	),
   424  	fixedFields: _NS(
   425  		_P("preserveField"),
   426  		_P("preserveField", "arbitrary"),
   427  		_P("preserveField", "list"),
   428  	),
   429  }}
   430  
   431  func TestReconcileFieldSetWithSchema(t *testing.T) {
   432  	for _, tt := range reconcileCases {
   433  		tt := tt
   434  		t.Run(tt.name, func(t *testing.T) {
   435  			t.Parallel()
   436  			tt.testReconcileCase(t)
   437  		})
   438  	}
   439  }
   440  
   441  func (tt reconcileTestCase) testReconcileCase(t *testing.T) {
   442  	parser, err := typed.NewParser(tt.newSchema)
   443  	if err != nil {
   444  		t.Fatalf("failed to create schema: %v", err)
   445  	}
   446  	pt := parser.Type(tt.rootTypeName)
   447  	liveObject, err := pt.FromYAML(tt.liveObject)
   448  	if err != nil {
   449  		t.Fatalf("failed to parse/validate yaml: %v\n%v", err, tt.liveObject)
   450  	}
   451  
   452  	fixed, err := typed.ReconcileFieldSetWithSchema(tt.oldFields, liveObject)
   453  	if err != nil {
   454  		t.Fatalf("fixup errors: %v", err)
   455  	}
   456  	if tt.fixedFields == nil {
   457  		if fixed != nil {
   458  			t.Fatalf("expected fieldset to be null but got\n:%s", fixed.String())
   459  		}
   460  		return
   461  	}
   462  
   463  	if fixed == nil {
   464  		t.Fatalf("expected fieldset to be\n:%s\n:but got null", tt.fixedFields.String())
   465  	}
   466  
   467  	if !fixed.Equals(tt.fixedFields) {
   468  		t.Errorf("expected fieldset:\n%s\n:but got\n:%s", tt.fixedFields.String(), fixed.String())
   469  	}
   470  }
   471  

View as plain text