github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/encoding_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package funcs 7 8 import ( 9 "fmt" 10 "testing" 11 12 "github.com/zclconf/go-cty/cty" 13 14 "github.com/opentofu/opentofu/internal/lang/marks" 15 ) 16 17 func TestBase64Decode(t *testing.T) { 18 tests := []struct { 19 String cty.Value 20 Want cty.Value 21 Err bool 22 }{ 23 { 24 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 25 cty.StringVal("abc123!?$*&()'-=@~"), 26 false, 27 }, 28 { 29 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+").Mark(marks.Sensitive), 30 cty.StringVal("abc123!?$*&()'-=@~").Mark(marks.Sensitive), 31 false, 32 }, 33 { // Invalid base64 data decoding 34 cty.StringVal("this-is-an-invalid-base64-data"), 35 cty.UnknownVal(cty.String), 36 true, 37 }, 38 { // Invalid utf-8 39 cty.StringVal("\xc3\x28"), 40 cty.UnknownVal(cty.String), 41 true, 42 }, 43 } 44 45 for _, test := range tests { 46 t.Run(fmt.Sprintf("base64decode(%#v)", test.String), func(t *testing.T) { 47 got, err := Base64Decode(test.String) 48 49 if test.Err { 50 if err == nil { 51 t.Fatal("succeeded; want error") 52 } 53 return 54 } else if err != nil { 55 t.Fatalf("unexpected error: %s", err) 56 } 57 58 if !got.RawEquals(test.Want) { 59 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 60 } 61 }) 62 } 63 } 64 65 func TestBase64Decode_error(t *testing.T) { 66 tests := map[string]struct { 67 String cty.Value 68 WantErr string 69 }{ 70 "invalid base64": { 71 cty.StringVal("dfg"), 72 `failed to decode base64 data "dfg"`, 73 }, 74 "sensitive invalid base64": { 75 cty.StringVal("dfg").Mark(marks.Sensitive), 76 `failed to decode base64 data (sensitive value)`, 77 }, 78 "invalid utf-8": { 79 cty.StringVal("whee"), 80 "the result of decoding the provided string is not valid UTF-8", 81 }, 82 } 83 84 for name, test := range tests { 85 t.Run(name, func(t *testing.T) { 86 _, err := Base64Decode(test.String) 87 88 if err == nil { 89 t.Fatal("succeeded; want error") 90 } 91 92 if err.Error() != test.WantErr { 93 t.Errorf("wrong error result\ngot: %#v\nwant: %#v", err.Error(), test.WantErr) 94 } 95 }) 96 } 97 } 98 99 func TestBase64Encode(t *testing.T) { 100 tests := []struct { 101 String cty.Value 102 Want cty.Value 103 Err bool 104 }{ 105 { 106 cty.StringVal("abc123!?$*&()'-=@~"), 107 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 108 false, 109 }, 110 } 111 112 for _, test := range tests { 113 t.Run(fmt.Sprintf("base64encode(%#v)", test.String), func(t *testing.T) { 114 got, err := Base64Encode(test.String) 115 116 if test.Err { 117 if err == nil { 118 t.Fatal("succeeded; want error") 119 } 120 return 121 } else if err != nil { 122 t.Fatalf("unexpected error: %s", err) 123 } 124 125 if !got.RawEquals(test.Want) { 126 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 127 } 128 }) 129 } 130 } 131 132 func TestBase64Gzip(t *testing.T) { 133 tests := []struct { 134 String cty.Value 135 Want cty.Value 136 Err bool 137 }{ 138 { 139 cty.StringVal("test"), 140 cty.StringVal("H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA"), 141 false, 142 }, 143 } 144 145 for _, test := range tests { 146 t.Run(fmt.Sprintf("base64gzip(%#v)", test.String), func(t *testing.T) { 147 got, err := Base64Gzip(test.String) 148 149 if test.Err { 150 if err == nil { 151 t.Fatal("succeeded; want error") 152 } 153 return 154 } else if err != nil { 155 t.Fatalf("unexpected error: %s", err) 156 } 157 158 if !got.RawEquals(test.Want) { 159 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 160 } 161 }) 162 } 163 } 164 165 func TestBase64Gunzip(t *testing.T) { 166 tests := []struct { 167 String cty.Value 168 Want cty.Value 169 Err bool 170 }{ 171 { 172 cty.StringVal("H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA"), 173 cty.StringVal("test"), 174 false, 175 }, 176 } 177 178 for _, test := range tests { 179 t.Run(fmt.Sprintf("base64gunzip(%#v)", test.String), func(t *testing.T) { 180 got, err := Base64Gunzip(test.String) 181 182 if test.Err { 183 if err == nil { 184 t.Fatal("succeeded; want error") 185 } 186 return 187 } else if err != nil { 188 t.Fatalf("unexpected error: %s", err) 189 } 190 191 if !got.RawEquals(test.Want) { 192 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 193 } 194 }) 195 } 196 } 197 198 func TestURLEncode(t *testing.T) { 199 tests := []struct { 200 String cty.Value 201 Want cty.Value 202 Err bool 203 }{ 204 { 205 cty.StringVal("abc123-_"), 206 cty.StringVal("abc123-_"), 207 false, 208 }, 209 { 210 cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"), 211 cty.StringVal("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz"), 212 false, 213 }, 214 { 215 cty.StringVal("mailto:email?subject=this+is+my+subject"), 216 cty.StringVal("mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject"), 217 false, 218 }, 219 { 220 cty.StringVal("foo/bar"), 221 cty.StringVal("foo%2Fbar"), 222 false, 223 }, 224 } 225 226 for _, test := range tests { 227 t.Run(fmt.Sprintf("urlencode(%#v)", test.String), func(t *testing.T) { 228 got, err := URLEncode(test.String) 229 230 if test.Err { 231 if err == nil { 232 t.Fatal("succeeded; want error") 233 } 234 return 235 } else if err != nil { 236 t.Fatalf("unexpected error: %s", err) 237 } 238 239 if !got.RawEquals(test.Want) { 240 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 241 } 242 }) 243 } 244 } 245 246 func TestURLDecode(t *testing.T) { 247 tests := []struct { 248 String cty.Value 249 Want cty.Value 250 Err bool 251 }{ 252 { 253 cty.StringVal("abc123-_"), 254 cty.StringVal("abc123-_"), 255 false, 256 }, 257 { 258 cty.StringVal("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz"), 259 cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"), 260 false, 261 }, 262 { 263 cty.StringVal("mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject"), 264 cty.StringVal("mailto:email?subject=this+is+my+subject"), 265 false, 266 }, 267 { 268 cty.StringVal("foo%2Fbar"), 269 cty.StringVal("foo/bar"), 270 false, 271 }, 272 { 273 cty.StringVal("foo% bar"), 274 cty.UnknownVal(cty.String), 275 true, 276 }, 277 { 278 cty.StringVal("foo%2 bar"), 279 cty.UnknownVal(cty.String), 280 true, 281 }, 282 { 283 cty.StringVal("%GGfoo%2bar"), 284 cty.UnknownVal(cty.String), 285 true, 286 }, 287 { 288 cty.StringVal("foo%00, bar!"), 289 cty.StringVal("foo\x00, bar!"), 290 false, 291 }, 292 { 293 cty.StringVal("hello%20%E4%B8%96%E7%95%8C"), //Unicode character support 294 cty.StringVal("hello 世界"), 295 false, 296 }, 297 { 298 cty.StringVal("hello%20%D8%AF%D9%86%DB%8C%D8%A7"), //Unicode character support 299 cty.StringVal("hello دنیا"), 300 false, 301 }, 302 } 303 304 for _, test := range tests { 305 t.Run(fmt.Sprintf("urldecode(%#v)", test.String), func(t *testing.T) { 306 got, err := URLDecode(test.String) 307 308 if test.Err { 309 if err == nil { 310 t.Fatal("succeeded; want error") 311 } 312 return 313 } else if err != nil { 314 t.Fatalf("unexpected error: %s", err) 315 } 316 317 if !got.RawEquals(test.Want) { 318 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 319 } 320 }) 321 } 322 } 323 324 func TestURLEncodeDecode(t *testing.T) { 325 tests := []struct { 326 String cty.Value 327 Want cty.Value 328 Err bool 329 }{ 330 { 331 cty.StringVal("abc123-_"), 332 cty.StringVal("abc123-_"), 333 false, 334 }, 335 { 336 cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"), 337 cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"), 338 false, 339 }, 340 { 341 cty.StringVal("mailto:email?subject=this+is+my+subject"), 342 cty.StringVal("mailto:email?subject=this+is+my+subject"), 343 false, 344 }, 345 { 346 cty.StringVal("foo/bar"), 347 cty.StringVal("foo/bar"), 348 false, 349 }, 350 { 351 cty.StringVal("foo%00, bar!"), 352 cty.StringVal("foo%00, bar!"), 353 false, 354 }, 355 } 356 357 for _, test := range tests { 358 t.Run(fmt.Sprintf("url encode decode(%#v)", test.String), func(t *testing.T) { 359 encoded, err := URLEncode(test.String) 360 if err != nil { 361 t.Fatalf("unexpected error: %s", err) 362 } 363 got, err := URLDecode(encoded) 364 365 if test.Err { 366 if err == nil { 367 t.Fatal("succeeded; want error") 368 } 369 return 370 } else if err != nil { 371 t.Fatalf("unexpected error: %s", err) 372 } 373 374 if !got.RawEquals(test.Want) { 375 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 376 } 377 }) 378 } 379 } 380 381 func TestBase64TextEncode(t *testing.T) { 382 tests := []struct { 383 String cty.Value 384 Encoding cty.Value 385 Want cty.Value 386 Err string 387 }{ 388 { 389 cty.StringVal("abc123!?$*&()'-=@~"), 390 cty.StringVal("UTF-8"), 391 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 392 ``, 393 }, 394 { 395 cty.StringVal("abc123!?$*&()'-=@~"), 396 cty.StringVal("UTF-16LE"), 397 cty.StringVal("YQBiAGMAMQAyADMAIQA/ACQAKgAmACgAKQAnAC0APQBAAH4A"), 398 ``, 399 }, 400 { 401 cty.StringVal("abc123!?$*&()'-=@~"), 402 cty.StringVal("CP936"), 403 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 404 ``, 405 }, 406 { 407 cty.StringVal("abc123!?$*&()'-=@~"), 408 cty.StringVal("NOT-EXISTS"), 409 cty.UnknownVal(cty.String).RefineNotNull(), 410 `"NOT-EXISTS" is not a supported IANA encoding name or alias in this OpenTofu version`, 411 }, 412 { 413 cty.StringVal("🤔"), 414 cty.StringVal("cp437"), 415 cty.UnknownVal(cty.String).RefineNotNull(), 416 `the given string contains characters that cannot be represented in IBM437`, 417 }, 418 { 419 cty.UnknownVal(cty.String), 420 cty.StringVal("windows-1250"), 421 cty.UnknownVal(cty.String).RefineNotNull(), 422 ``, 423 }, 424 { 425 cty.StringVal("hello world"), 426 cty.UnknownVal(cty.String), 427 cty.UnknownVal(cty.String).RefineNotNull(), 428 ``, 429 }, 430 } 431 432 for _, test := range tests { 433 t.Run(fmt.Sprintf("textencodebase64(%#v, %#v)", test.String, test.Encoding), func(t *testing.T) { 434 got, err := TextEncodeBase64(test.String, test.Encoding) 435 436 if test.Err != "" { 437 if err == nil { 438 t.Fatal("succeeded; want error") 439 } 440 if got, want := err.Error(), test.Err; got != want { 441 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 442 } 443 return 444 } else if err != nil { 445 t.Fatalf("unexpected error: %s", err) 446 } 447 448 if !got.RawEquals(test.Want) { 449 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 450 } 451 }) 452 } 453 } 454 455 func TestBase64TextDecode(t *testing.T) { 456 tests := []struct { 457 String cty.Value 458 Encoding cty.Value 459 Want cty.Value 460 Err string 461 }{ 462 { 463 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 464 cty.StringVal("UTF-8"), 465 cty.StringVal("abc123!?$*&()'-=@~"), 466 ``, 467 }, 468 { 469 cty.StringVal("YQBiAGMAMQAyADMAIQA/ACQAKgAmACgAKQAnAC0APQBAAH4A"), 470 cty.StringVal("UTF-16LE"), 471 cty.StringVal("abc123!?$*&()'-=@~"), 472 ``, 473 }, 474 { 475 cty.StringVal("YWJjMTIzIT8kKiYoKSctPUB+"), 476 cty.StringVal("CP936"), 477 cty.StringVal("abc123!?$*&()'-=@~"), 478 ``, 479 }, 480 { 481 cty.StringVal("doesn't matter"), 482 cty.StringVal("NOT-EXISTS"), 483 cty.UnknownVal(cty.String).RefineNotNull(), 484 `"NOT-EXISTS" is not a supported IANA encoding name or alias in this OpenTofu version`, 485 }, 486 { 487 cty.StringVal("<invalid base64>"), 488 cty.StringVal("cp437"), 489 cty.UnknownVal(cty.String).RefineNotNull(), 490 `the given value is has an invalid base64 symbol at offset 0`, 491 }, 492 { 493 cty.StringVal("gQ=="), // this is 0x81, which is not defined in windows-1250 494 cty.StringVal("windows-1250"), 495 cty.StringVal("�"), 496 `the given string contains symbols that are not defined for windows-1250`, 497 }, 498 { 499 cty.UnknownVal(cty.String), 500 cty.StringVal("windows-1250"), 501 cty.UnknownVal(cty.String).RefineNotNull(), 502 ``, 503 }, 504 { 505 cty.StringVal("YQBiAGMAMQAyADMAIQA/ACQAKgAmACgAKQAnAC0APQBAAH4A"), 506 cty.UnknownVal(cty.String), 507 cty.UnknownVal(cty.String).RefineNotNull(), 508 ``, 509 }, 510 } 511 512 for _, test := range tests { 513 t.Run(fmt.Sprintf("textdecodebase64(%#v, %#v)", test.String, test.Encoding), func(t *testing.T) { 514 got, err := TextDecodeBase64(test.String, test.Encoding) 515 516 if test.Err != "" { 517 if err == nil { 518 t.Fatal("succeeded; want error") 519 } 520 if got, want := err.Error(), test.Err; got != want { 521 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 522 } 523 return 524 } else if err != nil { 525 t.Fatalf("unexpected error: %s", err) 526 } 527 528 if !got.RawEquals(test.Want) { 529 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 530 } 531 }) 532 } 533 }