github.com/ali-iotechsys/cli@v20.10.0+incompatible/cli/compose/template/template_test.go (about)

     1  package template
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"gotest.tools/v3/assert"
     9  	is "gotest.tools/v3/assert/cmp"
    10  )
    11  
    12  var defaults = map[string]string{
    13  	"FOO": "first",
    14  	"BAR": "",
    15  }
    16  
    17  func defaultMapping(name string) (string, bool) {
    18  	val, ok := defaults[name]
    19  	return val, ok
    20  }
    21  
    22  func TestEscaped(t *testing.T) {
    23  	result, err := Substitute("$${foo}", defaultMapping)
    24  	assert.NilError(t, err)
    25  	assert.Check(t, is.Equal("${foo}", result))
    26  }
    27  
    28  func TestSubstituteNoMatch(t *testing.T) {
    29  	result, err := Substitute("foo", defaultMapping)
    30  	assert.NilError(t, err)
    31  	assert.Equal(t, "foo", result)
    32  }
    33  
    34  func TestInvalid(t *testing.T) {
    35  	invalidTemplates := []string{
    36  		"${",
    37  		"$}",
    38  		"${}",
    39  		"${ }",
    40  		"${ foo}",
    41  		"${foo }",
    42  		"${foo!}",
    43  	}
    44  
    45  	for _, template := range invalidTemplates {
    46  		_, err := Substitute(template, defaultMapping)
    47  		assert.ErrorContains(t, err, "Invalid template")
    48  	}
    49  }
    50  
    51  func TestNoValueNoDefault(t *testing.T) {
    52  	for _, template := range []string{"This ${missing} var", "This ${BAR} var"} {
    53  		result, err := Substitute(template, defaultMapping)
    54  		assert.NilError(t, err)
    55  		assert.Check(t, is.Equal("This  var", result))
    56  	}
    57  }
    58  
    59  func TestValueNoDefault(t *testing.T) {
    60  	for _, template := range []string{"This $FOO var", "This ${FOO} var"} {
    61  		result, err := Substitute(template, defaultMapping)
    62  		assert.NilError(t, err)
    63  		assert.Check(t, is.Equal("This first var", result))
    64  	}
    65  }
    66  
    67  func TestNoValueWithDefault(t *testing.T) {
    68  	for _, template := range []string{"ok ${missing:-def}", "ok ${missing-def}"} {
    69  		result, err := Substitute(template, defaultMapping)
    70  		assert.NilError(t, err)
    71  		assert.Check(t, is.Equal("ok def", result))
    72  	}
    73  }
    74  
    75  func TestEmptyValueWithSoftDefault(t *testing.T) {
    76  	result, err := Substitute("ok ${BAR:-def}", defaultMapping)
    77  	assert.NilError(t, err)
    78  	assert.Check(t, is.Equal("ok def", result))
    79  }
    80  
    81  func TestValueWithSoftDefault(t *testing.T) {
    82  	result, err := Substitute("ok ${FOO:-def}", defaultMapping)
    83  	assert.NilError(t, err)
    84  	assert.Check(t, is.Equal("ok first", result))
    85  }
    86  
    87  func TestEmptyValueWithHardDefault(t *testing.T) {
    88  	result, err := Substitute("ok ${BAR-def}", defaultMapping)
    89  	assert.NilError(t, err)
    90  	assert.Check(t, is.Equal("ok ", result))
    91  }
    92  
    93  func TestNonAlphanumericDefault(t *testing.T) {
    94  	result, err := Substitute("ok ${BAR:-/non:-alphanumeric}", defaultMapping)
    95  	assert.NilError(t, err)
    96  	assert.Check(t, is.Equal("ok /non:-alphanumeric", result))
    97  }
    98  
    99  func TestMandatoryVariableErrors(t *testing.T) {
   100  	testCases := []struct {
   101  		template      string
   102  		expectedError string
   103  	}{
   104  		{
   105  			template:      "not ok ${UNSET_VAR:?Mandatory Variable Unset}",
   106  			expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset",
   107  		},
   108  		{
   109  			template:      "not ok ${BAR:?Mandatory Variable Empty}",
   110  			expectedError: "required variable BAR is missing a value: Mandatory Variable Empty",
   111  		},
   112  		{
   113  			template:      "not ok ${UNSET_VAR:?}",
   114  			expectedError: "required variable UNSET_VAR is missing a value",
   115  		},
   116  		{
   117  			template:      "not ok ${UNSET_VAR?Mandatory Variable Unset}",
   118  			expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset",
   119  		},
   120  		{
   121  			template:      "not ok ${UNSET_VAR?}",
   122  			expectedError: "required variable UNSET_VAR is missing a value",
   123  		},
   124  	}
   125  
   126  	for _, tc := range testCases {
   127  		_, err := Substitute(tc.template, defaultMapping)
   128  		assert.ErrorContains(t, err, tc.expectedError)
   129  		assert.ErrorType(t, err, reflect.TypeOf(&InvalidTemplateError{}))
   130  	}
   131  }
   132  
   133  func TestDefaultsForMandatoryVariables(t *testing.T) {
   134  	testCases := []struct {
   135  		template string
   136  		expected string
   137  	}{
   138  		{
   139  			template: "ok ${FOO:?err}",
   140  			expected: "ok first",
   141  		},
   142  		{
   143  			template: "ok ${FOO?err}",
   144  			expected: "ok first",
   145  		},
   146  		{
   147  			template: "ok ${BAR?err}",
   148  			expected: "ok ",
   149  		},
   150  	}
   151  
   152  	for _, tc := range testCases {
   153  		result, err := Substitute(tc.template, defaultMapping)
   154  		assert.NilError(t, err)
   155  		assert.Check(t, is.Equal(tc.expected, result))
   156  	}
   157  }
   158  
   159  func TestSubstituteWithCustomFunc(t *testing.T) {
   160  	errIsMissing := func(substitution string, mapping Mapping) (string, bool, error) {
   161  		value, found := mapping(substitution)
   162  		if !found {
   163  			return "", true, &InvalidTemplateError{
   164  				Template: fmt.Sprintf("required variable %s is missing a value", substitution),
   165  			}
   166  		}
   167  		return value, true, nil
   168  	}
   169  
   170  	result, err := SubstituteWith("ok ${FOO}", defaultMapping, defaultPattern, errIsMissing)
   171  	assert.NilError(t, err)
   172  	assert.Check(t, is.Equal("ok first", result))
   173  
   174  	result, err = SubstituteWith("ok ${BAR}", defaultMapping, defaultPattern, errIsMissing)
   175  	assert.NilError(t, err)
   176  	assert.Check(t, is.Equal("ok ", result))
   177  
   178  	_, err = SubstituteWith("ok ${NOTHERE}", defaultMapping, defaultPattern, errIsMissing)
   179  	assert.Check(t, is.ErrorContains(err, "required variable"))
   180  }
   181  
   182  func TestExtractVariables(t *testing.T) {
   183  	testCases := []struct {
   184  		name     string
   185  		dict     map[string]interface{}
   186  		expected map[string]string
   187  	}{
   188  		{
   189  			name:     "empty",
   190  			dict:     map[string]interface{}{},
   191  			expected: map[string]string{},
   192  		},
   193  		{
   194  			name: "no-variables",
   195  			dict: map[string]interface{}{
   196  				"foo": "bar",
   197  			},
   198  			expected: map[string]string{},
   199  		},
   200  		{
   201  			name: "variable-without-curly-braces",
   202  			dict: map[string]interface{}{
   203  				"foo": "$bar",
   204  			},
   205  			expected: map[string]string{
   206  				"bar": "",
   207  			},
   208  		},
   209  		{
   210  			name: "variable",
   211  			dict: map[string]interface{}{
   212  				"foo": "${bar}",
   213  			},
   214  			expected: map[string]string{
   215  				"bar": "",
   216  			},
   217  		},
   218  		{
   219  			name: "required-variable",
   220  			dict: map[string]interface{}{
   221  				"foo": "${bar?:foo}",
   222  			},
   223  			expected: map[string]string{
   224  				"bar": "",
   225  			},
   226  		},
   227  		{
   228  			name: "required-variable2",
   229  			dict: map[string]interface{}{
   230  				"foo": "${bar?foo}",
   231  			},
   232  			expected: map[string]string{
   233  				"bar": "",
   234  			},
   235  		},
   236  		{
   237  			name: "default-variable",
   238  			dict: map[string]interface{}{
   239  				"foo": "${bar:-foo}",
   240  			},
   241  			expected: map[string]string{
   242  				"bar": "foo",
   243  			},
   244  		},
   245  		{
   246  			name: "default-variable2",
   247  			dict: map[string]interface{}{
   248  				"foo": "${bar-foo}",
   249  			},
   250  			expected: map[string]string{
   251  				"bar": "foo",
   252  			},
   253  		},
   254  		{
   255  			name: "multiple-values",
   256  			dict: map[string]interface{}{
   257  				"foo": "${bar:-foo}",
   258  				"bar": map[string]interface{}{
   259  					"foo": "${fruit:-banana}",
   260  					"bar": "vegetable",
   261  				},
   262  				"baz": []interface{}{
   263  					"foo",
   264  					"$docker:${project:-cli}",
   265  					"$toto",
   266  				},
   267  			},
   268  			expected: map[string]string{
   269  				"bar":     "foo",
   270  				"fruit":   "banana",
   271  				"toto":    "",
   272  				"docker":  "",
   273  				"project": "cli",
   274  			},
   275  		},
   276  	}
   277  	for _, tc := range testCases {
   278  		tc := tc
   279  		t.Run(tc.name, func(t *testing.T) {
   280  			actual := ExtractVariables(tc.dict, defaultPattern)
   281  			assert.Check(t, is.DeepEqual(actual, tc.expected))
   282  		})
   283  	}
   284  }