github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/lang/function_calls_test.go (about)

     1  package lang
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/hcl/v2/hclsyntax"
     9  	"github.com/hashicorp/hcl/v2/json"
    10  	"github.com/terraform-linters/tflint-plugin-sdk/hclext"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  func TestFunctionCallsInExpr(t *testing.T) {
    15  	parse := func(src string) hcl.Expression {
    16  		expr, diags := hclsyntax.ParseExpression([]byte(src), "", hcl.InitialPos)
    17  		if diags.HasErrors() {
    18  			t.Fatalf("failed to parse `%s`, %s", src, diags)
    19  		}
    20  		return expr
    21  	}
    22  	parseJSON := func(src string) hcl.Expression {
    23  		expr, diags := json.ParseExpression([]byte(src), "")
    24  		if diags.HasErrors() {
    25  			t.Fatalf("failed to parse `%s`, %s", src, diags)
    26  		}
    27  		return expr
    28  	}
    29  
    30  	tests := []struct {
    31  		name string
    32  		expr hcl.Expression
    33  		want []*FunctionCall
    34  	}{
    35  		{
    36  			name: "nil expression",
    37  			expr: nil,
    38  			want: nil,
    39  		},
    40  		{
    41  			name: "string",
    42  			expr: parse(`"string"`),
    43  			want: []*FunctionCall{},
    44  		},
    45  		{
    46  			name: "string (JSON)",
    47  			expr: parseJSON(`"string"`),
    48  			want: []*FunctionCall{},
    49  		},
    50  		{
    51  			name: "number (JSON)",
    52  			expr: parseJSON(`123`),
    53  			want: []*FunctionCall{},
    54  		},
    55  		{
    56  			name: "null (JSON)",
    57  			expr: parseJSON(`null`),
    58  			want: []*FunctionCall{},
    59  		},
    60  		{
    61  			name: "unknown (JSON)",
    62  			expr: parseJSON(`"${var.foo}"`),
    63  			want: []*FunctionCall{},
    64  		},
    65  		{
    66  			name: "single function call",
    67  			expr: parse(`md5("hello")`),
    68  			want: []*FunctionCall{
    69  				{Name: "md5", ArgsCount: 1},
    70  			},
    71  		},
    72  		{
    73  			name: "single function call (JSON)",
    74  			expr: parseJSON(`"${md5(\"hello\")}"`),
    75  			want: []*FunctionCall{
    76  				{Name: "md5", ArgsCount: 1},
    77  			},
    78  		},
    79  		{
    80  			name: "multiple function calls",
    81  			expr: parse(`[md5("hello"), "world", provider::tflint::world()]`),
    82  			want: []*FunctionCall{
    83  				{Name: "md5", ArgsCount: 1},
    84  				{Name: "provider::tflint::world", ArgsCount: 0},
    85  			},
    86  		},
    87  		{
    88  			name: "multiple function calls (JSON)",
    89  			expr: parseJSON(`["${md5(\"hello\")}", "world", "${provider::tflint::world()}"]`),
    90  			want: []*FunctionCall{
    91  				{Name: "md5", ArgsCount: 1},
    92  				{Name: "provider::tflint::world", ArgsCount: 0},
    93  			},
    94  		},
    95  		{
    96  			name: "bound expr with native syntax",
    97  			expr: hclext.BindValue(cty.StringVal("foo-Hello, John and Mike"), parse(`"foo-${hello("John", "Mike")}"`)),
    98  			want: []*FunctionCall{
    99  				{Name: "hello", ArgsCount: 2},
   100  			},
   101  		},
   102  		{
   103  			name: "bound expr with JSON syntax",
   104  			expr: hclext.BindValue(cty.StringVal("foo-Hello, John and Mike"), parseJSON(`"foo-${hello(\"John\", \"Mike\")}"`)),
   105  			want: []*FunctionCall{
   106  				{Name: "hello", ArgsCount: 2},
   107  			},
   108  		},
   109  	}
   110  
   111  	for _, test := range tests {
   112  		t.Run(test.name, func(t *testing.T) {
   113  			got, diags := FunctionCallsInExpr(test.expr)
   114  			if diags.HasErrors() {
   115  				t.Fatal(diags)
   116  			}
   117  
   118  			if diff := cmp.Diff(test.want, got); diff != "" {
   119  				t.Errorf(diff)
   120  			}
   121  		})
   122  	}
   123  }
   124  
   125  func TestIsProviderDefined(t *testing.T) {
   126  	tests := []struct {
   127  		name string
   128  		want bool
   129  	}{
   130  		{
   131  			name: "md5",
   132  			want: false,
   133  		},
   134  		{
   135  			name: "core::md5",
   136  			want: false,
   137  		},
   138  		{
   139  			name: "provider::tflint::echo",
   140  			want: true,
   141  		},
   142  	}
   143  
   144  	for _, test := range tests {
   145  		t.Run(test.name, func(t *testing.T) {
   146  			f := &FunctionCall{Name: test.name}
   147  
   148  			got := f.IsProviderDefined()
   149  
   150  			if got != test.want {
   151  				t.Errorf("got %t, want %t", got, test.want)
   152  			}
   153  		})
   154  	}
   155  }