| // Copyright (c) The OpenTofu Authors | |
| // SPDX-License-Identifier: MPL-2.0 | |
| // Copyright (c) 2023 HashiCorp, Inc. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| package jsonprovider | |
| import ( | |
| "encoding/json" | |
| "testing" | |
| "github.com/google/go-cmp/cmp" | |
| "github.com/opentofu/opentofu/internal/providers" | |
| "github.com/zclconf/go-cty/cty" | |
| ) | |
| func TestMarshalReturnType(t *testing.T) { | |
| type testcase struct { | |
| Arg cty.Type | |
| Expected any | |
| } | |
| tests := map[string]testcase{ | |
| "string": { | |
| Arg: cty.String, | |
| Expected: "string", | |
| }, | |
| "number": { | |
| Arg: cty.Number, | |
| Expected: "number", | |
| }, | |
| "bool": { | |
| Arg: cty.Bool, | |
| Expected: "bool", | |
| }, | |
| "object": { | |
| Arg: cty.Object(map[string]cty.Type{"number_type": cty.Number}), | |
| Expected: []any{ | |
| string("object"), | |
| map[string]cty.Type{"number_type": cty.Number}, | |
| }, | |
| }, | |
| "map": { | |
| Arg: cty.Map(cty.String), | |
| Expected: []any{ | |
| string("map"), | |
| cty.String, | |
| }, | |
| }, | |
| "list": { | |
| Arg: cty.List(cty.Bool), | |
| Expected: []any{ | |
| string("list"), | |
| cty.Bool, | |
| }, | |
| }, | |
| "set": { | |
| Arg: cty.Set(cty.Number), | |
| Expected: []any{ | |
| string("set"), | |
| cty.Number, | |
| }, | |
| }, | |
| "tuple": { | |
| Arg: cty.Tuple([]cty.Type{cty.String}), | |
| Expected: []any{ | |
| string("tuple"), | |
| []any{cty.String}, | |
| }, | |
| }, | |
| } | |
| for tn, tc := range tests { | |
| t.Run(tn, func(t *testing.T) { | |
| actual := marshalReturnType(tc.Arg) | |
| // to avoid the nightmare of comparing cty primitive types we can marshal them to json and compare that | |
| actualJSON, _ := json.Marshal(actual) | |
| expectedJSON, _ := json.Marshal(tc.Expected) | |
| if !cmp.Equal(actualJSON, expectedJSON) { | |
| t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON))) | |
| } | |
| }) | |
| } | |
| } | |
| func TestMarshalParameter(t *testing.T) { | |
| // used so can make a pointer to it | |
| trueBoolVal := true | |
| type testcase struct { | |
| Arg providers.FunctionParameterSpec | |
| Expected FunctionParam | |
| } | |
| tests := map[string]testcase{ | |
| "basic": { | |
| Arg: providers.FunctionParameterSpec{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }, | |
| Expected: FunctionParam{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }, | |
| }, | |
| "nullable": { | |
| Arg: providers.FunctionParameterSpec{ | |
| Description: "nullable number func", | |
| Type: cty.Number, | |
| AllowNullValue: trueBoolVal, | |
| }, | |
| Expected: FunctionParam{ | |
| Description: "nullable number func", | |
| Type: cty.Number, | |
| IsNullable: &trueBoolVal, | |
| }, | |
| }, | |
| } | |
| for tn, tc := range tests { | |
| t.Run(tn, func(t *testing.T) { | |
| actual := marshalParameter(tc.Arg) | |
| // to avoid the nightmare of comparing cty primitive types we can marshal them to json and compare that | |
| actualJSON, _ := json.Marshal(actual) | |
| expectedJSON, _ := json.Marshal(tc.Expected) | |
| if !cmp.Equal(actualJSON, expectedJSON) { | |
| t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON))) | |
| } | |
| }) | |
| } | |
| } | |
| func TestMarshalParameters(t *testing.T) { | |
| type testcase struct { | |
| Arg []providers.FunctionParameterSpec | |
| Expected []FunctionParam | |
| } | |
| tests := map[string]testcase{ | |
| "basic": { | |
| Arg: []providers.FunctionParameterSpec{{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }}, | |
| Expected: []FunctionParam{{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }}, | |
| }, | |
| } | |
| for tn, tc := range tests { | |
| t.Run(tn, func(t *testing.T) { | |
| actual := marshalParameters(tc.Arg) | |
| // to avoid the nightmare of comparing cty primitive types we can marshal them to json and compare that | |
| actualJSON, _ := json.Marshal(actual) | |
| expectedJSON, _ := json.Marshal(tc.Expected) | |
| if !cmp.Equal(actualJSON, expectedJSON) { | |
| t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON))) | |
| } | |
| }) | |
| } | |
| } | |
| func TestMarshalFunction(t *testing.T) { | |
| type testcase struct { | |
| Arg providers.FunctionSpec | |
| Expected Function | |
| } | |
| tests := map[string]testcase{ | |
| "basic": { | |
| Arg: providers.FunctionSpec{ | |
| Description: "basic string func", | |
| Return: cty.String, | |
| }, | |
| Expected: Function{ | |
| Description: "basic string func", | |
| ReturnType: cty.String, | |
| }, | |
| }, | |
| "variadic": { | |
| Arg: providers.FunctionSpec{ | |
| Description: "basic string func", | |
| Return: cty.String, | |
| VariadicParameter: &providers.FunctionParameterSpec{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }, | |
| }, | |
| Expected: Function{ | |
| Description: "basic string func", | |
| ReturnType: cty.String, | |
| VariadicParameter: &FunctionParam{ | |
| Description: "basic string func", | |
| Type: cty.String, | |
| }, | |
| }, | |
| }, | |
| } | |
| for tn, tc := range tests { | |
| t.Run(tn, func(t *testing.T) { | |
| actual := marshalFunction(tc.Arg) | |
| // to avoid the nightmare of comparing cty primitive types we can marshal them to json and compare that | |
| actualJSON, _ := json.Marshal(actual) | |
| expectedJSON, _ := json.Marshal(tc.Expected) | |
| if !cmp.Equal(actualJSON, expectedJSON) { | |
| t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON))) | |
| } | |
| }) | |
| } | |
| } | |
| func TestMarshalFunctions(t *testing.T) { | |
| type testcase struct { | |
| Arg map[string]providers.FunctionSpec | |
| Expected map[string]Function | |
| } | |
| tests := map[string]testcase{ | |
| "basic": { | |
| Arg: map[string]providers.FunctionSpec{"basic_func": { | |
| Description: "basic string func", | |
| Return: cty.String, | |
| }}, | |
| Expected: map[string]Function{"basic_func": { | |
| Description: "basic string func", | |
| ReturnType: cty.String, | |
| }}, | |
| }, | |
| } | |
| for tn, tc := range tests { | |
| t.Run(tn, func(t *testing.T) { | |
| actual := marshalFunctions(tc.Arg) | |
| // to avoid the nightmare of comparing cty primitive types we can marshal them to json and compare that | |
| actualJSON, _ := json.Marshal(actual) | |
| expectedJSON, _ := json.Marshal(tc.Expected) | |
| if !cmp.Equal(actualJSON, expectedJSON) { | |
| t.Fatalf("values don't match:\n %v\n", cmp.Diff(string(actualJSON), string(expectedJSON))) | |
| } | |
| }) | |
| } | |
| } |