2024-02-08 04:48:59 -05:00
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
2023-05-02 11:33:06 -04:00
// SPDX-License-Identifier: MPL-2.0
2023-02-14 09:08:47 -05:00
package jsonfunction
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
2025-10-31 02:41:52 -04:00
"github.com/hashicorp/hcl/v2/ext/tryfunc"
2023-02-14 09:08:47 -05:00
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
func TestMarshal ( t * testing . T ) {
tests := [ ] struct {
Name string
Input map [ string ] function . Function
Want string
WantErr string
} {
{
"minimal function" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
Type : function . StaticReturnType ( cty . Bool ) ,
} ) ,
} ,
` { "format_version":"1.0","function_signatures": { "fun": { "return_type":"bool"}}} ` ,
"" ,
} ,
{
"function with description" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
Description : "`timestamp` returns a UTC timestamp string." ,
Type : function . StaticReturnType ( cty . String ) ,
} ) ,
} ,
"{\"format_version\":\"1.0\",\"function_signatures\":{\"fun\":{\"description\":\"`timestamp` returns a UTC timestamp string.\",\"return_type\":\"string\"}}}" ,
"" ,
} ,
{
"function with parameters" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "timestamp" ,
Description : "timestamp text" ,
Type : cty . String ,
} ,
{
Name : "duration" ,
Description : "duration text" ,
Type : cty . String ,
} ,
} ,
Type : function . StaticReturnType ( cty . String ) ,
} ) ,
} ,
` { "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"}]}}} ` ,
"" ,
} ,
{
"function with variadic parameter" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
VarParam : & function . Parameter {
Name : "default" ,
Description : "default description" ,
Type : cty . DynamicPseudoType ,
AllowUnknown : true ,
AllowDynamicType : true ,
AllowNull : true ,
AllowMarked : true ,
} ,
Type : function . StaticReturnType ( cty . DynamicPseudoType ) ,
} ) ,
} ,
` { "format_version":"1.0","function_signatures": { "fun": { "return_type":"dynamic","variadic_parameter": { "name":"default","description":"default description","is_nullable":true,"type":"dynamic"}}}} ` ,
"" ,
} ,
{
"function with list types" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . List ( cty . String ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . List ( cty . String ) ) ,
} ) ,
} ,
` { "format_version":"1.0","function_signatures": { "fun": { "return_type":["list","string"],"parameters":[ { "name":"list","type":["list","string"]}]}}} ` ,
"" ,
} ,
{
"returns diagnostics on failure" ,
map [ string ] function . Function {
"fun" : function . New ( & function . Spec {
Params : [ ] function . Parameter { } ,
Type : func ( args [ ] cty . Value ) ( ret cty . Type , err error ) {
return cty . DynamicPseudoType , fmt . Errorf ( "error" )
} ,
} ) ,
} ,
"" ,
"Failed to serialize function \"fun\": error" ,
} ,
2025-10-31 02:41:52 -04:00
{
"try function marshalled correctly" ,
map [ string ] function . Function {
"try" : tryfunc . TryFunc ,
} ,
` { "format_version":"1.0","function_signatures": { "try": { "return_type":"dynamic","variadic_parameter": { "name":"expressions","type":"dynamic"}}}} ` ,
"" ,
} ,
{
"core::try function marshalled correctly" ,
map [ string ] function . Function {
"core::try" : tryfunc . TryFunc ,
} ,
` { "format_version":"1.0","function_signatures": { "core::try": { "return_type":"dynamic","variadic_parameter": { "name":"expressions","type":"dynamic"}}}} ` ,
"" ,
} ,
{
// This checks that if a provider contains a function named the same as one of the core with custom marshaller, we identify that correctly
"provider::test::try function marshalled correctly" ,
map [ string ] function . Function {
"provider::test::try" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . List ( cty . String ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . List ( cty . String ) ) ,
} ) ,
} ,
` { "format_version":"1.0","function_signatures": { "provider::test::try": { "return_type":["list","string"],"parameters":[ { "name":"list","type":["list","string"]}]}}} ` ,
"" ,
} ,
{
"can function marshalled correctly" ,
map [ string ] function . Function {
"can" : tryfunc . CanFunc ,
} ,
` { "format_version":"1.0","function_signatures": { "can": { "return_type":"bool","parameters":[ { "name":"expression","type":"dynamic"}]}}} ` ,
"" ,
} ,
{
"core::can function marshalled correctly" ,
map [ string ] function . Function {
"core::can" : tryfunc . CanFunc ,
} ,
` { "format_version":"1.0","function_signatures": { "core::can": { "return_type":"bool","parameters":[ { "name":"expression","type":"dynamic"}]}}} ` ,
"" ,
} ,
{
// This checks that if a provider contains a function named the same as one of the core with custom marshaller, we identify that correctly
"provider::test::can function marshalled correctly" ,
map [ string ] function . Function {
"provider::test::can" : function . New ( & function . Spec {
Params : [ ] function . Parameter {
{
Name : "list" ,
Type : cty . List ( cty . String ) ,
} ,
} ,
Type : function . StaticReturnType ( cty . List ( cty . String ) ) ,
} ) ,
} ,
` { "format_version":"1.0","function_signatures": { "provider::test::can": { "return_type":["list","string"],"parameters":[ { "name":"list","type":["list","string"]}]}}} ` ,
"" ,
} ,
2023-02-14 09:08:47 -05:00
}
for i , test := range tests {
t . Run ( fmt . Sprintf ( "%d-%s" , i , test . Name ) , func ( t * testing . T ) {
got , diags := Marshal ( test . Input )
if test . WantErr != "" {
if ! diags . HasErrors ( ) {
t . Fatal ( "expected error, got none" )
}
if diags . Err ( ) . Error ( ) != test . WantErr {
t . Fatalf ( "expected error %q, got %q" , test . WantErr , diags . Err ( ) )
}
} else {
if diags . HasErrors ( ) {
t . Fatal ( diags )
}
if diff := cmp . Diff ( test . Want , string ( got ) , ctydebug . CmpOptions ) ; diff != "" {
t . Fatalf ( "mismatch of function signature: %s" , diff )
}
}
} )
}
}