...

Source file src/sigs.k8s.io/kustomize/api/filters/refvar/expand_test.go

Documentation: sigs.k8s.io/kustomize/api/filters/refvar

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package refvar_test
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	. "sigs.k8s.io/kustomize/api/filters/refvar"
    12  )
    13  
    14  type expected struct {
    15  	count  int
    16  	edited string
    17  }
    18  
    19  func TestPrimitiveReplacer(t *testing.T) {
    20  	varCounts := make(map[string]int)
    21  	f := MakePrimitiveReplacer(
    22  		varCounts,
    23  		map[string]interface{}{
    24  			"FOO":        "bar",
    25  			"ZOO":        "$(FOO)-1",
    26  			"BLU":        "$(ZOO)-2",
    27  			"EIGHT":      8,
    28  			"PI":         3.14159,
    29  			"ZINT":       "$(INT)",
    30  			"BOOL":       "true",
    31  			"HUGENUMBER": int64(9223372036854775807),
    32  			"CRAZYMAP":   map[string]int{"crazy": 200},
    33  			"ZBOOL":      "$(BOOL)",
    34  		})
    35  	assert.Equal(t, "$()", f(""))
    36  	assert.Equal(t, "$(      )", f("      "))
    37  	assert.Equal(t, "$(florida)", f("florida"))
    38  	assert.Equal(t, "$(0)", f("0"))
    39  	assert.Equal(t, "bar", f("FOO"))
    40  	assert.Equal(t, "bar", f("FOO"))
    41  	assert.Equal(t, "bar", f("FOO"))
    42  	assert.Equal(t, 8, f("EIGHT"))
    43  	assert.Equal(t, 8, f("EIGHT"))
    44  	assert.Equal(t, 3.14159, f("PI"))
    45  	assert.Equal(t, "true", f("BOOL"))
    46  	assert.Equal(t, int64(9223372036854775807), f("HUGENUMBER"))
    47  	assert.Equal(t, "$(FOO)-1", f("ZOO"))
    48  	assert.Equal(t, "$(CRAZYMAP)", f("CRAZYMAP"))
    49  	assert.Equal(t,
    50  		map[string]int{
    51  			"FOO":        3,
    52  			"EIGHT":      2,
    53  			"BOOL":       1,
    54  			"PI":         1,
    55  			"ZOO":        1,
    56  			"HUGENUMBER": 1,
    57  		},
    58  		varCounts)
    59  }
    60  
    61  func TestMapReference(t *testing.T) {
    62  	type env struct {
    63  		Name  string
    64  		Value interface{}
    65  	}
    66  	envs := []env{
    67  		{
    68  			Name:  "FOO",
    69  			Value: "bar",
    70  		},
    71  		{
    72  			Name:  "ZOO",
    73  			Value: "$(FOO)-1",
    74  		},
    75  		{
    76  			Name:  "BLU",
    77  			Value: "$(ZOO)-2",
    78  		},
    79  		{
    80  			Name:  "INT",
    81  			Value: 2,
    82  		},
    83  		{
    84  			Name:  "ZINT",
    85  			Value: "$(INT)",
    86  		},
    87  		{
    88  			Name:  "BOOL",
    89  			Value: true,
    90  		},
    91  		{
    92  			Name:  "ZBOOL",
    93  			Value: "$(BOOL)",
    94  		},
    95  	}
    96  
    97  	varMap := map[string]interface{}{
    98  		"FOO":   "bar",
    99  		"ZOO":   "$(FOO)-1",
   100  		"BLU":   "$(ZOO)-2",
   101  		"INT":   "2",
   102  		"ZINT":  "$(INT)",
   103  		"BOOL":  "true",
   104  		"ZBOOL": "$(BOOL)",
   105  	}
   106  
   107  	varCounts := make(map[string]int)
   108  	for _, env := range envs {
   109  		varMap[env.Name] = DoReplacements(
   110  			fmt.Sprintf("%v", env.Value),
   111  			MakePrimitiveReplacer(varCounts, varMap))
   112  	}
   113  
   114  	expectedEnv := map[string]expected{
   115  		"FOO":   {count: 1, edited: "bar"},
   116  		"ZOO":   {count: 1, edited: "bar-1"},
   117  		"BLU":   {count: 0, edited: "bar-1-2"},
   118  		"INT":   {count: 1, edited: "2"},
   119  		"ZINT":  {count: 0, edited: "2"},
   120  		"BOOL":  {count: 1, edited: "true"},
   121  		"ZBOOL": {count: 0, edited: "true"},
   122  	}
   123  
   124  	for k, v := range expectedEnv {
   125  		if e, a := v, varMap[k]; e.edited != a || e.count != varCounts[k] {
   126  			t.Errorf("Expected %v count=%d, got %v count=%d",
   127  				e.edited, e.count, a, varCounts[k])
   128  		} else {
   129  			delete(varMap, k)
   130  		}
   131  	}
   132  
   133  	if len(varMap) != 0 {
   134  		t.Errorf("Unexpected keys in declared env: %v", varMap)
   135  	}
   136  }
   137  
   138  func TestMapping(t *testing.T) {
   139  	cases := []struct {
   140  		name     string
   141  		input    string
   142  		expected string
   143  		counts   map[string]int
   144  	}{
   145  		{
   146  			name:     "whole string",
   147  			input:    "$(VAR_A)",
   148  			expected: "A",
   149  			counts:   map[string]int{"VAR_A": 1},
   150  		},
   151  		{
   152  			name:     "repeat",
   153  			input:    "$(VAR_A)-$(VAR_A)",
   154  			expected: "A-A",
   155  			counts:   map[string]int{"VAR_A": 2},
   156  		},
   157  		{
   158  			name:     "multiple repeats",
   159  			input:    "$(VAR_A)-$(VAR_B)-$(VAR_B)-$(VAR_B)-$(VAR_A)",
   160  			expected: "A-B-B-B-A",
   161  			counts:   map[string]int{"VAR_A": 2, "VAR_B": 3},
   162  		},
   163  		{
   164  			name:     "beginning",
   165  			input:    "$(VAR_A)-1",
   166  			expected: "A-1",
   167  			counts:   map[string]int{"VAR_A": 1},
   168  		},
   169  		{
   170  			name:     "middle",
   171  			input:    "___$(VAR_B)___",
   172  			expected: "___B___",
   173  			counts:   map[string]int{"VAR_B": 1},
   174  		},
   175  		{
   176  			name:     "end",
   177  			input:    "___$(VAR_C)",
   178  			expected: "___C",
   179  			counts:   map[string]int{"VAR_C": 1},
   180  		},
   181  		{
   182  			name:     "compound",
   183  			input:    "$(VAR_A)_$(VAR_B)_$(VAR_C)",
   184  			expected: "A_B_C",
   185  			counts:   map[string]int{"VAR_A": 1, "VAR_B": 1, "VAR_C": 1},
   186  		},
   187  		{
   188  			name:     "escape & expand",
   189  			input:    "$$(VAR_B)_$(VAR_A)",
   190  			expected: "$(VAR_B)_A",
   191  			counts:   map[string]int{"VAR_A": 1},
   192  		},
   193  		{
   194  			name:     "compound escape",
   195  			input:    "$$(VAR_A)_$$(VAR_B)",
   196  			expected: "$(VAR_A)_$(VAR_B)",
   197  		},
   198  		{
   199  			name:     "mixed in escapes",
   200  			input:    "f000-$$VAR_A",
   201  			expected: "f000-$VAR_A",
   202  		},
   203  		{
   204  			name:     "backslash escape ignored",
   205  			input:    "foo\\$(VAR_C)bar",
   206  			expected: "foo\\Cbar",
   207  			counts:   map[string]int{"VAR_C": 1},
   208  		},
   209  		{
   210  			name:     "backslash escape ignored",
   211  			input:    "foo\\\\$(VAR_C)bar",
   212  			expected: "foo\\\\Cbar",
   213  			counts:   map[string]int{"VAR_C": 1},
   214  		},
   215  		{
   216  			name:     "lots of backslashes",
   217  			input:    "foo\\\\\\\\$(VAR_A)bar",
   218  			expected: "foo\\\\\\\\Abar",
   219  			counts:   map[string]int{"VAR_A": 1},
   220  		},
   221  		{
   222  			name:     "nested var references",
   223  			input:    "$(VAR_A$(VAR_B))",
   224  			expected: "$(VAR_A$(VAR_B))",
   225  		},
   226  		{
   227  			name:     "nested var references second type",
   228  			input:    "$(VAR_A$(VAR_B)",
   229  			expected: "$(VAR_A$(VAR_B)",
   230  		},
   231  		{
   232  			name:     "value is a reference",
   233  			input:    "$(VAR_REF)",
   234  			expected: "$(VAR_A)",
   235  			counts:   map[string]int{"VAR_REF": 1},
   236  		},
   237  		{
   238  			name:     "value is a reference x 2",
   239  			input:    "%%$(VAR_REF)--$(VAR_REF)%%",
   240  			expected: "%%$(VAR_A)--$(VAR_A)%%",
   241  			counts:   map[string]int{"VAR_REF": 2},
   242  		},
   243  		{
   244  			name:     "empty var",
   245  			input:    "foo$(VAR_EMPTY)bar",
   246  			expected: "foobar",
   247  			counts:   map[string]int{"VAR_EMPTY": 1},
   248  		},
   249  		{
   250  			name:     "unterminated expression",
   251  			input:    "foo$(VAR_Awhoops!",
   252  			expected: "foo$(VAR_Awhoops!",
   253  		},
   254  		{
   255  			name:     "expression without operator",
   256  			input:    "f00__(VAR_A)__",
   257  			expected: "f00__(VAR_A)__",
   258  		},
   259  		{
   260  			name:     "shell special vars pass through",
   261  			input:    "$?_boo_$!",
   262  			expected: "$?_boo_$!",
   263  		},
   264  		{
   265  			name:     "bare operators are ignored",
   266  			input:    "$VAR_A",
   267  			expected: "$VAR_A",
   268  		},
   269  		{
   270  			name:     "undefined vars are passed through",
   271  			input:    "$(VAR_DNE)",
   272  			expected: "$(VAR_DNE)",
   273  		},
   274  		{
   275  			name:     "multiple (even) operators, var undefined",
   276  			input:    "$$$$$$(BIG_MONEY)",
   277  			expected: "$$$(BIG_MONEY)",
   278  		},
   279  		{
   280  			name:     "multiple (even) operators, var defined",
   281  			input:    "$$$$$$(VAR_A)",
   282  			expected: "$$$(VAR_A)",
   283  		},
   284  		{
   285  			name:     "multiple (odd) operators, var undefined",
   286  			input:    "$$$$$$$(GOOD_ODDS)",
   287  			expected: "$$$$(GOOD_ODDS)",
   288  		},
   289  		{
   290  			name:     "multiple (odd) operators, var defined",
   291  			input:    "$$$$$$$(VAR_A)",
   292  			expected: "$$$A",
   293  			counts:   map[string]int{"VAR_A": 1},
   294  		},
   295  		{
   296  			name:     "missing open expression",
   297  			input:    "$VAR_A)",
   298  			expected: "$VAR_A)",
   299  		},
   300  		{
   301  			name:     "shell syntax ignored",
   302  			input:    "${VAR_A}",
   303  			expected: "${VAR_A}",
   304  		},
   305  		{
   306  			name:     "trailing incomplete expression not consumed",
   307  			input:    "$(VAR_B)_______$(A",
   308  			expected: "B_______$(A",
   309  			counts:   map[string]int{"VAR_B": 1},
   310  		},
   311  		{
   312  			name:     "trailing incomplete expression, no content, is not consumed",
   313  			input:    "$(VAR_C)_______$(",
   314  			expected: "C_______$(",
   315  			counts:   map[string]int{"VAR_C": 1},
   316  		},
   317  		{
   318  			name:     "operator at end of input string is preserved",
   319  			input:    "$(VAR_A)foobarzab$",
   320  			expected: "Afoobarzab$",
   321  			counts:   map[string]int{"VAR_A": 1},
   322  		},
   323  		{
   324  			name:     "shell escaped incomplete expr",
   325  			input:    "foo-\\$(VAR_A",
   326  			expected: "foo-\\$(VAR_A",
   327  		},
   328  		{
   329  			name:     "lots of $( in middle",
   330  			input:    "--$($($($($--",
   331  			expected: "--$($($($($--",
   332  		},
   333  		{
   334  			name:     "lots of $( in beginning",
   335  			input:    "$($($($($--foo$(",
   336  			expected: "$($($($($--foo$(",
   337  		},
   338  		{
   339  			name:     "lots of $( at end",
   340  			input:    "foo0--$($($($(",
   341  			expected: "foo0--$($($($(",
   342  		},
   343  		{
   344  			name:     "escaped operators in variable names are not escaped",
   345  			input:    "$(foo$$var)",
   346  			expected: "$(foo$$var)",
   347  		},
   348  		{
   349  			name:     "newline not expanded",
   350  			input:    "\n",
   351  			expected: "\n",
   352  		},
   353  	}
   354  	for _, tc := range cases {
   355  		counts := make(map[string]int)
   356  		expanded := DoReplacements(
   357  			fmt.Sprintf("%v", tc.input),
   358  			MakePrimitiveReplacer(counts, map[string]interface{}{
   359  				"VAR_A":     "A",
   360  				"VAR_B":     "B",
   361  				"VAR_C":     "C",
   362  				"VAR_REF":   "$(VAR_A)",
   363  				"VAR_EMPTY": "",
   364  			}))
   365  		if e, a := tc.expected, expanded; e != a {
   366  			t.Errorf("%v: expected %q, got %q", tc.name, e, a)
   367  		}
   368  		if len(counts) != len(tc.counts) {
   369  			t.Errorf("%v: len(counts)=%d != len(tc.counts)=%d",
   370  				tc.name, len(counts), len(tc.counts))
   371  		}
   372  		if len(tc.counts) > 0 {
   373  			for k, expectedCount := range tc.counts {
   374  				if c, ok := counts[k]; ok {
   375  					if c != expectedCount {
   376  						t.Errorf(
   377  							"%v: k=%s, expected count %d, got %d",
   378  							tc.name, k, expectedCount, c)
   379  					}
   380  				} else {
   381  					t.Errorf(
   382  						"%v: k=%s, expected count %d, got zero",
   383  						tc.name, k, expectedCount)
   384  				}
   385  			}
   386  		}
   387  	}
   388  }
   389  

View as plain text