github.com/opentofu/opentofu@v1.7.1/internal/lang/funcs/sensitive_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package funcs 7 8 import ( 9 "fmt" 10 "testing" 11 12 "github.com/opentofu/opentofu/internal/lang/marks" 13 "github.com/zclconf/go-cty/cty" 14 ) 15 16 func TestSensitive(t *testing.T) { 17 tests := []struct { 18 Input cty.Value 19 WantErr string 20 }{ 21 { 22 cty.NumberIntVal(1), 23 ``, 24 }, 25 { 26 // Unknown values stay unknown while becoming sensitive 27 cty.UnknownVal(cty.String), 28 ``, 29 }, 30 { 31 // Null values stay unknown while becoming sensitive 32 cty.NullVal(cty.String), 33 ``, 34 }, 35 { 36 // DynamicVal can be marked as sensitive 37 cty.DynamicVal, 38 ``, 39 }, 40 { 41 // The marking is shallow only 42 cty.ListVal([]cty.Value{cty.NumberIntVal(1)}), 43 ``, 44 }, 45 { 46 // A value already marked is allowed and stays marked 47 cty.NumberIntVal(1).Mark(marks.Sensitive), 48 ``, 49 }, 50 { 51 // A value with some non-standard mark gets "fixed" to be marked 52 // with the standard "sensitive" mark. (This situation occurring 53 // would imply an inconsistency/bug elsewhere, so we're just 54 // being robust about it here.) 55 cty.NumberIntVal(1).Mark("bloop"), 56 ``, 57 }, 58 { 59 // A value deep already marked is allowed and stays marked, 60 // _and_ we'll also mark the outer collection as sensitive. 61 cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}), 62 ``, 63 }, 64 } 65 66 for _, test := range tests { 67 t.Run(fmt.Sprintf("sensitive(%#v)", test.Input), func(t *testing.T) { 68 got, err := Sensitive(test.Input) 69 70 if test.WantErr != "" { 71 if err == nil { 72 t.Fatal("succeeded; want error") 73 } 74 if got, want := err.Error(), test.WantErr; got != want { 75 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 76 } 77 return 78 } else if err != nil { 79 t.Fatalf("unexpected error: %s", err) 80 } 81 82 if !got.HasMark(marks.Sensitive) { 83 t.Errorf("result is not marked sensitive") 84 } 85 86 gotRaw, gotMarks := got.Unmark() 87 if len(gotMarks) != 1 { 88 // We're only expecting to have the "sensitive" mark we checked 89 // above. Any others are an error, even if they happen to 90 // appear alongside "sensitive". (We might change this rule 91 // if someday we decide to use marks for some additional 92 // unrelated thing in OpenTofu, but currently we assume that 93 // _all_ marks imply sensitive, and so returning any other 94 // marks would be confusing.) 95 t.Errorf("extraneous marks %#v", gotMarks) 96 } 97 98 // Disregarding shallow marks, the result should have the same 99 // effective value as the input. 100 wantRaw, _ := test.Input.Unmark() 101 if !gotRaw.RawEquals(wantRaw) { 102 t.Errorf("wrong unmarked result\ngot: %#v\nwant: %#v", got, wantRaw) 103 } 104 }) 105 } 106 } 107 108 func TestNonsensitive(t *testing.T) { 109 tests := []struct { 110 Input cty.Value 111 WantErr string 112 }{ 113 { 114 cty.NumberIntVal(1).Mark(marks.Sensitive), 115 ``, 116 }, 117 { 118 cty.DynamicVal.Mark(marks.Sensitive), 119 ``, 120 }, 121 { 122 cty.UnknownVal(cty.String).Mark(marks.Sensitive), 123 ``, 124 }, 125 { 126 cty.NullVal(cty.EmptyObject).Mark(marks.Sensitive), 127 ``, 128 }, 129 { 130 // The inner sensitive remains afterwards 131 cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark(marks.Sensitive)}).Mark(marks.Sensitive), 132 ``, 133 }, 134 135 // Passing a value that is already non-sensitive is not an error, 136 // as this function may be used with specific to ensure that all 137 // values are indeed non-sensitive 138 { 139 cty.NumberIntVal(1), 140 ``, 141 }, 142 { 143 cty.NullVal(cty.String), 144 ``, 145 }, 146 147 // Unknown values may become sensitive once they are known, so we 148 // permit them to be marked nonsensitive. 149 { 150 cty.DynamicVal, 151 ``, 152 }, 153 { 154 cty.UnknownVal(cty.String), 155 ``, 156 }, 157 } 158 159 for _, test := range tests { 160 t.Run(fmt.Sprintf("nonsensitive(%#v)", test.Input), func(t *testing.T) { 161 got, err := Nonsensitive(test.Input) 162 163 if test.WantErr != "" { 164 if err == nil { 165 t.Fatal("succeeded; want error") 166 } 167 if got, want := err.Error(), test.WantErr; got != want { 168 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 169 } 170 return 171 } else if err != nil { 172 t.Fatalf("unexpected error: %s", err) 173 } 174 175 if got.HasMark(marks.Sensitive) { 176 t.Errorf("result is still marked sensitive") 177 } 178 wantRaw, _ := test.Input.Unmark() 179 if !got.RawEquals(wantRaw) { 180 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Input) 181 } 182 }) 183 } 184 } 185 186 func TestIsSensitive(t *testing.T) { 187 tests := []struct { 188 Input cty.Value 189 IsSensitive bool 190 }{ 191 { 192 cty.NumberIntVal(1).Mark(marks.Sensitive), 193 true, 194 }, 195 { 196 cty.NumberIntVal(1), 197 false, 198 }, 199 { 200 cty.DynamicVal.Mark(marks.Sensitive), 201 true, 202 }, 203 { 204 cty.DynamicVal, 205 false, 206 }, 207 { 208 cty.UnknownVal(cty.String).Mark(marks.Sensitive), 209 true, 210 }, 211 { 212 cty.UnknownVal(cty.String), 213 false, 214 }, 215 { 216 cty.NullVal(cty.EmptyObject).Mark(marks.Sensitive), 217 true, 218 }, 219 { 220 cty.NullVal(cty.EmptyObject), 221 false, 222 }, 223 } 224 225 for _, test := range tests { 226 t.Run(fmt.Sprintf("issensitive(%#v)", test.Input), func(t *testing.T) { 227 got, err := IsSensitive(test.Input) 228 229 if err != nil { 230 t.Fatalf("unexpected error: %s", err) 231 } 232 233 if got.Equals(cty.BoolVal(test.IsSensitive)).False() { 234 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, cty.BoolVal(test.IsSensitive)) 235 } 236 }) 237 } 238 }