package funcs import ( "fmt" "testing" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/zclconf/go-cty/cty" ) func TestSensitive(t *testing.T) { tests := []struct { Input cty.Value WantErr string }{ { cty.NumberIntVal(1), ``, }, { // Unknown values stay unknown while becoming sensitive cty.UnknownVal(cty.String), ``, }, { // Null values stay unknown while becoming sensitive cty.NullVal(cty.String), ``, }, { // DynamicVal can be marked as sensitive cty.DynamicVal, ``, }, { // The marking is shallow only cty.ListVal([]cty.Value{cty.NumberIntVal(1)}), ``, }, { // A value already marked is allowed and stays marked cty.NumberIntVal(1).Mark(marks.Sensitive), ``, }, { // A value with some non-standard mark gets "fixed" to be marked // with the standard "sensitive" mark. (This situation occurring // would imply an inconsistency/bug elsewhere, so we're just // being robust about it here.) cty.NumberIntVal(1).Mark("bloop"), ``, }, { // A value deep already marked is allowed and stays marked, // _and_ we'll also mark the outer collection as sensitive. cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}), ``, }, } for _, test := range tests { t.Run(fmt.Sprintf("sensitive(%#v)", test.Input), func(t *testing.T) { got, err := Sensitive(test.Input) if test.WantErr != "" { if err == nil { t.Fatal("succeeded; want error") } if got, want := err.Error(), test.WantErr; got != want { t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) } return } else if err != nil { t.Fatalf("unexpected error: %s", err) } if !got.HasMark(marks.Sensitive) { t.Errorf("result is not marked sensitive") } gotRaw, gotMarks := got.Unmark() if len(gotMarks) != 1 { // We're only expecting to have the "sensitive" mark we checked // above. Any others are an error, even if they happen to // appear alongside "sensitive". (We might change this rule // if someday we decide to use marks for some additional // unrelated thing in Terraform, but currently we assume that // _all_ marks imply sensitive, and so returning any other // marks would be confusing.) t.Errorf("extraneous marks %#v", gotMarks) } // Disregarding shallow marks, the result should have the same // effective value as the input. wantRaw, _ := test.Input.Unmark() if !gotRaw.RawEquals(wantRaw) { t.Errorf("wrong unmarked result\ngot: %#v\nwant: %#v", got, wantRaw) } }) } } func TestNonsensitive(t *testing.T) { tests := []struct { Input cty.Value WantErr string }{ { cty.NumberIntVal(1).Mark(marks.Sensitive), ``, }, { cty.DynamicVal.Mark(marks.Sensitive), ``, }, { cty.UnknownVal(cty.String).Mark(marks.Sensitive), ``, }, { cty.NullVal(cty.EmptyObject).Mark(marks.Sensitive), ``, }, { // The inner sensitive remains afterwards cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}).Mark(marks.Sensitive), ``, }, // Passing a value that is already non-sensitive is an error, // because this function should always be used with specific // intention, not just as a "make everything visible" hammer. { cty.NumberIntVal(1), `the given value is not sensitive, so this call is redundant`, }, { cty.NullVal(cty.String), `the given value is not sensitive, so this call is redundant`, }, // Unknown values may become sensitive once they are known, so we // permit them to be marked nonsensitive. { cty.DynamicVal, ``, }, { cty.UnknownVal(cty.String), ``, }, } for _, test := range tests { t.Run(fmt.Sprintf("nonsensitive(%#v)", test.Input), func(t *testing.T) { got, err := Nonsensitive(test.Input) if test.WantErr != "" { if err == nil { t.Fatal("succeeded; want error") } if got, want := err.Error(), test.WantErr; got != want { t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) } return } else if err != nil { t.Fatalf("unexpected error: %s", err) } if got.HasMark(marks.Sensitive) { t.Errorf("result is still marked sensitive") } wantRaw, _ := test.Input.Unmark() if !got.RawEquals(wantRaw) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Input) } }) } }