// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package jsonplan import ( "encoding/json" "testing" "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/msgpack" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/terraform" ) func TestMarshalActionInvocations(t *testing.T) { action := addrs.AbsActionInstance{ Module: addrs.RootModuleInstance, Action: addrs.ActionInstance{ Action: addrs.Action{ Type: "test_action", Name: "test", }, Key: addrs.NoKey, }, } provider := addrs.AbsProviderConfig{ Module: addrs.RootModule, Provider: addrs.Provider{ Type: "test", Namespace: "hashicorp", Hostname: addrs.DefaultProviderRegistryHost, }, } schemas := &terraform.Schemas{ Providers: map[addrs.Provider]providers.ProviderSchema{ provider.Provider: { Actions: map[string]providers.ActionSchema{ "test_action": { ConfigSchema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "optional": { Type: cty.String, }, "sensitive": { Type: cty.String, Sensitive: true, }, }, }, }, }, }, }, } tcs := map[string]struct { input *plans.ActionInvocationInstanceSrc output ActionInvocation }{ "no metadata": { input: &plans.ActionInvocationInstanceSrc{ Addr: action, ActionTrigger: new(plans.InvokeActionTrigger), ConfigValue: mustDynamicValue(t, cty.ObjectVal(map[string]cty.Value{ "optional": cty.StringVal("hello"), "sensitive": cty.StringVal("world"), })), SensitiveConfigPaths: nil, ProviderAddr: provider, }, output: ActionInvocation{ Address: "action.test_action.test", Type: "test_action", Name: "test", ConfigValues: mustJson(t, map[string]interface{}{ "optional": "hello", "sensitive": "world", }), ConfigSensitive: mustJson(t, map[string]interface{}{ "sensitive": true, }), ConfigUnknown: mustJson(t, map[string]interface{}{}), ProviderName: "registry.terraform.io/hashicorp/test", InvokeActionTrigger: new(InvokeActionTrigger), }, }, "unknown value": { input: &plans.ActionInvocationInstanceSrc{ Addr: action, ActionTrigger: new(plans.InvokeActionTrigger), ConfigValue: mustDynamicValue(t, cty.ObjectVal(map[string]cty.Value{ "optional": cty.UnknownVal(cty.String), "sensitive": cty.StringVal("world"), })), SensitiveConfigPaths: nil, ProviderAddr: provider, }, output: ActionInvocation{ Address: "action.test_action.test", Type: "test_action", Name: "test", ConfigValues: mustJson(t, map[string]interface{}{ "sensitive": "world", }), ConfigSensitive: mustJson(t, map[string]interface{}{ "sensitive": true, }), ConfigUnknown: mustJson(t, map[string]interface{}{ "optional": true, }), ProviderName: "registry.terraform.io/hashicorp/test", InvokeActionTrigger: new(InvokeActionTrigger), }, }, "extra sensitive": { input: &plans.ActionInvocationInstanceSrc{ Addr: action, ActionTrigger: new(plans.InvokeActionTrigger), ConfigValue: mustDynamicValue(t, cty.ObjectVal(map[string]cty.Value{ "optional": cty.StringVal("hello"), "sensitive": cty.StringVal("world"), })), SensitiveConfigPaths: []cty.Path{cty.GetAttrPath("optional")}, ProviderAddr: provider, }, output: ActionInvocation{ Address: "action.test_action.test", Type: "test_action", Name: "test", ConfigValues: mustJson(t, map[string]interface{}{ "optional": "hello", "sensitive": "world", }), ConfigSensitive: mustJson(t, map[string]interface{}{ "optional": true, "sensitive": true, }), ConfigUnknown: mustJson(t, map[string]interface{}{}), ProviderName: "registry.terraform.io/hashicorp/test", InvokeActionTrigger: new(InvokeActionTrigger), }, }, } for name, tc := range tcs { t.Run(name, func(t *testing.T) { out, err := MarshalActionInvocation(tc.input, schemas) if err != nil { t.Fatal(err) } if diff := cmp.Diff(tc.output, out); len(diff) > 0 { t.Fatal(diff) } }) } } func mustDynamicValue(t *testing.T, value cty.Value) []byte { out, err := msgpack.Marshal(value, value.Type()) if err != nil { t.Fatal(err) } return out } func mustJson(t *testing.T, data interface{}) json.RawMessage { out, err := json.Marshal(data) if err != nil { t.Fatal(err) } return out }