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  }