github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/funcs/conversion_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package funcs
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/terramate-io/tf/lang/marks"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestTo(t *testing.T) {
    15  	tests := []struct {
    16  		Value    cty.Value
    17  		TargetTy cty.Type
    18  		Want     cty.Value
    19  		Err      string
    20  	}{
    21  		{
    22  			cty.StringVal("a"),
    23  			cty.String,
    24  			cty.StringVal("a"),
    25  			``,
    26  		},
    27  		{
    28  			cty.UnknownVal(cty.String),
    29  			cty.String,
    30  			cty.UnknownVal(cty.String),
    31  			``,
    32  		},
    33  		{
    34  			cty.NullVal(cty.String),
    35  			cty.String,
    36  			cty.NullVal(cty.String),
    37  			``,
    38  		},
    39  		{
    40  			// This test case represents evaluating the expression tostring(null)
    41  			// from HCL, since null in HCL is cty.NullVal(cty.DynamicPseudoType).
    42  			// The result in that case should still be null, but a null specifically
    43  			// of type string.
    44  			cty.NullVal(cty.DynamicPseudoType),
    45  			cty.String,
    46  			cty.NullVal(cty.String),
    47  			``,
    48  		},
    49  		{
    50  			cty.StringVal("a").Mark("boop"),
    51  			cty.String,
    52  			cty.StringVal("a").Mark("boop"),
    53  			``,
    54  		},
    55  		{
    56  			cty.NullVal(cty.String).Mark("boop"),
    57  			cty.String,
    58  			cty.NullVal(cty.String).Mark("boop"),
    59  			``,
    60  		},
    61  		{
    62  			cty.True,
    63  			cty.String,
    64  			cty.StringVal("true"),
    65  			``,
    66  		},
    67  		{
    68  			cty.StringVal("a"),
    69  			cty.Bool,
    70  			cty.DynamicVal,
    71  			`cannot convert "a" to bool; only the strings "true" or "false" are allowed`,
    72  		},
    73  		{
    74  			cty.StringVal("a").Mark("boop"),
    75  			cty.Bool,
    76  			cty.DynamicVal,
    77  			`cannot convert "a" to bool; only the strings "true" or "false" are allowed`,
    78  		},
    79  		{
    80  			cty.StringVal("a").Mark(marks.Sensitive),
    81  			cty.Bool,
    82  			cty.DynamicVal,
    83  			`cannot convert this sensitive string to bool`,
    84  		},
    85  		{
    86  			cty.StringVal("a"),
    87  			cty.Number,
    88  			cty.DynamicVal,
    89  			`cannot convert "a" to number; given string must be a decimal representation of a number`,
    90  		},
    91  		{
    92  			cty.StringVal("a").Mark("boop"),
    93  			cty.Number,
    94  			cty.DynamicVal,
    95  			`cannot convert "a" to number; given string must be a decimal representation of a number`,
    96  		},
    97  		{
    98  			cty.StringVal("a").Mark(marks.Sensitive),
    99  			cty.Number,
   100  			cty.DynamicVal,
   101  			`cannot convert this sensitive string to number`,
   102  		},
   103  		{
   104  			cty.NullVal(cty.String),
   105  			cty.Number,
   106  			cty.NullVal(cty.Number),
   107  			``,
   108  		},
   109  		{
   110  			cty.UnknownVal(cty.Bool),
   111  			cty.String,
   112  			cty.UnknownVal(cty.String),
   113  			``,
   114  		},
   115  		{
   116  			cty.UnknownVal(cty.String),
   117  			cty.Bool,
   118  			cty.UnknownVal(cty.Bool), // conversion is optimistic
   119  			``,
   120  		},
   121  		{
   122  			cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}),
   123  			cty.List(cty.String),
   124  			cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}),
   125  			``,
   126  		},
   127  		{
   128  			cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}),
   129  			cty.Set(cty.String),
   130  			cty.SetVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}),
   131  			``,
   132  		},
   133  		{
   134  			cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.True}),
   135  			cty.Map(cty.String),
   136  			cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("true")}),
   137  			``,
   138  		},
   139  		{
   140  			cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}),
   141  			cty.Map(cty.String),
   142  			cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}),
   143  			``,
   144  		},
   145  		{
   146  			cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"),
   147  			cty.Map(cty.String),
   148  			cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"),
   149  			``,
   150  		},
   151  		{
   152  			cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}),
   153  			cty.List(cty.String),
   154  			cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}),
   155  			``,
   156  		},
   157  		{
   158  			cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"),
   159  			cty.List(cty.String),
   160  			cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"),
   161  			``,
   162  		},
   163  		{
   164  			cty.EmptyTupleVal,
   165  			cty.String,
   166  			cty.DynamicVal,
   167  			`cannot convert tuple to string`,
   168  		},
   169  		{
   170  			cty.UnknownVal(cty.EmptyTuple),
   171  			cty.String,
   172  			cty.DynamicVal,
   173  			`cannot convert tuple to string`,
   174  		},
   175  		{
   176  			cty.EmptyObjectVal,
   177  			cty.Object(map[string]cty.Type{"foo": cty.String}),
   178  			cty.DynamicVal,
   179  			`incompatible object type for conversion: attribute "foo" is required`,
   180  		},
   181  	}
   182  
   183  	for _, test := range tests {
   184  		t.Run(fmt.Sprintf("to %s(%#v)", test.TargetTy.FriendlyNameForConstraint(), test.Value), func(t *testing.T) {
   185  			f := MakeToFunc(test.TargetTy)
   186  			got, err := f.Call([]cty.Value{test.Value})
   187  
   188  			if test.Err != "" {
   189  				if err == nil {
   190  					t.Fatal("succeeded; want error")
   191  				}
   192  				if got, want := err.Error(), test.Err; got != want {
   193  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   194  				}
   195  				return
   196  			} else if err != nil {
   197  				t.Fatalf("unexpected error: %s", err)
   198  			}
   199  
   200  			if !got.RawEquals(test.Want) {
   201  				t.Errorf("wrong result\ngot:  %#v\nwant: %#v", got, test.Want)
   202  			}
   203  		})
   204  	}
   205  }