github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/taskenv/util_test.go (about) 1 package taskenv 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 // TestAddNestedKey_Ok asserts test cases that succeed when passed to 12 // addNestedKey. 13 func TestAddNestedKey_Ok(t *testing.T) { 14 cases := []struct { 15 // M will be initialized if unset 16 M map[string]interface{} 17 K string 18 // Value is always "x" 19 Result map[string]interface{} 20 }{ 21 { 22 K: "foo", 23 Result: map[string]interface{}{ 24 "foo": "x", 25 }, 26 }, 27 { 28 K: "foo.bar", 29 Result: map[string]interface{}{ 30 "foo": map[string]interface{}{ 31 "bar": "x", 32 }, 33 }, 34 }, 35 { 36 K: "foo.bar.quux", 37 Result: map[string]interface{}{ 38 "foo": map[string]interface{}{ 39 "bar": map[string]interface{}{ 40 "quux": "x", 41 }, 42 }, 43 }, 44 }, 45 { 46 K: "a.b.c", 47 Result: map[string]interface{}{ 48 "a": map[string]interface{}{ 49 "b": map[string]interface{}{ 50 "c": "x", 51 }, 52 }, 53 }, 54 }, 55 { 56 // Nested object b should take precedence over values 57 M: map[string]interface{}{ 58 "a": map[string]interface{}{ 59 "b": map[string]interface{}{ 60 "c": "c", 61 }, 62 }, 63 }, 64 K: "a.b", 65 Result: map[string]interface{}{ 66 "a": map[string]interface{}{ 67 "b": map[string]interface{}{ 68 "c": "c", 69 }, 70 }, 71 }, 72 }, 73 { 74 M: map[string]interface{}{ 75 "a": map[string]interface{}{ 76 "x": "x", 77 }, 78 "z": "z", 79 }, 80 K: "a.b.c", 81 Result: map[string]interface{}{ 82 "a": map[string]interface{}{ 83 "b": map[string]interface{}{ 84 "c": "x", 85 }, 86 "x": "x", 87 }, 88 "z": "z", 89 }, 90 }, 91 { 92 M: map[string]interface{}{ 93 "foo": map[string]interface{}{ 94 "bar": map[string]interface{}{ 95 "a": "z", 96 "quux": "z", 97 }, 98 }, 99 }, 100 K: "foo.bar.quux", 101 Result: map[string]interface{}{ 102 "foo": map[string]interface{}{ 103 "bar": map[string]interface{}{ 104 "a": "z", 105 "quux": "x", 106 }, 107 }, 108 }, 109 }, 110 { 111 M: map[string]interface{}{ 112 "foo": "1", 113 "bar": "2", 114 "quux": "3", 115 }, 116 K: "a.bbbbbb.c", 117 Result: map[string]interface{}{ 118 "foo": "1", 119 "bar": "2", 120 "quux": "3", 121 "a": map[string]interface{}{ 122 "bbbbbb": map[string]interface{}{ 123 "c": "x", 124 }, 125 }, 126 }, 127 }, 128 // Regardless of whether attr.driver.qemu = "1" is added first 129 // or second, attr.driver.qemu.version = "..." should take 130 // precedence (nested maps take precedence over values) 131 { 132 M: map[string]interface{}{ 133 "attr": map[string]interface{}{ 134 "driver": map[string]interface{}{ 135 "qemu": "1", 136 }, 137 }, 138 }, 139 K: "attr.driver.qemu.version", 140 Result: map[string]interface{}{ 141 "attr": map[string]interface{}{ 142 "driver": map[string]interface{}{ 143 "qemu": map[string]interface{}{ 144 "version": "x", 145 }, 146 }, 147 }, 148 }, 149 }, 150 { 151 M: map[string]interface{}{ 152 "attr": map[string]interface{}{ 153 "driver": map[string]interface{}{ 154 "qemu": map[string]interface{}{ 155 "version": "1.2.3", 156 }, 157 }, 158 }, 159 }, 160 K: "attr.driver.qemu", 161 Result: map[string]interface{}{ 162 "attr": map[string]interface{}{ 163 "driver": map[string]interface{}{ 164 "qemu": map[string]interface{}{ 165 "version": "1.2.3", 166 }, 167 }, 168 }, 169 }, 170 }, 171 { 172 M: map[string]interface{}{ 173 "a": "a", 174 }, 175 K: "a.b", 176 Result: map[string]interface{}{ 177 "a": map[string]interface{}{ 178 "b": "x", 179 }, 180 }, 181 }, 182 { 183 M: map[string]interface{}{ 184 "a": "a", 185 "foo": map[string]interface{}{ 186 "b": "b", 187 "bar": "quux", 188 }, 189 "c": map[string]interface{}{}, 190 }, 191 K: "foo.bar.quux", 192 Result: map[string]interface{}{ 193 "a": "a", 194 "foo": map[string]interface{}{ 195 "b": "b", 196 "bar": map[string]interface{}{ 197 "quux": "x", 198 }, 199 }, 200 "c": map[string]interface{}{}, 201 }, 202 }, 203 } 204 205 for i := range cases { 206 tc := cases[i] 207 name := tc.K 208 if len(tc.M) > 0 { 209 name = fmt.Sprintf("%s-%d", name, len(tc.M)) 210 } 211 t.Run(name, func(t *testing.T) { 212 t.Parallel() 213 if tc.M == nil { 214 tc.M = map[string]interface{}{} 215 } 216 require.NoError(t, addNestedKey(tc.M, tc.K, "x")) 217 require.Equal(t, tc.Result, tc.M) 218 }) 219 } 220 } 221 222 // TestAddNestedKey_Bad asserts test cases return an error when passed to 223 // addNestedKey. 224 func TestAddNestedKey_Bad(t *testing.T) { 225 cases := []struct { 226 // M will be initialized if unset 227 M func() map[string]interface{} 228 K string 229 // Value is always "x" 230 // Result is compared by Error() string equality 231 Result error 232 }{ 233 { 234 K: ".", 235 Result: ErrInvalidObjectPath, 236 }, 237 { 238 K: ".foo", 239 Result: ErrInvalidObjectPath, 240 }, 241 { 242 K: "foo.", 243 Result: ErrInvalidObjectPath, 244 }, 245 { 246 K: ".a.", 247 Result: ErrInvalidObjectPath, 248 }, 249 { 250 K: "foo..bar", 251 Result: ErrInvalidObjectPath, 252 }, 253 { 254 K: "foo...bar", 255 Result: ErrInvalidObjectPath, 256 }, 257 { 258 K: "foo.bar..quux", 259 Result: ErrInvalidObjectPath, 260 }, 261 { 262 K: "foo..bar.quux", 263 Result: ErrInvalidObjectPath, 264 }, 265 { 266 K: "foo.bar.quux.", 267 Result: ErrInvalidObjectPath, 268 }, 269 { 270 M: func() map[string]interface{} { 271 return map[string]interface{}{ 272 "a": "a", 273 "foo": map[string]interface{}{ 274 "b": "b", 275 "bar": map[string]interface{}{ 276 "c": "c", 277 }, 278 }, 279 } 280 }, 281 K: "foo.bar.quux.", 282 Result: ErrInvalidObjectPath, 283 }, 284 { 285 M: func() map[string]interface{} { 286 return map[string]interface{}{ 287 "a": "a", 288 "foo": map[string]interface{}{ 289 "b": "b", 290 "bar": map[string]interface{}{ 291 "c": "c", 292 }, 293 }, 294 } 295 }, 296 K: "foo.bar..quux", 297 Result: ErrInvalidObjectPath, 298 }, 299 { 300 M: func() map[string]interface{} { 301 return map[string]interface{}{ 302 "a": "a", 303 "foo": map[string]interface{}{ 304 "b": "b", 305 "bar": map[string]interface{}{ 306 "c": "c", 307 }, 308 }, 309 } 310 }, 311 K: "foo.bar..quux", 312 Result: ErrInvalidObjectPath, 313 }, 314 } 315 316 for i := range cases { 317 tc := cases[i] 318 name := tc.K 319 if tc.M != nil { 320 name += "-cleanup" 321 } 322 t.Run(name, func(t *testing.T) { 323 t.Parallel() 324 325 // Copy original M value to ensure it doesn't get altered 326 if tc.M == nil { 327 tc.M = func() map[string]interface{} { 328 return map[string]interface{}{} 329 } 330 } 331 332 // Call func and assert error 333 m := tc.M() 334 err := addNestedKey(m, tc.K, "x") 335 require.EqualError(t, err, tc.Result.Error()) 336 337 // Ensure M wasn't altered 338 require.Equal(t, tc.M(), m) 339 }) 340 } 341 } 342 343 func TestCtyify_Ok(t *testing.T) { 344 cases := []struct { 345 Name string 346 In map[string]interface{} 347 Out map[string]cty.Value 348 }{ 349 { 350 Name: "OneVal", 351 In: map[string]interface{}{ 352 "a": "b", 353 }, 354 Out: map[string]cty.Value{ 355 "a": cty.StringVal("b"), 356 }, 357 }, 358 { 359 Name: "MultiVal", 360 In: map[string]interface{}{ 361 "a": "b", 362 "foo": "bar", 363 }, 364 Out: map[string]cty.Value{ 365 "a": cty.StringVal("b"), 366 "foo": cty.StringVal("bar"), 367 }, 368 }, 369 { 370 Name: "NestedVals", 371 In: map[string]interface{}{ 372 "a": "b", 373 "foo": map[string]interface{}{ 374 "c": "d", 375 "bar": map[string]interface{}{ 376 "quux": "z", 377 }, 378 }, 379 "123": map[string]interface{}{ 380 "bar": map[string]interface{}{ 381 "456": "789", 382 }, 383 }, 384 }, 385 Out: map[string]cty.Value{ 386 "a": cty.StringVal("b"), 387 "foo": cty.ObjectVal(map[string]cty.Value{ 388 "c": cty.StringVal("d"), 389 "bar": cty.ObjectVal(map[string]cty.Value{ 390 "quux": cty.StringVal("z"), 391 }), 392 }), 393 "123": cty.ObjectVal(map[string]cty.Value{ 394 "bar": cty.ObjectVal(map[string]cty.Value{ 395 "456": cty.StringVal("789"), 396 }), 397 }), 398 }, 399 }, 400 } 401 402 for i := range cases { 403 tc := cases[i] 404 t.Run(tc.Name, func(t *testing.T) { 405 t.Parallel() 406 407 // ctiyif and check for errors 408 result, err := ctyify(tc.In) 409 require.NoError(t, err) 410 411 // convert results to ObjectVals and compare with RawEquals 412 resultObj := cty.ObjectVal(result) 413 OutObj := cty.ObjectVal(tc.Out) 414 require.True(t, OutObj.RawEquals(resultObj)) 415 }) 416 } 417 } 418 419 func TestCtyify_Bad(t *testing.T) { 420 cases := []struct { 421 Name string 422 In map[string]interface{} 423 Out map[string]cty.Value 424 }{ 425 { 426 Name: "NonStringVal", 427 In: map[string]interface{}{ 428 "a": 1, 429 }, 430 }, 431 { 432 Name: "NestedNonString", 433 In: map[string]interface{}{ 434 "foo": map[string]interface{}{ 435 "c": 1, 436 }, 437 }, 438 }, 439 } 440 441 for i := range cases { 442 tc := cases[i] 443 t.Run(tc.Name, func(t *testing.T) { 444 t.Parallel() 445 446 // ctiyif and check for errors 447 result, err := ctyify(tc.In) 448 require.Error(t, err) 449 require.Nil(t, result) 450 }) 451 } 452 }