| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| package tfstackdata1 | |
| import ( | |
| "testing" | |
| "github.com/google/go-cmp/cmp" | |
| "github.com/zclconf/go-cty/cty" | |
| "github.com/zclconf/go-cty/cty/msgpack" | |
| "google.golang.org/protobuf/testing/protocmp" | |
| "github.com/hashicorp/terraform/internal/lang/marks" | |
| "github.com/hashicorp/terraform/internal/plans/planproto" | |
| "github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks" | |
| ) | |
| func TestDynamicValueToTFStackData1(t *testing.T) { | |
| startVal := cty.ObjectVal(map[string]cty.Value{ | |
| "a": cty.StringVal("a").Mark(marks.Sensitive), | |
| "b": cty.StringVal("b"), | |
| "c": cty.ListVal([]cty.Value{ | |
| cty.StringVal("c[0]"), | |
| cty.StringVal("c[1]").Mark(marks.Sensitive), | |
| }), | |
| }) | |
| ty := startVal.Type() | |
| partial, err := stacks.ToDynamicValue(startVal, ty) | |
| if err != nil { | |
| t.Fatalf("unexpected error: %s", err) | |
| } | |
| got := Terraform1ToStackDataDynamicValue(partial) | |
| want := &DynamicValue{ | |
| Value: &planproto.DynamicValue{ | |
| // The following is cty's canonical MessagePack encoding of | |
| // the unmarked version of startVal: | |
| // - \x83 marks the start of a three-element "fixmap" | |
| // - \xa1 and \xa4 mark a one-element and a four-element fixstr respectively | |
| // - \x92 marks the start of a two-element "fixarray" | |
| // cty/msgpack always orders object attribute names lexically when | |
| // serializing, so we can safely rely on the order of the attrs. | |
| Msgpack: []byte("\x83\xa1a\xa1a\xa1b\xa1b\xa1c\x92\xa4c[0]\xa4c[1]"), | |
| }, | |
| SensitivePaths: []*planproto.Path{ | |
| { | |
| Steps: []*planproto.Path_Step{ | |
| { | |
| Selector: &planproto.Path_Step_AttributeName{ | |
| AttributeName: "a", | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| Steps: []*planproto.Path_Step{ | |
| { | |
| Selector: &planproto.Path_Step_AttributeName{ | |
| AttributeName: "c", | |
| }, | |
| }, | |
| { | |
| Selector: &planproto.Path_Step_ElementKey{ | |
| ElementKey: &planproto.DynamicValue{ | |
| Msgpack: []byte{0b00000001}, // MessagePack-encoded fixint 1 | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| // DynamicValueToTFStackData1 doesn't guarantee the order of the | |
| // entries in SensitivePaths, so we'll normalize what we got. | |
| // We distinguish the two expected paths by their number of steps. | |
| if len(got.SensitivePaths) == 2 && len(got.SensitivePaths[0].Steps) == 2 { | |
| got.SensitivePaths[0], got.SensitivePaths[1] = got.SensitivePaths[1], got.SensitivePaths[0] | |
| } | |
| if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { | |
| t.Errorf("wrong result\n%s", diff) | |
| } | |
| } | |
| func TestDynamicValueFromTFStackData1(t *testing.T) { | |
| startVal := cty.ObjectVal(map[string]cty.Value{ | |
| "a": cty.StringVal("a").Mark(marks.Sensitive), | |
| "b": cty.StringVal("b"), | |
| "c": cty.ListVal([]cty.Value{ | |
| cty.StringVal("c[0]"), | |
| cty.StringVal("c[1]").Mark(marks.Sensitive), | |
| }), | |
| }) | |
| ty := startVal.Type() | |
| // We'll use the MessagePack encoder directly to get the raw bytes | |
| // representing the above, just for maintainability's sake since it's | |
| // challenging to read and modify raw MessagePack values. | |
| unmarkedVal, _ := startVal.UnmarkDeep() | |
| raw, err := msgpack.Marshal(unmarkedVal, ty) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| input := &DynamicValue{ | |
| Value: &planproto.DynamicValue{ | |
| Msgpack: raw, | |
| }, | |
| SensitivePaths: []*planproto.Path{ | |
| { | |
| Steps: []*planproto.Path_Step{ | |
| { | |
| Selector: &planproto.Path_Step_AttributeName{ | |
| AttributeName: "a", | |
| }, | |
| }, | |
| }, | |
| }, | |
| { | |
| Steps: []*planproto.Path_Step{ | |
| { | |
| Selector: &planproto.Path_Step_AttributeName{ | |
| AttributeName: "c", | |
| }, | |
| }, | |
| { | |
| Selector: &planproto.Path_Step_ElementKey{ | |
| ElementKey: &planproto.DynamicValue{ | |
| Msgpack: []byte{0b00000001}, // MessagePack-encoded fixint 1 | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| }, | |
| } | |
| got, err := DynamicValueFromTFStackData1(input, ty) | |
| if err != nil { | |
| t.Fatalf("unexpected error: %s", err) | |
| } | |
| want := startVal | |
| if !want.RawEquals(got) { | |
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) | |
| } | |
| } |