...

Source file src/sigs.k8s.io/kustomize/kyaml/yaml/rnode_test.go

Documentation: sigs.k8s.io/kustomize/kyaml/yaml

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package yaml
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	yaml "sigs.k8s.io/yaml/goyaml.v3"
    16  )
    17  
    18  func TestRNodeHasNilEntryInList(t *testing.T) {
    19  	testConfigMap := map[string]interface{}{
    20  		"apiVersion": "v1",
    21  		"kind":       "ConfigMap",
    22  		"metadata": map[string]interface{}{
    23  			"name": "winnie",
    24  		},
    25  	}
    26  	type resultExpected struct {
    27  		hasNil bool
    28  		path   string
    29  	}
    30  
    31  	testCases := map[string]struct {
    32  		theMap map[string]interface{}
    33  		rsExp  resultExpected
    34  	}{
    35  		"actuallyNil": {
    36  			theMap: nil,
    37  			rsExp:  resultExpected{},
    38  		},
    39  		"empty": {
    40  			theMap: map[string]interface{}{},
    41  			rsExp:  resultExpected{},
    42  		},
    43  		"list": {
    44  			theMap: map[string]interface{}{
    45  				"apiVersion": "v1",
    46  				"kind":       "List",
    47  				"items": []interface{}{
    48  					testConfigMap,
    49  					testConfigMap,
    50  				},
    51  			},
    52  			rsExp: resultExpected{},
    53  		},
    54  		"listWithNil": {
    55  			theMap: map[string]interface{}{
    56  				"apiVersion": "v1",
    57  				"kind":       "List",
    58  				"items": []interface{}{
    59  					testConfigMap,
    60  					nil,
    61  				},
    62  			},
    63  			rsExp: resultExpected{
    64  				hasNil: false, // TODO: This should be true.
    65  				path:   "this/should/be/non-empty",
    66  			},
    67  		},
    68  	}
    69  
    70  	for n := range testCases {
    71  		tc := testCases[n]
    72  		t.Run(n, func(t *testing.T) {
    73  			rn, err := FromMap(tc.theMap)
    74  			if !assert.NoError(t, err) {
    75  				t.FailNow()
    76  			}
    77  			hasNil, path := rn.HasNilEntryInList()
    78  			if tc.rsExp.hasNil {
    79  				if !assert.True(t, hasNil) {
    80  					t.FailNow()
    81  				}
    82  				if !assert.Equal(t, tc.rsExp.path, path) {
    83  					t.FailNow()
    84  				}
    85  			} else {
    86  				if !assert.False(t, hasNil) {
    87  					t.FailNow()
    88  				}
    89  				if !assert.Empty(t, path) {
    90  					t.FailNow()
    91  				}
    92  			}
    93  		})
    94  	}
    95  }
    96  
    97  func TestRNodeNewStringRNodeText(t *testing.T) {
    98  	rn := NewStringRNode("cat")
    99  	if !assert.Equal(t, `cat
   100  `,
   101  		rn.MustString()) {
   102  		t.FailNow()
   103  	}
   104  }
   105  
   106  func TestRNodeNewStringRNodeBinary(t *testing.T) {
   107  	rn := NewStringRNode(string([]byte{
   108  		0xff, // non-utf8
   109  		0x68, // h
   110  		0x65, // e
   111  		0x6c, // l
   112  		0x6c, // l
   113  		0x6f, // o
   114  	}))
   115  	if !assert.Equal(t, `!!binary /2hlbGxv
   116  `,
   117  		rn.MustString()) {
   118  		t.FailNow()
   119  	}
   120  }
   121  
   122  func TestRNodeGetDataMap(t *testing.T) {
   123  	emptyMap := map[string]string{}
   124  	testCases := map[string]struct {
   125  		theMap   map[string]interface{}
   126  		expected map[string]string
   127  	}{
   128  		"actuallyNil": {
   129  			theMap:   nil,
   130  			expected: emptyMap,
   131  		},
   132  		"empty": {
   133  			theMap:   map[string]interface{}{},
   134  			expected: emptyMap,
   135  		},
   136  		"mostlyEmpty": {
   137  			theMap: map[string]interface{}{
   138  				"hey": "there",
   139  			},
   140  			expected: emptyMap,
   141  		},
   142  		"noNameConfigMap": {
   143  			theMap: map[string]interface{}{
   144  				"apiVersion": "v1",
   145  				"kind":       "ConfigMap",
   146  			},
   147  			expected: emptyMap,
   148  		},
   149  		"configmap": {
   150  			theMap: map[string]interface{}{
   151  				"apiVersion": "v1",
   152  				"kind":       "ConfigMap",
   153  				"metadata": map[string]interface{}{
   154  					"name": "winnie",
   155  				},
   156  				"data": map[string]string{
   157  					"wine":   "cabernet",
   158  					"truck":  "ford",
   159  					"rocket": "falcon9",
   160  					"planet": "mars",
   161  					"city":   "brownsville",
   162  				},
   163  			},
   164  			// order irrelevant, because assert.Equals is smart about maps.
   165  			expected: map[string]string{
   166  				"city":   "brownsville",
   167  				"wine":   "cabernet",
   168  				"planet": "mars",
   169  				"rocket": "falcon9",
   170  				"truck":  "ford",
   171  			},
   172  		},
   173  	}
   174  
   175  	for n := range testCases {
   176  		tc := testCases[n]
   177  		t.Run(n, func(t *testing.T) {
   178  			rn, err := FromMap(tc.theMap)
   179  			if !assert.NoError(t, err) {
   180  				t.FailNow()
   181  			}
   182  			m := rn.GetDataMap()
   183  			if !assert.Equal(t, tc.expected, m) {
   184  				t.FailNow()
   185  			}
   186  		})
   187  	}
   188  }
   189  
   190  func TestRNodeGetValidatedDataMap(t *testing.T) {
   191  	emptyMap := map[string]string{}
   192  	testCases := map[string]struct {
   193  		theMap         map[string]interface{}
   194  		theAllowedKeys []string
   195  		expected       map[string]string
   196  		expectedError  error
   197  	}{
   198  		"nilResultEmptyKeys": {
   199  			theMap:         nil,
   200  			theAllowedKeys: []string{},
   201  			expected:       emptyMap,
   202  			expectedError:  nil,
   203  		},
   204  		"empty": {
   205  			theMap:         map[string]interface{}{},
   206  			theAllowedKeys: []string{},
   207  			expected:       emptyMap,
   208  			expectedError:  nil,
   209  		},
   210  		"expectedKeysMatch": {
   211  			theMap: map[string]interface{}{
   212  				"apiVersion": "v1",
   213  				"kind":       "ConfigMap",
   214  				"metadata": map[string]interface{}{
   215  					"name": "winnie",
   216  				},
   217  				"data": map[string]string{
   218  					"wine":   "cabernet",
   219  					"truck":  "ford",
   220  					"rocket": "falcon9",
   221  					"planet": "mars",
   222  					"city":   "brownsville",
   223  				},
   224  			},
   225  			theAllowedKeys: []string{
   226  				"wine",
   227  				"truck",
   228  				"rocket",
   229  				"planet",
   230  				"city",
   231  				"plane",
   232  				"country",
   233  			},
   234  			// order irrelevant, because assert.Equals is smart about maps.
   235  			expected: map[string]string{
   236  				"city":   "brownsville",
   237  				"wine":   "cabernet",
   238  				"planet": "mars",
   239  				"rocket": "falcon9",
   240  				"truck":  "ford",
   241  			},
   242  			expectedError: nil,
   243  		},
   244  		"unexpectedKeyInConfigMap": {
   245  			theMap: map[string]interface{}{
   246  				"apiVersion": "v1",
   247  				"kind":       "ConfigMap",
   248  				"metadata": map[string]interface{}{
   249  					"name": "winnie",
   250  				},
   251  				"data": map[string]string{
   252  					"wine":   "cabernet",
   253  					"truck":  "ford",
   254  					"rocket": "falcon9",
   255  				},
   256  			},
   257  			theAllowedKeys: []string{
   258  				"wine",
   259  				"truck",
   260  			},
   261  			// order irrelevant, because assert.Equals is smart about maps.
   262  			expected: map[string]string{
   263  				"wine":   "cabernet",
   264  				"rocket": "falcon9",
   265  				"truck":  "ford",
   266  			},
   267  			expectedError: fmt.Errorf("an unexpected key (rocket) was found"),
   268  		},
   269  	}
   270  
   271  	for n := range testCases {
   272  		tc := testCases[n]
   273  		t.Run(n, func(t *testing.T) {
   274  			rn, err := FromMap(tc.theMap)
   275  			if !assert.NoError(t, err) {
   276  				t.FailNow()
   277  			}
   278  			m, err := rn.GetValidatedDataMap(tc.theAllowedKeys)
   279  			if !assert.Equal(t, tc.expected, m) {
   280  				t.FailNow()
   281  			}
   282  			if !assert.Equal(t, tc.expectedError, err) {
   283  				t.FailNow()
   284  			}
   285  		})
   286  	}
   287  }
   288  
   289  func TestRNodeSetDataMap(t *testing.T) {
   290  	testCases := map[string]struct {
   291  		theMap   map[string]interface{}
   292  		input    map[string]string
   293  		expected map[string]string
   294  	}{
   295  		"empty": {
   296  			theMap: map[string]interface{}{},
   297  			input: map[string]string{
   298  				"wine":  "cabernet",
   299  				"truck": "ford",
   300  			},
   301  			expected: map[string]string{
   302  				"wine":  "cabernet",
   303  				"truck": "ford",
   304  			},
   305  		},
   306  		"replace": {
   307  			theMap: map[string]interface{}{
   308  				"foo": 3,
   309  				"data": map[string]string{
   310  					"rocket": "falcon9",
   311  					"planet": "mars",
   312  				},
   313  			},
   314  			input: map[string]string{
   315  				"wine":  "cabernet",
   316  				"truck": "ford",
   317  			},
   318  			expected: map[string]string{
   319  				"wine":  "cabernet",
   320  				"truck": "ford",
   321  			},
   322  		},
   323  		"clear1": {
   324  			theMap: map[string]interface{}{
   325  				"foo": 3,
   326  				"data": map[string]string{
   327  					"rocket": "falcon9",
   328  					"planet": "mars",
   329  				},
   330  			},
   331  			input:    map[string]string{},
   332  			expected: map[string]string{},
   333  		},
   334  		"clear2": {
   335  			theMap: map[string]interface{}{
   336  				"foo": 3,
   337  				"data": map[string]string{
   338  					"rocket": "falcon9",
   339  					"planet": "mars",
   340  				},
   341  			},
   342  			input:    nil,
   343  			expected: map[string]string{},
   344  		},
   345  	}
   346  
   347  	for n := range testCases {
   348  		tc := testCases[n]
   349  		t.Run(n, func(t *testing.T) {
   350  			rn, err := FromMap(tc.theMap)
   351  			if !assert.NoError(t, err) {
   352  				t.FailNow()
   353  			}
   354  			rn.SetDataMap(tc.input)
   355  			m := rn.GetDataMap()
   356  			if !assert.Equal(t, tc.expected, m) {
   357  				t.FailNow()
   358  			}
   359  		})
   360  	}
   361  }
   362  
   363  func TestRNodeGetValidatedMetadata(t *testing.T) {
   364  	testConfigMap := map[string]interface{}{
   365  		"apiVersion": "v1",
   366  		"kind":       "ConfigMap",
   367  		"metadata": map[string]interface{}{
   368  			"name": "winnie",
   369  		},
   370  	}
   371  	type resultExpected struct {
   372  		out    ResourceMeta
   373  		errMsg string
   374  	}
   375  
   376  	testCases := map[string]struct {
   377  		theMap map[string]interface{}
   378  		rsExp  resultExpected
   379  	}{
   380  		"actuallyNil": {
   381  			theMap: nil,
   382  			rsExp: resultExpected{
   383  				errMsg: "missing Resource metadata",
   384  			},
   385  		},
   386  		"empty": {
   387  			theMap: map[string]interface{}{},
   388  			rsExp: resultExpected{
   389  				errMsg: "missing Resource metadata",
   390  			},
   391  		},
   392  		"mostlyEmpty": {
   393  			theMap: map[string]interface{}{
   394  				"hey": "there",
   395  			},
   396  			rsExp: resultExpected{
   397  				errMsg: "missing Resource metadata",
   398  			},
   399  		},
   400  		"noNameConfigMap": {
   401  			theMap: map[string]interface{}{
   402  				"apiVersion": "v1",
   403  				"kind":       "ConfigMap",
   404  			},
   405  			rsExp: resultExpected{
   406  				errMsg: "missing metadata.name",
   407  			},
   408  		},
   409  		"configmap": {
   410  			theMap: testConfigMap,
   411  			rsExp: resultExpected{
   412  				out: ResourceMeta{
   413  					TypeMeta: TypeMeta{
   414  						APIVersion: "v1",
   415  						Kind:       "ConfigMap",
   416  					},
   417  					ObjectMeta: ObjectMeta{
   418  						NameMeta: NameMeta{
   419  							Name: "winnie",
   420  						},
   421  					},
   422  				},
   423  			},
   424  		},
   425  		"list": {
   426  			theMap: map[string]interface{}{
   427  				"apiVersion": "v1",
   428  				"kind":       "List",
   429  				"items": []interface{}{
   430  					testConfigMap,
   431  					testConfigMap,
   432  				},
   433  			},
   434  			rsExp: resultExpected{
   435  				out: ResourceMeta{
   436  					TypeMeta: TypeMeta{
   437  						APIVersion: "v1",
   438  						Kind:       "List",
   439  					},
   440  				},
   441  			},
   442  		},
   443  		"configmaplist": {
   444  			theMap: map[string]interface{}{
   445  				"apiVersion": "v1",
   446  				"kind":       "ConfigMapList",
   447  				"items": []interface{}{
   448  					testConfigMap,
   449  					testConfigMap,
   450  				},
   451  			},
   452  			rsExp: resultExpected{
   453  				out: ResourceMeta{
   454  					TypeMeta: TypeMeta{
   455  						APIVersion: "v1",
   456  						Kind:       "ConfigMapList",
   457  					},
   458  				},
   459  			},
   460  		},
   461  	}
   462  
   463  	for n := range testCases {
   464  		tc := testCases[n]
   465  		t.Run(n, func(t *testing.T) {
   466  			rn, err := FromMap(tc.theMap)
   467  			if !assert.NoError(t, err) {
   468  				t.FailNow()
   469  			}
   470  			m, err := rn.GetValidatedMetadata()
   471  			if tc.rsExp.errMsg == "" {
   472  				if !assert.NoError(t, err) {
   473  					t.FailNow()
   474  				}
   475  				if !assert.Equal(t, tc.rsExp.out, m) {
   476  					t.FailNow()
   477  				}
   478  			} else {
   479  				if !assert.Error(t, err) {
   480  					t.FailNow()
   481  				}
   482  				if !assert.Contains(t, err.Error(), tc.rsExp.errMsg) {
   483  					t.FailNow()
   484  				}
   485  			}
   486  		})
   487  	}
   488  }
   489  
   490  func TestRNodeMapEmpty(t *testing.T) {
   491  	newRNodeMap, err := NewRNode(nil).Map()
   492  	require.NoError(t, err)
   493  	assert.Equal(t, 0, len(newRNodeMap))
   494  }
   495  
   496  func TestRNodeMap(t *testing.T) {
   497  	wn := NewRNode(nil)
   498  	if err := wn.UnmarshalJSON([]byte(`{
   499    "apiVersion": "apps/v1",
   500    "kind": "Deployment",
   501    "metadata": {
   502      "name": "homer",
   503      "namespace": "simpsons"
   504    }
   505  }`)); err != nil {
   506  		t.Fatalf("unexpected unmarshaljson err: %v", err)
   507  	}
   508  
   509  	expected := map[string]interface{}{
   510  		"apiVersion": "apps/v1",
   511  		"kind":       "Deployment",
   512  		"metadata": map[string]interface{}{
   513  			"name":      "homer",
   514  			"namespace": "simpsons",
   515  		},
   516  	}
   517  
   518  	actual, err := wn.Map()
   519  	require.NoError(t, err)
   520  	if diff := cmp.Diff(expected, actual); diff != "" {
   521  		t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
   522  	}
   523  }
   524  
   525  func TestRNodeFromMap(t *testing.T) {
   526  	testConfigMap := map[string]interface{}{
   527  		"apiVersion": "v1",
   528  		"kind":       "ConfigMap",
   529  		"metadata": map[string]interface{}{
   530  			"name": "winnie",
   531  		},
   532  	}
   533  	type resultExpected struct {
   534  		out string
   535  		err error
   536  	}
   537  
   538  	testCases := map[string]struct {
   539  		theMap map[string]interface{}
   540  		rsExp  resultExpected
   541  	}{
   542  		"actuallyNil": {
   543  			theMap: nil,
   544  			rsExp: resultExpected{
   545  				out: `{}`,
   546  				err: nil,
   547  			},
   548  		},
   549  		"empty": {
   550  			theMap: map[string]interface{}{},
   551  			rsExp: resultExpected{
   552  				out: `{}`,
   553  				err: nil,
   554  			},
   555  		},
   556  		"mostlyEmpty": {
   557  			theMap: map[string]interface{}{
   558  				"hey": "there",
   559  			},
   560  			rsExp: resultExpected{
   561  				out: `hey: there`,
   562  				err: nil,
   563  			},
   564  		},
   565  		"configmap": {
   566  			theMap: testConfigMap,
   567  			rsExp: resultExpected{
   568  				out: `
   569  apiVersion: v1
   570  kind: ConfigMap
   571  metadata:
   572    name: winnie
   573  `,
   574  				err: nil,
   575  			},
   576  		},
   577  		"list": {
   578  			theMap: map[string]interface{}{
   579  				"apiVersion": "v1",
   580  				"kind":       "List",
   581  				"items": []interface{}{
   582  					testConfigMap,
   583  					testConfigMap,
   584  				},
   585  			},
   586  			rsExp: resultExpected{
   587  				out: `
   588  apiVersion: v1
   589  items:
   590  - apiVersion: v1
   591    kind: ConfigMap
   592    metadata:
   593      name: winnie
   594  - apiVersion: v1
   595    kind: ConfigMap
   596    metadata:
   597      name: winnie
   598  kind: List
   599  `,
   600  				err: nil,
   601  			},
   602  		},
   603  		"configmaplist": {
   604  			theMap: map[string]interface{}{
   605  				"apiVersion": "v1",
   606  				"kind":       "ConfigMapList",
   607  				"items": []interface{}{
   608  					testConfigMap,
   609  					testConfigMap,
   610  				},
   611  			},
   612  			rsExp: resultExpected{
   613  				out: `
   614  apiVersion: v1
   615  items:
   616  - apiVersion: v1
   617    kind: ConfigMap
   618    metadata:
   619      name: winnie
   620  - apiVersion: v1
   621    kind: ConfigMap
   622    metadata:
   623      name: winnie
   624  kind: ConfigMapList
   625  `,
   626  				err: nil,
   627  			},
   628  		},
   629  	}
   630  
   631  	for n := range testCases {
   632  		tc := testCases[n]
   633  		t.Run(n, func(t *testing.T) {
   634  			rn, err := FromMap(tc.theMap)
   635  			if tc.rsExp.err == nil {
   636  				if !assert.NoError(t, err) {
   637  					t.FailNow()
   638  				}
   639  				if !assert.Equal(t,
   640  					strings.TrimSpace(tc.rsExp.out),
   641  					strings.TrimSpace(rn.MustString())) {
   642  					t.FailNow()
   643  				}
   644  			} else {
   645  				if !assert.Error(t, err) {
   646  					t.FailNow()
   647  				}
   648  				if !assert.Equal(t, tc.rsExp.err, err) {
   649  					t.FailNow()
   650  				}
   651  			}
   652  		})
   653  	}
   654  }
   655  
   656  // Test that non-UTF8 characters in comments don't cause failures
   657  func TestRNode_GetMeta_UTF16(t *testing.T) {
   658  	sr, err := Parse(`apiVersion: rbac.istio.io/v1alpha1
   659  kind: ServiceRole
   660  metadata:
   661    name: wildcard
   662    namespace: default
   663    # If set to [“*”], it refers to all services in the namespace
   664    annotations:
   665      foo: bar
   666  spec:
   667    rules:
   668      # There is one service in default namespace, should not result in a validation error
   669      # If set to [“*”], it refers to all services in the namespace
   670      - services: ["*"]
   671        methods: ["GET", "HEAD"]
   672  `)
   673  	if !assert.NoError(t, err) {
   674  		t.FailNow()
   675  	}
   676  	actual, err := sr.GetMeta()
   677  	if !assert.NoError(t, err) {
   678  		t.FailNow()
   679  	}
   680  
   681  	expected := ResourceMeta{
   682  		TypeMeta: TypeMeta{
   683  			APIVersion: "rbac.istio.io/v1alpha1",
   684  			Kind:       "ServiceRole",
   685  		},
   686  		ObjectMeta: ObjectMeta{
   687  			NameMeta: NameMeta{
   688  				Name:      "wildcard",
   689  				Namespace: "default",
   690  			},
   691  			Annotations: map[string]string{"foo": "bar"},
   692  		},
   693  	}
   694  	if !assert.Equal(t, expected, actual) {
   695  		t.FailNow()
   696  	}
   697  }
   698  
   699  func TestDeAnchor(t *testing.T) {
   700  	rn, err := Parse(`
   701  apiVersion: v1
   702  kind: ConfigMap
   703  metadata:
   704    name: wildcard
   705  data:
   706    color: &color-used blue
   707    feeling: *color-used
   708  `)
   709  	require.NoError(t, err)
   710  	require.NoError(t, rn.DeAnchor())
   711  	actual, err := rn.String()
   712  	require.NoError(t, err)
   713  	assert.Equal(t, strings.TrimSpace(`
   714  apiVersion: v1
   715  kind: ConfigMap
   716  metadata:
   717    name: wildcard
   718  data:
   719    color: blue
   720    feeling: blue
   721  `), strings.TrimSpace(actual))
   722  }
   723  
   724  func TestDeAnchorMerge(t *testing.T) {
   725  	testCases := []struct {
   726  		description string
   727  		input       string
   728  		expected    string
   729  		expectedErr error
   730  	}{
   731  		// *********
   732  		// Test Case
   733  		// *********
   734  		{
   735  			description: "simplest merge tag",
   736  			input: `
   737  apiVersion: v1
   738  kind: MergeTagTest
   739  metadata:
   740    name: test
   741  data:
   742    color: &color-used
   743      foo: bar
   744    primaryColor:
   745      <<: *color-used
   746  `,
   747  			expected: `
   748  apiVersion: v1
   749  kind: MergeTagTest
   750  metadata:
   751    name: test
   752  data:
   753    color:
   754      foo: bar
   755    primaryColor:
   756      foo: bar
   757  `,
   758  		},
   759  		// *********
   760  		// Test Case
   761  		// *********
   762  		{
   763  			description: "keep duplicated keys",
   764  			input: `
   765  apiVersion: v1
   766  kind: MergeTagTest
   767  metadata:
   768    name: test
   769  data:
   770    color: "#FF0000"
   771    color: "#FF00FF"
   772  `,
   773  			expected: `
   774  apiVersion: v1
   775  kind: MergeTagTest
   776  metadata:
   777    name: test
   778  data:
   779    color: "#FF0000"
   780    color: "#FF00FF"
   781  `,
   782  		},
   783  		// *********
   784  		// Test Case
   785  		// *********
   786  		{
   787  			description: "keep json",
   788  			input:       `{"apiVersion": "v1", "kind": "MergeTagTest", "spec": {"color": {"rgb": "#FF0000"}}}`,
   789  			expected:    `{"apiVersion": "v1", "kind": "MergeTagTest", "spec": {"color": {"rgb": "#FF0000"}}}`,
   790  		},
   791  		// *********
   792  		// Test Case
   793  		// *********
   794  		{
   795  			description: "keep comments",
   796  			input: `
   797  apiVersion: v1
   798  kind: MergeTagTest
   799  metadata:
   800    name: test
   801  data:
   802    color: &color-used
   803      foo: bar
   804    primaryColor:
   805      # use same color because is pretty
   806      rgb: "#FF0000"
   807  `,
   808  			expected: `
   809  apiVersion: v1
   810  kind: MergeTagTest
   811  metadata:
   812    name: test
   813  data:
   814    color:
   815      foo: bar
   816    primaryColor:
   817      # use same color because is pretty
   818      rgb: "#FF0000"
   819  `,
   820  		},
   821  		// *********
   822  		// Test Case
   823  		// *********
   824  		{
   825  			description: "works with explicit merge tag",
   826  			input: `
   827  apiVersion: v1
   828  kind: MergeTagTest
   829  metadata:
   830    name: test
   831  data:
   832    color: &color-used
   833      foo: bar
   834    primaryColor:
   835      !!merge <<: *color-used
   836  `,
   837  			expected: `
   838  apiVersion: v1
   839  kind: MergeTagTest
   840  metadata:
   841    name: test
   842  data:
   843    color:
   844      foo: bar
   845    primaryColor:
   846      foo: bar
   847  `,
   848  		},
   849  		// *********
   850  		// Test Case
   851  		// *********
   852  		{
   853  			description: "works with explicit long merge tag",
   854  			input: `
   855  apiVersion: v1
   856  kind: MergeTagTest
   857  metadata:
   858    name: test
   859  data:
   860    color: &color-used
   861      foo: bar
   862    primaryColor:
   863      !<tag:yaml.org,2002:merge> "<<" : *color-used
   864  `,
   865  			expected: `
   866  apiVersion: v1
   867  kind: MergeTagTest
   868  metadata:
   869    name: test
   870  data:
   871    color:
   872      foo: bar
   873    primaryColor:
   874      foo: bar
   875  `,
   876  		},
   877  		// *********
   878  		// Test Case
   879  		// *********
   880  		{
   881  			description: "merging properties",
   882  			input: `
   883  apiVersion: v1
   884  kind: MergeTagTest
   885  metadata:
   886    name: test
   887  data:
   888    color: &color-used
   889      rgb: "#FF0000"
   890    primaryColor:
   891      <<: *color-used
   892      pretty: true
   893  `,
   894  			expected: `
   895  apiVersion: v1
   896  kind: MergeTagTest
   897  metadata:
   898    name: test
   899  data:
   900    color:
   901      rgb: "#FF0000"
   902    primaryColor:
   903      pretty: true
   904      rgb: "#FF0000"
   905  `,
   906  		},
   907  		// *********
   908  		// Test Case
   909  		// *********
   910  		{
   911  			description: "overriding value",
   912  			input: `
   913  apiVersion: v1
   914  kind: MergeTagTest
   915  metadata:
   916    name: test
   917  data:
   918    color: &color-used
   919      rgb: "#FF0000"
   920      pretty: false
   921    primaryColor:
   922      <<: *color-used
   923      pretty: true
   924  `,
   925  			expected: `
   926  apiVersion: v1
   927  kind: MergeTagTest
   928  metadata:
   929    name: test
   930  data:
   931    color:
   932      rgb: "#FF0000"
   933      pretty: false
   934    primaryColor:
   935      pretty: true
   936      rgb: "#FF0000"
   937  `,
   938  		},
   939  		// *********
   940  		// Test Case
   941  		// *********
   942  		{
   943  			description: "returns error when defining multiple merge keys",
   944  			input: `
   945  apiVersion: v1
   946  kind: MergeTagTest
   947  metadata:
   948    name: test
   949  data:
   950    color: &color
   951      rgb: "#FF0000"
   952      pretty: false
   953    primaryColor: &primary
   954      rgb: "#0000FF"
   955      alpha: 50
   956    secondaryColor:
   957      <<: *color
   958      <<: *primary
   959      secondary: true
   960  `,
   961  			expectedErr: fmt.Errorf("duplicate merge key"),
   962  		},
   963  		// *********
   964  		// Test Case
   965  		// *********
   966  		{
   967  			description: "merging multiple anchors with sequence node",
   968  			input: `
   969  apiVersion: v1
   970  kind: MergeTagTest
   971  metadata:
   972    name: test
   973  data:
   974    color: &color
   975      rgb: "#FF0000"
   976      pretty: false
   977    primaryColor: &primary
   978      rgb: "#0000FF"
   979      alpha: 50
   980    secondaryColor:
   981      <<: [ *color, *primary ]
   982      secondary: true
   983  `,
   984  			expected: `
   985  apiVersion: v1
   986  kind: MergeTagTest
   987  metadata:
   988    name: test
   989  data:
   990    color:
   991      rgb: "#FF0000"
   992      pretty: false
   993    primaryColor:
   994      rgb: "#0000FF"
   995      alpha: 50
   996    secondaryColor:
   997      secondary: true
   998      rgb: "#FF0000"
   999      alpha: 50
  1000      pretty: false
  1001  `,
  1002  		},
  1003  		// *********
  1004  		// Test Case
  1005  		// *********
  1006  		{
  1007  			description: "merging inline map",
  1008  			input: `
  1009  apiVersion: v1
  1010  kind: MergeTagTest
  1011  metadata:
  1012    name: test
  1013  data:
  1014    color: &color
  1015      rgb: "#FF0000"
  1016      pretty: false
  1017    primaryColor:
  1018      <<: {"pretty": true}
  1019      rgb: "#0000FF"
  1020      alpha: 50
  1021  `,
  1022  			expected: `
  1023  apiVersion: v1
  1024  kind: MergeTagTest
  1025  metadata:
  1026    name: test
  1027  data:
  1028    color:
  1029      rgb: "#FF0000"
  1030      pretty: false
  1031    primaryColor:
  1032      rgb: "#0000FF"
  1033      alpha: 50
  1034      "pretty": true
  1035  `,
  1036  		},
  1037  		// *********
  1038  		// Test Case
  1039  		// *********
  1040  		{
  1041  			description: "merging inline sequence map",
  1042  			input: `
  1043  apiVersion: v1
  1044  kind: MergeTagTest
  1045  metadata:
  1046    name: test
  1047  data:
  1048    color: &color
  1049      rgb: "#FF0000"
  1050      pretty: false
  1051    primaryColor:
  1052      <<: [ *color, {"name": "ugly blue"}]
  1053      rgb: "#0000FF"
  1054      alpha: 50
  1055  `,
  1056  			expected: `
  1057  apiVersion: v1
  1058  kind: MergeTagTest
  1059  metadata:
  1060    name: test
  1061  data:
  1062    color:
  1063      rgb: "#FF0000"
  1064      pretty: false
  1065    primaryColor:
  1066      rgb: "#0000FF"
  1067      alpha: 50
  1068      "name": "ugly blue"
  1069      pretty: false
  1070  `,
  1071  		},
  1072  		// *********
  1073  		// Test Case
  1074  		// *********
  1075  		{
  1076  			description: "error on nested lists on merges",
  1077  			input: `
  1078  apiVersion: v1
  1079  kind: MergeTagTest
  1080  metadata:
  1081    name: test
  1082  data:
  1083    color: &color
  1084      rgb: "#FF0000"
  1085      pretty: false
  1086    primaryColor:
  1087      <<: [ *color, [{"name": "ugly blue"}]]
  1088      rgb: "#0000FF"
  1089      alpha: 50
  1090  `,
  1091  			expectedErr: fmt.Errorf("invalid map merge: received a nested sequence"),
  1092  		},
  1093  		// *********
  1094  		// Test Case
  1095  		// *********
  1096  		{
  1097  			description: "error on non-map references on merges",
  1098  			input: `
  1099  apiVersion: v1
  1100  kind: MergeTagTest
  1101  metadata:
  1102    name: test
  1103  data:
  1104    color: &color
  1105      - rgb: "#FF0000"
  1106        pretty: false
  1107    primaryColor:
  1108      <<: [ *color, [{"name": "ugly blue"}]]
  1109      rgb: "#0000FF"
  1110      alpha: 50
  1111  `,
  1112  			expectedErr: fmt.Errorf("invalid map merge: received alias for a non-map value"),
  1113  		},
  1114  		// *********
  1115  		// Test Case
  1116  		// *********
  1117  		{
  1118  			description: "merging on a list",
  1119  			input: `
  1120  apiVersion: v1
  1121  kind: MergeTagTestList
  1122  items:
  1123  - apiVersion: v1
  1124    kind: MergeTagTest
  1125    metadata:
  1126      name: test
  1127    spec: &merge-spec
  1128      something: true
  1129  - apiVersion: v1
  1130    kind: MergeTagTest
  1131    metadata:
  1132      name: test
  1133    spec:
  1134      <<: *merge-spec
  1135  `,
  1136  			expected: `
  1137  apiVersion: v1
  1138  kind: MergeTagTestList
  1139  items:
  1140  - apiVersion: v1
  1141    kind: MergeTagTest
  1142    metadata:
  1143      name: test
  1144    spec:
  1145      something: true
  1146  - apiVersion: v1
  1147    kind: MergeTagTest
  1148    metadata:
  1149      name: test
  1150    spec:
  1151      something: true
  1152  `,
  1153  		},
  1154  	}
  1155  
  1156  	for _, tc := range testCases {
  1157  		t.Run(tc.description, func(t *testing.T) {
  1158  			rn, err := Parse(tc.input)
  1159  
  1160  			require.NoError(t, err)
  1161  			err = rn.DeAnchor()
  1162  			if tc.expectedErr == nil {
  1163  				require.NoError(t, err)
  1164  				actual, err := rn.String()
  1165  				require.NoError(t, err)
  1166  				assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual))
  1167  			} else {
  1168  				assert.NotNil(t, err)
  1169  				assert.Equal(t, tc.expectedErr.Error(), err.Error())
  1170  			}
  1171  		})
  1172  	}
  1173  }
  1174  func TestRNode_UnmarshalJSON(t *testing.T) {
  1175  	testCases := []struct {
  1176  		testName string
  1177  		input    string
  1178  		output   string
  1179  	}{
  1180  		{
  1181  			testName: "simple document",
  1182  			input:    `{"hello":"world"}`,
  1183  			output:   `hello: world`,
  1184  		},
  1185  		{
  1186  			testName: "nested structure",
  1187  			input: `
  1188  {
  1189    "apiVersion": "apps/v1",
  1190    "kind": "Deployment",
  1191    "metadata": {
  1192      "name": "my-deployment",
  1193      "namespace": "default"
  1194    }
  1195  }
  1196  `,
  1197  			output: `
  1198  apiVersion: apps/v1
  1199  kind: Deployment
  1200  metadata:
  1201    name: my-deployment
  1202    namespace: default
  1203  `,
  1204  		},
  1205  	}
  1206  
  1207  	for i := range testCases {
  1208  		tc := testCases[i]
  1209  		t.Run(tc.testName, func(t *testing.T) {
  1210  			instance := &RNode{}
  1211  			err := instance.UnmarshalJSON([]byte(tc.input))
  1212  			if !assert.NoError(t, err) {
  1213  				t.FailNow()
  1214  			}
  1215  
  1216  			actual, err := instance.String()
  1217  			if !assert.NoError(t, err) {
  1218  				t.FailNow()
  1219  			}
  1220  
  1221  			if !assert.Equal(t,
  1222  				strings.TrimSpace(tc.output), strings.TrimSpace(actual)) {
  1223  				t.FailNow()
  1224  			}
  1225  		})
  1226  	}
  1227  }
  1228  
  1229  func TestRNode_MarshalJSON(t *testing.T) {
  1230  	tests := []struct {
  1231  		name string
  1232  		ydoc string
  1233  		want string
  1234  	}{
  1235  		{
  1236  			name: "object",
  1237  			ydoc: `
  1238  hello: world
  1239  `,
  1240  			want: `{"hello":"world"}`,
  1241  		},
  1242  		{
  1243  			name: "array",
  1244  			ydoc: `
  1245  - name: s1
  1246  - name: s2
  1247  `,
  1248  			want: `[{"name":"s1"},{"name":"s2"}]`,
  1249  		},
  1250  	}
  1251  	for idx := range tests {
  1252  		tt := tests[idx]
  1253  		t.Run(tt.name, func(t *testing.T) {
  1254  			instance, err := Parse(tt.ydoc)
  1255  			if !assert.NoError(t, err) {
  1256  				t.FailNow()
  1257  			}
  1258  
  1259  			actual, err := instance.MarshalJSON()
  1260  			if !assert.NoError(t, err) {
  1261  				t.FailNow()
  1262  			}
  1263  
  1264  			if !assert.Equal(t,
  1265  				strings.TrimSpace(tt.want), strings.TrimSpace(string(actual))) {
  1266  				t.FailNow()
  1267  			}
  1268  		})
  1269  	}
  1270  }
  1271  
  1272  func TestConvertJSONToYamlNode(t *testing.T) {
  1273  	inputJSON := `{"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`
  1274  	expected := `enum:
  1275  - allowedValue1
  1276  - allowedValue2
  1277  maxLength: 15
  1278  type: string
  1279  `
  1280  
  1281  	node, err := ConvertJSONToYamlNode(inputJSON)
  1282  	if !assert.NoError(t, err) {
  1283  		t.FailNow()
  1284  	}
  1285  	actual, err := node.String()
  1286  	if !assert.NoError(t, err) {
  1287  		t.FailNow()
  1288  	}
  1289  	assert.Equal(t, expected, actual)
  1290  }
  1291  
  1292  func TestIsMissingOrNull(t *testing.T) {
  1293  	if !IsMissingOrNull(nil) {
  1294  		t.Fatalf("input: nil")
  1295  	}
  1296  	// missing value or null value
  1297  	if !IsMissingOrNull(NewRNode(nil)) {
  1298  		t.Fatalf("input: nil value")
  1299  	}
  1300  
  1301  	if IsMissingOrNull(NewScalarRNode("foo")) {
  1302  		t.Fatalf("input: valid node")
  1303  	}
  1304  	// node with NullNodeTag
  1305  	if !IsMissingOrNull(MakeNullNode()) {
  1306  		t.Fatalf("input: with NullNodeTag")
  1307  	}
  1308  
  1309  	// empty array. empty array is not expected as empty
  1310  	if IsMissingOrNull(NewListRNode()) {
  1311  		t.Fatalf("input: empty array")
  1312  	}
  1313  
  1314  	// array with 1 item
  1315  	node := NewListRNode("foo")
  1316  	if IsMissingOrNull(node) {
  1317  		t.Fatalf("input: array with 1 item")
  1318  	}
  1319  
  1320  	// delete the item in array
  1321  	node.value.Content = nil
  1322  	if IsMissingOrNull(node) {
  1323  		t.Fatalf("input: empty array")
  1324  	}
  1325  }
  1326  
  1327  func TestCopy(t *testing.T) {
  1328  	rn := RNode{
  1329  		fieldPath: []string{"fp1", "fp2"},
  1330  		value: &Node{
  1331  			Kind: 200,
  1332  		},
  1333  		Match: []string{"m1", "m2"},
  1334  	}
  1335  	rnC := rn.Copy()
  1336  	if !reflect.DeepEqual(&rn, rnC) {
  1337  		t.Fatalf("copy %v is not deep equal to %v", rnC, rn)
  1338  	}
  1339  	tmp := rn.value.Kind
  1340  	rn.value.Kind = 666
  1341  	if reflect.DeepEqual(rn, rnC) {
  1342  		t.Fatalf("changing component should break equality")
  1343  	}
  1344  	rn.value.Kind = tmp
  1345  	if !reflect.DeepEqual(&rn, rnC) {
  1346  		t.Fatalf("should be back to normal")
  1347  	}
  1348  	rn.fieldPath[0] = "Different"
  1349  	if reflect.DeepEqual(rn, rnC) {
  1350  		t.Fatalf("changing fieldpath should break equality")
  1351  	}
  1352  }
  1353  
  1354  func TestFieldRNodes(t *testing.T) {
  1355  	testCases := []struct {
  1356  		testName string
  1357  		input    string
  1358  		output   []string
  1359  		err      string
  1360  	}{
  1361  		{
  1362  			testName: "simple document",
  1363  			input: `apiVersion: example.com/v1beta1
  1364  kind: Example1
  1365  spec:
  1366    list:
  1367    - "a"
  1368    - "b"
  1369    - "c"`,
  1370  			output: []string{"apiVersion", "kind", "spec", "list"},
  1371  		},
  1372  		{
  1373  			testName: "non mapping node error",
  1374  			input:    `apiVersion`,
  1375  			err: `wrong node kind: expected MappingNode but got ScalarNode: node contents:
  1376  apiVersion
  1377  `,
  1378  		},
  1379  	}
  1380  
  1381  	for i := range testCases {
  1382  		tc := testCases[i]
  1383  		t.Run(tc.testName, func(t *testing.T) {
  1384  			rNode, err := Parse(tc.input)
  1385  			if !assert.NoError(t, err) {
  1386  				t.FailNow()
  1387  			}
  1388  
  1389  			fieldRNodes, err := rNode.FieldRNodes()
  1390  			if tc.err == "" {
  1391  				if !assert.NoError(t, err) {
  1392  					t.FailNow()
  1393  				}
  1394  			} else {
  1395  				if !assert.Equal(t, tc.err, err.Error()) {
  1396  					t.FailNow()
  1397  				}
  1398  			}
  1399  			for i := range fieldRNodes {
  1400  				actual, err := fieldRNodes[i].String()
  1401  				if !assert.NoError(t, err) {
  1402  					t.FailNow()
  1403  				}
  1404  				if !assert.Equal(t, tc.output[i], strings.TrimSpace(actual)) {
  1405  					t.FailNow()
  1406  				}
  1407  			}
  1408  		})
  1409  	}
  1410  }
  1411  
  1412  func TestIsEmptyMap(t *testing.T) {
  1413  	node := NewMapRNode(nil)
  1414  	// empty map
  1415  	if !IsEmptyMap(node) {
  1416  		t.Fatalf("input: empty map")
  1417  	}
  1418  	// map with 1 item
  1419  	node = NewMapRNode(&map[string]string{
  1420  		"foo": "bar",
  1421  	})
  1422  	if IsEmptyMap(node) {
  1423  		t.Fatalf("input: map with 1 item")
  1424  	}
  1425  	// delete the item in map
  1426  	node.value.Content = nil
  1427  	if !IsEmptyMap(node) {
  1428  		t.Fatalf("input: empty map")
  1429  	}
  1430  }
  1431  
  1432  func TestIsNil(t *testing.T) {
  1433  	var rn *RNode
  1434  
  1435  	if !rn.IsNil() {
  1436  		t.Fatalf("uninitialized RNode should be nil")
  1437  	}
  1438  
  1439  	if !NewRNode(nil).IsNil() {
  1440  		t.Fatalf("missing value YNode should be nil")
  1441  	}
  1442  
  1443  	if MakeNullNode().IsNil() {
  1444  		t.Fatalf("value tagged null is not nil")
  1445  	}
  1446  
  1447  	if NewMapRNode(nil).IsNil() {
  1448  		t.Fatalf("empty map not nil")
  1449  	}
  1450  
  1451  	if NewListRNode().IsNil() {
  1452  		t.Fatalf("empty list not nil")
  1453  	}
  1454  }
  1455  
  1456  func TestIsTaggedNull(t *testing.T) {
  1457  	var rn *RNode
  1458  
  1459  	if rn.IsTaggedNull() {
  1460  		t.Fatalf("nil RNode cannot be tagged")
  1461  	}
  1462  
  1463  	if NewRNode(nil).IsTaggedNull() {
  1464  		t.Fatalf("bare RNode should not be tagged")
  1465  	}
  1466  
  1467  	if !MakeNullNode().IsTaggedNull() {
  1468  		t.Fatalf("a null node is tagged null by definition")
  1469  	}
  1470  
  1471  	if NewMapRNode(nil).IsTaggedNull() {
  1472  		t.Fatalf("empty map should not be tagged null")
  1473  	}
  1474  
  1475  	if NewListRNode().IsTaggedNull() {
  1476  		t.Fatalf("empty list should not be tagged null")
  1477  	}
  1478  }
  1479  
  1480  func TestRNodeIsNilOrEmpty(t *testing.T) {
  1481  	var rn *RNode
  1482  
  1483  	if !rn.IsNilOrEmpty() {
  1484  		t.Fatalf("uninitialized RNode should be empty")
  1485  	}
  1486  
  1487  	if !NewRNode(nil).IsNilOrEmpty() {
  1488  		t.Fatalf("missing value YNode should be empty")
  1489  	}
  1490  
  1491  	if !MakeNullNode().IsNilOrEmpty() {
  1492  		t.Fatalf("value tagged null should be empty")
  1493  	}
  1494  
  1495  	if !NewMapRNode(nil).IsNilOrEmpty() {
  1496  		t.Fatalf("empty map should be empty")
  1497  	}
  1498  
  1499  	if NewMapRNode(&map[string]string{"foo": "bar"}).IsNilOrEmpty() {
  1500  		t.Fatalf("non-empty map should not be empty")
  1501  	}
  1502  
  1503  	if !NewListRNode().IsNilOrEmpty() {
  1504  		t.Fatalf("empty list should be empty")
  1505  	}
  1506  
  1507  	if NewListRNode("foo").IsNilOrEmpty() {
  1508  		t.Fatalf("non-empty list should not be empty")
  1509  	}
  1510  
  1511  	if !NewRNode(&Node{}).IsNilOrEmpty() {
  1512  		t.Fatalf("zero YNode should be empty")
  1513  	}
  1514  }
  1515  
  1516  const deploymentJSON = `
  1517  {
  1518    "apiVersion": "apps/v1",
  1519    "kind": "Deployment",
  1520    "metadata": {
  1521      "name": "homer",
  1522      "namespace": "simpsons",
  1523      "labels": {
  1524        "fruit": "apple",
  1525        "veggie": "carrot"
  1526      },
  1527      "annotations": {
  1528        "area": "51",
  1529        "greeting": "Take me to your leader."
  1530      }
  1531    }
  1532  }
  1533  `
  1534  
  1535  func TestRNodeSetNamespace(t *testing.T) {
  1536  	n := NewRNode(nil)
  1537  	assert.Equal(t, "", n.GetNamespace())
  1538  	require.NoError(t, n.SetNamespace(""))
  1539  	assert.Equal(t, "", n.GetNamespace())
  1540  	if err := n.UnmarshalJSON([]byte(deploymentJSON)); err != nil {
  1541  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  1542  	}
  1543  	require.NoError(t, n.SetNamespace("flanders"))
  1544  	assert.Equal(t, "flanders", n.GetNamespace())
  1545  }
  1546  
  1547  func TestRNodeSetLabels(t *testing.T) {
  1548  	n := NewRNode(nil)
  1549  	assert.Equal(t, 0, len(n.GetLabels()))
  1550  	require.NoError(t, n.SetLabels(map[string]string{}))
  1551  	assert.Equal(t, 0, len(n.GetLabels()))
  1552  
  1553  	n = NewRNode(nil)
  1554  	if err := n.UnmarshalJSON([]byte(deploymentJSON)); err != nil {
  1555  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  1556  	}
  1557  	labels := n.GetLabels()
  1558  	assert.Equal(t, 2, len(labels))
  1559  	assert.Equal(t, "apple", labels["fruit"])
  1560  	assert.Equal(t, "carrot", labels["veggie"])
  1561  	require.NoError(t, n.SetLabels(map[string]string{
  1562  		"label1": "foo",
  1563  		"label2": "bar",
  1564  	}))
  1565  	labels = n.GetLabels()
  1566  	assert.Equal(t, 2, len(labels))
  1567  	assert.Equal(t, "foo", labels["label1"])
  1568  	assert.Equal(t, "bar", labels["label2"])
  1569  	require.NoError(t, n.SetLabels(map[string]string{}))
  1570  	assert.Equal(t, 0, len(n.GetLabels()))
  1571  }
  1572  
  1573  func TestRNodeGetAnnotations(t *testing.T) {
  1574  	n := NewRNode(nil)
  1575  	assert.Equal(t, 0, len(n.GetAnnotations()))
  1576  	require.NoError(t, n.SetAnnotations(map[string]string{}))
  1577  	assert.Equal(t, 0, len(n.GetAnnotations()))
  1578  
  1579  	n = NewRNode(nil)
  1580  	if err := n.UnmarshalJSON([]byte(deploymentJSON)); err != nil {
  1581  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  1582  	}
  1583  	annotations := n.GetAnnotations()
  1584  	assert.Equal(t, 2, len(annotations))
  1585  	assert.Equal(t, "51", annotations["area"])
  1586  	assert.Equal(t, "Take me to your leader.", annotations["greeting"])
  1587  	require.NoError(t, n.SetAnnotations(map[string]string{
  1588  		"annotation1": "foo",
  1589  		"annotation2": "bar",
  1590  	}))
  1591  	annotations = n.GetAnnotations()
  1592  	assert.Equal(t, 2, len(annotations))
  1593  	assert.Equal(t, "foo", annotations["annotation1"])
  1594  	assert.Equal(t, "bar", annotations["annotation2"])
  1595  	require.NoError(t, n.SetAnnotations(map[string]string{}))
  1596  	assert.Equal(t, 0, len(n.GetAnnotations()))
  1597  }
  1598  
  1599  func TestRNodeMatchesAnnotationSelector(t *testing.T) {
  1600  	rn := NewRNode(nil)
  1601  	require.NoError(t, rn.UnmarshalJSON([]byte(deploymentJSON)))
  1602  	testcases := map[string]struct {
  1603  		selector string
  1604  		matches  bool
  1605  		errMsg   string
  1606  	}{
  1607  		"select_01": {
  1608  			selector: ".*",
  1609  			matches:  false,
  1610  			errMsg:   "name part must consist of alphanumeric character",
  1611  		},
  1612  		"select_02": {
  1613  			selector: "area=51",
  1614  			matches:  true,
  1615  		},
  1616  		"select_03": {
  1617  			selector: "area=florida",
  1618  			matches:  false,
  1619  		},
  1620  		"select_04": {
  1621  			selector: "area in (disneyland, 51, iowa)",
  1622  			matches:  true,
  1623  		},
  1624  		"select_05": {
  1625  			selector: "area in (disneyland, iowa)",
  1626  			matches:  false,
  1627  		},
  1628  		"select_06": {
  1629  			selector: "area notin (disneyland, 51, iowa)",
  1630  			matches:  false,
  1631  		},
  1632  	}
  1633  	for n, tc := range testcases {
  1634  		gotMatch, err := rn.MatchesAnnotationSelector(tc.selector)
  1635  		if tc.errMsg == "" {
  1636  			require.NoError(t, err)
  1637  			assert.Equalf(
  1638  				t, tc.matches, gotMatch, "test=%s selector=%v", n, tc.selector)
  1639  		} else {
  1640  			assert.Truef(
  1641  				t, strings.Contains(err.Error(), tc.errMsg),
  1642  				"test=%s selector=%v", n, tc.selector)
  1643  		}
  1644  	}
  1645  }
  1646  
  1647  func TestRNodeMatchesLabelSelector(t *testing.T) {
  1648  	rn := NewRNode(nil)
  1649  	require.NoError(t, rn.UnmarshalJSON([]byte(deploymentJSON)))
  1650  	testcases := map[string]struct {
  1651  		selector string
  1652  		matches  bool
  1653  		errMsg   string
  1654  	}{
  1655  		"select_01": {
  1656  			selector: ".*",
  1657  			matches:  false,
  1658  			errMsg:   "name part must consist of alphanumeric character",
  1659  		},
  1660  		"select_02": {
  1661  			selector: "fruit=apple",
  1662  			matches:  true,
  1663  		},
  1664  		"select_03": {
  1665  			selector: "fruit=banana",
  1666  			matches:  false,
  1667  		},
  1668  		"select_04": {
  1669  			selector: "fruit in (orange, banana, apple)",
  1670  			matches:  true,
  1671  		},
  1672  		"select_05": {
  1673  			selector: "fruit in (orange, banana)",
  1674  			matches:  false,
  1675  		},
  1676  		"select_06": {
  1677  			selector: "fruit notin (orange, banana, apple)",
  1678  			matches:  false,
  1679  		},
  1680  	}
  1681  	for n, tc := range testcases {
  1682  		gotMatch, err := rn.MatchesLabelSelector(tc.selector)
  1683  		if tc.errMsg == "" {
  1684  			require.NoError(t, err)
  1685  			assert.Equalf(
  1686  				t, tc.matches, gotMatch, "test=%s selector=%v", n, tc.selector)
  1687  		} else {
  1688  			assert.Truef(
  1689  				t, strings.Contains(err.Error(), tc.errMsg),
  1690  				"test=%s selector=%v", n, tc.selector)
  1691  		}
  1692  	}
  1693  }
  1694  
  1695  const (
  1696  	deploymentLittleJson = `{"apiVersion":"apps/v1","kind":"Deployment",` +
  1697  		`"metadata":{"name":"homer","namespace":"simpsons"}}`
  1698  
  1699  	deploymentBiggerJson = `
  1700  {
  1701    "apiVersion": "apps/v1",
  1702    "kind": "Deployment",
  1703    "metadata": {
  1704      "name": "homer",
  1705      "namespace": "simpsons",
  1706      "labels": {
  1707        "fruit": "apple",
  1708        "veggie": "carrot"
  1709      },
  1710      "annotations": {
  1711        "area": "51",
  1712        "greeting": "Take me to your leader."
  1713      }
  1714    },
  1715    "spec": {
  1716      "template": {
  1717        "spec": {
  1718          "containers": [
  1719            {
  1720              "env": [
  1721                {
  1722                  "name": "CM_FOO",
  1723                  "valueFrom": {
  1724                    "configMapKeyRef": {
  1725                      "key": "somekey",
  1726                      "name": "myCm"
  1727                    }
  1728                  }
  1729                },
  1730                {
  1731                  "name": "SECRET_FOO",
  1732                  "valueFrom": {
  1733                    "secretKeyRef": {
  1734                      "key": "someKey",
  1735                      "name": "mySecret"
  1736                    }
  1737                  }
  1738                }
  1739              ],
  1740              "image": "nginx:1.7.9",
  1741              "name": "nginx"
  1742            }
  1743          ]
  1744        }
  1745      }
  1746    }
  1747  }
  1748  `
  1749  	bigMapYaml = `Kind: Service
  1750  complextree:
  1751  - field1:
  1752    - boolfield: true
  1753      floatsubfield: 1.01
  1754      intsubfield: 1010
  1755      stringsubfield: idx1010
  1756    - boolfield: false
  1757      floatsubfield: 1.011
  1758      intsubfield: 1011
  1759      stringsubfield: idx1011
  1760    field2:
  1761    - boolfield: true
  1762      floatsubfield: 1.02
  1763      intsubfield: 1020
  1764      stringsubfield: idx1020
  1765    - boolfield: false
  1766      floatsubfield: 1.021
  1767      intsubfield: 1021
  1768      stringsubfield: idx1021
  1769  - field1:
  1770    - boolfield: true
  1771      floatsubfield: 1.11
  1772      intsubfield: 1110
  1773      stringsubfield: idx1110
  1774    - boolfield: false
  1775      floatsubfield: 1.111
  1776      intsubfield: 1111
  1777      stringsubfield: idx1111
  1778    field2:
  1779    - boolfield: true
  1780      floatsubfield: 1.112
  1781      intsubfield: 1120
  1782      stringsubfield: idx1120
  1783    - boolfield: false
  1784      floatsubfield: 1.1121
  1785      intsubfield: 1121
  1786      stringsubfield: idx1121
  1787  metadata:
  1788    labels:
  1789      app: application-name
  1790    name: service-name
  1791  spec:
  1792    ports:
  1793      port: 80
  1794  that:
  1795  - idx0
  1796  - idx1
  1797  - idx2
  1798  - idx3
  1799  these:
  1800  - field1:
  1801    - idx010
  1802    - idx011
  1803    field2:
  1804    - idx020
  1805    - idx021
  1806  - field1:
  1807    - idx110
  1808    - idx111
  1809    field2:
  1810    - idx120
  1811    - idx121
  1812  - field1:
  1813    - idx210
  1814    - idx211
  1815    field2:
  1816    - idx220
  1817    - idx221
  1818  this:
  1819    is:
  1820      aBool: true
  1821      aFloat: 1.001
  1822      aNilValue: null
  1823      aNumber: 1000
  1824      anEmptyMap: {}
  1825      anEmptySlice: []
  1826  those:
  1827  - field1: idx0foo
  1828    field2: idx0bar
  1829  - field1: idx1foo
  1830    field2: idx1bar
  1831  - field1: idx2foo
  1832    field2: idx2bar
  1833  `
  1834  )
  1835  
  1836  func TestGetFieldValueReturnsMap(t *testing.T) {
  1837  	rn := NewRNode(nil)
  1838  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  1839  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  1840  	}
  1841  	expected := map[string]interface{}{
  1842  		"fruit":  "apple",
  1843  		"veggie": "carrot",
  1844  	}
  1845  	actual, err := rn.GetFieldValue("metadata.labels")
  1846  	if err != nil {
  1847  		t.Fatalf("error getting field value: %v", err)
  1848  	}
  1849  	if diff := cmp.Diff(expected, actual); diff != "" {
  1850  		t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
  1851  	}
  1852  }
  1853  
  1854  func TestGetFieldValueReturnsStuff(t *testing.T) {
  1855  	wn := NewRNode(nil)
  1856  	if err := wn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  1857  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  1858  	}
  1859  	expected := []interface{}{
  1860  		map[string]interface{}{
  1861  			"env": []interface{}{
  1862  				map[string]interface{}{
  1863  					"name": "CM_FOO",
  1864  					"valueFrom": map[string]interface{}{
  1865  						"configMapKeyRef": map[string]interface{}{
  1866  							"key":  "somekey",
  1867  							"name": "myCm",
  1868  						},
  1869  					},
  1870  				},
  1871  				map[string]interface{}{
  1872  					"name": "SECRET_FOO",
  1873  					"valueFrom": map[string]interface{}{
  1874  						"secretKeyRef": map[string]interface{}{
  1875  							"key":  "someKey",
  1876  							"name": "mySecret",
  1877  						},
  1878  					},
  1879  				},
  1880  			},
  1881  			"image": string("nginx:1.7.9"),
  1882  			"name":  string("nginx"),
  1883  		},
  1884  	}
  1885  	actual, err := wn.GetFieldValue("spec.template.spec.containers")
  1886  	if err != nil {
  1887  		t.Fatalf("error getting field value: %v", err)
  1888  	}
  1889  	if diff := cmp.Diff(expected, actual); diff != "" {
  1890  		t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
  1891  	}
  1892  	// Cannot go deeper yet.
  1893  	_, err = wn.GetFieldValue("spec.template.spec.containers.env")
  1894  	if err == nil {
  1895  		t.Fatalf("expected err %v", err)
  1896  	}
  1897  }
  1898  
  1899  func makeBigMap() map[string]interface{} {
  1900  	return map[string]interface{}{
  1901  		"Kind": "Service",
  1902  		"metadata": map[string]interface{}{
  1903  			"labels": map[string]interface{}{
  1904  				"app": "application-name",
  1905  			},
  1906  			"name": "service-name",
  1907  		},
  1908  		"spec": map[string]interface{}{
  1909  			"ports": map[string]interface{}{
  1910  				"port": int64(80),
  1911  			},
  1912  		},
  1913  		"this": map[string]interface{}{
  1914  			"is": map[string]interface{}{
  1915  				"aNumber":      int64(1000),
  1916  				"aFloat":       float64(1.001),
  1917  				"aNilValue":    nil,
  1918  				"aBool":        true,
  1919  				"anEmptyMap":   map[string]interface{}{},
  1920  				"anEmptySlice": []interface{}{},
  1921  				/*
  1922  					TODO: test for unrecognizable (e.g. a function)
  1923  						"unrecognizable": testing.InternalExample{
  1924  							Name: "fooBar",
  1925  						},
  1926  				*/
  1927  			},
  1928  		},
  1929  		"that": []interface{}{
  1930  			"idx0",
  1931  			"idx1",
  1932  			"idx2",
  1933  			"idx3",
  1934  		},
  1935  		"those": []interface{}{
  1936  			map[string]interface{}{
  1937  				"field1": "idx0foo",
  1938  				"field2": "idx0bar",
  1939  			},
  1940  			map[string]interface{}{
  1941  				"field1": "idx1foo",
  1942  				"field2": "idx1bar",
  1943  			},
  1944  			map[string]interface{}{
  1945  				"field1": "idx2foo",
  1946  				"field2": "idx2bar",
  1947  			},
  1948  		},
  1949  		"these": []interface{}{
  1950  			map[string]interface{}{
  1951  				"field1": []interface{}{"idx010", "idx011"},
  1952  				"field2": []interface{}{"idx020", "idx021"},
  1953  			},
  1954  			map[string]interface{}{
  1955  				"field1": []interface{}{"idx110", "idx111"},
  1956  				"field2": []interface{}{"idx120", "idx121"},
  1957  			},
  1958  			map[string]interface{}{
  1959  				"field1": []interface{}{"idx210", "idx211"},
  1960  				"field2": []interface{}{"idx220", "idx221"},
  1961  			},
  1962  		},
  1963  		"complextree": []interface{}{
  1964  			map[string]interface{}{
  1965  				"field1": []interface{}{
  1966  					map[string]interface{}{
  1967  						"stringsubfield": "idx1010",
  1968  						"intsubfield":    int64(1010),
  1969  						"floatsubfield":  float64(1.010),
  1970  						"boolfield":      true,
  1971  					},
  1972  					map[string]interface{}{
  1973  						"stringsubfield": "idx1011",
  1974  						"intsubfield":    int64(1011),
  1975  						"floatsubfield":  float64(1.011),
  1976  						"boolfield":      false,
  1977  					},
  1978  				},
  1979  				"field2": []interface{}{
  1980  					map[string]interface{}{
  1981  						"stringsubfield": "idx1020",
  1982  						"intsubfield":    int64(1020),
  1983  						"floatsubfield":  float64(1.020),
  1984  						"boolfield":      true,
  1985  					},
  1986  					map[string]interface{}{
  1987  						"stringsubfield": "idx1021",
  1988  						"intsubfield":    int64(1021),
  1989  						"floatsubfield":  float64(1.021),
  1990  						"boolfield":      false,
  1991  					},
  1992  				},
  1993  			},
  1994  			map[string]interface{}{
  1995  				"field1": []interface{}{
  1996  					map[string]interface{}{
  1997  						"stringsubfield": "idx1110",
  1998  						"intsubfield":    int64(1110),
  1999  						"floatsubfield":  float64(1.110),
  2000  						"boolfield":      true,
  2001  					},
  2002  					map[string]interface{}{
  2003  						"stringsubfield": "idx1111",
  2004  						"intsubfield":    int64(1111),
  2005  						"floatsubfield":  float64(1.111),
  2006  						"boolfield":      false,
  2007  					},
  2008  				},
  2009  				"field2": []interface{}{
  2010  					map[string]interface{}{
  2011  						"stringsubfield": "idx1120",
  2012  						"intsubfield":    int64(1120),
  2013  						"floatsubfield":  float64(1.1120),
  2014  						"boolfield":      true,
  2015  					},
  2016  					map[string]interface{}{
  2017  						"stringsubfield": "idx1121",
  2018  						"intsubfield":    int64(1121),
  2019  						"floatsubfield":  float64(1.1121),
  2020  						"boolfield":      false,
  2021  					},
  2022  				},
  2023  			},
  2024  		},
  2025  	}
  2026  }
  2027  func TestBasicYamlOperationFromMap(t *testing.T) {
  2028  	bytes, err := Marshal(makeBigMap())
  2029  	if err != nil {
  2030  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2031  	}
  2032  	if string(bytes) != bigMapYaml {
  2033  		t.Fatalf("unexpected string equality")
  2034  	}
  2035  	rNode, err := Parse(string(bytes))
  2036  	if err != nil {
  2037  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2038  	}
  2039  	rNodeString := rNode.MustString()
  2040  	// The result from MustString has more indentation
  2041  	// than bigMapYaml.
  2042  	rNodeStrings := strings.Split(rNodeString, "\n")
  2043  	bigMapStrings := strings.Split(bigMapYaml, "\n")
  2044  	if len(rNodeStrings) != len(bigMapStrings) {
  2045  		t.Fatalf("line count mismatch")
  2046  	}
  2047  	for i := range rNodeStrings {
  2048  		s1 := strings.TrimSpace(rNodeStrings[i])
  2049  		s2 := strings.TrimSpace(bigMapStrings[i])
  2050  		if s1 != s2 {
  2051  			t.Fatalf("expected '%s'=='%s'", s1, s2)
  2052  		}
  2053  	}
  2054  }
  2055  
  2056  func TestGetFieldValueReturnsSlice(t *testing.T) {
  2057  	bytes, err := yaml.Marshal(makeBigMap())
  2058  	if err != nil {
  2059  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2060  	}
  2061  	rNode, err := Parse(string(bytes))
  2062  	if err != nil {
  2063  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2064  	}
  2065  	expected := []interface{}{"idx0", "idx1", "idx2", "idx3"}
  2066  	actual, err := rNode.GetFieldValue("that")
  2067  	if err != nil {
  2068  		t.Fatalf("error getting slice: %v", err)
  2069  	}
  2070  	if diff := cmp.Diff(expected, actual); diff != "" {
  2071  		t.Fatalf("actual slice does not deep equal expected slice:\n%v", diff)
  2072  	}
  2073  }
  2074  
  2075  func TestGetFieldValueReturnsSliceOfMappings(t *testing.T) {
  2076  	bytes, err := yaml.Marshal(makeBigMap())
  2077  	if err != nil {
  2078  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2079  	}
  2080  	rn, err := Parse(string(bytes))
  2081  	if err != nil {
  2082  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2083  	}
  2084  	expected := []interface{}{
  2085  		map[string]interface{}{
  2086  			"field1": "idx0foo",
  2087  			"field2": "idx0bar",
  2088  		},
  2089  		map[string]interface{}{
  2090  			"field1": "idx1foo",
  2091  			"field2": "idx1bar",
  2092  		},
  2093  		map[string]interface{}{
  2094  			"field1": "idx2foo",
  2095  			"field2": "idx2bar",
  2096  		},
  2097  	}
  2098  	actual, err := rn.GetFieldValue("those")
  2099  	if err != nil {
  2100  		t.Fatalf("error getting slice: %v", err)
  2101  	}
  2102  	if diff := cmp.Diff(expected, actual); diff != "" {
  2103  		t.Fatalf("actual slice does not deep equal expected slice:\n%v", diff)
  2104  	}
  2105  }
  2106  
  2107  func TestGetFieldValueReturnsString(t *testing.T) {
  2108  	rn := NewRNode(nil)
  2109  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2110  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2111  	}
  2112  	actual, err := rn.GetFieldValue("metadata.labels.fruit")
  2113  	if err != nil {
  2114  		t.Fatalf("error getting field value: %v", err)
  2115  	}
  2116  	v, ok := actual.(string)
  2117  	if !ok || v != "apple" {
  2118  		t.Fatalf("unexpected value '%v'", actual)
  2119  	}
  2120  }
  2121  
  2122  func TestGetFieldValueResolvesAlias(t *testing.T) {
  2123  	yamlWithAlias := `
  2124  foo: &a theValue
  2125  bar: *a
  2126  `
  2127  	rn, err := Parse(yamlWithAlias)
  2128  	if err != nil {
  2129  		t.Fatalf("unexpected yaml parse error: %v", err)
  2130  	}
  2131  	actual, err := rn.GetFieldValue("bar")
  2132  	if err != nil {
  2133  		t.Fatalf("error getting field value: %v", err)
  2134  	}
  2135  	v, ok := actual.(string)
  2136  	if !ok || v != "theValue" {
  2137  		t.Fatalf("unexpected value '%v'", actual)
  2138  	}
  2139  }
  2140  
  2141  func TestGetString(t *testing.T) {
  2142  	rn := NewRNode(nil)
  2143  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2144  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2145  	}
  2146  	expected := "carrot"
  2147  	actual, err := rn.GetString("metadata.labels.veggie")
  2148  	if err != nil {
  2149  		t.Fatalf("error getting string: %v", err)
  2150  	}
  2151  	if expected != actual {
  2152  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2153  	}
  2154  }
  2155  
  2156  func TestGetSlice(t *testing.T) {
  2157  	bytes, err := yaml.Marshal(makeBigMap())
  2158  	if err != nil {
  2159  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2160  	}
  2161  	rn, err := Parse(string(bytes))
  2162  	if err != nil {
  2163  		t.Fatalf("unexpected yaml.Marshal err: %v", err)
  2164  	}
  2165  	expected := []interface{}{"idx0", "idx1", "idx2", "idx3"}
  2166  	actual, err := rn.GetSlice("that")
  2167  	if err != nil {
  2168  		t.Fatalf("error getting slice: %v", err)
  2169  	}
  2170  	if diff := cmp.Diff(expected, actual); diff != "" {
  2171  		t.Fatalf("actual slice does not deep equal expected slice:\n%v", diff)
  2172  	}
  2173  }
  2174  
  2175  func TestRoundTripJSON(t *testing.T) {
  2176  	rn := NewRNode(nil)
  2177  	err := rn.UnmarshalJSON([]byte(deploymentLittleJson))
  2178  	if err != nil {
  2179  		t.Fatalf("unexpected UnmarshalJSON err: %v", err)
  2180  	}
  2181  	data, err := rn.MarshalJSON()
  2182  	if err != nil {
  2183  		t.Fatalf("unexpected MarshalJSON err: %v", err)
  2184  	}
  2185  	if actual := string(data); actual != deploymentLittleJson {
  2186  		t.Fatalf("expected %s, got %s", deploymentLittleJson, actual)
  2187  	}
  2188  }
  2189  
  2190  func TestGettingFields(t *testing.T) {
  2191  	rn := NewRNode(nil)
  2192  	err := rn.UnmarshalJSON([]byte(deploymentBiggerJson))
  2193  	if err != nil {
  2194  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2195  	}
  2196  	expected := "Deployment"
  2197  	actual := rn.GetKind()
  2198  	if expected != actual {
  2199  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2200  	}
  2201  	expected = "homer"
  2202  	actual = rn.GetName()
  2203  	if expected != actual {
  2204  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2205  	}
  2206  	actualMap := rn.GetLabels()
  2207  	v, ok := actualMap["fruit"]
  2208  	if !ok || v != "apple" {
  2209  		t.Fatalf("unexpected labels '%v'", actualMap)
  2210  	}
  2211  	actualMap = rn.GetAnnotations()
  2212  	v, ok = actualMap["greeting"]
  2213  	if !ok || v != "Take me to your leader." {
  2214  		t.Fatalf("unexpected annotations '%v'", actualMap)
  2215  	}
  2216  }
  2217  
  2218  func TestMapEmpty(t *testing.T) {
  2219  	newNodeMap, err := NewRNode(nil).Map()
  2220  	require.NoError(t, err)
  2221  	assert.Equal(t, 0, len(newNodeMap))
  2222  }
  2223  
  2224  func TestMap(t *testing.T) {
  2225  	rn := NewRNode(nil)
  2226  	if err := rn.UnmarshalJSON([]byte(deploymentLittleJson)); err != nil {
  2227  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2228  	}
  2229  
  2230  	expected := map[string]interface{}{
  2231  		"apiVersion": "apps/v1",
  2232  		"kind":       "Deployment",
  2233  		"metadata": map[string]interface{}{
  2234  			"name":      "homer",
  2235  			"namespace": "simpsons",
  2236  		},
  2237  	}
  2238  
  2239  	actual, err := rn.Map()
  2240  	require.NoError(t, err)
  2241  	if diff := cmp.Diff(expected, actual); diff != "" {
  2242  		t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
  2243  	}
  2244  }
  2245  
  2246  func TestSetName(t *testing.T) {
  2247  	rn := NewRNode(nil)
  2248  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2249  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2250  	}
  2251  	err := rn.SetName("marge")
  2252  	require.NoError(t, err)
  2253  	if expected, actual := "marge", rn.GetName(); expected != actual {
  2254  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2255  	}
  2256  }
  2257  
  2258  func TestSetNamespace(t *testing.T) {
  2259  	rn := NewRNode(nil)
  2260  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2261  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2262  	}
  2263  	err := rn.SetNamespace("flanders")
  2264  	require.NoError(t, err)
  2265  	meta, err := rn.GetMeta()
  2266  	require.NoError(t, err)
  2267  	if expected, actual := "flanders", meta.Namespace; expected != actual {
  2268  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2269  	}
  2270  }
  2271  
  2272  func TestSetLabels(t *testing.T) {
  2273  	rn := NewRNode(nil)
  2274  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2275  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2276  	}
  2277  	require.NoError(t, rn.SetLabels(map[string]string{
  2278  		"label1": "foo",
  2279  		"label2": "bar",
  2280  	}))
  2281  	labels := rn.GetLabels()
  2282  	if expected, actual := 2, len(labels); expected != actual {
  2283  		t.Fatalf("expected '%d', got '%d'", expected, actual)
  2284  	}
  2285  	if expected, actual := "foo", labels["label1"]; expected != actual {
  2286  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2287  	}
  2288  	if expected, actual := "bar", labels["label2"]; expected != actual {
  2289  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2290  	}
  2291  }
  2292  
  2293  func TestGetAnnotations(t *testing.T) {
  2294  	rn := NewRNode(nil)
  2295  	if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2296  		t.Fatalf("unexpected unmarshaljson err: %v", err)
  2297  	}
  2298  	require.NoError(t, rn.SetAnnotations(map[string]string{
  2299  		"annotation1": "foo",
  2300  		"annotation2": "bar",
  2301  	}))
  2302  	annotations := rn.GetAnnotations()
  2303  	if expected, actual := 2, len(annotations); expected != actual {
  2304  		t.Fatalf("expected '%d', got '%d'", expected, actual)
  2305  	}
  2306  	if expected, actual := "foo", annotations["annotation1"]; expected != actual {
  2307  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2308  	}
  2309  	if expected, actual := "bar", annotations["annotation2"]; expected != actual {
  2310  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  2311  	}
  2312  }
  2313  
  2314  func BenchmarkGetAnnotations(b *testing.B) {
  2315  	counts := []int{0, 2, 5, 8}
  2316  	for _, count := range counts {
  2317  		appliedAnnotations := make(map[string]string, count)
  2318  		for i := 1; i <= count; i++ {
  2319  			key := fmt.Sprintf("annotation-key-%d", i)
  2320  			value := fmt.Sprintf("annotation-value-%d", i)
  2321  			appliedAnnotations[key] = value
  2322  		}
  2323  		rn := NewRNode(nil)
  2324  		if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
  2325  			b.Fatalf("unexpected unmarshaljson err: %v", err)
  2326  		}
  2327  		require.NoError(b, rn.SetAnnotations(appliedAnnotations))
  2328  		b.Run(fmt.Sprintf("%02d", count), func(b *testing.B) {
  2329  			for i := 0; i < b.N; i++ {
  2330  				_ = rn.GetAnnotations()
  2331  			}
  2332  		})
  2333  	}
  2334  }
  2335  
  2336  func TestGetFieldValueWithDot(t *testing.T) {
  2337  	const input = `
  2338  kind: Pod
  2339  metadata:
  2340    name: hello-world
  2341    labels:
  2342      app: hello-world-app
  2343      foo.appname: hello-world-foo
  2344  `
  2345  	data, err := Parse(input)
  2346  	require.NoError(t, err)
  2347  
  2348  	labelRNode, err := data.Pipe(Lookup("metadata", "labels"))
  2349  	require.NoError(t, err)
  2350  
  2351  	app, err := labelRNode.GetFieldValue("app")
  2352  	require.NoError(t, err)
  2353  	require.Equal(t, "hello-world-app", app)
  2354  
  2355  	fooAppName, err := labelRNode.GetFieldValue(`foo\.appname`)
  2356  	require.NoError(t, err)
  2357  	require.Equal(t, "hello-world-foo", fooAppName) // no field named 'foo.appname'
  2358  }
  2359  

View as plain text