github.com/opentofu/opentofu@v1.7.1/internal/command/jsonfunction/function_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 jsonfunction
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/zclconf/go-cty-debug/ctydebug"
    14  	"github.com/zclconf/go-cty/cty"
    15  	"github.com/zclconf/go-cty/cty/function"
    16  )
    17  
    18  func TestMarshal(t *testing.T) {
    19  	tests := []struct {
    20  		Name    string
    21  		Input   map[string]function.Function
    22  		Want    string
    23  		WantErr string
    24  	}{
    25  		{
    26  			"minimal function",
    27  			map[string]function.Function{
    28  				"fun": function.New(&function.Spec{
    29  					Type: function.StaticReturnType(cty.Bool),
    30  				}),
    31  			},
    32  			`{"format_version":"1.0","function_signatures":{"fun":{"return_type":"bool"}}}`,
    33  			"",
    34  		},
    35  		{
    36  			"function with description",
    37  			map[string]function.Function{
    38  				"fun": function.New(&function.Spec{
    39  					Description: "`timestamp` returns a UTC timestamp string.",
    40  					Type:        function.StaticReturnType(cty.String),
    41  				}),
    42  			},
    43  			"{\"format_version\":\"1.0\",\"function_signatures\":{\"fun\":{\"description\":\"`timestamp` returns a UTC timestamp string.\",\"return_type\":\"string\"}}}",
    44  			"",
    45  		},
    46  		{
    47  			"function with parameters",
    48  			map[string]function.Function{
    49  				"fun": function.New(&function.Spec{
    50  					Params: []function.Parameter{
    51  						{
    52  							Name:        "timestamp",
    53  							Description: "timestamp text",
    54  							Type:        cty.String,
    55  						},
    56  						{
    57  							Name:        "duration",
    58  							Description: "duration text",
    59  							Type:        cty.String,
    60  						},
    61  					},
    62  					Type: function.StaticReturnType(cty.String),
    63  				}),
    64  			},
    65  			`{"format_version":"1.0","function_signatures":{"fun":{"return_type":"string","parameters":[{"name":"timestamp","description":"timestamp text","type":"string"},{"name":"duration","description":"duration text","type":"string"}]}}}`,
    66  			"",
    67  		},
    68  		{
    69  			"function with variadic parameter",
    70  			map[string]function.Function{
    71  				"fun": function.New(&function.Spec{
    72  					VarParam: &function.Parameter{
    73  						Name:             "default",
    74  						Description:      "default description",
    75  						Type:             cty.DynamicPseudoType,
    76  						AllowUnknown:     true,
    77  						AllowDynamicType: true,
    78  						AllowNull:        true,
    79  						AllowMarked:      true,
    80  					},
    81  					Type: function.StaticReturnType(cty.DynamicPseudoType),
    82  				}),
    83  			},
    84  			`{"format_version":"1.0","function_signatures":{"fun":{"return_type":"dynamic","variadic_parameter":{"name":"default","description":"default description","is_nullable":true,"type":"dynamic"}}}}`,
    85  			"",
    86  		},
    87  		{
    88  			"function with list types",
    89  			map[string]function.Function{
    90  				"fun": function.New(&function.Spec{
    91  					Params: []function.Parameter{
    92  						{
    93  							Name: "list",
    94  							Type: cty.List(cty.String),
    95  						},
    96  					},
    97  					Type: function.StaticReturnType(cty.List(cty.String)),
    98  				}),
    99  			},
   100  			`{"format_version":"1.0","function_signatures":{"fun":{"return_type":["list","string"],"parameters":[{"name":"list","type":["list","string"]}]}}}`,
   101  			"",
   102  		},
   103  		{
   104  			"returns diagnostics on failure",
   105  			map[string]function.Function{
   106  				"fun": function.New(&function.Spec{
   107  					Params: []function.Parameter{},
   108  					Type: func(args []cty.Value) (ret cty.Type, err error) {
   109  						return cty.DynamicPseudoType, fmt.Errorf("error")
   110  					},
   111  				}),
   112  			},
   113  			"",
   114  			"Failed to serialize function \"fun\": error",
   115  		},
   116  	}
   117  
   118  	for i, test := range tests {
   119  		t.Run(fmt.Sprintf("%d-%s", i, test.Name), func(t *testing.T) {
   120  			got, diags := Marshal(test.Input)
   121  			if test.WantErr != "" {
   122  				if !diags.HasErrors() {
   123  					t.Fatal("expected error, got none")
   124  				}
   125  				if diags.Err().Error() != test.WantErr {
   126  					t.Fatalf("expected error %q, got %q", test.WantErr, diags.Err())
   127  				}
   128  			} else {
   129  				if diags.HasErrors() {
   130  					t.Fatal(diags)
   131  				}
   132  
   133  				if diff := cmp.Diff(test.Want, string(got), ctydebug.CmpOptions); diff != "" {
   134  					t.Fatalf("mismatch of function signature: %s", diff)
   135  				}
   136  			}
   137  		})
   138  	}
   139  }