...

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

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

     1  /*
     2  Copyright 2019 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/typed"
    24  	"sigs.k8s.io/structured-merge-diff/v4/value"
    25  )
    26  
    27  func TestValidateDeducedType(t *testing.T) {
    28  	tests := []string{
    29  		`{"a": null}`,
    30  		`{"a": ["a", "b"]}`,
    31  		`{"a": {"b": [], "c": 2, "d": {"f": "string"}}}`,
    32  	}
    33  
    34  	for i, test := range tests {
    35  		t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
    36  			v, err := typed.DeducedParseableType.FromYAML(typed.YAMLObject(test))
    37  			if err != nil {
    38  				t.Fatalf("Failed to parse yaml: %v", err)
    39  			}
    40  			if err := v.Validate(); err != nil {
    41  				t.Fatalf("Validation failed: %v", err)
    42  			}
    43  		})
    44  	}
    45  }
    46  
    47  func TestMergeDeduced(t *testing.T) {
    48  	triplets := []mergeTriplet{
    49  		{
    50  			`{"key":"foo","value":{}}`,
    51  			`{"key":"foo","value":1}`,
    52  			`{"key":"foo","value":1}`,
    53  		}, {
    54  			`{"key":"foo","value":1}`,
    55  			`{"key":"foo","value":{}}`,
    56  			`{"key":"foo","value":{}}`,
    57  		}, {
    58  			`{"key":"foo","value":null}`,
    59  			`{"key":"foo","value":{}}`,
    60  			`{"key":"foo","value":{}}`,
    61  		}, {
    62  			`{"key":"foo"}`,
    63  			`{"value":true}`,
    64  			`{"key":"foo","value":true}`,
    65  		}, {
    66  			`{}`,
    67  			`{"inner":{}}`,
    68  			`{"inner":{}}`,
    69  		}, {
    70  			`{}`,
    71  			`{"inner":null}`,
    72  			`{"inner":null}`,
    73  		}, {
    74  			`{"inner":null}`,
    75  			`{"inner":{}}`,
    76  			`{"inner":{}}`,
    77  		}, {
    78  			`{"inner":{}}`,
    79  			`{"inner":null}`,
    80  			`{"inner":null}`,
    81  		}, {
    82  			`{"inner":{}}`,
    83  			`{"inner":{}}`,
    84  			`{"inner":{}}`,
    85  		}, {
    86  			`{}`,
    87  			`{"inner":{}}`,
    88  			`{"inner":{}}`,
    89  		}, {
    90  			`{"inner":null}`,
    91  			`{"inner":{}}`,
    92  			`{"inner":{}}`,
    93  		}, {
    94  			`{"inner":{}}`,
    95  			`{"inner":null}`,
    96  			`{"inner":null}`,
    97  		}, {
    98  			`{}`,
    99  			`{"inner":[]}`,
   100  			`{"inner":[]}`,
   101  		}, {
   102  			`{"inner":null}`,
   103  			`{"inner":[]}`,
   104  			`{"inner":[]}`,
   105  		}, {
   106  			`{"inner":[]}`,
   107  			`{"inner":null}`,
   108  			`{"inner":null}`,
   109  		}, {
   110  			`{"inner":[]}`,
   111  			`{"inner":[]}`,
   112  			`{"inner":[]}`,
   113  		}, {
   114  			`{"numeric":1}`,
   115  			`{"numeric":3.14159}`,
   116  			`{"numeric":3.14159}`,
   117  		}, {
   118  			`{"numeric":3.14159}`,
   119  			`{"numeric":1}`,
   120  			`{"numeric":1}`,
   121  		}, {
   122  			`{"string":"aoeu"}`,
   123  			`{"bool":true}`,
   124  			`{"string":"aoeu","bool":true}`,
   125  		}, {
   126  			`{"atomic":["a","b","c"]}`,
   127  			`{"atomic":["a","b"]}`,
   128  			`{"atomic":["a","b"]}`,
   129  		}, {
   130  			`{"atomic":["a","b"]}`,
   131  			`{"atomic":["a","b","c"]}`,
   132  			`{"atomic":["a","b","c"]}`,
   133  		}, {
   134  			`{"atomic":["a","b","c"]}`,
   135  			`{"atomic":[]}`,
   136  			`{"atomic":[]}`,
   137  		}, {
   138  			`{"atomic":[]}`,
   139  			`{"atomic":["a","b","c"]}`,
   140  			`{"atomic":["a","b","c"]}`,
   141  		}, {
   142  			`{"":[true]}`,
   143  			`{"setBool":[false]}`,
   144  			`{"":[true],"setBool":[false]}`,
   145  		}, {
   146  			`{"atomic":[1,2,3.14159]}`,
   147  			`{"atomic":[1,2,3]}`,
   148  			`{"atomic":[1,2,3]}`,
   149  		}, {
   150  			`{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   151  			`{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   152  			`{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   153  		}, {
   154  			`{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   155  			`{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
   156  			`{"list":[{"key":"a","id":2,"value":{"a":"a"}}]}`,
   157  		}, {
   158  			`{"list":[{"key":"a","id":1},{"key":"b","id":1}]}`,
   159  			`{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
   160  			`{"list":[{"key":"a","id":1},{"key":"a","id":2}]}`,
   161  		}, {
   162  			`{"atomicList":["a","a","a"]}`,
   163  			`{"atomicList":null}`,
   164  			`{"atomicList":null}`,
   165  		}, {
   166  			`{"atomicList":["a","b","c"]}`,
   167  			`{"atomicList":[]}`,
   168  			`{"atomicList":[]}`,
   169  		}, {
   170  			`{"atomicList":["a","a","a"]}`,
   171  			`{"atomicList":["a","a"]}`,
   172  			`{"atomicList":["a","a"]}`,
   173  		}, {
   174  			`{"a":1,"b":[null],"c":{"id":2,"list":["value"]}}`,
   175  			`{"a":2,"b":["value"],"c":{"name":"my_name"}}`,
   176  			`{"a":2,"b":["value"],"c":{"id":2,"list":["value"],"name":"my_name"}}`,
   177  		}}
   178  
   179  	for i, triplet := range triplets {
   180  		triplet := triplet
   181  		t.Run(fmt.Sprintf("triplet-%v", i), func(t *testing.T) {
   182  			t.Parallel()
   183  
   184  			pt := typed.DeducedParseableType
   185  			lhs, err := pt.FromYAML(triplet.lhs)
   186  			if err != nil {
   187  				t.Fatalf("unable to parser/validate lhs yaml: %v\n%v", err, triplet.lhs)
   188  			}
   189  
   190  			rhs, err := pt.FromYAML(triplet.rhs)
   191  			if err != nil {
   192  				t.Fatalf("unable to parser/validate rhs yaml: %v\n%v", err, triplet.rhs)
   193  			}
   194  
   195  			out, err := pt.FromYAML(triplet.out)
   196  			if err != nil {
   197  				t.Fatalf("unable to parser/validate out yaml: %v\n%v", err, triplet.out)
   198  			}
   199  
   200  			got, err := lhs.Merge(rhs)
   201  			if err != nil {
   202  				t.Errorf("got validation errors: %v", err)
   203  			} else {
   204  				if !value.Equals(got.AsValue(), out.AsValue()) {
   205  					t.Errorf("Expected\n%v\nbut got\n%v\n",
   206  						value.ToString(out.AsValue()), value.ToString(got.AsValue()),
   207  					)
   208  				}
   209  			}
   210  		})
   211  	}
   212  }
   213  
   214  func TestToSetDeduced(t *testing.T) {
   215  	tests := []objSetPair{
   216  		{`{"key":"foo","value":1}`, _NS(_P("key"), _P("value"))},
   217  		{`{"key":"foo","value":{"a": "b"}}`, _NS(_P("key"), _P("value"), _P("value", "a"))},
   218  		{`{"key":"foo","value":null}`, _NS(_P("key"), _P("value"))},
   219  		{`{"key":"foo"}`, _NS(_P("key"))},
   220  		{`{"key":"foo","value":true}`, _NS(_P("key"), _P("value"))},
   221  		{`{"numeric":1}`, _NS(_P("numeric"))},
   222  		{`{"numeric":3.14159}`, _NS(_P("numeric"))},
   223  		{`{"string":"aoeu"}`, _NS(_P("string"))},
   224  		{`{"bool":true}`, _NS(_P("bool"))},
   225  		{`{"bool":false}`, _NS(_P("bool"))},
   226  		{`{"list":["a","b","c"]}`, _NS(_P("list"))},
   227  		{`{"color":{}}`, _NS(_P("color"))},
   228  		{`{"color":null}`, _NS(_P("color"))},
   229  		{`{"color":{"R":255,"G":0,"B":0}}`, _NS(_P("color"), _P("color", "R"), _P("color", "G"), _P("color", "B"))},
   230  		{`{"arbitraryWavelengthColor":null}`, _NS(_P("arbitraryWavelengthColor"))},
   231  		{`{"arbitraryWavelengthColor":{"IR":255}}`, _NS(_P("arbitraryWavelengthColor"), _P("arbitraryWavelengthColor", "IR"))},
   232  		{`{"args":[]}`, _NS(_P("args"))},
   233  		{`{"args":null}`, _NS(_P("args"))},
   234  		{`{"args":[null]}`, _NS(_P("args"))},
   235  		{`{"args":[{"key":"a","value":"b"},{"key":"c","value":"d"}]}`, _NS(_P("args"))},
   236  		{`{"atomicList":["a","a","a"]}`, _NS(_P("atomicList"))},
   237  	}
   238  
   239  	for i, v := range tests {
   240  		v := v
   241  		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
   242  			t.Parallel()
   243  
   244  			tv, err := typed.DeducedParseableType.FromYAML(v.object)
   245  			if err != nil {
   246  				t.Fatalf("failed to parse object: %v", err)
   247  			}
   248  			fs, err := tv.ToFieldSet()
   249  			if err != nil {
   250  				t.Fatalf("got validation errors: %v", err)
   251  			}
   252  			if !fs.Equals(v.set) {
   253  				t.Errorf("wanted\n%s\ngot\n%s\n", v.set, fs)
   254  			}
   255  		})
   256  	}
   257  }
   258  
   259  func TestSymdiffDeduced(t *testing.T) {
   260  	quints := []symdiffQuint{{
   261  		lhs:      `{"key":"foo","value":1}`,
   262  		rhs:      `{"key":"foo","value":1}`,
   263  		removed:  _NS(),
   264  		modified: _NS(),
   265  		added:    _NS(),
   266  	}, {
   267  		lhs:      `{"key":"foo","value":{}}`,
   268  		rhs:      `{"key":"foo","value":1}`,
   269  		removed:  _NS(),
   270  		modified: _NS(_P("value")),
   271  		added:    _NS(),
   272  	}, {
   273  		lhs:      `{"key":"foo","value":1}`,
   274  		rhs:      `{"key":"foo","value":{}}`,
   275  		removed:  _NS(),
   276  		modified: _NS(_P("value")),
   277  		added:    _NS(),
   278  	}, {
   279  		lhs:      `{"key":"foo","value":1}`,
   280  		rhs:      `{"key":"foo","value":{"deep":{"nested":1}}}`,
   281  		removed:  _NS(),
   282  		modified: _NS(_P("value")),
   283  		added:    _NS(_P("value", "deep"), _P("value", "deep", "nested")),
   284  	}, {
   285  		lhs:      `{"key":"foo","value":null}`,
   286  		rhs:      `{"key":"foo","value":{}}`,
   287  		removed:  _NS(),
   288  		modified: _NS(_P("value")),
   289  		added:    _NS(),
   290  	}, {
   291  		lhs:      `{"key":"foo"}`,
   292  		rhs:      `{"value":true}`,
   293  		removed:  _NS(_P("key")),
   294  		modified: _NS(),
   295  		added:    _NS(_P("value")),
   296  	}, {
   297  		lhs:      `{"key":"foot"}`,
   298  		rhs:      `{"key":"foo","value":true}`,
   299  		removed:  _NS(),
   300  		modified: _NS(_P("key")),
   301  		added:    _NS(_P("value")),
   302  	}, {
   303  		lhs:      `{}`,
   304  		rhs:      `{"inner":{}}`,
   305  		removed:  _NS(),
   306  		modified: _NS(),
   307  		added:    _NS(_P("inner")),
   308  	}, {
   309  		lhs:      `{}`,
   310  		rhs:      `{"inner":null}`,
   311  		removed:  _NS(),
   312  		modified: _NS(),
   313  		added:    _NS(_P("inner")),
   314  	}, {
   315  		lhs:      `{"inner":null}`,
   316  		rhs:      `{"inner":{}}`,
   317  		removed:  _NS(),
   318  		modified: _NS(_P("inner")),
   319  		added:    _NS(),
   320  	}, {
   321  		lhs:      `{"inner":{}}`,
   322  		rhs:      `{"inner":null}`,
   323  		removed:  _NS(),
   324  		modified: _NS(_P("inner")),
   325  		added:    _NS(),
   326  	}, {
   327  		lhs:      `{"inner":{}}`,
   328  		rhs:      `{"inner":{}}`,
   329  		removed:  _NS(),
   330  		modified: _NS(),
   331  		added:    _NS(),
   332  	}, {
   333  		lhs:      `{}`,
   334  		rhs:      `{"inner":[]}`,
   335  		removed:  _NS(),
   336  		modified: _NS(),
   337  		added:    _NS(_P("inner")),
   338  	}, {
   339  		lhs:      `{}`,
   340  		rhs:      `{"inner":null}`,
   341  		removed:  _NS(),
   342  		modified: _NS(),
   343  		added:    _NS(_P("inner")),
   344  	}, {
   345  		lhs:      `{"inner":null}`,
   346  		rhs:      `{"inner":[]}`,
   347  		removed:  _NS(),
   348  		modified: _NS(_P("inner")),
   349  		added:    _NS(),
   350  	}, {
   351  		lhs:      `{"inner":[]}`,
   352  		rhs:      `{"inner":null}`,
   353  		removed:  _NS(),
   354  		modified: _NS(_P("inner")),
   355  		added:    _NS(),
   356  	}, {
   357  		lhs:      `{"inner":[]}`,
   358  		rhs:      `{"inner":[]}`,
   359  		removed:  _NS(),
   360  		modified: _NS(),
   361  		added:    _NS(),
   362  	}, {
   363  		lhs:      `{"a":{},"b":{}}`,
   364  		rhs:      `{"a":{},"b":{}}`,
   365  		removed:  _NS(),
   366  		modified: _NS(),
   367  		added:    _NS(),
   368  	}, {
   369  		lhs:      `{"a":{}}`,
   370  		rhs:      `{"b":{}}`,
   371  		removed:  _NS(_P("a")),
   372  		modified: _NS(),
   373  		added:    _NS(_P("b")),
   374  	}, {
   375  		lhs:      `{"a":{"b":{"c":{}}}}`,
   376  		rhs:      `{"a":{"b":{}}}`,
   377  		removed:  _NS(_P("a", "b", "c")),
   378  		modified: _NS(),
   379  		added:    _NS(),
   380  	}, {
   381  		lhs:      `{"a":{"b":{"c":[true]}}}`,
   382  		rhs:      `{"a":{"b":[false]}}`,
   383  		removed:  _NS(_P("a", "b", "c")),
   384  		modified: _NS(_P("a", "b")),
   385  		added:    _NS(),
   386  	}, {
   387  		lhs:      `{"a":{}}`,
   388  		rhs:      `{"a":{"b":"true"}}`,
   389  		removed:  _NS(),
   390  		modified: _NS(),
   391  		added:    _NS(_P("a", "b")),
   392  	}, {
   393  		lhs:      `{"numeric":1}`,
   394  		rhs:      `{"numeric":3.14159}`,
   395  		removed:  _NS(),
   396  		modified: _NS(_P("numeric")),
   397  		added:    _NS(),
   398  	}, {
   399  		lhs:      `{"numeric":3.14159}`,
   400  		rhs:      `{"numeric":1}`,
   401  		removed:  _NS(),
   402  		modified: _NS(_P("numeric")),
   403  		added:    _NS(),
   404  	}, {
   405  		lhs:      `{"string":"aoeu"}`,
   406  		rhs:      `{"bool":true}`,
   407  		removed:  _NS(_P("string")),
   408  		modified: _NS(),
   409  		added:    _NS(_P("bool")),
   410  	}, {
   411  		lhs:      `{"list":["a","b"]}`,
   412  		rhs:      `{"list":["a","b","c"]}`,
   413  		removed:  _NS(),
   414  		modified: _NS(_P("list")),
   415  		added:    _NS(),
   416  	}, {
   417  		lhs:      `{}`,
   418  		rhs:      `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   419  		removed:  _NS(),
   420  		modified: _NS(),
   421  		added:    _NS(_P("list")),
   422  	}, {
   423  		lhs:      `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   424  		rhs:      `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   425  		removed:  _NS(),
   426  		modified: _NS(),
   427  		added:    _NS(),
   428  	}, {
   429  		lhs:      `{"list":[{"key":"a","id":1,"value":{"a":"a"}}]}`,
   430  		rhs:      `{"list":[{"key":"a","id":1,"value":{"a":"b"}}]}`,
   431  		removed:  _NS(),
   432  		modified: _NS(_P("list")),
   433  		added:    _NS(),
   434  	}, {
   435  		lhs:      `{"atomicList":["a","a","a"]}`,
   436  		rhs:      `{"atomicList":null}`,
   437  		removed:  _NS(),
   438  		modified: _NS(_P("atomicList")),
   439  		added:    _NS(),
   440  	}, {
   441  		lhs:      `{"atomicList":["a","a","a"]}`,
   442  		rhs:      `{"atomicList":["a","a"]}`,
   443  		removed:  _NS(),
   444  		modified: _NS(_P("atomicList")),
   445  		added:    _NS(),
   446  	}}
   447  
   448  	for i, quint := range quints {
   449  		quint := quint
   450  		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
   451  			//t.Parallel()
   452  			pt := typed.DeducedParseableType
   453  
   454  			tvLHS, err := pt.FromYAML(quint.lhs)
   455  			if err != nil {
   456  				t.Fatalf("failed to parse lhs: %v", err)
   457  			}
   458  			tvRHS, err := pt.FromYAML(quint.rhs)
   459  			if err != nil {
   460  				t.Fatalf("failed to parse rhs: %v", err)
   461  			}
   462  			got, err := tvLHS.Compare(tvRHS)
   463  			if err != nil {
   464  				t.Fatalf("got validation errors: %v", err)
   465  			}
   466  			t.Logf("got added:\n%s\n", got.Added)
   467  			if !got.Added.Equals(quint.added) {
   468  				t.Errorf("Expected added:\n%s\n", quint.added)
   469  			}
   470  			t.Logf("got modified:\n%s", got.Modified)
   471  			if !got.Modified.Equals(quint.modified) {
   472  				t.Errorf("Expected modified:\n%s\n", quint.modified)
   473  			}
   474  			t.Logf("got removed:\n%s", got.Removed)
   475  			if !got.Removed.Equals(quint.removed) {
   476  				t.Errorf("Expected removed:\n%s\n", quint.removed)
   477  			}
   478  
   479  			// Do the reverse operation and sanity check.
   480  			gotR, err := tvRHS.Compare(tvLHS)
   481  			if err != nil {
   482  				t.Fatalf("(reverse) got validation errors: %v", err)
   483  			}
   484  			if !gotR.Modified.Equals(got.Modified) {
   485  				t.Errorf("reverse operation gave different modified list:\n%s", gotR.Modified)
   486  			}
   487  			if !gotR.Removed.Equals(got.Added) {
   488  				t.Errorf("reverse removed gave different result than added:\n%s", gotR.Removed)
   489  			}
   490  			if !gotR.Added.Equals(got.Removed) {
   491  				t.Errorf("reverse added gave different result than removed:\n%s", gotR.Added)
   492  			}
   493  
   494  		})
   495  	}
   496  }
   497  

View as plain text