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 }