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 }