| // Copyright (c) HashiCorp, Inc. |
| // 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) |
| } |
| } |